diff --git a/DEPS b/DEPS
index da2a9e0..6185422 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': '7e6e8430ad10f7ec20095b857dd14eb9525c5872',
+  'v8_revision': '86e7f46cd00bb3d18409576a62859e9e8757510b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -64,7 +64,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '603a31d0c509c7ce3c2709dcb5377a78e6ce4815',
+  'pdfium_revision': 'd321ef9bb7143e393deb9b0ff7420c96db6a0e4c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling openmax_dl
   # and whatever else without interference from each other.
@@ -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': 'c9212cbf522072a1011063b212da5ee7baeb56df',
+  'catapult_revision': '59a182b2a69435dd977052279c359784e9744a97',
   # 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/base/message_loop/message_pump_fuchsia.cc b/base/message_loop/message_pump_fuchsia.cc
index c69cd28..83fe291f 100644
--- a/base/message_loop/message_pump_fuchsia.cc
+++ b/base/message_loop/message_pump_fuchsia.cc
@@ -17,7 +17,8 @@
 
 MessagePumpFuchsia::FileDescriptorWatcher::~FileDescriptorWatcher() {
   StopWatchingFileDescriptor();
-  __mxio_release(io_);
+  if (io_)
+    __mxio_release(io_);
   if (was_destroyed_) {
     DCHECK(!*was_destroyed_);
     *was_destroyed_ = true;
@@ -68,11 +69,14 @@
       return false;
   }
 
+  controller->io_ = __mxio_fd_to_io(fd);
+  if (!controller->io_)
+    return false;
+
   controller->watcher_ = delegate;
   controller->fd_ = fd;
   controller->desired_events_ = events;
 
-  controller->io_ = __mxio_fd_to_io(fd);
   uint32_t signals;
   __mxio_wait_begin(controller->io_, events, &controller->handle_, &signals);
   if (controller->handle_ == MX_HANDLE_INVALID)
diff --git a/build/config/compiler/compiler.gni b/build/config/compiler/compiler.gni
index 46b048f..83369d9 100644
--- a/build/config/compiler/compiler.gni
+++ b/build/config/compiler/compiler.gni
@@ -122,13 +122,12 @@
 declare_args() {
   # Whether to use the gold linker from binutils instead of lld or bfd.
   use_gold =
-      !use_lld && !(is_chromecast && is_linux &&
-                    (current_cpu == "arm" || current_cpu == "mipsel")) &&
-      ((is_linux && (current_cpu == "x64" || current_cpu == "x86" ||
-                     current_cpu == "arm" || current_cpu == "mipsel")) ||
-       (is_android && (current_cpu == "x86" || current_cpu == "x64" ||
-                       current_cpu == "arm" || current_cpu == "arm64")) ||
-       is_fuchsia)
+      (!use_lld && !(is_chromecast && is_linux &&
+                     (current_cpu == "arm" || current_cpu == "mipsel")) &&
+       (is_linux && (current_cpu == "x64" || current_cpu == "x86" ||
+                     current_cpu == "arm" || current_cpu == "mipsel"))) ||
+      (is_android && (current_cpu == "x86" || current_cpu == "x64" ||
+                      current_cpu == "arm" || current_cpu == "arm64"))
 }
 
 # If it wasn't manually set, set to an appropriate default.
diff --git a/build/config/fuchsia/BUILD.gn b/build/config/fuchsia/BUILD.gn
index 1f8ee1a0..a80ff53 100644
--- a/build/config/fuchsia/BUILD.gn
+++ b/build/config/fuchsia/BUILD.gn
@@ -32,6 +32,13 @@
     rebase_path(fuchsia_sdk, root_build_dir) + "/toolchain_libs/clang/5.0.0",
   ]
 
+  ldflags += [
+    # The stack defaults to 80k on Fuchsia, but on other platforms it's much
+    # higher, so a variety of code assumes more will be available. Raise to 8M
+    # which matches e.g. macOS.
+    "-Wl,-z,stack-size=0x800000",
+  ]
+
   libs = [
     "mxio",
     "magenta",
diff --git a/build/toolchain/toolchain.gni b/build/toolchain/toolchain.gni
index 8513f83..70e0fa9 100644
--- a/build/toolchain/toolchain.gni
+++ b/build/toolchain/toolchain.gni
@@ -63,7 +63,7 @@
 
   # Set to true to use lld, the LLVM linker. This flag may be used on Windows
   # or Linux.
-  use_lld = (is_win && host_os != "win") ||
+  use_lld = (is_win && host_os != "win") || is_fuchsia ||
             (allow_posix_link_time_opt && target_os == "linux" &&
              !is_chromeos && target_cpu == "x64")
 }
diff --git a/build/vs_toolchain.py b/build/vs_toolchain.py
index 31af518..20cc2de 100755
--- a/build/vs_toolchain.py
+++ b/build/vs_toolchain.py
@@ -342,8 +342,8 @@
     # Update 3 final with patches with 10.0.14393.0 SDK.
     return ['d3cb0e37bdd120ad0ac4650b674b09e81be45616']
   if env_version == '2017':
-    # VS 2017 Update 3 Preview 1 with 10.0.14393.0 SDK and patched statreg.h.
-    return ['3915730f76bd9c6155aed871b944b0a25c18f15e']
+    # VS 2017 Update 3 Preview 2 with 10.0.15063.0 SDK and patched event.h.
+    return ['425bd64734a387734dfcf445b285a7c5073e4262']
   raise Exception('Unsupported VS version %s' % env_version)
 
 
diff --git a/cc/blink/web_content_layer_impl.cc b/cc/blink/web_content_layer_impl.cc
index ff863e6..82d152a 100644
--- a/cc/blink/web_content_layer_impl.cc
+++ b/cc/blink/web_content_layer_impl.cc
@@ -59,9 +59,14 @@
   return layer_.get();
 }
 
-void WebContentLayerImpl::SetAllowTransformedRasterization(bool allowed) {
+void WebContentLayerImpl::SetTransformedRasterizationAllowed(bool allowed) {
   static_cast<PictureLayer*>(layer_->layer())
-      ->SetAllowTransformedRasterization(allowed);
+      ->SetTransformedRasterizationAllowed(allowed);
+}
+
+bool WebContentLayerImpl::TransformedRasterizationAllowed() const {
+  return static_cast<PictureLayer*>(layer_->layer())
+      ->transformed_rasterization_allowed();
 }
 
 gfx::Rect WebContentLayerImpl::PaintableRegion() {
diff --git a/cc/blink/web_content_layer_impl.h b/cc/blink/web_content_layer_impl.h
index 0aa9bfd..046b79d 100644
--- a/cc/blink/web_content_layer_impl.h
+++ b/cc/blink/web_content_layer_impl.h
@@ -30,7 +30,8 @@
 
   // WebContentLayer implementation.
   blink::WebLayer* Layer() override;
-  void SetAllowTransformedRasterization(bool) override;
+  void SetTransformedRasterizationAllowed(bool) override;
+  bool TransformedRasterizationAllowed() const override;
 
  protected:
   // ContentLayerClient implementation.
diff --git a/cc/layers/picture_layer.cc b/cc/layers/picture_layer.cc
index f94dba2..39f7c65a 100644
--- a/cc/layers/picture_layer.cc
+++ b/cc/layers/picture_layer.cc
@@ -207,11 +207,11 @@
   SetNeedsCommit();
 }
 
-void PictureLayer::SetAllowTransformedRasterization(bool allowed) {
-  if (picture_layer_inputs_.allow_transformed_rasterization == allowed)
+void PictureLayer::SetTransformedRasterizationAllowed(bool allowed) {
+  if (picture_layer_inputs_.transformed_rasterization_allowed == allowed)
     return;
 
-  picture_layer_inputs_.allow_transformed_rasterization = allowed;
+  picture_layer_inputs_.transformed_rasterization_allowed = allowed;
   SetNeedsCommit();
 }
 
@@ -249,7 +249,7 @@
 }
 
 bool PictureLayer::ShouldUseTransformedRasterization() const {
-  if (!picture_layer_inputs_.allow_transformed_rasterization)
+  if (!picture_layer_inputs_.transformed_rasterization_allowed)
     return false;
 
   // Background color overfill is undesirable with transformed rasterization.
diff --git a/cc/layers/picture_layer.h b/cc/layers/picture_layer.h
index 5b8adf0b..af63e2f3 100644
--- a/cc/layers/picture_layer.h
+++ b/cc/layers/picture_layer.h
@@ -28,7 +28,10 @@
     return picture_layer_inputs_.nearest_neighbor;
   }
 
-  void SetAllowTransformedRasterization(bool allowed);
+  void SetTransformedRasterizationAllowed(bool allowed);
+  bool transformed_rasterization_allowed() const {
+    return picture_layer_inputs_.transformed_rasterization_allowed;
+  }
 
   // Layer interface.
   std::unique_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override;
@@ -59,7 +62,7 @@
 
     ContentLayerClient* client = nullptr;
     bool nearest_neighbor = false;
-    bool allow_transformed_rasterization = false;
+    bool transformed_rasterization_allowed = false;
     gfx::Rect recorded_viewport;
     scoped_refptr<DisplayItemList> display_list;
     size_t painter_reported_memory_usage = 0;
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 435cc8d..a845a8d 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -2850,16 +2850,17 @@
         (settings_.is_layer_tree_for_subframe ||
          (!scrolling_node->scrolls_outer_viewport &&
           !scrolling_node->scrolls_inner_viewport))) {
+      int size = scrolling_node->scroll_clip_layer_bounds.GetCheckedArea()
+                     .ValueOrDefault(std::numeric_limits<int>::max());
+      DCHECK_GT(size, 0);
       if (IsWheelBasedScroll(type)) {
-        UMA_HISTOGRAM_CUSTOM_COUNTS(
-            "Event.Scroll.ScrollerSize.OnScroll_Wheel",
-            scrolling_node->scroll_clip_layer_bounds.GetArea(), 1,
-            kScrollerSizeLargestBucket, kScrollerSizeBucketCount);
+        UMA_HISTOGRAM_CUSTOM_COUNTS("Event.Scroll.ScrollerSize.OnScroll_Wheel",
+                                    size, 1, kScrollerSizeLargestBucket,
+                                    kScrollerSizeBucketCount);
       } else {
-        UMA_HISTOGRAM_CUSTOM_COUNTS(
-            "Event.Scroll.ScrollerSize.OnScroll_Touch",
-            scrolling_node->scroll_clip_layer_bounds.GetArea(), 1,
-            kScrollerSizeLargestBucket, kScrollerSizeBucketCount);
+        UMA_HISTOGRAM_CUSTOM_COUNTS("Event.Scroll.ScrollerSize.OnScroll_Touch",
+                                    size, 1, kScrollerSizeLargestBucket,
+                                    kScrollerSizeBucketCount);
       }
     }
   }
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index ae511f7b..7a68819 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -188,7 +188,6 @@
     "//components/ntp_tiles/android:ntp_tiles_java",
     "//components/offline_items_collection/core:core_java",
     "//components/payments/content/android:java",
-    "//components/payments/mojom:mojom_java",
     "//components/payments/mojom:mojom_parser_java",
     "//components/policy/android:policy_java",
     "//components/precache/android:precache_java",
@@ -371,7 +370,6 @@
     "//components/minidump_uploader:minidump_uploader_java",
     "//components/offline_items_collection/core:core_java",
     "//components/payments/content/android:java",
-    "//components/payments/mojom:mojom_java",
     "//components/payments/mojom:mojom_parser_java",
     "//components/signin/core/browser/android:java",
     "//components/signin/core/browser/android:signin_java_test_support",
@@ -472,7 +470,6 @@
     "//components/navigation_interception/android:navigation_interception_java",
     "//components/offline_items_collection/core:core_java",
     "//components/payments/content/android:java",
-    "//components/payments/mojom:mojom_java",
     "//components/payments/mojom:mojom_parser_java",
     "//components/policy/android:policy_java",
     "//components/precache/android:precache_java",
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/locale/LocaleManager.java b/chrome/android/java/src/org/chromium/chrome/browser/locale/LocaleManager.java
index 41ffcfa..f22150ce 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/locale/LocaleManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/locale/LocaleManager.java
@@ -448,6 +448,11 @@
      * @return Whether we still have to check for whether search engine dialog is necessary.
      */
     public boolean needToCheckForSearchEnginePromo() {
+        if (ChromeFeatureList.isInitialized()
+                && !ChromeFeatureList.isEnabled(
+                           ChromeFeatureList.SEARCH_ENGINE_PROMO_EXISTING_DEVICE)) {
+            return false;
+        }
         int state = SEARCH_ENGINE_PROMO_SHOULD_CHECK;
         StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
         try {
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 21098432..1c35be8 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1564,8 +1564,6 @@
     "//components/password_manager/core/browser",
     "//components/password_manager/core/common",
     "//components/password_manager/sync/browser",
-    "//components/payments/mojom",
-    "//components/payments/mojom:mojom_payment_app",
     "//components/physical_web/eddystone",
     "//components/policy:generated",
     "//components/policy/core/browser",
diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS
index cfe68539..c649944 100644
--- a/chrome/browser/DEPS
+++ b/chrome/browser/DEPS
@@ -121,6 +121,8 @@
   "+third_party/WebKit/public/platform/modules/installation/installation.mojom.h",
   "+third_party/WebKit/public/platform/modules/installedapp/installed_app_provider.mojom.h",
   "+third_party/WebKit/public/platform/modules/notifications/WebNotificationConstants.h",
+  "+third_party/WebKit/public/platform/modules/payments/payment_app.mojom.h",
+  "+third_party/WebKit/public/platform/modules/payments/payment_request.mojom.h",
   "+third_party/WebKit/public/platform/modules/permissions/permission_status.mojom.h",
   "+third_party/WebKit/public/platform/modules/push_messaging/WebPushPermissionStatus.h",
   "+third_party/WebKit/public/platform/modules/remoteplayback/WebRemotePlaybackAvailability.h",
diff --git a/chrome/browser/android/payments/service_worker_payment_app_bridge.cc b/chrome/browser/android/payments/service_worker_payment_app_bridge.cc
index 98a4f22..c909e028 100644
--- a/chrome/browser/android/payments/service_worker_payment_app_bridge.cc
+++ b/chrome/browser/android/payments/service_worker_payment_app_bridge.cc
@@ -10,12 +10,12 @@
 #include "base/android/jni_string.h"
 #include "base/android/scoped_java_ref.h"
 #include "chrome/browser/profiles/profile_manager.h"
-#include "components/payments/mojom/payment_app.mojom.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/payment_app_provider.h"
 #include "content/public/browser/stored_payment_instrument.h"
 #include "content/public/browser/web_contents.h"
 #include "jni/ServiceWorkerPaymentAppBridge_jni.h"
+#include "third_party/WebKit/public/platform/modules/payments/payment_app.mojom.h"
 #include "ui/gfx/android/java_bitmap.h"
 
 namespace {
diff --git a/chrome/browser/android/tab_web_contents_delegate_android.cc b/chrome/browser/android/tab_web_contents_delegate_android.cc
index 7bea27a..3530b0e 100644
--- a/chrome/browser/android/tab_web_contents_delegate_android.cc
+++ b/chrome/browser/android/tab_web_contents_delegate_android.cc
@@ -343,9 +343,8 @@
        params.disposition == WindowOpenDisposition::NEW_FOREGROUND_TAB ||
        params.disposition == WindowOpenDisposition::NEW_BACKGROUND_TAB ||
        params.disposition == WindowOpenDisposition::NEW_WINDOW) &&
-      !params.user_gesture &&
-      !base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kDisablePopupBlocking)) {
+      PopupBlockerTabHelper::ConsiderForPopupBlocking(source,
+                                                      params.user_gesture)) {
     if (popup_blocker_helper->MaybeBlockPopup(nav_params,
                                               blink::mojom::WindowFeatures())) {
       return nullptr;
diff --git a/chrome/browser/android/vr_shell/ui_elements/screen_dimmer.cc b/chrome/browser/android/vr_shell/ui_elements/screen_dimmer.cc
index ab8c9255..26b25da 100644
--- a/chrome/browser/android/vr_shell/ui_elements/screen_dimmer.cc
+++ b/chrome/browser/android/vr_shell/ui_elements/screen_dimmer.cc
@@ -8,6 +8,8 @@
 #include "chrome/browser/android/vr_shell/ui_element_renderer.h"
 #include "device/vr/vr_math.h"
 #include "third_party/skia/include/core/SkColor.h"
+#include "ui/gfx/geometry/quaternion.h"
+#include "ui/gfx/transform.h"
 
 namespace vr_shell {
 
@@ -24,15 +26,14 @@
 }
 
 void ScreenDimmer::Render(UiElementRenderer* renderer,
-                          vr::Mat4f view_proj_matrix) const {
-  vr::Mat4f m;
-  vr::SetIdentityM(&m);
-  vr::ScaleM(m, {2.0f, 2.0f, 1.0f}, &m);
+                          gfx::Transform view_proj_matrix) const {
+  gfx::Transform m;
+  m.Scale3d(2.0f, 2.0f, 1.0f);
 
   // Always use normal scheme for dimmer.
   const ColorScheme& color_scheme =
       ColorScheme::GetColorScheme(ColorScheme::kModeNormal);
-  renderer->DrawGradientQuad(m, color_scheme.dimmer_outer,
+  renderer->DrawGradientQuad(vr::ToMat4F(m), color_scheme.dimmer_outer,
                              color_scheme.dimmer_inner, kDimmerOpacity);
 }
 
diff --git a/chrome/browser/android/vr_shell/ui_elements/screen_dimmer.h b/chrome/browser/android/vr_shell/ui_elements/screen_dimmer.h
index 886e5199..47dbac1 100644
--- a/chrome/browser/android/vr_shell/ui_elements/screen_dimmer.h
+++ b/chrome/browser/android/vr_shell/ui_elements/screen_dimmer.h
@@ -19,7 +19,7 @@
 
   // UiElement interface.
   void Render(UiElementRenderer* renderer,
-              vr::Mat4f view_proj_matrix) const final;
+              gfx::Transform view_proj_matrix) const final;
 
   DISALLOW_COPY_AND_ASSIGN(ScreenDimmer);
 };
diff --git a/chrome/browser/android/vr_shell/ui_elements/textured_element.cc b/chrome/browser/android/vr_shell/ui_elements/textured_element.cc
index 1f62cdc..842d09f3 100644
--- a/chrome/browser/android/vr_shell/ui_elements/textured_element.cc
+++ b/chrome/browser/android/vr_shell/ui_elements/textured_element.cc
@@ -8,7 +8,9 @@
 #include "cc/paint/skia_paint_canvas.h"
 #include "chrome/browser/android/vr_shell/textures/ui_texture.h"
 #include "chrome/browser/android/vr_shell/ui_element_renderer.h"
+#include "device/vr/vr_math.h"
 #include "third_party/skia/include/core/SkSurface.h"
+#include "ui/gfx/geometry/rect_f.h"
 
 namespace vr_shell {
 
@@ -40,14 +42,14 @@
 }
 
 void TexturedElement::Render(UiElementRenderer* renderer,
-                             vr::Mat4f view_proj_matrix) const {
+                             gfx::Transform view_proj_matrix) const {
   if (!initialized_)
     return;
   gfx::SizeF drawn_size = GetTexture()->GetDrawnSize();
   gfx::RectF copy_rect(0, 0, drawn_size.width() / texture_size_.width(),
                        drawn_size.height() / texture_size_.height());
-  renderer->DrawTexturedQuad(texture_handle_, view_proj_matrix, copy_rect,
-                             opacity());
+  renderer->DrawTexturedQuad(texture_handle_, vr::ToMat4F(view_proj_matrix),
+                             copy_rect, opacity());
 }
 
 void TexturedElement::Flush(SkSurface* surface) {
diff --git a/chrome/browser/android/vr_shell/ui_elements/textured_element.h b/chrome/browser/android/vr_shell/ui_elements/textured_element.h
index 07b04e7..0347b69 100644
--- a/chrome/browser/android/vr_shell/ui_elements/textured_element.h
+++ b/chrome/browser/android/vr_shell/ui_elements/textured_element.h
@@ -7,6 +7,7 @@
 
 #include "base/macros.h"
 #include "chrome/browser/android/vr_shell/ui_elements/ui_element.h"
+#include "ui/gfx/geometry/size.h"
 #include "ui/gl/gl_bindings.h"
 
 class SkSurface;
@@ -27,7 +28,7 @@
 
   // UiElement interface.
   void Render(UiElementRenderer* renderer,
-              vr::Mat4f view_proj_matrix) const final;
+              gfx::Transform view_proj_matrix) const final;
 
  protected:
   virtual UiTexture* GetTexture() const = 0;
diff --git a/chrome/browser/android/vr_shell/ui_elements/ui_element.cc b/chrome/browser/android/vr_shell/ui_elements/ui_element.cc
index 10ce389..f45f82b 100644
--- a/chrome/browser/android/vr_shell/ui_elements/ui_element.cc
+++ b/chrome/browser/android/vr_shell/ui_elements/ui_element.cc
@@ -10,7 +10,6 @@
 #include "base/time/time.h"
 #include "chrome/browser/android/vr_shell/animation.h"
 #include "chrome/browser/android/vr_shell/easing.h"
-#include "device/vr/vr_math.h"
 
 namespace vr_shell {
 
@@ -41,61 +40,59 @@
 }
 
 void Transform::MakeIdentity() {
-  vr::SetIdentityM(&to_world);
+  to_world.MakeIdentity();
 }
 
-void Transform::Rotate(const vr::Quatf& quat) {
+void Transform::Rotate(const gfx::Quaternion& q) {
   // TODO(klausw): use specialized rotation code? Constructing the matrix
   // via axis-angle quaternion is inefficient.
-  vr::Mat4f forward;
-  vr::QuatToMatrix(quat, &forward);
-  vr::MatrixMul(forward, to_world, &to_world);
-}
-
-void Transform::Rotate(const vr::RotationAxisAngle& axis_angle) {
-  Rotate(vr::QuatFromAxisAngle(axis_angle));
+  to_world.ConcatTransform(gfx::Transform(q));
 }
 
 void Transform::Translate(const gfx::Vector3dF& translation) {
-  vr::TranslateM(to_world, translation, &to_world);
+  to_world.matrix().postTranslate(translation.x(), translation.y(),
+                                  translation.z());
 }
 
 void Transform::Scale(const gfx::Vector3dF& scale) {
-  vr::ScaleM(to_world, scale, &to_world);
+  to_world.matrix().postScale(scale.x(), scale.y(), scale.z());
 }
 
-const vr::Mat4f& WorldRectangle::TransformMatrix() const {
+const gfx::Transform& WorldRectangle::TransformMatrix() const {
   return transform_.to_world;
 }
 
 gfx::Point3F WorldRectangle::GetCenter() const {
-  const gfx::Point3F kOrigin(0.0f, 0.0f, 0.0f);
-  return kOrigin + vr::GetTranslation(transform_.to_world);
+  gfx::Point3F center;
+  transform_.to_world.TransformPoint(&center);
+  return center;
 }
 
 gfx::PointF WorldRectangle::GetUnitRectangleCoordinates(
     const gfx::Point3F& world_point) const {
   // TODO(acondor): Simplify the math in this function.
-  const vr::Mat4f& transform = transform_.to_world;
-  gfx::Vector3dF origin =
-      vr::MatrixVectorMul(transform, gfx::Vector3dF(0, 0, 0));
-  gfx::Vector3dF x_axis =
-      vr::MatrixVectorMul(transform, gfx::Vector3dF(1, 0, 0));
-  gfx::Vector3dF y_axis =
-      vr::MatrixVectorMul(transform, gfx::Vector3dF(0, 1, 0));
-  x_axis.Subtract(origin);
-  y_axis.Subtract(origin);
-  gfx::Point3F point = world_point - origin;
-  gfx::Vector3dF v_point(point.x(), point.y(), point.z());
-
-  float x = gfx::DotProduct(v_point, x_axis) / gfx::DotProduct(x_axis, x_axis);
-  float y = gfx::DotProduct(v_point, y_axis) / gfx::DotProduct(y_axis, y_axis);
+  gfx::Point3F origin(0, 0, 0);
+  gfx::Vector3dF x_axis(1, 0, 0);
+  gfx::Vector3dF y_axis(0, 1, 0);
+  transform_.to_world.TransformPoint(&origin);
+  transform_.to_world.TransformVector(&x_axis);
+  transform_.to_world.TransformVector(&y_axis);
+  gfx::Vector3dF origin_to_world = world_point - origin;
+  float x = gfx::DotProduct(origin_to_world, x_axis) /
+            gfx::DotProduct(x_axis, x_axis);
+  float y = gfx::DotProduct(origin_to_world, y_axis) /
+            gfx::DotProduct(y_axis, y_axis);
   return gfx::PointF(x, y);
 }
 
 gfx::Vector3dF WorldRectangle::GetNormal() const {
-  const gfx::Vector3dF kNormalOrig = {0.0f, 0.0f, -1.0f};
-  return vr::MatrixVectorRotate(transform_.to_world, kNormalOrig);
+  gfx::Vector3dF x_axis(1, 0, 0);
+  gfx::Vector3dF y_axis(0, 1, 0);
+  transform_.to_world.TransformVector(&x_axis);
+  transform_.to_world.TransformVector(&y_axis);
+  gfx::Vector3dF normal = CrossProduct(y_axis, x_axis);
+  normal.GetNormalized(&normal);
+  return normal;
 }
 
 bool WorldRectangle::GetRayDistance(const gfx::Point3F& ray_origin,
@@ -105,12 +102,12 @@
                              distance);
 }
 
-UiElement::UiElement() = default;
+UiElement::UiElement() : rotation_(gfx::Vector3dF(1, 0, 0), 0) {}
 
 UiElement::~UiElement() = default;
 
 void UiElement::Render(UiElementRenderer* renderer,
-                       vr::Mat4f view_proj_matrix) const {
+                       gfx::Transform view_proj_matrix) const {
   NOTREACHED();
 }
 
@@ -147,10 +144,10 @@
           animation.from.push_back(scale_.z());
           break;
         case Animation::ROTATION:
-          animation.from.push_back(rotation_.x);
-          animation.from.push_back(rotation_.y);
-          animation.from.push_back(rotation_.z);
-          animation.from.push_back(rotation_.angle);
+          animation.from.push_back(rotation_.x());
+          animation.from.push_back(rotation_.y());
+          animation.from.push_back(rotation_.z());
+          animation.from.push_back(rotation_.w());
           break;
         case Animation::TRANSLATION:
           animation.from.push_back(translation_.x());
@@ -189,10 +186,10 @@
         break;
       case Animation::ROTATION:
         CHECK_EQ(animation.from.size(), 4u);
-        rotation_.x = values[0];
-        rotation_.y = values[1];
-        rotation_.z = values[2];
-        rotation_.angle = values[3];
+        rotation_.set_x(values[0]);
+        rotation_.set_y(values[1]);
+        rotation_.set_z(values[2]);
+        rotation_.set_w(values[3]);
         break;
       case Animation::TRANSLATION:
         CHECK_EQ(animation.from.size(), 3u);
diff --git a/chrome/browser/android/vr_shell/ui_elements/ui_element.h b/chrome/browser/android/vr_shell/ui_elements/ui_element.h
index 0a5b354..e4e0942b 100644
--- a/chrome/browser/android/vr_shell/ui_elements/ui_element.h
+++ b/chrome/browser/android/vr_shell/ui_elements/ui_element.h
@@ -12,8 +12,11 @@
 #include "base/macros.h"
 #include "chrome/browser/android/vr_shell/color_scheme.h"
 #include "chrome/browser/android/vr_shell/ui_elements/ui_element_debug_id.h"
-#include "device/vr/vr_types.h"
 #include "third_party/skia/include/core/SkColor.h"
+#include "ui/gfx/geometry/point3_f.h"
+#include "ui/gfx/geometry/quaternion.h"
+#include "ui/gfx/geometry/vector3d_f.h"
+#include "ui/gfx/transform.h"
 
 namespace base {
 class TimeTicks;
@@ -57,17 +60,16 @@
   explicit Transform(const Transform& other);
 
   void MakeIdentity();
-  void Rotate(const vr::Quatf& quat);
-  void Rotate(const vr::RotationAxisAngle& axis_angle);
+  void Rotate(const gfx::Quaternion& quat);
   void Translate(const gfx::Vector3dF& translation);
   void Scale(const gfx::Vector3dF& scale);
 
-  vr::Mat4f to_world;
+  gfx::Transform to_world;
 };
 
 class WorldRectangle {
  public:
-  const vr::Mat4f& TransformMatrix() const;
+  const gfx::Transform& TransformMatrix() const;
   Transform* mutable_transform() { return &transform_; }
 
   gfx::Point3F GetCenter() const;
@@ -108,7 +110,7 @@
   bool IsHitTestable() const;
 
   virtual void Render(UiElementRenderer* renderer,
-                      vr::Mat4f view_proj_matrix) const;
+                      gfx::Transform view_proj_matrix) const;
 
   virtual void Initialize();
 
@@ -167,10 +169,8 @@
   void set_scale(const gfx::Vector3dF& scale) { scale_ = scale; }
 
   // The rotation of the object, and its children.
-  vr::RotationAxisAngle rotation() const { return rotation_; }
-  void set_rotation(const vr::RotationAxisAngle& rotation) {
-    rotation_ = rotation;
-  }
+  gfx::Quaternion rotation() const { return rotation_; }
+  void set_rotation(const gfx::Quaternion& rotation) { rotation_ = rotation; }
 
   // The translation of the object, and its children.  Translation is applied
   // after rotation and scaling.
@@ -227,11 +227,13 @@
   void set_draw_phase(int draw_phase) { draw_phase_ = draw_phase; }
 
   // This transform can be used by children to derive position of its parent.
-  Transform& inheritable_transform() { return inheritable_transform_; }
-  const Transform& inheritable_transform() const {
+  vr_shell::Transform& inheritable_transform() {
     return inheritable_transform_;
   }
-  void set_inheritable_transform(const Transform& transform) {
+  const vr_shell::Transform& inheritable_transform() const {
+    return inheritable_transform_;
+  }
+  void set_inheritable_transform(const vr_shell::Transform& transform) {
     inheritable_transform_ = transform;
   }
 
@@ -285,7 +287,7 @@
   gfx::Vector3dF scale_ = {1.0f, 1.0f, 1.0f};
 
   // The rotation of the object, and its children.
-  vr::RotationAxisAngle rotation_ = {1.0f, 0.0f, 0.0f, 0.0f};
+  gfx::Quaternion rotation_;
 
   // The translation of the object, and its children.  Translation is applied
   // after rotation and scaling.
@@ -317,7 +319,7 @@
   int draw_phase_ = 1;
 
   // This transform can be used by children to derive position of its parent.
-  Transform inheritable_transform_;
+  vr_shell::Transform inheritable_transform_;
 
   // A flag usable during transformation calculates to avoid duplicate work.
   bool dirty_ = false;
@@ -325,7 +327,7 @@
   // An identifier used for testing and debugging, in lieu of a string.
   UiElementDebugId debug_id_ = UiElementDebugId::kNone;
 
-  Transform transform_;
+  vr_shell::Transform transform_;
 
   ColorScheme::Mode mode_ = ColorScheme::kModeNormal;
 
diff --git a/chrome/browser/android/vr_shell/ui_elements/ui_element_unittest.cc b/chrome/browser/android/vr_shell/ui_elements/ui_element_unittest.cc
index a511cd5..e6ecdbb0 100644
--- a/chrome/browser/android/vr_shell/ui_elements/ui_element_unittest.cc
+++ b/chrome/browser/android/vr_shell/ui_elements/ui_element_unittest.cc
@@ -9,7 +9,6 @@
 #include "base/macros.h"
 #include "chrome/browser/android/vr_shell/animation.h"
 #include "chrome/browser/android/vr_shell/easing.h"
-#include "device/vr/vr_types.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 #define EXPECT_VEC3F_EQ(a, b)    \
@@ -23,11 +22,11 @@
   EXPECT_FLOAT_EQ(a.width(), b.width()); \
   EXPECT_FLOAT_EQ(a.height(), b.height());
 
-#define EXPECT_ROTATION(a, b) \
-  EXPECT_FLOAT_EQ(a.x, b.x);  \
-  EXPECT_FLOAT_EQ(a.y, b.y);  \
-  EXPECT_FLOAT_EQ(a.z, b.z);  \
-  EXPECT_FLOAT_EQ(a.angle, b.angle);
+#define EXPECT_ROTATION(a, b)    \
+  EXPECT_FLOAT_EQ(a.x(), b.x()); \
+  EXPECT_FLOAT_EQ(a.y(), b.y()); \
+  EXPECT_FLOAT_EQ(a.z(), b.z()); \
+  EXPECT_FLOAT_EQ(a.w(), b.w());
 
 namespace vr_shell {
 
@@ -74,19 +73,19 @@
 
 TEST(UiElements, AnimateRotation) {
   UiElement rect;
-  rect.set_rotation({10, 100, 1000, 10000});
+  gfx::Quaternion from(gfx::Vector3dF(10, 100, 1000), 10000);
+  gfx::Quaternion to(gfx::Vector3dF(20, 200, 2000), 20000);
+  rect.set_rotation(from);
   std::unique_ptr<Animation> animation(
       new Animation(0, Animation::Property::ROTATION,
                     std::unique_ptr<easing::Easing>(new easing::Linear()),
-                    std::vector<float>(), {20, 200, 2000, 20000},
+                    std::vector<float>(), {to.x(), to.y(), to.z(), to.w()},
                     usToTicks(50000), usToDelta(10000)));
   rect.animations().emplace_back(std::move(animation));
   rect.Animate(usToTicks(50000));
-  EXPECT_ROTATION(vr::RotationAxisAngle({10, 100, 1000, 10000}),
-                  rect.rotation());
+  EXPECT_ROTATION(from, rect.rotation());
   rect.Animate(usToTicks(60000));
-  EXPECT_ROTATION(vr::RotationAxisAngle({20, 200, 2000, 20000}),
-                  rect.rotation());
+  EXPECT_ROTATION(to, rect.rotation());
 }
 
 TEST(UiElements, AnimationHasNoEffectBeforeScheduledStart) {
diff --git a/chrome/browser/android/vr_shell/ui_scene.cc b/chrome/browser/android/vr_shell/ui_scene.cc
index f3caf40..d67a011c 100644
--- a/chrome/browser/android/vr_shell/ui_scene.cc
+++ b/chrome/browser/android/vr_shell/ui_scene.cc
@@ -251,16 +251,17 @@
     ApplyAnchoring(*parent, element->x_anchoring(), element->y_anchoring(),
                    inheritable);
     ApplyRecursiveTransforms(parent);
-    vr::MatrixMul(parent->inheritable_transform().to_world,
-                  inheritable->to_world, &inheritable->to_world);
+    vr::Mat4f product;
+    vr::MatrixMul(vr::ToMat4F(parent->inheritable_transform().to_world),
+                  vr::ToMat4F(inheritable->to_world), &product);
+    inheritable->to_world = vr::ToTransform(product);
 
     element->set_computed_opacity(element->computed_opacity() *
                                   parent->opacity());
     element->set_computed_lock_to_fov(parent->lock_to_fov());
   }
 
-  vr::MatrixMul(inheritable->to_world, transform->to_world,
-                &transform->to_world);
+  transform->to_world = inheritable->to_world * transform->to_world;
   element->set_dirty(false);
 }
 
diff --git a/chrome/browser/android/vr_shell/ui_scene_manager.cc b/chrome/browser/android/vr_shell/ui_scene_manager.cc
index 055d2005..5421dda 100644
--- a/chrome/browser/android/vr_shell/ui_scene_manager.cc
+++ b/chrome/browser/android/vr_shell/ui_scene_manager.cc
@@ -152,7 +152,8 @@
   element->set_translation(
       gfx::Vector3dF(0, kWarningDistance * sin(kWarningAngleRadians),
                      -kWarningDistance * cos(kWarningAngleRadians)));
-  element->set_rotation({1.0f, 0, 0, kWarningAngleRadians});
+  element->set_rotation(
+      gfx::Quaternion(gfx::Vector3dF(1, 0, 0), kWarningAngleRadians));
   element->set_visible(false);
   element->set_hit_testable(false);
   element->set_lock_to_fov(true);
@@ -269,7 +270,7 @@
   element->set_id(AllocateId());
   element->set_size({kSceneSize, kSceneSize, 1.0});
   element->set_translation({0.0, -kSceneHeight / 2, 0.0});
-  element->set_rotation({1.0, 0.0, 0.0, -M_PI / 2.0});
+  element->set_rotation(gfx::Quaternion(gfx::Vector3dF(1, 0, 0), -M_PI / 2));
   element->set_fill(vr_shell::Fill::GRID_GRADIENT);
   element->set_draw_phase(0);
   element->set_gridline_count(kFloorGridlineCount);
@@ -283,7 +284,7 @@
   element->set_id(AllocateId());
   element->set_size({kSceneSize, kSceneSize, 1.0});
   element->set_translation({0.0, kSceneHeight / 2, 0.0});
-  element->set_rotation({1.0, 0.0, 0.0, M_PI / 2});
+  element->set_rotation(gfx::Quaternion(gfx::Vector3dF(1, 0, 0), M_PI / 2));
   element->set_fill(vr_shell::Fill::OPAQUE_GRADIENT);
   element->set_draw_phase(0);
   ceiling_ = element.get();
@@ -304,7 +305,8 @@
   url_bar->set_debug_id(kUrlBar);
   url_bar->set_id(AllocateId());
   url_bar->set_translation({0, kUrlBarVerticalOffset, -kUrlBarDistance});
-  url_bar->set_rotation({1.0, 0.0, 0.0, kUrlBarRotationRad});
+  url_bar->set_rotation(
+      gfx::Quaternion(gfx::Vector3dF(1, 0, 0), kUrlBarRotationRad));
   url_bar->set_size({kUrlBarWidth, kUrlBarHeight, 1});
   url_bar_ = url_bar.get();
   control_elements_.push_back(url_bar.get());
diff --git a/chrome/browser/android/vr_shell/ui_scene_unittest.cc b/chrome/browser/android/vr_shell/ui_scene_unittest.cc
index 2a14566..7e5ea2c 100644
--- a/chrome/browser/android/vr_shell/ui_scene_unittest.cc
+++ b/chrome/browser/android/vr_shell/ui_scene_unittest.cc
@@ -118,7 +118,7 @@
   element->set_id(0);
   element->set_size({1000, 1000, 1});
   element->set_scale({3, 3, 1});
-  element->set_rotation({0, 0, 1, M_PI / 2});
+  element->set_rotation(gfx::Quaternion(gfx::Vector3dF(0, 0, 1), M_PI / 2));
   element->set_translation({6, 1, 0});
   scene.AddUiElement(std::move(element));
 
@@ -128,7 +128,7 @@
   element->set_parent_id(0);
   element->set_size({1, 1, 1});
   element->set_scale({2, 2, 1});
-  element->set_rotation({0, 0, 1, M_PI / 2});
+  element->set_rotation(gfx::Quaternion(gfx::Vector3dF(0, 0, 1), M_PI / 2));
   element->set_translation({3, 0, 0});
   scene.AddUiElement(std::move(element));
   const UiElement* child = scene.GetUiElementById(1);
@@ -137,8 +137,10 @@
   const gfx::Vector3dF point(1, 0, 0);
 
   scene.OnBeginFrame(usToTicks(0));
-  auto new_origin = vr::MatrixVectorMul(child->TransformMatrix(), origin);
-  auto new_point = vr::MatrixVectorMul(child->TransformMatrix(), point);
+  auto new_origin =
+      vr::MatrixVectorMul(vr::ToMat4F(child->TransformMatrix()), origin);
+  auto new_point =
+      vr::MatrixVectorMul(vr::ToMat4F(child->TransformMatrix()), point);
   EXPECT_VEC3F_NEAR(gfx::Vector3dF(6, 10, 0), new_origin);
   EXPECT_VEC3F_NEAR(gfx::Vector3dF(0, 10, 0), new_point);
 }
diff --git a/chrome/browser/android/vr_shell/vr_shell_gl.cc b/chrome/browser/android/vr_shell/vr_shell_gl.cc
index 5af6089..bdc3ee3 100644
--- a/chrome/browser/android/vr_shell/vr_shell_gl.cc
+++ b/chrome/browser/android/vr_shell/vr_shell_gl.cc
@@ -1247,7 +1247,8 @@
 void VrShellGl::DrawElement(const vr::Mat4f& view_proj_matrix,
                             const UiElement& element) {
   vr::Mat4f transform;
-  vr::MatrixMul(view_proj_matrix, element.TransformMatrix(), &transform);
+  vr::MatrixMul(view_proj_matrix, vr::ToMat4F(element.TransformMatrix()),
+                &transform);
 
   switch (element.fill()) {
     case Fill::OPAQUE_GRADIENT: {
@@ -1271,7 +1272,7 @@
       break;
     }
     case Fill::SELF: {
-      element.Render(vr_shell_renderer_.get(), transform);
+      element.Render(vr_shell_renderer_.get(), vr::ToTransform(transform));
       break;
     }
     default:
@@ -1295,8 +1296,13 @@
               if (first->draw_phase() != second->draw_phase()) {
                 return first->draw_phase() < second->draw_phase();
               } else {
-                return vr::GetTranslation(first->TransformMatrix()).z() <
-                       vr::GetTranslation(second->TransformMatrix()).z();
+                const float first_depth =
+                    vr::GetTranslation(vr::ToMat4F(first->TransformMatrix()))
+                        .z();
+                const float second_depth =
+                    vr::GetTranslation(vr::ToMat4F(second->TransformMatrix()))
+                        .z();
+                return first_depth < second_depth;
               }
             });
 
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index ee5890e..9876c8c 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -152,7 +152,6 @@
 #include "components/signin/core/common/profile_management_switches.h"
 #include "components/spellcheck/spellcheck_build_features.h"
 #include "components/startup_metric_utils/browser/startup_metric_host_impl.h"
-#include "components/subresource_filter/content/browser/content_subresource_filter_driver_factory.h"
 #include "components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.h"
 #include "components/task_scheduler_util/browser/initialization.h"
 #include "components/task_scheduler_util/common/variations_util.h"
@@ -271,8 +270,8 @@
 #include "chrome/common/descriptors_android.h"
 #include "components/crash/content/browser/crash_dump_manager_android.h"
 #include "components/navigation_interception/intercept_navigation_delegate.h"
-#include "components/payments/mojom/payment_request.mojom.h"
 #include "content/public/browser/android/java_interfaces.h"
+#include "third_party/WebKit/public/platform/modules/payments/payment_request.mojom.h"
 #include "ui/base/resource/resource_bundle_android.h"
 #include "ui/base/ui_base_paths.h"
 #elif defined(OS_POSIX)
@@ -2412,15 +2411,8 @@
   }
 #endif
 
-  auto* driver_factory = subresource_filter::
-      ContentSubresourceFilterDriverFactory::FromWebContents(web_contents);
-  const bool popup_block_candidate =
-      !user_gesture ||
-      (driver_factory &&
-       driver_factory->throttle_manager()->ShouldDisallowNewWindow());
-  if (popup_block_candidate &&
-      !base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kDisablePopupBlocking)) {
+  if (PopupBlockerTabHelper::ConsiderForPopupBlocking(web_contents,
+                                                      user_gesture)) {
     if (content_settings->GetContentSetting(
             opener_top_level_frame_url, opener_top_level_frame_url,
             CONTENT_SETTINGS_TYPE_POPUPS,
diff --git a/chrome/browser/chromeos/attestation/platform_verification_dialog.cc b/chrome/browser/chromeos/attestation/platform_verification_dialog.cc
index 52565f8..b5f1b9d4 100644
--- a/chrome/browser/chromeos/attestation/platform_verification_dialog.cc
+++ b/chrome/browser/chromeos/attestation/platform_verification_dialog.cc
@@ -18,6 +18,7 @@
 #include "chrome/grit/generated_resources.h"
 #include "components/constrained_window/constrained_window_views.h"
 #include "components/strings/grit/components_strings.h"
+#include "components/web_modal/web_contents_modal_dialog_manager.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/web_contents.h"
 #include "extensions/browser/extension_registry.h"
@@ -45,6 +46,16 @@
     content::WebContents* web_contents,
     const GURL& requesting_origin,
     const ConsentCallback& callback) {
+  // This could happen when the permission is requested from an extension. See
+  // http://crbug.com/728534
+  // TODO(wittman): Remove this check after ShowWebModalDialogViews() API is
+  // improved/fixed. See http://crbug.com/733355
+  if (!web_modal::WebContentsModalDialogManager::FromWebContents(
+          web_contents)) {
+    DVLOG(1) << "WebContentsModalDialogManager not registered for WebContents.";
+    return nullptr;
+  }
+
   // In the case of an extension or hosted app, the origin of the request is
   // best described by the extension / app name.
   const extensions::Extension* extension =
diff --git a/chrome/browser/extensions/extension_storage_monitor.cc b/chrome/browser/extensions/extension_storage_monitor.cc
index b9bcfbd..69efb43 100644
--- a/chrome/browser/extensions/extension_storage_monitor.cc
+++ b/chrome/browser/extensions/extension_storage_monitor.cc
@@ -46,7 +46,7 @@
 namespace {
 
 // The rate at which we would like to observe storage events.
-const int kStorageEventRateSec = 30;
+constexpr base::TimeDelta kStorageEventRate = base::TimeDelta::FromSeconds(30);
 
 // Set the thresholds for the first notification. Once a threshold is exceeded,
 // it will be doubled to throttle notifications.
@@ -100,20 +100,81 @@
 
 }  // namespace
 
-// StorageEventObserver monitors the storage usage of extensions and lives on
-// the IO thread. When a threshold is exceeded, a message will be posted to the
-// UI thread, which displays the notification.
-class StorageEventObserver
-    : public base::RefCountedThreadSafe<StorageEventObserver,
-                                        BrowserThread::DeleteOnIOThread>,
-      public storage::StorageObserver {
+// SingleExtensionStorageObserver monitors the storage usage of one extension,
+// and lives on the IO thread. When a threshold is exceeded, a message will be
+// posted to the ExtensionStorageMonitor on the UI thread, which displays the
+// notification.
+class SingleExtensionStorageObserver : public storage::StorageObserver {
  public:
-  explicit StorageEventObserver(
-      base::WeakPtr<ExtensionStorageMonitor> storage_monitor)
-      : storage_monitor_(storage_monitor) {
+  SingleExtensionStorageObserver(
+      ExtensionStorageMonitorIOHelper* io_helper,
+      const std::string& extension_id,
+      scoped_refptr<storage::QuotaManager> quota_manager,
+      const GURL& origin,
+      int64_t next_threshold,
+      base::TimeDelta rate,
+      bool should_uma)
+      : io_helper_(io_helper),
+        extension_id_(extension_id),
+        quota_manager_(std::move(quota_manager)),
+        next_threshold_(next_threshold),
+        should_uma_(should_uma) {
+    // We always observe persistent storage usage.
+    storage::StorageObserver::MonitorParams params(
+        storage::kStorageTypePersistent, origin, rate, false);
+    quota_manager_->AddStorageObserver(this, params);
+    if (should_uma) {
+      // And if this is for uma, we also observe temporary storage usage.
+      MonitorParams temporary_params(storage::kStorageTypeTemporary, origin,
+                                     rate, false);
+      quota_manager_->AddStorageObserver(this, temporary_params);
+    }
   }
 
-  // Register as an observer for the extension's storage events.
+  ~SingleExtensionStorageObserver() override {
+    // This removes all our registrations.
+    quota_manager_->RemoveStorageObserver(this);
+  }
+
+  void set_next_threshold(int64_t next_threshold) {
+    next_threshold_ = next_threshold;
+  }
+
+  // storage::StorageObserver implementation.
+  void OnStorageEvent(const Event& event) override;
+
+ private:
+  // The IO thread helper that owns this instance.
+  ExtensionStorageMonitorIOHelper* const io_helper_;
+
+  // The extension associated with the origin under observation.
+  const std::string extension_id_;
+
+  // The quota manager being observed, corresponding to the extension's storage
+  // partition.
+  scoped_refptr<storage::QuotaManager> quota_manager_;
+
+  // If |next_threshold| is -1, it signifies that we should not enforce (and
+  // only track) storage for this extension.
+  int64_t next_threshold_;
+
+  const bool should_uma_;
+
+  DISALLOW_COPY_AND_ASSIGN(SingleExtensionStorageObserver);
+};
+
+// The IO thread part of ExtensionStorageMonitor. This class manages a flock of
+// SingleExtensionStorageObserver instances, one for each tracked extension.
+// This class is owned by, and reports back to, ExtensionStorageMonitor.
+class ExtensionStorageMonitorIOHelper
+    : public base::RefCountedThreadSafe<ExtensionStorageMonitorIOHelper,
+                                        BrowserThread::DeleteOnIOThread> {
+ public:
+  explicit ExtensionStorageMonitorIOHelper(
+      base::WeakPtr<ExtensionStorageMonitor> extension_storage_monitor)
+      : extension_storage_monitor_(std::move(extension_storage_monitor)) {}
+
+  // Register a StorageObserver for the extension's storage events.
   void StartObservingForExtension(
       scoped_refptr<storage::QuotaManager> quota_manager,
       const std::string& extension_id,
@@ -124,23 +185,12 @@
     DCHECK_CURRENTLY_ON(BrowserThread::IO);
     DCHECK(quota_manager.get());
 
-    GURL origin = site_url.GetOrigin();
-    StorageState& state = origin_state_map_[origin];
-    state.quota_manager = quota_manager;
-    state.extension_id = extension_id;
-    state.next_threshold = next_threshold;
-    state.should_uma = should_uma;
+    DCHECK(!FindObserver(extension_id));
 
-    // We always observe persistent storage usage.
-    storage::StorageObserver::MonitorParams params(
-        storage::kStorageTypePersistent, origin, rate, false);
-    quota_manager->AddStorageObserver(this, params);
-    if (should_uma) {
-      // And if this is for uma, we also observe temporary storage usage.
-      MonitorParams temporary_params(
-          storage::kStorageTypeTemporary, origin, rate, false);
-      quota_manager->AddStorageObserver(this, temporary_params);
-    }
+    storage_observers_[extension_id] =
+        base::MakeUnique<SingleExtensionStorageObserver>(
+            this, extension_id, std::move(quota_manager), site_url.GetOrigin(),
+            next_threshold, rate, should_uma);
   }
 
   // Updates the threshold for an extension already being monitored.
@@ -148,117 +198,77 @@
                                    int64_t next_threshold) {
     DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
-    for (OriginStorageStateMap::iterator it = origin_state_map_.begin();
-         it != origin_state_map_.end();
-         ++it) {
-      if (it->second.extension_id == extension_id) {
-        it->second.next_threshold = next_threshold;
-        break;
-      }
-    }
+    // Note that |extension_id| may not be in the map, since some extensions may
+    // be exempt from monitoring.
+    SingleExtensionStorageObserver* observer = FindObserver(extension_id);
+    if (observer)
+      observer->set_next_threshold(next_threshold);
   }
 
   // Deregister as an observer for the extension's storage events.
   void StopObservingForExtension(const std::string& extension_id) {
     DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
-    for (OriginStorageStateMap::iterator it = origin_state_map_.begin();
-         it != origin_state_map_.end(); ) {
-      if (it->second.extension_id == extension_id) {
-        storage::StorageObserver::Filter filter(
-            storage::kStorageTypePersistent, it->first);
-        it->second.quota_manager->RemoveStorageObserverForFilter(this, filter);
-        // We also need to unregister temporary storage observation, if this was
-        // being tracked for uma.
-        if (it->second.should_uma) {
-          storage::StorageObserver::Filter temporary_filter(
-              storage::kStorageTypeTemporary, it->first);
-          it->second.quota_manager->RemoveStorageObserverForFilter(this,
-                                                                   filter);
-        }
-        origin_state_map_.erase(it++);
-      } else {
-        ++it;
-      }
-    }
+    // Note that |extension_id| may not be in the map, since some extensions may
+    // be exempt from monitoring.
+    storage_observers_.erase(extension_id);
   }
 
-  // Stop observing all storage events. Called during shutdown.
-  void StopObserving() {
-    DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-    for (OriginStorageStateMap::iterator it = origin_state_map_.begin();
-         it != origin_state_map_.end(); ++it) {
-      it->second.quota_manager->RemoveStorageObserver(this);
-    }
-    origin_state_map_.clear();
+  base::WeakPtr<ExtensionStorageMonitor> extension_storage_monitor() {
+    return extension_storage_monitor_;
   }
 
  private:
-  friend class base::DeleteHelper<StorageEventObserver>;
+  friend class base::DeleteHelper<ExtensionStorageMonitorIOHelper>;
   friend struct content::BrowserThread::DeleteOnThread<
       content::BrowserThread::IO>;
 
-  struct StorageState {
-    scoped_refptr<storage::QuotaManager> quota_manager;
+  ~ExtensionStorageMonitorIOHelper() {}
 
-    std::string extension_id;
-
-    // If |next_threshold| is -1, it signifies that we should not enforce (and
-    // only track) storage for this extension.
-    int64_t next_threshold;
-
-    bool should_uma;
-
-    StorageState() : next_threshold(-1), should_uma(false) {}
-  };
-  typedef std::map<GURL, StorageState> OriginStorageStateMap;
-
-  ~StorageEventObserver() override {
-    DCHECK(origin_state_map_.empty());
-    StopObserving();
+  SingleExtensionStorageObserver* FindObserver(
+      const std::string& extension_id) {
+    auto it = storage_observers_.find(extension_id);
+    if (it != storage_observers_.end())
+      return it->second.get();
+    return nullptr;
   }
 
-  // storage::StorageObserver implementation.
-  void OnStorageEvent(const Event& event) override {
-    OriginStorageStateMap::iterator iter =
-        origin_state_map_.find(event.filter.origin);
-    if (iter == origin_state_map_.end())
-      return;
-    StorageState& state = iter->second;
+  // Keys are extension IDs. Values are self-registering StorageObservers.
+  std::map<std::string, std::unique_ptr<SingleExtensionStorageObserver>>
+      storage_observers_;
 
-    if (state.should_uma) {
-      if (event.filter.storage_type == storage::kStorageTypePersistent) {
-        UMA_HISTOGRAM_MEMORY_KB(
-            "Extensions.HostedAppUnlimitedStoragePersistentStorageUsage",
-            event.usage);
-      } else {
-        // We can't use the quota in the event because it assumes unlimited
-        // storage.
-        BrowserThread::PostTask(
-            BrowserThread::IO, FROM_HERE,
-            base::BindOnce(&LogTemporaryStorageUsage, state.quota_manager,
-                           event.usage));
-      }
-    }
+  base::WeakPtr<ExtensionStorageMonitor> extension_storage_monitor_;
 
-    if (state.next_threshold != -1 &&
-        event.usage >= state.next_threshold) {
-      while (event.usage >= state.next_threshold)
-        state.next_threshold *= 2;
-
-      BrowserThread::PostTask(
-          BrowserThread::UI, FROM_HERE,
-          base::BindOnce(&ExtensionStorageMonitor::OnStorageThresholdExceeded,
-                         storage_monitor_, state.extension_id,
-                         state.next_threshold, event.usage));
-    }
-  }
-
-  OriginStorageStateMap origin_state_map_;
-  base::WeakPtr<ExtensionStorageMonitor> storage_monitor_;
+  DISALLOW_COPY_AND_ASSIGN(ExtensionStorageMonitorIOHelper);
 };
 
+void SingleExtensionStorageObserver::OnStorageEvent(const Event& event) {
+  if (should_uma_) {
+    if (event.filter.storage_type == storage::kStorageTypePersistent) {
+      UMA_HISTOGRAM_MEMORY_KB(
+          "Extensions.HostedAppUnlimitedStoragePersistentStorageUsage",
+          event.usage);
+    } else {
+      // We can't use the quota in the event because it assumes unlimited
+      // storage.
+      BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
+                              base::BindOnce(&LogTemporaryStorageUsage,
+                                             quota_manager_, event.usage));
+    }
+  }
+
+  if (next_threshold_ != -1 && event.usage >= next_threshold_) {
+    while (event.usage >= next_threshold_)
+      next_threshold_ *= 2;
+
+    BrowserThread::PostTask(
+        BrowserThread::UI, FROM_HERE,
+        base::BindOnce(&ExtensionStorageMonitor::OnStorageThresholdExceeded,
+                       io_helper_->extension_storage_monitor(), extension_id_,
+                       next_threshold_, event.usage));
+  }
+}
+
 // ExtensionStorageMonitor
 
 // static
@@ -271,7 +281,7 @@
     content::BrowserContext* context)
     : enable_for_all_extensions_(false),
       initial_extension_threshold_(kExtensionInitialThreshold),
-      observer_rate_(base::TimeDelta::FromSeconds(kStorageEventRateSec)),
+      observer_rate_(kStorageEventRate),
       context_(context),
       extension_prefs_(ExtensionPrefs::Get(context)),
       extension_registry_observer_(this),
@@ -331,12 +341,12 @@
     // higher than this, leave it as is.
     SetNextStorageThreshold(extension->id(), 0);
 
-    if (storage_observer_.get()) {
+    if (io_helper_) {
       BrowserThread::PostTask(
           BrowserThread::IO, FROM_HERE,
-          base::BindOnce(&StorageEventObserver::UpdateThresholdForExtension,
-                         storage_observer_, extension->id(),
-                         initial_extension_threshold_));
+          base::BindOnce(
+              &ExtensionStorageMonitorIOHelper::UpdateThresholdForExtension,
+              io_helper_, extension->id(), initial_extension_threshold_));
     }
   }
 }
@@ -486,9 +496,9 @@
     return;  // Don't track this extension.
 
   // Lazily create the storage monitor proxy on the IO thread.
-  if (!storage_observer_.get()) {
-    storage_observer_ =
-        new StorageEventObserver(weak_ptr_factory_.GetWeakPtr());
+  if (!io_helper_) {
+    io_helper_ = base::MakeRefCounted<ExtensionStorageMonitorIOHelper>(
+        weak_ptr_factory_.GetWeakPtr());
   }
 
   GURL site_url = util::GetSiteForExtensionId(extension->id(), context_);
@@ -508,21 +518,22 @@
 
   BrowserThread::PostTask(
       BrowserThread::IO, FROM_HERE,
-      base::BindOnce(&StorageEventObserver::StartObservingForExtension,
-                     storage_observer_, quota_manager, extension->id(),
-                     storage_origin, next_threshold, observer_rate_,
-                     for_metrics));
+      base::BindOnce(
+          &ExtensionStorageMonitorIOHelper::StartObservingForExtension,
+          io_helper_, quota_manager, extension->id(), storage_origin,
+          next_threshold, observer_rate_, for_metrics));
 }
 
 void ExtensionStorageMonitor::StopMonitoringStorage(
     const std::string& extension_id) {
-  if (!storage_observer_.get())
+  if (!io_helper_.get())
     return;
 
   BrowserThread::PostTask(
       BrowserThread::IO, FROM_HERE,
-      base::BindOnce(&StorageEventObserver::StopObservingForExtension,
-                     storage_observer_, extension_id));
+      base::BindOnce(
+          &ExtensionStorageMonitorIOHelper::StopObservingForExtension,
+          io_helper_, extension_id));
 }
 
 void ExtensionStorageMonitor::StopMonitoringAll() {
@@ -530,13 +541,8 @@
 
   RemoveAllNotifications();
 
-  if (!storage_observer_.get())
-    return;
-
-  BrowserThread::PostTask(
-      BrowserThread::IO, FROM_HERE,
-      base::BindOnce(&StorageEventObserver::StopObserving, storage_observer_));
-  storage_observer_ = NULL;
+  io_helper_ = nullptr;
+  weak_ptr_factory_.InvalidateWeakPtrs();
 }
 
 void ExtensionStorageMonitor::RemoveNotificationForExtension(
diff --git a/chrome/browser/extensions/extension_storage_monitor.h b/chrome/browser/extensions/extension_storage_monitor.h
index 8c59fd5b..2e9cafb 100644
--- a/chrome/browser/extensions/extension_storage_monitor.h
+++ b/chrome/browser/extensions/extension_storage_monitor.h
@@ -32,7 +32,7 @@
 class Extension;
 class ExtensionPrefs;
 class ExtensionRegistry;
-class StorageEventObserver;
+class ExtensionStorageMonitorIOHelper;
 
 // ExtensionStorageMonitor monitors the storage usage of extensions and apps
 // that are granted unlimited storage and displays notifications when high
@@ -139,8 +139,9 @@
                  extensions::ExtensionRegistryObserver>
       extension_registry_observer_;
 
-  // StorageEventObserver monitors storage for extensions on the IO thread.
-  scoped_refptr<StorageEventObserver> storage_observer_;
+  // ExtensionStorageMonitorIOHelper maintains, on the IO thread, an instance of
+  // SingleExtensionStorageObserver for each extension.
+  scoped_refptr<ExtensionStorageMonitorIOHelper> io_helper_;
 
   // Modal dialog used to confirm removal of an extension.
   std::unique_ptr<ExtensionUninstallDialog> uninstall_dialog_;
@@ -151,7 +152,7 @@
 
   base::WeakPtrFactory<ExtensionStorageMonitor> weak_ptr_factory_;
 
-  friend class StorageEventObserver;
+  friend class SingleExtensionStorageObserver;
   friend class ExtensionStorageMonitorTest;
 
   DISALLOW_COPY_AND_ASSIGN(ExtensionStorageMonitor);
diff --git a/chrome/browser/extensions/extension_storage_monitor_browsertest.cc b/chrome/browser/extensions/extension_storage_monitor_browsertest.cc
index 0a356e4..eb9b926 100644
--- a/chrome/browser/extensions/extension_storage_monitor_browsertest.cc
+++ b/chrome/browser/extensions/extension_storage_monitor_browsertest.cc
@@ -5,6 +5,8 @@
 #include <stdint.h>
 
 #include <set>
+#include <string>
+#include <vector>
 
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
@@ -12,8 +14,10 @@
 #include "chrome/browser/extensions/extension_browsertest.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/extension_storage_monitor.h"
+#include "chrome/browser/extensions/test_extension_dir.h"
 #include "chrome/browser/ui/extensions/app_launch_params.h"
 #include "chrome/browser/ui/extensions/application_launch.h"
+#include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_utils.h"
 #include "extensions/browser/extension_dialog_auto_confirm.h"
 #include "extensions/browser/extension_prefs.h"
@@ -21,7 +25,9 @@
 #include "extensions/browser/extension_system.h"
 #include "extensions/browser/test_extension_registry_observer.h"
 #include "extensions/common/constants.h"
+#include "extensions/common/value_builder.h"
 #include "extensions/test/extension_test_message_listener.h"
+#include "net/dns/mock_host_resolver.h"
 #include "ui/message_center/message_center.h"
 #include "ui/message_center/message_center_observer.h"
 
@@ -87,6 +93,8 @@
   void SetUpOnMainThread() override {
     ExtensionBrowserTest::SetUpOnMainThread();
 
+    host_resolver()->AddRule("*", "127.0.0.1");
+
     InitStorageMonitor();
   }
 
@@ -112,6 +120,40 @@
     return extension;
   }
 
+  const Extension* CreateHostedApp(const std::string& name,
+                                   GURL app_url,
+                                   std::vector<std::string> permissions) {
+    auto dir = base::MakeUnique<TestExtensionDir>();
+
+    url::Replacements<char> clear_port;
+    clear_port.ClearPort();
+
+    DictionaryBuilder manifest;
+    manifest.Set("name", name)
+        .Set("version", "1.0")
+        .Set("manifest_version", 2)
+        .Set(
+            "app",
+            DictionaryBuilder()
+                .Set("urls",
+                     ListBuilder()
+                         .Append(app_url.ReplaceComponents(clear_port).spec())
+                         .Build())
+                .Set("launch",
+                     DictionaryBuilder().Set("web_url", app_url.spec()).Build())
+                .Build());
+    ListBuilder permissions_builder;
+    for (const std::string& permission : permissions)
+      permissions_builder.Append(permission);
+    manifest.Set("permissions", permissions_builder.Build());
+    dir->WriteManifest(manifest.ToJSON());
+
+    const Extension* extension = LoadExtension(dir->UnpackedPath());
+    EXPECT_TRUE(extension);
+    temp_dirs_.push_back(std::move(dir));
+    return extension;
+  }
+
   std::string GetNotificationId(const std::string& extension_id) {
     return monitor()->GetNotificationId(extension_id);
   }
@@ -125,17 +167,22 @@
   }
 
   void WriteBytesExpectingNotification(const Extension* extension,
-                                       int num_bytes) {
+                                       int num_bytes,
+                                       const char* filesystem = "PERSISTENT") {
     int64_t previous_threshold = GetNextStorageThreshold(extension->id());
-    WriteBytes(extension, num_bytes, true);
-    EXPECT_GT(GetNextStorageThreshold(extension->id()), previous_threshold);
+    WriteBytes(extension, num_bytes, filesystem, true);
+    ASSERT_GT(GetNextStorageThreshold(extension->id()), previous_threshold);
   }
 
-  void WriteBytesNotExpectingNotification(const Extension* extension,
-                                         int num_bytes) {
-    WriteBytes(extension, num_bytes, false);
+  void WriteBytesNotExpectingNotification(
+      const Extension* extension,
+      int num_bytes,
+      const char* filesystem = "PERSISTENT") {
+    WriteBytes(extension, num_bytes, filesystem, false);
   }
 
+  void SimulateProfileShutdown() { storage_monitor_->StopMonitoringAll(); }
+
  private:
   void InitStorageMonitor() {
     storage_monitor_ = ExtensionStorageMonitor::Get(profile());
@@ -150,34 +197,71 @@
     storage_monitor_->observer_rate_ = base::TimeDelta();
   }
 
-  // Write a number of bytes to persistent storage.
-  void WriteBytes(const Extension* extension,
-                  int num_bytes,
-                  bool expected_notification) {
+  // Write bytes for a hosted app page that's loaded the script:
+  //     //chrome/test/data/extensions/storage_monitor/hosted_apps/common.js
+  void WriteBytesForHostedApp(const Extension* extension,
+                              int num_bytes,
+                              const std::string& filesystem) {
+    content::WebContents* web_contents = OpenApplication(AppLaunchParams(
+        profile(), extension, LAUNCH_CONTAINER_TAB,
+        WindowOpenDisposition::SINGLETON_TAB, extensions::SOURCE_TEST));
+
+    ASSERT_TRUE(WaitForLoadStop(web_contents));
+    std::string result;
+    const char* script = R"(
+        HostedAppWriteData(%s, %d)
+           .then(() => domAutomationController.send('write_done'))
+           .catch(e => domAutomationController.send('write_error: ' + e));
+    )";
+    ASSERT_TRUE(ExecuteScriptAndExtractString(
+        web_contents, base::StringPrintf(script, filesystem.c_str(), num_bytes),
+        &result));
+    ASSERT_EQ("write_done", result);
+  }
+
+  // Write bytes for the extension loaded from:
+  //     //chrome/test/data/extensions/storage_monitor/write_data
+  void WriteBytesForExtension(const Extension* extension, int num_bytes) {
     ExtensionTestMessageListener launched_listener("launched", true);
-    ExtensionTestMessageListener write_complete_listener(
-        "write_complete", false);
-    NotificationObserver notification_observer(
-        GetNotificationId(extension->id()));
+    ExtensionTestMessageListener write_complete_listener("write_complete",
+                                                         false);
 
     OpenApplication(AppLaunchParams(profile(), extension, LAUNCH_CONTAINER_NONE,
                                     WindowOpenDisposition::NEW_WINDOW,
                                     extensions::SOURCE_TEST));
+
     ASSERT_TRUE(launched_listener.WaitUntilSatisfied());
 
     // Instruct the app to write |num_bytes| of data.
     launched_listener.Reply(base::IntToString(num_bytes));
     ASSERT_TRUE(write_complete_listener.WaitUntilSatisfied());
+  }
+
+  // Write a number of bytes to persistent storage.
+  void WriteBytes(const Extension* extension,
+                  int num_bytes,
+                  const std::string& filesystem,
+                  bool expected_notification) {
+    NotificationObserver notification_observer(
+        GetNotificationId(extension->id()));
+
+    if (extension->is_hosted_app()) {
+      WriteBytesForHostedApp(extension, num_bytes, filesystem);
+    } else {
+      ASSERT_EQ("PERSISTENT", filesystem) << "Not implemented in the js code.";
+      WriteBytesForExtension(extension, num_bytes);
+    }
 
     if (expected_notification) {
-      EXPECT_TRUE(notification_observer.WaitForNotification());
+      ASSERT_TRUE(notification_observer.WaitForNotification());
     } else {
       base::RunLoop().RunUntilIdle();
-      EXPECT_FALSE(notification_observer.HasReceivedNotification());
+      ASSERT_FALSE(notification_observer.HasReceivedNotification());
     }
   }
 
   ExtensionStorageMonitor* storage_monitor_;
+  std::vector<std::unique_ptr<TestExtensionDir>> temp_dirs_;
 };
 
 // Control - No notifications should be shown if usage remains under the
@@ -254,6 +338,67 @@
   WriteBytesNotExpectingNotification(extension, GetInitialExtensionThreshold());
 }
 
+// Regression test for https://crbug.com/716426
+IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest,
+                       HostedAppTemporaryFilesystem) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  GURL url = embedded_test_server()->GetURL(
+      "chromium.org", "/extensions/storage_monitor/hosted_apps/one/index.html");
+  const Extension* app =
+      CreateHostedApp("Hosted App", url, {"unlimitedStorage"});
+
+  EXPECT_NO_FATAL_FAILURE(WriteBytesExpectingNotification(
+      app, GetInitialExtensionThreshold(), "TEMPORARY"));
+  EXPECT_NO_FATAL_FAILURE(WriteBytesNotExpectingNotification(
+      app, GetInitialExtensionThreshold(), "PERSISTENT"));
+  EXPECT_NO_FATAL_FAILURE(WriteBytesExpectingNotification(
+      app, GetInitialExtensionThreshold(), "TEMPORARY"));
+
+  // Bug 716426 was a shutdown crash due to not removing a
+  // storage::StorageObserver registration before deleting the observer. To
+  // recreate that scenario, first disable the app (which leaks the observer
+  // registration), then simulate the step of profile exit where we delete the
+  // StorageObserver.
+  DisableExtension(app->id());
+  SimulateProfileShutdown();
+
+  // Now generate more storage activity for the hosted app's temporary
+  // filesystem. Note that it's not a hosted app anymore -- it's just a webpage.
+  // Bug 716426 caused this to crash the browser.
+  EXPECT_NO_FATAL_FAILURE(WriteBytesNotExpectingNotification(
+      app, GetInitialExtensionThreshold(), "TEMPORARY"));
+}
+
+// Exercises the case where two hosted apps are same-origin but have non-
+// overlapping extents. Disabling one should not suppress storage monitoring for
+// the other.
+IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest, TwoHostedAppsInSameOrigin) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  GURL url1 = embedded_test_server()->GetURL(
+      "chromium.org", "/extensions/storage_monitor/hosted_apps/one/index.html");
+  const Extension* app1 = CreateHostedApp("App 1", url1, {"unlimitedStorage"});
+
+  GURL url2 = embedded_test_server()->GetURL(
+      "chromium.org", "/extensions/storage_monitor/hosted_apps/two/index.html");
+  const Extension* app2 = CreateHostedApp("App 2", url2, {"unlimitedStorage"});
+
+  EXPECT_EQ(url1.GetOrigin(), url2.GetOrigin());
+
+  EXPECT_NO_FATAL_FAILURE(
+      WriteBytesExpectingNotification(app1, GetInitialExtensionThreshold()));
+  EXPECT_NO_FATAL_FAILURE(WriteBytesExpectingNotification(
+      app2, GetInitialExtensionThreshold() * 2));
+
+  // Disable app2. We should still be monitoring the origin on behalf of app1.
+  DisableExtension(app2->id());
+
+  // Writing a bunch of data in app1 should trigger the warning.
+  EXPECT_NO_FATAL_FAILURE(WriteBytesExpectingNotification(
+      app1, GetInitialExtensionThreshold() * 4));
+}
+
 // Verify that notifications are disabled when the user clicks the action button
 // in the notification.
 // Flaky: https://crbug.com/617801
diff --git a/chrome/browser/media/protected_media_identifier_permission_context.cc b/chrome/browser/media/protected_media_identifier_permission_context.cc
index 7fd089b..a6bf43e 100644
--- a/chrome/browser/media/protected_media_identifier_permission_context.cc
+++ b/chrome/browser/media/protected_media_identifier_permission_context.cc
@@ -82,6 +82,14 @@
                      OnPlatformVerificationConsentResponse,
                  weak_factory_.GetWeakPtr(), web_contents, id,
                  requesting_origin, embedding_origin, callback));
+
+  // This could happen when the permission is requested from an extension. See
+  // http://crbug.com/728534
+  if (!widget) {
+    callback.Run(CONTENT_SETTING_ASK);
+    return;
+  }
+
   pending_requests_.insert(
       std::make_pair(web_contents, std::make_pair(widget, id)));
 }
diff --git a/chrome/browser/payments/payment_request_factory.h b/chrome/browser/payments/payment_request_factory.h
index f575a56..c1d176a 100644
--- a/chrome/browser/payments/payment_request_factory.h
+++ b/chrome/browser/payments/payment_request_factory.h
@@ -5,8 +5,8 @@
 #ifndef CHROME_BROWSER_PAYMENTS_PAYMENT_REQUEST_FACTORY_H_
 #define CHROME_BROWSER_PAYMENTS_PAYMENT_REQUEST_FACTORY_H_
 
-#include "components/payments/mojom/payment_request.mojom.h"
 #include "services/service_manager/public/cpp/bind_source_info.h"
+#include "third_party/WebKit/public/platform/modules/payments/payment_request.mojom.h"
 
 namespace content {
 class RenderFrameHost;
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_win.cc b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_win.cc
index 839e41e..f070575 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_win.cc
+++ b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_win.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_win.h"
 
+#include <windows.h>
+
 #include <utility>
 
 #include "base/bind.h"
@@ -13,7 +15,6 @@
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
-#include "base/memory/singleton.h"
 #include "base/task_scheduler/post_task.h"
 #include "base/task_scheduler/task_traits.h"
 #include "base/threading/thread_restrictions.h"
@@ -22,6 +23,7 @@
 #include "chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_fetcher_win.h"
 #include "chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_runner_win.h"
 #include "chrome/browser/safe_browsing/chrome_cleaner/srt_client_info_win.h"
+#include "chrome/installer/util/scoped_token_privilege.h"
 #include "components/chrome_cleaner/public/constants/constants.h"
 #include "components/safe_browsing/common/safe_browsing_prefs.h"
 #include "content/public/browser/browser_thread.h"
@@ -117,8 +119,11 @@
 // static
 ChromeCleanerController* ChromeCleanerController::GetInstance() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  static ChromeCleanerController* const kInstance =
+      new ChromeCleanerController();
   g_instance_exists = true;
-  return base::Singleton<ChromeCleanerController>::get();
+  return kInstance;
 }
 
 // static
@@ -165,6 +170,8 @@
   reporter_invocation_ =
       base::MakeUnique<SwReporterInvocation>(reporter_invocation);
   SetStateAndNotifyObservers(State::kScanning);
+  // base::Unretained is safe because the ChromeCleanerController instance is
+  // guaranteed to outlive the UI thread.
   delegate_->FetchAndVerifyChromeCleaner(base::BindOnce(
       &ChromeCleanerController::OnChromeCleanerFetchedAndVerified,
       base::Unretained(this)));
@@ -172,7 +179,7 @@
 
 void ChromeCleanerController::ReplyWithUserResponse(
     UserResponse user_response) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(prompt_user_callback_);
 
   if (state() != State::kInfected)
@@ -203,11 +210,20 @@
   SetStateAndNotifyObservers(new_state);
 }
 
+void ChromeCleanerController::Reboot() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  if (state() != State::kRebootRequired)
+    return;
+
+  InitiateReboot();
+}
+
 ChromeCleanerController::ChromeCleanerController()
     : real_delegate_(base::MakeUnique<ChromeCleanerControllerDelegate>()),
       delegate_(real_delegate_.get()),
       weak_factory_(this) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 }
 
 ChromeCleanerController::~ChromeCleanerController() = default;
@@ -248,6 +264,8 @@
 }
 
 void ChromeCleanerController::ResetCleanerDataAndInvalidateWeakPtrs() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
   weak_factory_.InvalidateWeakPtrs();
   reporter_invocation_.reset();
   files_to_delete_.reset();
@@ -388,4 +406,18 @@
   SetStateAndNotifyObservers(State::kIdle);
 }
 
+void ChromeCleanerController::InitiateReboot() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  installer::ScopedTokenPrivilege scoped_se_shutdown_privilege(
+      SE_SHUTDOWN_NAME);
+  if (!scoped_se_shutdown_privilege.is_enabled() ||
+      !::ExitWindowsEx(EWX_REBOOT, SHTDN_REASON_MAJOR_SOFTWARE |
+                                       SHTDN_REASON_MINOR_OTHER |
+                                       SHTDN_REASON_FLAG_PLANNED)) {
+    for (auto& observer : observer_list_)
+      observer.OnRebootFailed();
+  }
+}
+
 }  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_win.h b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_win.h
index 40424ad..0d35c97 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_win.h
+++ b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_win.h
@@ -17,11 +17,6 @@
 #include "chrome/browser/safe_browsing/chrome_cleaner/reporter_runner_win.h"
 #include "components/chrome_cleaner/public/interfaces/chrome_prompt.mojom.h"
 
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}
-
 namespace safe_browsing {
 
 // Delegate class that provides services to the ChromeCleanerController class
@@ -104,16 +99,17 @@
     virtual void OnInfected(const std::set<base::FilePath>& files_to_delete) {}
     virtual void OnCleaning(const std::set<base::FilePath>& files_to_delete) {}
     virtual void OnRebootRequired() {}
+    virtual void OnRebootFailed() {}
 
    protected:
     virtual ~Observer() = default;
   };
 
-  // Returns the singleton controller object.
+  // Returns the global controller object.
   static ChromeCleanerController* GetInstance();
 
   // Returns whether the Cleanup card in settings should be displayed.
-  // Static to prevent instantiation of the singleton.
+  // Static to prevent instantiation of the global controller object.
   static bool ShouldShowCleanupInSettingsUI();
 
   State state() const { return state_; }
@@ -150,14 +146,23 @@
   // "Cleanup" button multiple times.
   void ReplyWithUserResponse(UserResponse user_response);
 
+  // If the controller is in the kRebootRequired state, initiates a reboot of
+  // the computer. Call this after obtaining permission from the user to
+  // reboot.
+  //
+  // If initiating the reboot fails, observers will be notified via a call to
+  // OnRebootFailed().
+  //
+  // Note that there are no guarantees that the reboot will in fact happen even
+  // if the system calls to initiate a reboot return success.
+  void Reboot();
+
   // Passing in a nullptr as |delegate| resets the delegate to a default
   // production version.
   void SetDelegateForTesting(ChromeCleanerControllerDelegate* delegate);
   void DismissRebootForTesting();
 
  private:
-  friend struct base::DefaultSingletonTraits<ChromeCleanerController>;
-
   ChromeCleanerController();
   ~ChromeCleanerController();
 
@@ -191,6 +196,7 @@
                         prompt_user_callback);
   void OnConnectionClosed();
   void OnCleanerProcessDone(ChromeCleanerRunner::ProcessStatus process_status);
+  void InitiateReboot();
 
   std::unique_ptr<ChromeCleanerControllerDelegate> real_delegate_;
   // Pointer to either real_delegate_ or one set by tests.
diff --git a/chrome/browser/subresource_filter/subresource_filter_browsertest.cc b/chrome/browser/subresource_filter/subresource_filter_browsertest.cc
index 8eb565c..a46e50e6b 100644
--- a/chrome/browser/subresource_filter/subresource_filter_browsertest.cc
+++ b/chrome/browser/subresource_filter/subresource_filter_browsertest.cc
@@ -1067,6 +1067,47 @@
                    ->IsContentBlocked(CONTENT_SETTINGS_TYPE_POPUPS));
 }
 
+IN_PROC_BROWSER_TEST_F(SubresourceFilterBrowserTest, BlockOpenURLFromTab) {
+  base::HistogramTester tester;
+  const char kWindowOpenPath[] =
+      "/subresource_filter/window_open_spoof_click.html";
+  GURL a_url(embedded_test_server()->GetURL("a.com", kWindowOpenPath));
+  GURL b_url(embedded_test_server()->GetURL("b.com", kWindowOpenPath));
+  // Only configure |a_url| as a phishing URL.
+  ConfigureAsPhishingURL(a_url);
+
+  // Only necessary so we have a valid ruleset.
+  ASSERT_NO_FATAL_FAILURE(SetRulesetWithRules(std::vector<proto::UrlRule>()));
+
+  // Navigate to a_url, should trigger the popup blocker.
+  ui_test_utils::NavigateToURL(browser(), a_url);
+
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  EXPECT_TRUE(content::ExecuteScript(web_contents, "openWindow()"));
+  // Do not force the UI if the popup was the only thing disallowed. The popup
+  // UI is good enough.
+  tester.ExpectBucketCount(kSubresourceFilterActionsHistogram, kActionUIShown,
+                           0);
+
+  // Make sure the popup UI was shown.
+  EXPECT_TRUE(TabSpecificContentSettings::FromWebContents(web_contents)
+                  ->IsContentBlocked(CONTENT_SETTINGS_TYPE_POPUPS));
+
+  // Navigate to |b_url|, which should successfully open the popup.
+
+  ui_test_utils::NavigateToURL(browser(), b_url);
+
+  content::TestNavigationObserver navigation_observer(nullptr, 1);
+  navigation_observer.StartWatchingNewWebContents();
+  EXPECT_TRUE(content::ExecuteScript(web_contents, "openWindow()"));
+  navigation_observer.Wait();
+
+  // Popup UI should not be shown.
+  EXPECT_FALSE(TabSpecificContentSettings::FromWebContents(web_contents)
+                   ->IsContentBlocked(CONTENT_SETTINGS_TYPE_POPUPS));
+}
+
 IN_PROC_BROWSER_TEST_F(SubresourceFilterBrowserTest,
                        ContentSettingsWhitelist_DoNotActivate) {
   ASSERT_NO_FATAL_FAILURE(
diff --git a/chrome/browser/ui/blocked_content/popup_blocker_tab_helper.cc b/chrome/browser/ui/blocked_content/popup_blocker_tab_helper.cc
index 54475af..49eabde 100644
--- a/chrome/browser/ui/blocked_content/popup_blocker_tab_helper.cc
+++ b/chrome/browser/ui/blocked_content/popup_blocker_tab_helper.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/blocked_content/popup_blocker_tab_helper.h"
 
+#include "base/command_line.h"
 #include "base/memory/ptr_util.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/content_settings/tab_specific_content_settings.h"
@@ -11,8 +12,11 @@
 #include "chrome/browser/ui/blocked_content/blocked_window_params.h"
 #include "chrome/browser/ui/browser_navigator.h"
 #include "chrome/browser/ui/browser_navigator_params.h"
+#include "chrome/common/chrome_switches.h"
 #include "chrome/common/render_messages.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
+#include "components/subresource_filter/content/browser/content_subresource_filter_driver_factory.h"
+#include "components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/navigation_handle.h"
@@ -36,6 +40,26 @@
   blink::mojom::WindowFeatures window_features;
 };
 
+bool PopupBlockerTabHelper::ConsiderForPopupBlocking(
+    content::WebContents* web_contents,
+    bool user_gesture) {
+  DCHECK(web_contents);
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kDisablePopupBlocking)) {
+    return false;
+  }
+
+  if (!user_gesture)
+    return true;
+
+  // The subresource_filter triggers an extra aggressive popup blocker on
+  // pages where ads are being blocked, even if there is a user gesture.
+  auto* driver_factory = subresource_filter::
+      ContentSubresourceFilterDriverFactory::FromWebContents(web_contents);
+  return driver_factory &&
+         driver_factory->throttle_manager()->ShouldDisallowNewWindow();
+}
+
 PopupBlockerTabHelper::PopupBlockerTabHelper(
     content::WebContents* web_contents)
     : content::WebContentsObserver(web_contents) {
diff --git a/chrome/browser/ui/blocked_content/popup_blocker_tab_helper.h b/chrome/browser/ui/blocked_content/popup_blocker_tab_helper.h
index a43f8d14..f204b52 100644
--- a/chrome/browser/ui/blocked_content/popup_blocker_tab_helper.h
+++ b/chrome/browser/ui/blocked_content/popup_blocker_tab_helper.h
@@ -30,6 +30,11 @@
   // Mapping from popup IDs to blocked popup requests.
   typedef std::map<int32_t, GURL> PopupIdMap;
 
+  // Returns true if a popup with |user_gesture| should be considered for
+  // blocking from |web_contents|.
+  static bool ConsiderForPopupBlocking(content::WebContents* web_contents,
+                                       bool user_gesture);
+
   ~PopupBlockerTabHelper() override;
 
   // Returns true if the popup request defined by |params| should be blocked.
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index 3be29f1..ace467d 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -1456,22 +1456,18 @@
     nav_params.window_action = chrome::NavigateParams::SHOW_WINDOW;
   nav_params.user_gesture = params.user_gesture;
 
-  PopupBlockerTabHelper* popup_blocker_helper = NULL;
-  if (source)
-    popup_blocker_helper = PopupBlockerTabHelper::FromWebContents(source);
-
-  if (popup_blocker_helper) {
-    if ((params.disposition == WindowOpenDisposition::NEW_POPUP ||
-         params.disposition == WindowOpenDisposition::NEW_FOREGROUND_TAB ||
-         params.disposition == WindowOpenDisposition::NEW_BACKGROUND_TAB ||
-         params.disposition == WindowOpenDisposition::NEW_WINDOW) &&
-        !params.user_gesture &&
-        !base::CommandLine::ForCurrentProcess()->HasSwitch(
-            switches::kDisablePopupBlocking)) {
-      if (popup_blocker_helper->MaybeBlockPopup(
-              nav_params, blink::mojom::WindowFeatures())) {
-        return NULL;
-      }
+  PopupBlockerTabHelper* popup_blocker_helper =
+      source ? PopupBlockerTabHelper::FromWebContents(source) : nullptr;
+  if ((params.disposition == WindowOpenDisposition::NEW_POPUP ||
+       params.disposition == WindowOpenDisposition::NEW_FOREGROUND_TAB ||
+       params.disposition == WindowOpenDisposition::NEW_BACKGROUND_TAB ||
+       params.disposition == WindowOpenDisposition::NEW_WINDOW) &&
+      popup_blocker_helper &&
+      PopupBlockerTabHelper::ConsiderForPopupBlocking(source,
+                                                      params.user_gesture)) {
+    if (popup_blocker_helper->MaybeBlockPopup(nav_params,
+                                              blink::mojom::WindowFeatures())) {
+      return nullptr;
     }
   }
 
diff --git a/chrome/browser/ui/views/payments/payment_request_browsertest_base.h b/chrome/browser/ui/views/payments/payment_request_browsertest_base.h
index 649756b..4dc2c35 100644
--- a/chrome/browser/ui/views/payments/payment_request_browsertest_base.h
+++ b/chrome/browser/ui/views/payments/payment_request_browsertest_base.h
@@ -21,9 +21,9 @@
 #include "components/autofill/core/browser/field_types.h"
 #include "components/autofill/core/browser/personal_data_manager_observer.h"
 #include "components/payments/content/payment_request.h"
-#include "components/payments/mojom/payment_request.mojom.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "testing/gmock/include/gmock/gmock.h"
+#include "third_party/WebKit/public/platform/modules/payments/payment_request.mojom.h"
 
 namespace autofill {
 class AutofillProfile;
diff --git a/chrome/browser/ui/views/payments/payment_request_views_util.h b/chrome/browser/ui/views/payments/payment_request_views_util.h
index 480b5a0..a8c39a4 100644
--- a/chrome/browser/ui/views/payments/payment_request_views_util.h
+++ b/chrome/browser/ui/views/payments/payment_request_views_util.h
@@ -9,7 +9,7 @@
 #include <string>
 
 #include "base/strings/string16.h"
-#include "components/payments/mojom/payment_request.mojom.h"
+#include "third_party/WebKit/public/platform/modules/payments/payment_request.mojom.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/text_constants.h"
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 7e40976..a6731e0 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -2910,6 +2910,16 @@
   ]
 }
 
+# Difference between this and telemetry_perf_tests is that this runs a devil
+# script before the build, to remove the system webview. See
+# //testing/buildbot/gn_isolate_map.pyl
+group("telemetry_perf_webview_tests") {
+  testonly = true
+  deps = [
+    "//chrome/test:telemetry_perf_tests",
+  ]
+}
+
 group("angle_perftests") {
   testonly = true
   if (is_win || is_linux) {
diff --git a/chrome/test/data/extensions/storage_monitor/hosted_apps/common.js b/chrome/test/data/extensions/storage_monitor/hosted_apps/common.js
new file mode 100644
index 0000000..a673abd
--- /dev/null
+++ b/chrome/test/data/extensions/storage_monitor/hosted_apps/common.js
@@ -0,0 +1,51 @@
+// 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.
+
+// Opens the filesystem and returns a Promise that resolves to the opened
+// filesystem.
+function GetFileSystem(type, size) {
+  return new Promise(
+      (resolve, reject) =>
+          webkitRequestFileSystem(type, size, resolve, reject));
+}
+
+// Returns a .then()-chainable handler that accepts a fileystem, creates a file,
+// and returns a Promise that resolves to the successfully created file.
+function CreateFile(filename) {
+  return (filesystem) => new Promise(
+             (resolve, reject) => filesystem.root.getFile(
+                 filename, {create: true}, resolve, reject));
+}
+
+// Returns a .then()-chainable handler that accepts a filesystem file, appends
+// |numChars| to it, and returns a Promise that resolves to the file once the
+// append operation is successful.
+function AppendDataToFile(numChars) {
+  return ((fileEntry) => {
+    return new Promise((resolve, reject) => {
+      fileEntry.createWriter((fileWriter) => {
+        // FileWriter's onwriteend resolves the promise; onerror rejects it.
+        fileWriter.onwriteend = (e) => resolve(fileEntry);
+        fileWriter.onerror = reject;
+        fileWriter.seek(fileWriter.length);
+
+        var str = 'a'.repeat(numChars);
+
+        var blob = new Blob([str], {type: 'text/plain'});
+        console.assert(blob.size == numChars);
+        fileWriter.write(blob);
+      }, reject);
+    });
+  });
+}
+
+// Entry point to be called via ExecuteScript in the browser test C++ code.
+//
+// Asynchronously opens writes |numChars| to the filesystem. Returns a Promise
+// that resolves upon completion.
+function HostedAppWriteData(type, numChars) {
+  return GetFileSystem(type, 16384)
+      .then(CreateFile('test.txt'))
+      .then(AppendDataToFile(numChars));
+}
diff --git a/chrome/test/data/extensions/storage_monitor/hosted_apps/one/index.html b/chrome/test/data/extensions/storage_monitor/hosted_apps/one/index.html
new file mode 100644
index 0000000..eacb101
--- /dev/null
+++ b/chrome/test/data/extensions/storage_monitor/hosted_apps/one/index.html
@@ -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.
+-->
+<!DOCTYPE html>
+<html>
+<body>
+  <script src="../common.js"></script>
+</body>
+</html>
diff --git a/chrome/test/data/extensions/storage_monitor/hosted_apps/two/index.html b/chrome/test/data/extensions/storage_monitor/hosted_apps/two/index.html
new file mode 100644
index 0000000..eacb101
--- /dev/null
+++ b/chrome/test/data/extensions/storage_monitor/hosted_apps/two/index.html
@@ -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.
+-->
+<!DOCTYPE html>
+<html>
+<body>
+  <script src="../common.js"></script>
+</body>
+</html>
diff --git a/chrome/test/data/subresource_filter/window_open_spoof_click.html b/chrome/test/data/subresource_filter/window_open_spoof_click.html
new file mode 100644
index 0000000..fe245d2
--- /dev/null
+++ b/chrome/test/data/subresource_filter/window_open_spoof_click.html
@@ -0,0 +1,12 @@
+<a href = "https://www.google.com" id='target'>target</a>
+<script>
+function openWindow() {
+  var evt = new MouseEvent("click", {
+    view : window,
+    bubbles : true,
+    cancelable : true,
+    shiftKey : true,
+  });
+  document.getElementById('target').dispatchEvent(evt);
+}
+</script>
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_pingback_client.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_pingback_client.cc
index f3557fb4..bae6a4e 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_pingback_client.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_pingback_client.cc
@@ -42,7 +42,7 @@
                               PageloadMetrics* request,
                               bool opted_out) {
   request->set_session_key(request_data.session_key());
-  request->set_holdback(params::IsIncludedInHoldbackFieldTrial());
+  request->set_holdback_group(params::HoldbackFieldTrialGroup());
   // For the timing events, any of them could be zero. Fill the message as a
   // best effort.
   request->set_allocated_first_request_time(
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_pingback_client_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_pingback_client_unittest.cc
index 0753748..829bc3d9 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_pingback_client_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_pingback_client_unittest.cc
@@ -216,7 +216,7 @@
   EXPECT_EQ(
       PageloadMetrics_EffectiveConnectionType_EFFECTIVE_CONNECTION_TYPE_OFFLINE,
       pageload_metrics.effective_connection_type());
-  EXPECT_FALSE(pageload_metrics.holdback());
+  EXPECT_EQ(std::string(), pageload_metrics.holdback_group());
   test_fetcher->delegate()->OnURLFetchComplete(test_fetcher);
   histogram_tester().ExpectUniqueSample(kHistogramSucceeded, true, 1);
   EXPECT_FALSE(factory()->GetFetcherByID(0));
@@ -240,7 +240,7 @@
   batched_request.ParseFromString(test_fetcher->upload_data());
   EXPECT_EQ(batched_request.pageloads_size(), 1);
   PageloadMetrics pageload_metrics = batched_request.pageloads(0);
-  EXPECT_TRUE(pageload_metrics.holdback());
+  EXPECT_EQ("Enabled", pageload_metrics.holdback_group());
   test_fetcher->delegate()->OnURLFetchComplete(test_fetcher);
   histogram_tester().ExpectUniqueSample(kHistogramSucceeded, true, 1);
   EXPECT_FALSE(factory()->GetFetcherByID(0));
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_params.cc b/components/data_reduction_proxy/core/common/data_reduction_proxy_params.cc
index 9b507c3f..4fa6305 100644
--- a/components/data_reduction_proxy/core/common/data_reduction_proxy_params.cc
+++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_params.cc
@@ -23,8 +23,6 @@
 #include "base/android/build_info.h"
 #endif
 
-using base::FieldTrialList;
-
 namespace {
 
 const char kEnabled[] = "Enabled";
@@ -66,7 +64,7 @@
 const char kLitePageBlackListVersion[] = "lite-page-blacklist-version";
 
 bool IsIncludedInFieldTrial(const std::string& name) {
-  return base::StartsWith(FieldTrialList::FindFullName(name), kEnabled,
+  return base::StartsWith(base::FieldTrialList::FindFullName(name), kEnabled,
                           base::CompareCase::SENSITIVE);
 }
 
@@ -115,19 +113,23 @@
   return IsIncludedInFieldTrial("DataCompressionProxyHoldback");
 }
 
+std::string HoldbackFieldTrialGroup() {
+  return base::FieldTrialList::FindFullName("DataCompressionProxyHoldback");
+}
+
 const char* GetTrustedSpdyProxyFieldTrialName() {
   return kTrustedSpdyProxyFieldTrialName;
 }
 
 bool IsIncludedInTrustedSpdyProxyFieldTrial() {
-  if (base::StartsWith(
-          FieldTrialList::FindFullName(GetTrustedSpdyProxyFieldTrialName()),
-          kControl, base::CompareCase::SENSITIVE)) {
+  if (base::StartsWith(base::FieldTrialList::FindFullName(
+                           GetTrustedSpdyProxyFieldTrialName()),
+                       kControl, base::CompareCase::SENSITIVE)) {
     return false;
   }
-  if (base::StartsWith(
-          FieldTrialList::FindFullName(GetTrustedSpdyProxyFieldTrialName()),
-          kDisabled, base::CompareCase::SENSITIVE)) {
+  if (base::StartsWith(base::FieldTrialList::FindFullName(
+                           GetTrustedSpdyProxyFieldTrialName()),
+                       kDisabled, base::CompareCase::SENSITIVE)) {
     return false;
   }
   // Trusted SPDY proxy experiment is enabled by default.
@@ -149,27 +151,29 @@
 
 bool IsIncludedInLoFiControlFieldTrial() {
   return !IsLoFiOnViaFlags() && !IsLoFiDisabledViaFlags() &&
-         base::StartsWith(FieldTrialList::FindFullName(GetLoFiFieldTrialName()),
-                          kControl, base::CompareCase::SENSITIVE);
+         base::StartsWith(
+             base::FieldTrialList::FindFullName(GetLoFiFieldTrialName()),
+             kControl, base::CompareCase::SENSITIVE);
 }
 
 bool IsIncludedInLitePageFieldTrial() {
   return !IsLoFiOnViaFlags() && !IsLoFiDisabledViaFlags() &&
-         base::StartsWith(FieldTrialList::FindFullName(GetLoFiFieldTrialName()),
-                          kLitePage, base::CompareCase::SENSITIVE);
+         base::StartsWith(
+             base::FieldTrialList::FindFullName(GetLoFiFieldTrialName()),
+             kLitePage, base::CompareCase::SENSITIVE);
 }
 
 bool IsIncludedInServerExperimentsFieldTrial() {
   return !base::CommandLine::ForCurrentProcess()->HasSwitch(
              data_reduction_proxy::switches::
                  kDataReductionProxyServerExperimentsDisabled) &&
-         FieldTrialList::FindFullName(kServerExperimentsFieldTrial)
+         base::FieldTrialList::FindFullName(kServerExperimentsFieldTrial)
                  .find(kDisabled) != 0;
 }
 bool IsIncludedInTamperDetectionExperiment() {
   return IsIncludedInServerExperimentsFieldTrial() &&
          base::StartsWith(
-             FieldTrialList::FindFullName(kServerExperimentsFieldTrial),
+             base::FieldTrialList::FindFullName(kServerExperimentsFieldTrial),
              "TamperDetection_Enabled", base::CompareCase::SENSITIVE);
 }
 
@@ -177,8 +181,8 @@
   // Fetching of the warmup URL can be enabled only for Enabled* and Control*
   // groups.
   if (!IsIncludedInQuicFieldTrial() &&
-      !base::StartsWith(FieldTrialList::FindFullName(kQuicFieldTrial), kControl,
-                        base::CompareCase::SENSITIVE)) {
+      !base::StartsWith(base::FieldTrialList::FindFullName(kQuicFieldTrial),
+                        kControl, base::CompareCase::SENSITIVE)) {
     return false;
   }
 
@@ -248,12 +252,12 @@
 }
 
 bool IsIncludedInQuicFieldTrial() {
-  if (base::StartsWith(FieldTrialList::FindFullName(kQuicFieldTrial), kControl,
-                       base::CompareCase::SENSITIVE)) {
+  if (base::StartsWith(base::FieldTrialList::FindFullName(kQuicFieldTrial),
+                       kControl, base::CompareCase::SENSITIVE)) {
     return false;
   }
-  if (base::StartsWith(FieldTrialList::FindFullName(kQuicFieldTrial), kDisabled,
-                       base::CompareCase::SENSITIVE)) {
+  if (base::StartsWith(base::FieldTrialList::FindFullName(kQuicFieldTrial),
+                       kDisabled, base::CompareCase::SENSITIVE)) {
     return false;
   }
   // QUIC is enabled by default.
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_params.h b/components/data_reduction_proxy/core/common/data_reduction_proxy_params.h
index c78c5199..1d8e37e 100644
--- a/components/data_reduction_proxy/core/common/data_reduction_proxy_params.h
+++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_params.h
@@ -38,6 +38,10 @@
 // is in effect.
 bool IsIncludedInHoldbackFieldTrial();
 
+// The name of the Holdback experiment group, this can return an empty string if
+// not included in a group.
+std::string HoldbackFieldTrialGroup();
+
 // Returns the name of the trusted SPDY/HTTP2 proxy field trial.
 const char* GetTrustedSpdyProxyFieldTrialName();
 
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_params_unittest.cc b/components/data_reduction_proxy/core/common/data_reduction_proxy_params_unittest.cc
index e5fda38..4ba6173c 100644
--- a/components/data_reduction_proxy/core/common/data_reduction_proxy_params_unittest.cc
+++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_params_unittest.cc
@@ -494,6 +494,7 @@
 
     ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial(
         "DataCompressionProxyHoldback", test.trial_group_name));
+    EXPECT_EQ(test.trial_group_name, params::HoldbackFieldTrialGroup());
     EXPECT_EQ(test.expected_enabled, params::IsIncludedInHoldbackFieldTrial())
         << test.trial_group_name;
   }
diff --git a/components/data_reduction_proxy/proto/pageload_metrics.proto b/components/data_reduction_proxy/proto/pageload_metrics.proto
index 840537a..457cbff 100644
--- a/components/data_reduction_proxy/proto/pageload_metrics.proto
+++ b/components/data_reduction_proxy/proto/pageload_metrics.proto
@@ -102,7 +102,7 @@
   // The previews type that was used on the page.
   optional PreviewsType previews_type = 18;
 
-  // Whether the user is included in the data reduction proxy holdback
-  // experiment.
-  optional bool holdback = 19;
+  // The name of the finch group for the data reduction proxy holdback
+  // experiment. This can be an empty string if not included in a finch group.
+  optional string holdback_group = 19;
 }
diff --git a/components/offline_pages/core/prefetch/get_operation_request_unittest.cc b/components/offline_pages/core/prefetch/get_operation_request_unittest.cc
index 45f2717..770cba5 100644
--- a/components/offline_pages/core/prefetch/get_operation_request_unittest.cc
+++ b/components/offline_pages/core/prefetch/get_operation_request_unittest.cc
@@ -23,7 +23,13 @@
 
 namespace {
 const version_info::Channel kTestChannel = version_info::Channel::UNKNOWN;
-const char kTestMethodName[] = "Test name";
+
+// Operations include part of the path in the operation name.
+const char kTestOperationName[] = "operations/test-operation-1234";
+
+// This is hard coded so we can express the known URL structure for operations.
+const char kServerPathForTestOperation[] = "/v1/operations/test-operation-1234";
+
 }  // namespace
 
 // All tests cases here only validate the request data and check for general
@@ -34,7 +40,7 @@
   std::unique_ptr<GetOperationRequest> CreateRequest(
       const PrefetchRequestFinishedCallback& callback) {
     return std::unique_ptr<GetOperationRequest>(new GetOperationRequest(
-        kTestMethodName, kTestChannel, request_context(), callback));
+        kTestOperationName, kTestChannel, request_context(), callback));
   }
 };
 
@@ -43,8 +49,10 @@
   std::unique_ptr<GetOperationRequest> request(CreateRequest(callback.Get()));
 
   net::TestURLFetcher* fetcher = GetRunningFetcher();
-  EXPECT_TRUE(fetcher->GetOriginalURL().SchemeIs(url::kHttpsScheme));
-  EXPECT_TRUE(base::StartsWith(fetcher->GetOriginalURL().query(), "key",
+  GURL fetcher_url = fetcher->GetOriginalURL();
+  EXPECT_TRUE(fetcher_url.SchemeIs(url::kHttpsScheme));
+  EXPECT_EQ(kServerPathForTestOperation, fetcher_url.path());
+  EXPECT_TRUE(base::StartsWith(fetcher_url.query(), "key",
                                base::CompareCase::SENSITIVE));
 
   net::HttpRequestHeaders headers;
diff --git a/components/payments/content/BUILD.gn b/components/payments/content/BUILD.gn
index 6396049..c856ae90 100644
--- a/components/payments/content/BUILD.gn
+++ b/components/payments/content/BUILD.gn
@@ -24,11 +24,11 @@
     "//components/autofill/core/browser",
     "//components/keyed_service/content",
     "//components/payments/core",
-    "//components/payments/mojom",
     "//components/prefs",
     "//components/strings:components_strings_grit",
     "//content/public/browser",
     "//mojo/public/cpp/bindings",
+    "//third_party/WebKit/public:blink_headers",
     "//third_party/libphonenumber",
   ]
 }
@@ -53,11 +53,11 @@
     "//components/data_use_measurement/core",
     "//components/link_header_util",
     "//components/payments/core",
-    "//components/payments/mojom",
     "//components/payments/mojom:mojom_parser",
     "//components/strings",
     "//content/public/browser",
     "//net",
+    "//third_party/WebKit/public:blink_headers",
     "//third_party/re2",
     "//ui/base",
     "//url",
@@ -88,11 +88,11 @@
     "//components/autofill/core/browser:test_support",
     "//components/payments/core",
     "//components/payments/core:test_support",
-    "//components/payments/mojom",
     "//components/strings:components_strings_grit",
     "//content/test:test_support",
     "//net:test_support",
     "//testing/gtest",
+    "//third_party/WebKit/public:blink_headers",
     "//third_party/icu",
     "//third_party/libaddressinput:test_support",
   ]
diff --git a/components/payments/content/DEPS b/components/payments/content/DEPS
index de79a74ac..0d4155dd 100644
--- a/components/payments/content/DEPS
+++ b/components/payments/content/DEPS
@@ -10,6 +10,7 @@
   "+content/public",
   "+mojo/public/cpp",
   "+net",
+  "+third_party/WebKit/public/platform/modules/payments",
   "+third_party/libphonenumber",
   "+third_party/re2",
   "+ui/base",
diff --git a/components/payments/content/android/BUILD.gn b/components/payments/content/android/BUILD.gn
index c11f428..3393923 100644
--- a/components/payments/content/android/BUILD.gn
+++ b/components/payments/content/android/BUILD.gn
@@ -26,7 +26,6 @@
     "//base",
     "//components/payments/content:utils",
     "//components/payments/core",
-    "//components/payments/mojom",
     "//content/public/browser",
     "//net",
   ]
@@ -53,9 +52,9 @@
   ]
   deps = [
     "//base:base_java",
-    "//components/payments/mojom:mojom_java",
     "//components/payments/mojom:mojom_parser_java",
     "//content/public/android:content_java",
     "//mojo/public/java:bindings_java",
+    "//third_party/WebKit/public:android_mojo_bindings_java",
   ]
 }
diff --git a/components/payments/content/android/payment_details_validation_android.cc b/components/payments/content/android/payment_details_validation_android.cc
index fa88d63..d13afe6 100644
--- a/components/payments/content/android/payment_details_validation_android.cc
+++ b/components/payments/content/android/payment_details_validation_android.cc
@@ -13,8 +13,8 @@
 
 #include "base/android/jni_android.h"
 #include "components/payments/content/payment_details_validation.h"
-#include "components/payments/mojom/payment_request.mojom.h"
 #include "jni/PaymentValidator_jni.h"
+#include "third_party/WebKit/public/platform/modules/payments/payment_request.mojom.h"
 
 namespace payments {
 
diff --git a/components/payments/content/payment_details_validation.cc b/components/payments/content/payment_details_validation.cc
index e953cfb7..c61946e 100644
--- a/components/payments/content/payment_details_validation.cc
+++ b/components/payments/content/payment_details_validation.cc
@@ -8,7 +8,7 @@
 #include <vector>
 
 #include "components/payments/content/payments_validators.h"
-#include "components/payments/mojom/payment_request.mojom.h"
+#include "third_party/WebKit/public/platform/modules/payments/payment_request.mojom.h"
 
 namespace payments {
 namespace {
diff --git a/components/payments/content/payment_details_validation.h b/components/payments/content/payment_details_validation.h
index fda9990..358fa8e 100644
--- a/components/payments/content/payment_details_validation.h
+++ b/components/payments/content/payment_details_validation.h
@@ -7,7 +7,7 @@
 
 #include <string>
 
-#include "components/payments/mojom/payment_request.mojom.h"
+#include "third_party/WebKit/public/platform/modules/payments/payment_request.mojom.h"
 
 namespace payments {
 
diff --git a/components/payments/content/payment_request.h b/components/payments/content/payment_request.h
index e0ab3fa..ff8c2474 100644
--- a/components/payments/content/payment_request.h
+++ b/components/payments/content/payment_request.h
@@ -13,9 +13,9 @@
 #include "components/payments/content/payment_request_state.h"
 #include "components/payments/core/journey_logger.h"
 #include "components/payments/core/payment_request_delegate.h"
-#include "components/payments/mojom/payment_request.mojom.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "mojo/public/cpp/bindings/interface_request.h"
+#include "third_party/WebKit/public/platform/modules/payments/payment_request.mojom.h"
 #include "url/gurl.h"
 
 namespace content {
diff --git a/components/payments/content/payment_request_spec.h b/components/payments/content/payment_request_spec.h
index 1835c75..c8fd2a6 100644
--- a/components/payments/content/payment_request_spec.h
+++ b/components/payments/content/payment_request_spec.h
@@ -15,7 +15,7 @@
 #include "base/strings/string16.h"
 #include "components/payments/core/currency_formatter.h"
 #include "components/payments/core/payment_options_provider.h"
-#include "components/payments/mojom/payment_request.mojom.h"
+#include "third_party/WebKit/public/platform/modules/payments/payment_request.mojom.h"
 
 namespace payments {
 
diff --git a/components/payments/content/payment_request_spec_unittest.cc b/components/payments/content/payment_request_spec_unittest.cc
index 235a17ad..e5388c7 100644
--- a/components/payments/content/payment_request_spec_unittest.cc
+++ b/components/payments/content/payment_request_spec_unittest.cc
@@ -8,9 +8,9 @@
 
 #include "base/memory/weak_ptr.h"
 #include "base/strings/utf_string_conversions.h"
-#include "components/payments/mojom/payment_request.mojom.h"
 #include "components/strings/grit/components_strings.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/public/platform/modules/payments/payment_request.mojom.h"
 #include "ui/base/l10n/l10n_util.h"
 
 namespace payments {
diff --git a/components/payments/content/payment_request_state.h b/components/payments/content/payment_request_state.h
index a62d2b84..19fcb35 100644
--- a/components/payments/content/payment_request_state.h
+++ b/components/payments/content/payment_request_state.h
@@ -15,7 +15,7 @@
 #include "components/payments/content/payment_response_helper.h"
 #include "components/payments/core/address_normalizer.h"
 #include "components/payments/core/payments_profile_comparator.h"
-#include "components/payments/mojom/payment_request.mojom.h"
+#include "third_party/WebKit/public/platform/modules/payments/payment_request.mojom.h"
 
 namespace autofill {
 class AutofillProfile;
diff --git a/components/payments/content/payment_request_state_unittest.cc b/components/payments/content/payment_request_state_unittest.cc
index 186fca9..bb6c602 100644
--- a/components/payments/content/payment_request_state_unittest.cc
+++ b/components/payments/content/payment_request_state_unittest.cc
@@ -14,8 +14,8 @@
 #include "components/autofill/core/browser/test_personal_data_manager.h"
 #include "components/payments/content/payment_request_spec.h"
 #include "components/payments/core/test_payment_request_delegate.h"
-#include "components/payments/mojom/payment_request.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/public/platform/modules/payments/payment_request.mojom.h"
 
 namespace payments {
 
diff --git a/components/payments/content/payment_request_web_contents_manager.h b/components/payments/content/payment_request_web_contents_manager.h
index 25a47ef..8e35e704 100644
--- a/components/payments/content/payment_request_web_contents_manager.h
+++ b/components/payments/content/payment_request_web_contents_manager.h
@@ -10,10 +10,10 @@
 
 #include "base/macros.h"
 #include "components/payments/content/payment_request.h"
-#include "components/payments/mojom/payment_request.mojom.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/web_contents_user_data.h"
 #include "mojo/public/cpp/bindings/binding.h"
+#include "third_party/WebKit/public/platform/modules/payments/payment_request.mojom.h"
 
 namespace content {
 class RenderFrameHost;
diff --git a/components/payments/content/payment_response_helper.h b/components/payments/content/payment_response_helper.h
index b9cf024..fb87bdb 100644
--- a/components/payments/content/payment_response_helper.h
+++ b/components/payments/content/payment_response_helper.h
@@ -9,7 +9,7 @@
 #include "components/autofill/core/browser/autofill_profile.h"
 #include "components/payments/core/address_normalizer.h"
 #include "components/payments/core/payment_instrument.h"
-#include "components/payments/mojom/payment_request.mojom.h"
+#include "third_party/WebKit/public/platform/modules/payments/payment_request.mojom.h"
 
 namespace payments {
 
diff --git a/components/payments/content/payment_response_helper_unittest.cc b/components/payments/content/payment_response_helper_unittest.cc
index 6279886ca..d21dac6 100644
--- a/components/payments/content/payment_response_helper_unittest.cc
+++ b/components/payments/content/payment_response_helper_unittest.cc
@@ -18,8 +18,8 @@
 #include "components/payments/content/payment_request_spec.h"
 #include "components/payments/core/autofill_payment_instrument.h"
 #include "components/payments/core/test_payment_request_delegate.h"
-#include "components/payments/mojom/payment_request.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/public/platform/modules/payments/payment_request.mojom.h"
 
 namespace payments {
 
diff --git a/components/payments/content/payments_validators.h b/components/payments/content/payments_validators.h
index 33af759..331e185 100644
--- a/components/payments/content/payments_validators.h
+++ b/components/payments/content/payments_validators.h
@@ -8,7 +8,7 @@
 #include <string>
 
 #include "base/macros.h"
-#include "components/payments/mojom/payment_request.mojom.h"
+#include "third_party/WebKit/public/platform/modules/payments/payment_request.mojom.h"
 
 namespace payments {
 
diff --git a/components/payments/mojom/BUILD.gn b/components/payments/mojom/BUILD.gn
index fc51993..2f529d48 100644
--- a/components/payments/mojom/BUILD.gn
+++ b/components/payments/mojom/BUILD.gn
@@ -4,15 +4,6 @@
 
 import("//mojo/public/tools/bindings/mojom.gni")
 
-mojom("mojom") {
-  sources = [
-    "payment_request.mojom",
-  ]
-
-  # TODO(crbug.com/699569): Convert to use the new JS bindings.
-  use_new_js_bindings = false
-}
-
 mojom("mojom_parser") {
   sources = [
     "payment_manifest_parser.mojom",
@@ -22,15 +13,3 @@
     "//url/mojo:url_mojom_gurl",
   ]
 }
-
-mojom("mojom_payment_app") {
-  sources = [
-    "payment_app.mojom",
-  ]
-
-  public_deps = [
-    ":mojom",
-    "//mojo/common:common_custom_types",
-    "//url/mojo:url_mojom_gurl",
-  ]
-}
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 94563c7..3f9be0a 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -46,7 +46,6 @@
     "//components/metrics:single_sample_metrics",
     "//components/mime_util",
     "//components/network_session_configurator/browser",
-    "//components/payments/mojom:mojom_payment_app",
     "//components/rappor",
     "//components/tracing",
     "//components/tracing:startup_tracing",
diff --git a/content/browser/DEPS b/content/browser/DEPS
index 437c816..f33d518 100644
--- a/content/browser/DEPS
+++ b/content/browser/DEPS
@@ -101,6 +101,7 @@
   "+third_party/WebKit/public/platform/modules/notifications/notification.mojom.h",
   "+third_party/WebKit/public/platform/modules/notifications/notification_service.mojom.h",
   "+third_party/WebKit/public/platform/modules/offscreencanvas/offscreen_canvas_surface.mojom.h",
+  "+third_party/WebKit/public/platform/modules/payments/payment_app.mojom.h",
   "+third_party/WebKit/public/platform/modules/permissions/permission.mojom.h",
   "+third_party/WebKit/public/platform/modules/permissions/permission_status.mojom.h",
   "+third_party/WebKit/public/platform/modules/presentation/presentation.mojom.h",
diff --git a/content/browser/frame_host/interstitial_page_impl.cc b/content/browser/frame_host/interstitial_page_impl.cc
index 465086c..a4f63821 100644
--- a/content/browser/frame_host/interstitial_page_impl.cc
+++ b/content/browser/frame_host/interstitial_page_impl.cc
@@ -807,6 +807,10 @@
 
 void InterstitialPageImpl::Disable() {
   enabled_ = false;
+
+  // Also let the InterstitialPageNavigatorImpl know.
+  static_cast<InterstitialPageNavigatorImpl*>(frame_tree_->root()->navigator())
+      ->Disable();
 }
 
 void InterstitialPageImpl::Shutdown() {
diff --git a/content/browser/frame_host/interstitial_page_navigator_impl.cc b/content/browser/frame_host/interstitial_page_navigator_impl.cc
index 76f3840..a240942a 100644
--- a/content/browser/frame_host/interstitial_page_navigator_impl.cc
+++ b/content/browser/frame_host/interstitial_page_navigator_impl.cc
@@ -15,7 +15,8 @@
     InterstitialPageImpl* interstitial,
     NavigationControllerImpl* navigation_controller)
     : interstitial_(interstitial),
-      controller_(navigation_controller) {}
+      controller_(navigation_controller),
+      enabled_(true) {}
 
 InterstitialPageNavigatorImpl::~InterstitialPageNavigatorImpl() {}
 
@@ -32,6 +33,10 @@
     const GURL& url,
     const std::vector<GURL>& redirect_chain,
     const base::TimeTicks& navigation_start) {
+  // Do not proceed if the interstitial itself has been disabled.
+  if (!enabled_)
+    return;
+
   // The interstitial page should only navigate once.
   DCHECK(!render_frame_host->navigation_handle());
   render_frame_host->SetNavigationHandle(NavigationHandleImpl::Create(
@@ -50,6 +55,10 @@
     RenderFrameHostImpl* render_frame_host,
     const FrameHostMsg_DidCommitProvisionalLoad_Params& input_params,
     std::unique_ptr<NavigationHandleImpl> navigation_handle) {
+  // Do not proceed if the interstitial itself has been disabled.
+  if (!enabled_)
+    return;
+
   navigation_handle->DidCommitNavigation(
       input_params, true, false, GURL(), NAVIGATION_TYPE_NEW_PAGE,
       render_frame_host);
@@ -61,4 +70,11 @@
                              input_params);
 }
 
+void InterstitialPageNavigatorImpl::Disable() {
+  enabled_ = false;
+
+  // This is no longer safe to access.
+  controller_ = nullptr;
+}
+
 }  // namespace content
diff --git a/content/browser/frame_host/interstitial_page_navigator_impl.h b/content/browser/frame_host/interstitial_page_navigator_impl.h
index f0f8f32..6eed5a5 100644
--- a/content/browser/frame_host/interstitial_page_navigator_impl.h
+++ b/content/browser/frame_host/interstitial_page_navigator_impl.h
@@ -24,6 +24,7 @@
       InterstitialPageImpl* interstitial,
       NavigationControllerImpl* navigation_controller);
 
+  // Navigator implementation.
   NavigatorDelegate* GetDelegate() override;
   NavigationController* GetController() override;
   void DidStartProvisionalLoad(
@@ -36,6 +37,10 @@
       const FrameHostMsg_DidCommitProvisionalLoad_Params& input_params,
       std::unique_ptr<NavigationHandleImpl> navigation_handle) override;
 
+  // Disables any further action when the interstitial page is preparing to
+  // delete itself.
+  void Disable();
+
  private:
   ~InterstitialPageNavigatorImpl() override;
 
@@ -46,6 +51,10 @@
   // The NavigationController associated with this navigator.
   NavigationControllerImpl* controller_;
 
+  // Whether this interstitial is still enabled.  Becomes false when the
+  // interstitial page is asychronously deleting itself.
+  bool enabled_;
+
   DISALLOW_COPY_AND_ASSIGN(InterstitialPageNavigatorImpl);
 };
 
diff --git a/content/browser/payments/payment_app_browsertest.cc b/content/browser/payments/payment_app_browsertest.cc
index eb99e22..6e6ca7e 100644
--- a/content/browser/payments/payment_app_browsertest.cc
+++ b/content/browser/payments/payment_app_browsertest.cc
@@ -5,7 +5,6 @@
 #include "base/command_line.h"
 #include "base/macros.h"
 #include "base/run_loop.h"
-#include "components/payments/mojom/payment_app.mojom.h"
 #include "content/browser/storage_partition_impl.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/payment_app_provider.h"
@@ -17,6 +16,7 @@
 #include "content/shell/browser/shell.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/public/platform/modules/payments/payment_app.mojom.h"
 
 namespace content {
 namespace {
diff --git a/content/browser/payments/payment_app_content_unittest_base.h b/content/browser/payments/payment_app_content_unittest_base.h
index 3204ba56..5d1d7b3 100644
--- a/content/browser/payments/payment_app_content_unittest_base.h
+++ b/content/browser/payments/payment_app_content_unittest_base.h
@@ -11,9 +11,9 @@
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
-#include "components/payments/mojom/payment_app.mojom.h"
 #include "content/browser/payments/payment_manager.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/public/platform/modules/payments/payment_app.mojom.h"
 #include "url/gurl.h"
 
 namespace content {
diff --git a/content/browser/payments/payment_app_context_impl.h b/content/browser/payments/payment_app_context_impl.h
index 82d0422..7dcaa15 100644
--- a/content/browser/payments/payment_app_context_impl.h
+++ b/content/browser/payments/payment_app_context_impl.h
@@ -10,9 +10,9 @@
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
-#include "components/payments/mojom/payment_app.mojom.h"
 #include "content/browser/payments/payment_app_database.h"
 #include "content/common/content_export.h"
+#include "third_party/WebKit/public/platform/modules/payments/payment_app.mojom.h"
 
 namespace service_manager {
 struct BindSourceInfo;
diff --git a/content/browser/payments/payment_app_database.h b/content/browser/payments/payment_app_database.h
index 230db47a..6931226 100644
--- a/content/browser/payments/payment_app_database.h
+++ b/content/browser/payments/payment_app_database.h
@@ -11,7 +11,6 @@
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "components/payments/mojom/payment_app.mojom.h"
 #include "content/browser/payments/payment_instrument_icon_fetcher.h"
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
 #include "content/browser/service_worker/service_worker_registration.h"
@@ -19,6 +18,7 @@
 #include "content/common/service_worker/service_worker_status_code.h"
 #include "content/public/browser/stored_payment_instrument.h"
 #include "mojo/public/cpp/bindings/binding.h"
+#include "third_party/WebKit/public/platform/modules/payments/payment_app.mojom.h"
 
 namespace content {
 
diff --git a/content/browser/payments/payment_app_provider_impl_unittest.cc b/content/browser/payments/payment_app_provider_impl_unittest.cc
index 08f832c..af91b215 100644
--- a/content/browser/payments/payment_app_provider_impl_unittest.cc
+++ b/content/browser/payments/payment_app_provider_impl_unittest.cc
@@ -8,10 +8,10 @@
 
 #include "base/macros.h"
 #include "base/run_loop.h"
-#include "components/payments/mojom/payment_app.mojom.h"
 #include "content/browser/payments/payment_app_content_unittest_base.h"
 #include "content/browser/payments/payment_app_provider_impl.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/public/platform/modules/payments/payment_app.mojom.h"
 #include "url/gurl.h"
 
 namespace content {
diff --git a/content/browser/payments/payment_instrument_icon_fetcher.h b/content/browser/payments/payment_instrument_icon_fetcher.h
index a4fc3958..38416ba 100644
--- a/content/browser/payments/payment_instrument_icon_fetcher.h
+++ b/content/browser/payments/payment_instrument_icon_fetcher.h
@@ -10,11 +10,11 @@
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
-#include "components/payments/mojom/payment_app.mojom.h"
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
 #include "net/url_request/url_fetcher.h"
 #include "net/url_request/url_fetcher_delegate.h"
 #include "net/url_request/url_request_context_getter.h"
+#include "third_party/WebKit/public/platform/modules/payments/payment_app.mojom.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 
 namespace content {
diff --git a/content/browser/payments/payment_manager.cc b/content/browser/payments/payment_manager.cc
index ce9445e..d1d303f 100644
--- a/content/browser/payments/payment_manager.cc
+++ b/content/browser/payments/payment_manager.cc
@@ -41,7 +41,7 @@
 
 void PaymentManager::DeletePaymentInstrument(
     const std::string& instrument_key,
-    PaymentManager::DeletePaymentInstrumentCallback callback) {
+    const PaymentManager::DeletePaymentInstrumentCallback& callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   payment_app_context_->payment_app_database()->DeletePaymentInstrument(
@@ -50,7 +50,7 @@
 
 void PaymentManager::GetPaymentInstrument(
     const std::string& instrument_key,
-    PaymentManager::GetPaymentInstrumentCallback callback) {
+    const PaymentManager::GetPaymentInstrumentCallback& callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   payment_app_context_->payment_app_database()->ReadPaymentInstrument(
@@ -58,7 +58,7 @@
 }
 
 void PaymentManager::KeysOfPaymentInstruments(
-    PaymentManager::KeysOfPaymentInstrumentsCallback callback) {
+    const PaymentManager::KeysOfPaymentInstrumentsCallback& callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   payment_app_context_->payment_app_database()->KeysOfPaymentInstruments(
@@ -67,7 +67,7 @@
 
 void PaymentManager::HasPaymentInstrument(
     const std::string& instrument_key,
-    PaymentManager::HasPaymentInstrumentCallback callback) {
+    const PaymentManager::HasPaymentInstrumentCallback& callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   payment_app_context_->payment_app_database()->HasPaymentInstrument(
@@ -77,7 +77,7 @@
 void PaymentManager::SetPaymentInstrument(
     const std::string& instrument_key,
     payments::mojom::PaymentInstrumentPtr details,
-    PaymentManager::SetPaymentInstrumentCallback callback) {
+    const PaymentManager::SetPaymentInstrumentCallback& callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   payment_app_context_->payment_app_database()->WritePaymentInstrument(
@@ -85,7 +85,7 @@
 }
 
 void PaymentManager::ClearPaymentInstruments(
-    ClearPaymentInstrumentsCallback callback) {
+    const ClearPaymentInstrumentsCallback& callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   payment_app_context_->payment_app_database()->ClearPaymentInstruments(
diff --git a/content/browser/payments/payment_manager.h b/content/browser/payments/payment_manager.h
index ebb2f73..86704cf 100644
--- a/content/browser/payments/payment_manager.h
+++ b/content/browser/payments/payment_manager.h
@@ -9,9 +9,9 @@
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "components/payments/mojom/payment_app.mojom.h"
 #include "content/common/content_export.h"
 #include "mojo/public/cpp/bindings/binding.h"
+#include "third_party/WebKit/public/platform/modules/payments/payment_app.mojom.h"
 #include "url/gurl.h"
 
 namespace content {
@@ -36,18 +36,21 @@
   void Init(const std::string& scope) override;
   void DeletePaymentInstrument(
       const std::string& instrument_key,
-      DeletePaymentInstrumentCallback callback) override;
-  void GetPaymentInstrument(const std::string& instrument_key,
-                            GetPaymentInstrumentCallback callback) override;
+      const DeletePaymentInstrumentCallback& callback) override;
+  void GetPaymentInstrument(
+      const std::string& instrument_key,
+      const GetPaymentInstrumentCallback& callback) override;
   void KeysOfPaymentInstruments(
-      KeysOfPaymentInstrumentsCallback callback) override;
-  void HasPaymentInstrument(const std::string& instrument_key,
-                            HasPaymentInstrumentCallback callback) override;
-  void SetPaymentInstrument(const std::string& instrument_key,
-                            payments::mojom::PaymentInstrumentPtr details,
-                            SetPaymentInstrumentCallback callback) override;
+      const KeysOfPaymentInstrumentsCallback& callback) override;
+  void HasPaymentInstrument(
+      const std::string& instrument_key,
+      const HasPaymentInstrumentCallback& callback) override;
+  void SetPaymentInstrument(
+      const std::string& instrument_key,
+      payments::mojom::PaymentInstrumentPtr details,
+      const SetPaymentInstrumentCallback& callback) override;
   void ClearPaymentInstruments(
-      ClearPaymentInstrumentsCallback callback) override;
+      const ClearPaymentInstrumentsCallback& callback) override;
 
   // Called when an error is detected on binding_.
   void OnConnectionError();
diff --git a/content/browser/payments/payment_manager_unittest.cc b/content/browser/payments/payment_manager_unittest.cc
index 42d80cb..c027448 100644
--- a/content/browser/payments/payment_manager_unittest.cc
+++ b/content/browser/payments/payment_manager_unittest.cc
@@ -6,9 +6,9 @@
 
 #include "base/macros.h"
 #include "base/run_loop.h"
-#include "components/payments/mojom/payment_app.mojom.h"
 #include "content/browser/payments/payment_app_content_unittest_base.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/public/platform/modules/payments/payment_app.mojom.h"
 #include "url/gurl.h"
 
 namespace content {
diff --git a/content/browser/renderer_host/media/service_launched_video_capture_device.cc b/content/browser/renderer_host/media/service_launched_video_capture_device.cc
index dc58efa..43b0e49 100644
--- a/content/browser/renderer_host/media/service_launched_video_capture_device.cc
+++ b/content/browser/renderer_host/media/service_launched_video_capture_device.cc
@@ -24,35 +24,42 @@
 void ServiceLaunchedVideoCaptureDevice::GetPhotoState(
     media::VideoCaptureDevice::GetPhotoStateCallback callback) const {
   DCHECK(sequence_checker_.CalledOnValidSequence());
-  NOTIMPLEMENTED();
+  device_->GetPhotoState(
+      base::Bind(&ServiceLaunchedVideoCaptureDevice::OnGetPhotoStateResponse,
+                 base::Unretained(this), base::Passed(&callback)));
 }
 
 void ServiceLaunchedVideoCaptureDevice::SetPhotoOptions(
     media::mojom::PhotoSettingsPtr settings,
     media::VideoCaptureDevice::SetPhotoOptionsCallback callback) {
   DCHECK(sequence_checker_.CalledOnValidSequence());
-  NOTIMPLEMENTED();
+  device_->SetPhotoOptions(
+      std::move(settings),
+      base::Bind(&ServiceLaunchedVideoCaptureDevice::OnSetPhotoOptionsResponse,
+                 base::Unretained(this), base::Passed(&callback)));
 }
 
 void ServiceLaunchedVideoCaptureDevice::TakePhoto(
     media::VideoCaptureDevice::TakePhotoCallback callback) {
   DCHECK(sequence_checker_.CalledOnValidSequence());
-  NOTIMPLEMENTED();
+  device_->TakePhoto(
+      base::Bind(&ServiceLaunchedVideoCaptureDevice::OnTakePhotoResponse,
+                 base::Unretained(this), base::Passed(&callback)));
 }
 
 void ServiceLaunchedVideoCaptureDevice::MaybeSuspendDevice() {
   DCHECK(sequence_checker_.CalledOnValidSequence());
-  // Not yet implemented on service side. Do nothing here.
+  device_->MaybeSuspend();
 }
 
 void ServiceLaunchedVideoCaptureDevice::ResumeDevice() {
   DCHECK(sequence_checker_.CalledOnValidSequence());
-  // Not yet implemented on service side. Do nothing here.
+  device_->Resume();
 }
 
 void ServiceLaunchedVideoCaptureDevice::RequestRefreshFrame() {
   DCHECK(sequence_checker_.CalledOnValidSequence());
-  // Not yet implemented on service side. Do nothing here.
+  device_->RequestRefreshFrame();
 }
 
 void ServiceLaunchedVideoCaptureDevice::SetDesktopCaptureWindowIdAsync(
@@ -76,4 +83,28 @@
   base::ResetAndReturn(&connection_lost_cb_).Run();
 }
 
+void ServiceLaunchedVideoCaptureDevice::OnGetPhotoStateResponse(
+    media::VideoCaptureDevice::GetPhotoStateCallback callback,
+    media::mojom::PhotoStatePtr capabilities) const {
+  if (!capabilities)
+    return;
+  callback.Run(std::move(capabilities));
+}
+
+void ServiceLaunchedVideoCaptureDevice::OnSetPhotoOptionsResponse(
+    media::VideoCaptureDevice::SetPhotoOptionsCallback callback,
+    bool success) {
+  if (!success)
+    return;
+  callback.Run(true);
+}
+
+void ServiceLaunchedVideoCaptureDevice::OnTakePhotoResponse(
+    media::VideoCaptureDevice::TakePhotoCallback callback,
+    media::mojom::BlobPtr blob) {
+  if (!blob)
+    return;
+  callback.Run(std::move(blob));
+}
+
 }  // namespace content
diff --git a/content/browser/renderer_host/media/service_launched_video_capture_device.h b/content/browser/renderer_host/media/service_launched_video_capture_device.h
index 97a1bcd..3ff63ee 100644
--- a/content/browser/renderer_host/media/service_launched_video_capture_device.h
+++ b/content/browser/renderer_host/media/service_launched_video_capture_device.h
@@ -37,6 +37,15 @@
 
  private:
   void OnLostConnectionToDevice();
+  void OnGetPhotoStateResponse(
+      media::VideoCaptureDevice::GetPhotoStateCallback callback,
+      media::mojom::PhotoStatePtr capabilities) const;
+  void OnSetPhotoOptionsResponse(
+      media::VideoCaptureDevice::SetPhotoOptionsCallback callback,
+      bool success);
+  void OnTakePhotoResponse(
+      media::VideoCaptureDevice::TakePhotoCallback callback,
+      media::mojom::BlobPtr blob);
 
   video_capture::mojom::DevicePtr device_;
   base::OnceClosure connection_lost_cb_;
diff --git a/content/browser/service_worker/service_worker_metrics.cc b/content/browser/service_worker/service_worker_metrics.cc
index 20c5fd9..418e9748 100644
--- a/content/browser/service_worker/service_worker_metrics.cc
+++ b/content/browser/service_worker/service_worker_metrics.cc
@@ -515,13 +515,11 @@
         "ServiceWorker.ActivatedWorkerPreparationForMainFrame.Time_"
         "NavigationPreloadEnabled",
         time);
-    // We're mostly interested in when the worker needed to start up. To avoid
-    // using too much memory, just log the the common case of startup in an
-    // existing process.
-    if (preparation == WorkerPreparationType::START_IN_EXISTING_PROCESS) {
+    // We're mostly interested in when the worker needed to start up.
+    if (initial_worker_status != EmbeddedWorkerStatus::RUNNING) {
       UMA_HISTOGRAM_MEDIUM_TIMES(
           "ServiceWorker.ActivatedWorkerPreparationForMainFrame.Time_"
-          "StartWorkerExistingProcess_NavigationPreloadEnabled",
+          "WorkerStartOccurred_NavigationPreloadEnabled",
           time);
     }
   }
@@ -849,7 +847,6 @@
     base::TimeDelta worker_start,
     base::TimeDelta response_start,
     EmbeddedWorkerStatus initial_worker_status,
-    StartSituation start_situation,
     ResourceType resource_type) {
   DCHECK_GE(worker_start.ToInternalValue(), 0);
   DCHECK_GE(response_start.ToInternalValue(), 0);
@@ -860,10 +857,6 @@
   if (!is_main_frame) {
     return;
   }
-  const bool existing_process_startup =
-      (initial_worker_status == EmbeddedWorkerStatus::STOPPED &&
-       start_situation ==
-           ServiceWorkerMetrics::StartSituation::EXISTING_PROCESS);
   const bool nav_preload_finished_first = response_start < worker_start;
   const base::TimeDelta concurrent_time =
       nav_preload_finished_first ? response_start : worker_start;
@@ -883,23 +876,25 @@
         "ServiceWorker.NavPreload.WorkerWaitTime_MainFrame", worker_wait_time);
   }
 
-  if (existing_process_startup) {
+  const bool worker_start_occurred =
+      initial_worker_status != EmbeddedWorkerStatus::RUNNING;
+  if (worker_start_occurred) {
     UMA_HISTOGRAM_MEDIUM_TIMES(
         "ServiceWorker.NavPreload.ResponseTime_MainFrame_"
-        "StartWorkerExistingProcess",
+        "WorkerStartOccurred",
         response_start);
     UMA_HISTOGRAM_BOOLEAN(
         "ServiceWorker.NavPreload.FinishedFirst_MainFrame_"
-        "StartWorkerExistingProcess",
+        "WorkerStartOccurred",
         nav_preload_finished_first);
     UMA_HISTOGRAM_MEDIUM_TIMES(
         "ServiceWorker.NavPreload.ConcurrentTime_MainFrame_"
-        "StartWorkerExistingProcess",
+        "WorkerStartOccurred",
         concurrent_time);
     if (nav_preload_finished_first) {
       UMA_HISTOGRAM_MEDIUM_TIMES(
           "ServiceWorker.NavPreload.WorkerWaitTime_MainFrame_"
-          "StartWorkerExistingProcess",
+          "WorkerStartOccurred",
           worker_wait_time);
     }
   }
diff --git a/content/browser/service_worker/service_worker_metrics.h b/content/browser/service_worker/service_worker_metrics.h
index 0305ca76..9298deb 100644
--- a/content/browser/service_worker/service_worker_metrics.h
+++ b/content/browser/service_worker/service_worker_metrics.h
@@ -344,7 +344,6 @@
       base::TimeDelta worker_start,
       base::TimeDelta response_start,
       EmbeddedWorkerStatus initial_worker_status,
-      StartSituation start_situation,
       ResourceType resource_type);
 
   // Records the result of trying to handle a request for a service worker
diff --git a/content/browser/service_worker/service_worker_metrics_unittest.cc b/content/browser/service_worker/service_worker_metrics_unittest.cc
index 9069e1f..50efb2c 100644
--- a/content/browser/service_worker/service_worker_metrics_unittest.cc
+++ b/content/browser/service_worker/service_worker_metrics_unittest.cc
@@ -13,6 +13,7 @@
 namespace {
 const std::string kNavigationPreloadSuffix = "_NavigationPreloadEnabled";
 const std::string kStartWorkerDuringStartupSuffix = "_StartWorkerDuringStartup";
+const std::string kWorkerStartOccurred = "_WorkerStartOccurred";
 const std::string kStartWorkerExistingProcessSuffix =
     "_StartWorkerExistingProcess";
 const std::string kPreparationTime =
@@ -33,39 +34,39 @@
 
   histogram_tester.ExpectTotalCount(
       "ServiceWorker.NavPreload.ResponseTime_MainFrame_"
-      "StartWorkerExistingProcess",
+      "WorkerStartOccurred",
       0);
   histogram_tester.ExpectTotalCount(
       "ServiceWorker.NavPreload.FinishedFirst_MainFrame_"
-      "StartWorkerExistingProcess",
+      "WorkerStartOccurred",
       0);
   histogram_tester.ExpectTotalCount(
       "ServiceWorker.NavPreload.ConcurrentTime_MainFrame_"
-      "StartWorkerExistingProcess",
+      "WorkerStartOccurred",
       0);
   histogram_tester.ExpectTotalCount(
       "ServiceWorker.NavPreload.WorkerWaitTime_MainFrame_"
-      "StartWorkerExistingProcess",
+      "WorkerStartOccurred",
       0);
 }
 
-void ExpectNoNavPreloadStartWorkerExistingProcessUMA(
+void ExpectNoNavPreloadWorkerStartOccurredUMA(
     const base::HistogramTester& histogram_tester) {
   histogram_tester.ExpectTotalCount(
       "ServiceWorker.NavPreload.ResponseTime_MainFrame_"
-      "StartWorkerExistingProcess",
+      "WorkerStartOccurred",
       0);
   histogram_tester.ExpectTotalCount(
       "ServiceWorker.NavPreload.FinishedFirst_MainFrame_"
-      "StartWorkerExistingProcess",
+      "WorkerStartOccurred",
       0);
   histogram_tester.ExpectTotalCount(
       "ServiceWorker.NavPreload.ConcurrentTime_MainFrame_"
-      "StartWorkerExistingProcess",
+      "WorkerStartOccurred",
       0);
   histogram_tester.ExpectTotalCount(
       "ServiceWorker.NavPreload.WorkerWaitTime_MainFrame_"
-      "StartWorkerExistingProcess",
+      "WorkerStartOccurred",
       0);
 }
 
@@ -113,17 +114,8 @@
     histogram_tester.ExpectTimeBucketCount(kPreparationTime, time, 1);
     histogram_tester.ExpectTimeBucketCount(
         kPreparationTime + kNavigationPreloadSuffix, time, 1);
-    // This would be the correct name for such a histogram, but it's
-    // intentionally not logged (see comment in
-    // RecordActivatedWorkerPreparationForMainFrame), so expect zero count.
-    histogram_tester.ExpectTotalCount(kPreparationTime +
-                                          kStartWorkerDuringStartupSuffix +
-                                          kNavigationPreloadSuffix,
-                                      0);
-    histogram_tester.ExpectTotalCount(kPreparationTime +
-                                          kStartWorkerExistingProcessSuffix +
-                                          kNavigationPreloadSuffix,
-                                      0);
+    histogram_tester.ExpectTotalCount(
+        kPreparationTime + kWorkerStartOccurred + kNavigationPreloadSuffix, 1);
   }
 
   {
@@ -147,8 +139,7 @@
     histogram_tester.ExpectTimeBucketCount(
         kPreparationTime + kNavigationPreloadSuffix, time, 1);
     histogram_tester.ExpectTimeBucketCount(
-        kPreparationTime + kStartWorkerExistingProcessSuffix +
-            kNavigationPreloadSuffix,
+        kPreparationTime + kWorkerStartOccurred + kNavigationPreloadSuffix,
         time, 1);
   }
 }
@@ -162,11 +153,9 @@
   base::TimeDelta worker_start = base::TimeDelta::FromMilliseconds(123);
   base::TimeDelta response_start = base::TimeDelta::FromMilliseconds(789);
   EmbeddedWorkerStatus initial_worker_status = EmbeddedWorkerStatus::RUNNING;
-  ServiceWorkerMetrics::StartSituation start_situation =
-      ServiceWorkerMetrics::StartSituation::UNKNOWN;
   base::HistogramTester histogram_tester;
   ServiceWorkerMetrics::RecordNavigationPreloadResponse(
-      worker_start, response_start, initial_worker_status, start_situation,
+      worker_start, response_start, initial_worker_status,
       RESOURCE_TYPE_MAIN_FRAME);
 
   histogram_tester.ExpectTimeBucketCount(
@@ -178,7 +167,7 @@
   histogram_tester.ExpectTotalCount(
       "ServiceWorker.NavPreload.WorkerWaitTime_MainFrame", 0);
 
-  ExpectNoNavPreloadStartWorkerExistingProcessUMA(histogram_tester);
+  ExpectNoNavPreloadWorkerStartOccurredUMA(histogram_tester);
 }
 
 // The worker was already running (subframe).
@@ -187,11 +176,9 @@
   base::TimeDelta worker_start = base::TimeDelta::FromMilliseconds(123);
   base::TimeDelta response_start = base::TimeDelta::FromMilliseconds(789);
   EmbeddedWorkerStatus initial_worker_status = EmbeddedWorkerStatus::RUNNING;
-  ServiceWorkerMetrics::StartSituation start_situation =
-      ServiceWorkerMetrics::StartSituation::UNKNOWN;
   base::HistogramTester histogram_tester;
   ServiceWorkerMetrics::RecordNavigationPreloadResponse(
-      worker_start, response_start, initial_worker_status, start_situation,
+      worker_start, response_start, initial_worker_status,
       RESOURCE_TYPE_SUB_FRAME);
 
   ExpectNoNavPreloadMainFrameUMA(histogram_tester);
@@ -203,11 +190,9 @@
   base::TimeDelta worker_start = base::TimeDelta::FromMilliseconds(234);
   base::TimeDelta response_start = base::TimeDelta::FromMilliseconds(789);
   EmbeddedWorkerStatus initial_worker_status = EmbeddedWorkerStatus::STOPPED;
-  ServiceWorkerMetrics::StartSituation start_situation =
-      ServiceWorkerMetrics::StartSituation::EXISTING_PROCESS;
   base::HistogramTester histogram_tester;
   ServiceWorkerMetrics::RecordNavigationPreloadResponse(
-      worker_start, response_start, initial_worker_status, start_situation,
+      worker_start, response_start, initial_worker_status,
       RESOURCE_TYPE_MAIN_FRAME);
 
   histogram_tester.ExpectTimeBucketCount(
@@ -221,19 +206,19 @@
 
   histogram_tester.ExpectTimeBucketCount(
       "ServiceWorker.NavPreload.ResponseTime_MainFrame_"
-      "StartWorkerExistingProcess",
+      "WorkerStartOccurred",
       response_start, 1);
   histogram_tester.ExpectUniqueSample(
       "ServiceWorker.NavPreload.FinishedFirst_MainFrame_"
-      "StartWorkerExistingProcess",
+      "WorkerStartOccurred",
       false, 1);
   histogram_tester.ExpectTimeBucketCount(
       "ServiceWorker.NavPreload.ConcurrentTime_MainFrame_"
-      "StartWorkerExistingProcess",
+      "WorkerStartOccurred",
       worker_start, 1);
   histogram_tester.ExpectTotalCount(
       "ServiceWorker.NavPreload.WorkerWaitTime_MainFrame_"
-      "StartWorkerExistingProcess",
+      "WorkerStartOccurred",
       0);
 }
 
@@ -242,27 +227,25 @@
   base::TimeDelta worker_start = base::TimeDelta::FromMilliseconds(234);
   base::TimeDelta response_start = base::TimeDelta::FromMilliseconds(789);
   EmbeddedWorkerStatus initial_worker_status = EmbeddedWorkerStatus::STOPPED;
-  ServiceWorkerMetrics::StartSituation start_situation =
-      ServiceWorkerMetrics::StartSituation::EXISTING_PROCESS;
   base::HistogramTester histogram_tester;
   ServiceWorkerMetrics::RecordNavigationPreloadResponse(
-      worker_start, response_start, initial_worker_status, start_situation,
+      worker_start, response_start, initial_worker_status,
       RESOURCE_TYPE_SUB_FRAME);
 
   ExpectNoNavPreloadMainFrameUMA(histogram_tester);
 }
 
-// The worker started up, but during browser startup.
+// The worker was already starting up (STARTING). This gets logged the same as
+// if it were STOPPED, since any status other than RUNNING gets logged to the
+// "_WorkerStartOccurred" suffix.
 TEST(ServiceWorkerMetricsTest,
      NavigationPreloadResponse_WorkerStart_BrowserStartup) {
-  base::TimeDelta worker_start = base::TimeDelta::FromMilliseconds(456);
+  base::TimeDelta worker_start = base::TimeDelta::FromMilliseconds(234);
   base::TimeDelta response_start = base::TimeDelta::FromMilliseconds(789);
-  EmbeddedWorkerStatus initial_worker_status = EmbeddedWorkerStatus::STOPPED;
-  ServiceWorkerMetrics::StartSituation start_situation =
-      ServiceWorkerMetrics::StartSituation::DURING_STARTUP;
+  EmbeddedWorkerStatus initial_worker_status = EmbeddedWorkerStatus::STARTING;
   base::HistogramTester histogram_tester;
   ServiceWorkerMetrics::RecordNavigationPreloadResponse(
-      worker_start, response_start, initial_worker_status, start_situation,
+      worker_start, response_start, initial_worker_status,
       RESOURCE_TYPE_MAIN_FRAME);
 
   histogram_tester.ExpectTimeBucketCount(
@@ -274,7 +257,22 @@
   histogram_tester.ExpectTotalCount(
       "ServiceWorker.NavPreload.WorkerWaitTime_MainFrame", 0);
 
-  ExpectNoNavPreloadStartWorkerExistingProcessUMA(histogram_tester);
+  histogram_tester.ExpectTimeBucketCount(
+      "ServiceWorker.NavPreload.ResponseTime_MainFrame_"
+      "WorkerStartOccurred",
+      response_start, 1);
+  histogram_tester.ExpectUniqueSample(
+      "ServiceWorker.NavPreload.FinishedFirst_MainFrame_"
+      "WorkerStartOccurred",
+      false, 1);
+  histogram_tester.ExpectTimeBucketCount(
+      "ServiceWorker.NavPreload.ConcurrentTime_MainFrame_"
+      "WorkerStartOccurred",
+      worker_start, 1);
+  histogram_tester.ExpectTotalCount(
+      "ServiceWorker.NavPreload.WorkerWaitTime_MainFrame_"
+      "WorkerStartOccurred",
+      0);
 }
 
 // The worker started up, but navigation preload arrived first.
@@ -283,12 +281,11 @@
   base::TimeDelta worker_start = base::TimeDelta::FromMilliseconds(2345);
   base::TimeDelta response_start = base::TimeDelta::FromMilliseconds(456);
   base::TimeDelta wait_time = worker_start - response_start;
-  EmbeddedWorkerStatus initial_worker_status = EmbeddedWorkerStatus::STOPPED;
-  ServiceWorkerMetrics::StartSituation start_situation =
-      ServiceWorkerMetrics::StartSituation::EXISTING_PROCESS;
+  // STOPPING is also logged the same as STOPPED.
+  EmbeddedWorkerStatus initial_worker_status = EmbeddedWorkerStatus::STOPPING;
   base::HistogramTester histogram_tester;
   ServiceWorkerMetrics::RecordNavigationPreloadResponse(
-      worker_start, response_start, initial_worker_status, start_situation,
+      worker_start, response_start, initial_worker_status,
       RESOURCE_TYPE_MAIN_FRAME);
   histogram_tester.ExpectTimeBucketCount(
       "ServiceWorker.NavPreload.ResponseTime_MainFrame", response_start, 1);
@@ -301,15 +298,15 @@
 
   histogram_tester.ExpectUniqueSample(
       "ServiceWorker.NavPreload.FinishedFirst_MainFrame_"
-      "StartWorkerExistingProcess",
+      "WorkerStartOccurred",
       true, 1);
   histogram_tester.ExpectTimeBucketCount(
       "ServiceWorker.NavPreload.ConcurrentTime_MainFrame_"
-      "StartWorkerExistingProcess",
+      "WorkerStartOccurred",
       response_start, 1);
   histogram_tester.ExpectTimeBucketCount(
       "ServiceWorker.NavPreload.WorkerWaitTime_MainFrame_"
-      "StartWorkerExistingProcess",
+      "WorkerStartOccurred",
       wait_time, 1);
 }
 
@@ -319,11 +316,9 @@
   base::TimeDelta worker_start = base::TimeDelta::FromMilliseconds(2345);
   base::TimeDelta response_start = base::TimeDelta::FromMilliseconds(456);
   EmbeddedWorkerStatus initial_worker_status = EmbeddedWorkerStatus::STOPPED;
-  ServiceWorkerMetrics::StartSituation start_situation =
-      ServiceWorkerMetrics::StartSituation::EXISTING_PROCESS;
   base::HistogramTester histogram_tester;
   ServiceWorkerMetrics::RecordNavigationPreloadResponse(
-      worker_start, response_start, initial_worker_status, start_situation,
+      worker_start, response_start, initial_worker_status,
       RESOURCE_TYPE_SUB_FRAME);
 
   ExpectNoNavPreloadMainFrameUMA(histogram_tester);
diff --git a/content/browser/service_worker/service_worker_read_from_cache_job.cc b/content/browser/service_worker/service_worker_read_from_cache_job.cc
index 3dc7233..46835d5 100644
--- a/content/browser/service_worker/service_worker_read_from_cache_job.cc
+++ b/content/browser/service_worker/service_worker_read_from_cache_job.cc
@@ -40,18 +40,25 @@
   DCHECK(resource_type_ == RESOURCE_TYPE_SCRIPT ||
          (resource_type_ == RESOURCE_TYPE_SERVICE_WORKER &&
           version_->script_url() == request_->url()));
+  TRACE_EVENT_NESTABLE_ASYNC_BEGIN1("ServiceWorker",
+                                    "ServiceWorkerReadFromCacheJob", this,
+                                    "URL", request_->url().spec());
 }
 
 ServiceWorkerReadFromCacheJob::~ServiceWorkerReadFromCacheJob() {
+  TRACE_EVENT_NESTABLE_ASYNC_END0("ServiceWorker",
+                                  "ServiceWorkerReadFromCacheJob", this);
 }
 
 void ServiceWorkerReadFromCacheJob::Start() {
+  TRACE_EVENT_NESTABLE_ASYNC_BEGIN0("ServiceWorker", "ReadInfo", this);
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE, base::Bind(&ServiceWorkerReadFromCacheJob::StartAsync,
                             weak_factory_.GetWeakPtr()));
 }
 
 void ServiceWorkerReadFromCacheJob::Kill() {
+  TRACE_EVENT_NESTABLE_ASYNC_INSTANT0("ServiceWorker", "Kill", this);
   if (has_been_killed_)
     return;
   weak_factory_.InvalidateWeakPtrs();
@@ -108,10 +115,8 @@
                                                int buf_size) {
   DCHECK_NE(buf_size, 0);
   DCHECK(!reader_->IsReadPending());
-  TRACE_EVENT_ASYNC_BEGIN1("ServiceWorker",
-                           "ServiceWorkerReadFromCacheJob::ReadRawData",
-                           this,
-                           "URL", request_->url().spec());
+  TRACE_EVENT_NESTABLE_ASYNC_BEGIN1("ServiceWorker", "ReadRawData", this,
+                                    "buf_size", buf_size);
   reader_->ReadData(buf, buf_size,
                     base::Bind(&ServiceWorkerReadFromCacheJob::OnReadComplete,
                                weak_factory_.GetWeakPtr()));
@@ -119,9 +124,7 @@
 }
 
 void ServiceWorkerReadFromCacheJob::StartAsync() {
-  TRACE_EVENT_ASYNC_BEGIN1("ServiceWorker",
-                           "ServiceWorkerReadFromCacheJob::ReadInfo", this,
-                           "URL", request_->url().spec());
+  TRACE_EVENT_NESTABLE_ASYNC_INSTANT0("ServiceWorker", "StartAsync", this);
   if (!context_) {
     // NotifyStartError is not safe to call synchronously in Start.
     NotifyStartError(
@@ -166,10 +169,8 @@
   http_info_io_buffer_ = nullptr;
   if (is_main_script())
     version_->SetMainScriptHttpResponseInfo(*http_info_);
-  TRACE_EVENT_ASYNC_END1("ServiceWorker",
-                         "ServiceWorkerReadFromCacheJob::ReadInfo",
-                         this,
-                         "Result", result);
+  TRACE_EVENT_NESTABLE_ASYNC_END1("ServiceWorker", "ReadInfo", this, "Result",
+                                  result);
   NotifyHeadersComplete();
 }
 
@@ -223,12 +224,10 @@
     Done(net::URLRequestStatus(net::URLRequestStatus::FAILED, result));
   }
 
+  TRACE_EVENT_NESTABLE_ASYNC_END1("ServiceWorker", "ReadRawData", this,
+                                  "Result", result);
   ServiceWorkerMetrics::CountReadResponseResult(check_result);
   ReadRawDataComplete(result);
-  TRACE_EVENT_ASYNC_END1("ServiceWorker",
-                         "ServiceWorkerReadFromCacheJob::ReadRawData",
-                         this,
-                         "Result", result);
 }
 
 }  // namespace content
diff --git a/content/browser/service_worker/service_worker_url_request_job.cc b/content/browser/service_worker/service_worker_url_request_job.cc
index 01d4344..5633043 100644
--- a/content/browser/service_worker/service_worker_url_request_job.cc
+++ b/content/browser/service_worker/service_worker_url_request_job.cc
@@ -288,8 +288,7 @@
     ServiceWorkerMetrics::RecordNavigationPreloadResponse(
         owner_->worker_ready_time_ - owner_->worker_start_time_,
         navigation_preload_response_time_ - owner_->worker_start_time_,
-        owner_->initial_worker_status_, owner_->worker_start_situation_,
-        owner_->resource_type_);
+        owner_->initial_worker_status_, owner_->resource_type_);
     phase_ = Phase::RECORDED;
   }
 
diff --git a/content/browser/service_worker/service_worker_url_request_job_unittest.cc b/content/browser/service_worker/service_worker_url_request_job_unittest.cc
index 09dffcc..54ab731 100644
--- a/content/browser/service_worker/service_worker_url_request_job_unittest.cc
+++ b/content/browser/service_worker/service_worker_url_request_job_unittest.cc
@@ -563,8 +563,7 @@
   histogram_tester.ExpectUniqueSample(
       "ServiceWorker.NavPreload.FinishedFirst_MainFrame", false, 1);
   histogram_tester.ExpectUniqueSample(
-      "ServiceWorker.NavPreload.FinishedFirst_MainFrame_"
-      "StartWorkerExistingProcess",
+      "ServiceWorker.NavPreload.FinishedFirst_MainFrame_WorkerStartOccurred",
       false, 1);
 }
 
@@ -591,8 +590,7 @@
   histogram_tester.ExpectUniqueSample(
       "ServiceWorker.NavPreload.FinishedFirst_MainFrame", true, 1);
   histogram_tester.ExpectUniqueSample(
-      "ServiceWorker.NavPreload.FinishedFirst_MainFrame_"
-      "StartWorkerExistingProcess",
+      "ServiceWorker.NavPreload.FinishedFirst_MainFrame_WorkerStartOccurred",
       true, 1);
 }
 
diff --git a/content/browser/web_contents/web_contents_impl_unittest.cc b/content/browser/web_contents/web_contents_impl_unittest.cc
index 1eda2756..b859ecb 100644
--- a/content/browser/web_contents/web_contents_impl_unittest.cc
+++ b/content/browser/web_contents/web_contents_impl_unittest.cc
@@ -12,6 +12,7 @@
 #include "build/build_config.h"
 #include "content/browser/frame_host/interstitial_page_impl.h"
 #include "content/browser/frame_host/navigation_entry_impl.h"
+#include "content/browser/frame_host/navigator.h"
 #include "content/browser/frame_host/render_frame_host_impl.h"
 #include "content/browser/frame_host/render_frame_proxy_host.h"
 #include "content/browser/media/audio_stream_monitor.h"
@@ -2168,6 +2169,49 @@
   EXPECT_TRUE(deleted);
 }
 
+// Test for https://crbug.com/730592, where deleting a WebContents while its
+// interstitial is navigating could lead to a crash.
+TEST_F(WebContentsImplTest, CreateInterstitialForClosingTab) {
+  // Navigate to a page.
+  GURL url1("http://www.google.com");
+  main_test_rfh()->NavigateAndCommitRendererInitiated(true, url1);
+  EXPECT_EQ(1, controller().GetEntryCount());
+
+  // Initiate a browser navigation that will trigger an interstitial.
+  controller().LoadURL(GURL("http://www.evil.com"), Referrer(),
+                       ui::PAGE_TRANSITION_TYPED, std::string());
+
+  // Show an interstitial.
+  TestInterstitialPage::InterstitialState state = TestInterstitialPage::INVALID;
+  bool deleted = false;
+  GURL url2("http://interstitial");
+  TestInterstitialPage* interstitial =
+      new TestInterstitialPage(contents(), true, url2, &state, &deleted);
+  TestInterstitialPageStateGuard state_guard(interstitial);
+  interstitial->Show();
+  RenderFrameHostImpl* interstitial_rfh =
+      static_cast<RenderFrameHostImpl*>(interstitial->GetMainFrame());
+  // The interstitial should not show until its navigation has committed.
+  EXPECT_FALSE(interstitial->is_showing());
+  EXPECT_FALSE(contents()->ShowingInterstitialPage());
+  EXPECT_EQ(nullptr, contents()->GetInterstitialPage());
+
+  // Close the tab before the interstitial commits.
+  DeleteContents();
+  EXPECT_EQ(TestInterstitialPage::CANCELED, state);
+
+  // The interstitial page triggers a DidStartNavigation after the tab is gone,
+  // but before the interstitial page itself is deleted.  This should not crash.
+  Navigator* interstitial_navigator =
+      interstitial_rfh->frame_tree_node()->navigator();
+  interstitial_navigator->DidStartProvisionalLoad(
+      interstitial_rfh, url2, std::vector<GURL>(), base::TimeTicks::Now());
+  EXPECT_FALSE(deleted);
+
+  RunAllPendingInMessageLoop();
+  EXPECT_TRUE(deleted);
+}
+
 // Test that after Proceed is called and an interstitial is still shown, no more
 // commands get executed.
 TEST_F(WebContentsImplTest, ShowInterstitialProceedMultipleCommands) {
diff --git a/content/browser/webrtc/webrtc_image_capture_browsertest.cc b/content/browser/webrtc/webrtc_image_capture_browsertest.cc
index 687c325..176f990e 100644
--- a/content/browser/webrtc/webrtc_image_capture_browsertest.cc
+++ b/content/browser/webrtc/webrtc_image_capture_browsertest.cc
@@ -12,7 +12,9 @@
 #include "content/public/test/test_utils.h"
 #include "content/shell/browser/shell.h"
 #include "media/base/media_switches.h"
+#include "media/capture/video/fake_video_capture_device_factory.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
+#include "services/video_capture/public/cpp/constants.h"
 
 #if defined(OS_ANDROID)
 #include "base/android/build_info.h"
@@ -45,9 +47,20 @@
 // platforms where the ImageCaptureCode is landed, https://crbug.com/656810
 static struct TargetCamera {
   bool use_fake;
-} const kTestParameters[] = {{true},
+} const kTargetCameras[] = {{true},
 #if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_ANDROID)
-                             {false}
+                            {false}
+#endif
+};
+
+static struct TargetVideoCaptureStack {
+  bool use_video_capture_service;
+} const kTargetVideoCaptureStacks[] = {{false},
+// Mojo video capture is currently not supported on Android
+// TODO(chfremer): Remove this as soon as https://crbug.com/720500 is
+// resolved.
+#if !defined(OS_ANDROID)
+                                       {true}
 #endif
 };
 
@@ -56,24 +69,16 @@
 // This class is the content_browsertests for Image Capture API, which allows
 // for capturing still images out of a MediaStreamTrack. Is a
 // WebRtcWebcamBrowserTest to be able to use a physical camera.
-class WebRtcImageCaptureBrowserTest
-    : public WebRtcWebcamBrowserTest,
-      public testing::WithParamInterface<struct TargetCamera> {
+class WebRtcImageCaptureBrowserTestBase : public WebRtcWebcamBrowserTest {
  public:
-  WebRtcImageCaptureBrowserTest() = default;
-  ~WebRtcImageCaptureBrowserTest() override = default;
+  WebRtcImageCaptureBrowserTestBase() = default;
+  ~WebRtcImageCaptureBrowserTestBase() override = default;
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
     WebRtcWebcamBrowserTest::SetUpCommandLine(command_line);
 
     ASSERT_FALSE(base::CommandLine::ForCurrentProcess()->HasSwitch(
         switches::kUseFakeDeviceForMediaStream));
-    if (GetParam().use_fake) {
-      base::CommandLine::ForCurrentProcess()->AppendSwitch(
-          switches::kUseFakeDeviceForMediaStream);
-      ASSERT_TRUE(base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kUseFakeDeviceForMediaStream));
-    }
 
     // "GetUserMedia": enables navigator.mediaDevices.getUserMedia();
     // TODO(mcasas): remove GetUserMedia after https://crbug.com/503227.
@@ -113,43 +118,182 @@
   }
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(WebRtcImageCaptureBrowserTest);
+  DISALLOW_COPY_AND_ASSIGN(WebRtcImageCaptureBrowserTestBase);
 };
 
-IN_PROC_BROWSER_TEST_P(WebRtcImageCaptureBrowserTest,
+// Test fixture for setting up a capture device (real or fake) that successfully
+// serves all image capture requests.
+class WebRtcImageCaptureSucceedsBrowserTest
+    : public WebRtcImageCaptureBrowserTestBase,
+      public testing::WithParamInterface<
+          std::tuple<TargetCamera, TargetVideoCaptureStack>> {
+ public:
+  WebRtcImageCaptureSucceedsBrowserTest() = default;
+  ~WebRtcImageCaptureSucceedsBrowserTest() override = default;
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    WebRtcImageCaptureBrowserTestBase::SetUpCommandLine(command_line);
+
+    if (std::get<0>(GetParam()).use_fake) {
+      base::CommandLine::ForCurrentProcess()->AppendSwitch(
+          switches::kUseFakeDeviceForMediaStream);
+      ASSERT_TRUE(base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kUseFakeDeviceForMediaStream));
+    }
+    if (std::get<1>(GetParam()).use_video_capture_service) {
+      base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+          switches::kEnableFeatures, video_capture::kMojoVideoCapture.name);
+    }
+  }
+};
+
+IN_PROC_BROWSER_TEST_P(WebRtcImageCaptureSucceedsBrowserTest,
                        MAYBE_GetPhotoCapabilities) {
   embedded_test_server()->StartAcceptingConnections();
-  ASSERT_TRUE(RunImageCaptureTestCase("testCreateAndGetPhotoCapabilities()"));
+  ASSERT_TRUE(
+      RunImageCaptureTestCase("testCreateAndGetPhotoCapabilitiesSucceeds()"));
 }
 
-IN_PROC_BROWSER_TEST_P(WebRtcImageCaptureBrowserTest, MAYBE_TakePhoto) {
+IN_PROC_BROWSER_TEST_P(WebRtcImageCaptureSucceedsBrowserTest, MAYBE_TakePhoto) {
   embedded_test_server()->StartAcceptingConnections();
-  ASSERT_TRUE(RunImageCaptureTestCase("testCreateAndTakePhoto()"));
+  ASSERT_TRUE(RunImageCaptureTestCase("testCreateAndTakePhotoSucceeds()"));
 }
 
-IN_PROC_BROWSER_TEST_P(WebRtcImageCaptureBrowserTest, MAYBE_GrabFrame) {
+IN_PROC_BROWSER_TEST_P(WebRtcImageCaptureSucceedsBrowserTest, MAYBE_GrabFrame) {
   embedded_test_server()->StartAcceptingConnections();
-  ASSERT_TRUE(RunImageCaptureTestCase("testCreateAndGrabFrame()"));
+  ASSERT_TRUE(RunImageCaptureTestCase("testCreateAndGrabFrameSucceeds()"));
 }
 
-IN_PROC_BROWSER_TEST_P(WebRtcImageCaptureBrowserTest,
+IN_PROC_BROWSER_TEST_P(WebRtcImageCaptureSucceedsBrowserTest,
                        MAYBE_GetTrackCapabilities) {
   embedded_test_server()->StartAcceptingConnections();
   ASSERT_TRUE(RunImageCaptureTestCase("testCreateAndGetTrackCapabilities()"));
 }
 
-IN_PROC_BROWSER_TEST_P(WebRtcImageCaptureBrowserTest, MAYBE_GetTrackSettings) {
+IN_PROC_BROWSER_TEST_P(WebRtcImageCaptureSucceedsBrowserTest,
+                       MAYBE_GetTrackSettings) {
   embedded_test_server()->StartAcceptingConnections();
   ASSERT_TRUE(RunImageCaptureTestCase("testCreateAndGetTrackSettings()"));
 }
 
-IN_PROC_BROWSER_TEST_P(WebRtcImageCaptureBrowserTest, MAYBE_ManipulateZoom) {
+IN_PROC_BROWSER_TEST_P(WebRtcImageCaptureSucceedsBrowserTest,
+                       MAYBE_ManipulateZoom) {
   embedded_test_server()->StartAcceptingConnections();
   ASSERT_TRUE(RunImageCaptureTestCase("testManipulateZoom()"));
 }
 
+INSTANTIATE_TEST_CASE_P(
+    ,
+    WebRtcImageCaptureSucceedsBrowserTest,
+    testing::Combine(testing::ValuesIn(kTargetCameras),
+                     testing::ValuesIn(kTargetVideoCaptureStacks)));
+
+// Test fixture template for setting up a fake device with a custom
+// configuration. We are going to use this to set up fake devices that respond
+// to invocation of various ImageCapture API calls with a failure response.
+template <typename FakeDeviceConfigTraits>
+class WebRtcImageCaptureCustomConfigFakeDeviceBrowserTest
+    : public WebRtcImageCaptureBrowserTestBase,
+      public testing::WithParamInterface<TargetVideoCaptureStack> {
+ public:
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    WebRtcImageCaptureBrowserTestBase::SetUpCommandLine(command_line);
+
+    base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+        switches::kUseFakeDeviceForMediaStream,
+        std::string("config=") + FakeDeviceConfigTraits::config());
+    if (GetParam().use_video_capture_service) {
+      base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+          switches::kEnableFeatures, video_capture::kMojoVideoCapture.name);
+    }
+  }
+};
+
+struct GetPhotoStateFailsConfigTraits {
+  static std::string config() {
+    return media::FakeVideoCaptureDeviceFactory::
+        kDeviceConfigForGetPhotoStateFails;
+  }
+};
+
+using WebRtcImageCaptureGetPhotoStateFailsBrowserTest =
+    WebRtcImageCaptureCustomConfigFakeDeviceBrowserTest<
+        GetPhotoStateFailsConfigTraits>;
+
+IN_PROC_BROWSER_TEST_P(WebRtcImageCaptureGetPhotoStateFailsBrowserTest,
+                       GetCapabilities) {
+  embedded_test_server()->StartAcceptingConnections();
+  // When the fake device faile, we expect an empty set of capabilities to
+  // reported back to JS.
+  ASSERT_TRUE(
+      RunImageCaptureTestCase("testCreateAndGetPhotoCapabilitiesSucceeds()"));
+}
+
+IN_PROC_BROWSER_TEST_P(WebRtcImageCaptureGetPhotoStateFailsBrowserTest,
+                       TakePhoto) {
+  embedded_test_server()->StartAcceptingConnections();
+  ASSERT_TRUE(RunImageCaptureTestCase("testCreateAndTakePhotoSucceeds()"));
+}
+
+IN_PROC_BROWSER_TEST_P(WebRtcImageCaptureGetPhotoStateFailsBrowserTest,
+                       GrabFrame) {
+  embedded_test_server()->StartAcceptingConnections();
+  ASSERT_TRUE(RunImageCaptureTestCase("testCreateAndGrabFrameSucceeds()"));
+}
+
 INSTANTIATE_TEST_CASE_P(,
-                        WebRtcImageCaptureBrowserTest,
-                        testing::ValuesIn(kTestParameters));
+                        WebRtcImageCaptureGetPhotoStateFailsBrowserTest,
+                        testing::ValuesIn(kTargetVideoCaptureStacks));
+
+struct SetPhotoOptionsFailsConfigTraits {
+  static std::string config() {
+    return media::FakeVideoCaptureDeviceFactory::
+        kDeviceConfigForSetPhotoOptionsFails;
+  }
+};
+
+using WebRtcImageCaptureSetPhotoOptionsFailsBrowserTest =
+    WebRtcImageCaptureCustomConfigFakeDeviceBrowserTest<
+        SetPhotoOptionsFailsConfigTraits>;
+
+IN_PROC_BROWSER_TEST_P(WebRtcImageCaptureSetPhotoOptionsFailsBrowserTest,
+                       TakePhoto) {
+  embedded_test_server()->StartAcceptingConnections();
+  ASSERT_TRUE(RunImageCaptureTestCase("testCreateAndTakePhotoIsRejected()"));
+}
+
+IN_PROC_BROWSER_TEST_P(WebRtcImageCaptureSetPhotoOptionsFailsBrowserTest,
+                       GrabFrame) {
+  embedded_test_server()->StartAcceptingConnections();
+  ASSERT_TRUE(RunImageCaptureTestCase("testCreateAndGrabFrameSucceeds()"));
+}
+
+INSTANTIATE_TEST_CASE_P(,
+                        WebRtcImageCaptureSetPhotoOptionsFailsBrowserTest,
+                        testing::ValuesIn(kTargetVideoCaptureStacks));
+
+struct TakePhotoFailsConfigTraits {
+  static std::string config() {
+    return media::FakeVideoCaptureDeviceFactory::kDeviceConfigForTakePhotoFails;
+  }
+};
+
+using WebRtcImageCaptureTakePhotoFailsBrowserTest =
+    WebRtcImageCaptureCustomConfigFakeDeviceBrowserTest<
+        TakePhotoFailsConfigTraits>;
+
+IN_PROC_BROWSER_TEST_P(WebRtcImageCaptureTakePhotoFailsBrowserTest, TakePhoto) {
+  embedded_test_server()->StartAcceptingConnections();
+  ASSERT_TRUE(RunImageCaptureTestCase("testCreateAndTakePhotoIsRejected()"));
+}
+
+IN_PROC_BROWSER_TEST_P(WebRtcImageCaptureTakePhotoFailsBrowserTest, GrabFrame) {
+  embedded_test_server()->StartAcceptingConnections();
+  ASSERT_TRUE(RunImageCaptureTestCase("testCreateAndGrabFrameSucceeds()"));
+}
+
+INSTANTIATE_TEST_CASE_P(,
+                        WebRtcImageCaptureTakePhotoFailsBrowserTest,
+                        testing::ValuesIn(kTargetVideoCaptureStacks));
 
 }  // namespace content
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index bebbdfd..1987286 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -639,7 +639,6 @@
 
   public_deps = [
     "//components/leveldb/public/interfaces",
-    "//components/payments/mojom:mojom_payment_app",
     "//content/public/common:interfaces",
     "//ipc:mojom",
     "//media/capture/mojo:capture_types",
diff --git a/content/common/cross_site_document_classifier.cc b/content/common/cross_site_document_classifier.cc
index 7e2522e..78417aa 100644
--- a/content/common/cross_site_document_classifier.cc
+++ b/content/common/cross_site_document_classifier.cc
@@ -4,6 +4,8 @@
 
 #include "content/common/cross_site_document_classifier.h"
 
+#include <stddef.h>
+
 #include "base/command_line.h"
 #include "base/lazy_instance.h"
 #include "base/logging.h"
@@ -137,8 +139,6 @@
   // signatures. This can weaken our document block policy, but we can
   // break less websites.
   // TODO(dsjang): parameterize |net::SniffForHTML| with an option
-#include <stddef.h>
-
   // that decides whether to include <!-- or not, so that we can
   // remove this function.
   // TODO(dsjang): Once CrossSiteDocumentClassifier is moved into the browser
diff --git a/content/common/message_port.cc b/content/common/message_port.cc
index 9353516..5e70e7e 100644
--- a/content/common/message_port.cc
+++ b/content/common/message_port.cc
@@ -7,6 +7,7 @@
 #include "base/bind.h"
 #include "base/logging.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "mojo/public/cpp/system/message_pipe.h"
 
 namespace content {
 
@@ -52,65 +53,62 @@
 
   uint32_t num_bytes = encoded_message.size() * sizeof(base::char16);
 
-  // NOTE: It is OK to ignore the return value of MojoWriteMessage here. HTML
-  // MessagePorts have no way of reporting when the peer is gone.
+  // NOTE: It is OK to ignore the return value of mojo::WriteMessageRaw here.
+  // HTML MessagePorts have no way of reporting when the peer is gone.
 
   if (ports.empty()) {
-    MojoWriteMessage(state_->handle().get().value(), encoded_message.data(),
-                     num_bytes, nullptr, 0, MOJO_WRITE_MESSAGE_FLAG_NONE);
+    mojo::WriteMessageRaw(state_->handle().get(), encoded_message.data(),
+                          num_bytes, nullptr, 0, MOJO_WRITE_MESSAGE_FLAG_NONE);
   } else {
     uint32_t num_handles = static_cast<uint32_t>(ports.size());
     std::unique_ptr<MojoHandle[]> handles(new MojoHandle[num_handles]);
     for (uint32_t i = 0; i < num_handles; ++i)
       handles[i] = ports[i].ReleaseHandle().release().value();
-    MojoWriteMessage(state_->handle().get().value(), encoded_message.data(),
-                     num_bytes, handles.get(), num_handles,
-                     MOJO_WRITE_MESSAGE_FLAG_NONE);
+    mojo::WriteMessageRaw(state_->handle().get(), encoded_message.data(),
+                          num_bytes, handles.get(), num_handles,
+                          MOJO_WRITE_MESSAGE_FLAG_NONE);
   }
 }
 
 bool MessagePort::GetMessage(base::string16* encoded_message,
                              std::vector<MessagePort>* ports) {
   DCHECK(state_->handle().is_valid());
-
-  uint32_t num_bytes = 0;
-  uint32_t num_handles = 0;
-
-  MojoResult rv =
-      MojoReadMessage(state_->handle().get().value(), nullptr, &num_bytes,
-                      nullptr, &num_handles, MOJO_READ_MESSAGE_FLAG_NONE);
-  if (rv == MOJO_RESULT_OK) {
-    encoded_message->clear();
-    ports->clear();
-    return true;
-  }
-  if (rv != MOJO_RESULT_RESOURCE_EXHAUSTED)
-    return false;
-
-  CHECK(num_bytes % 2 == 0);
-
-  base::string16 buffer;
-  buffer.resize(num_bytes / sizeof(base::char16));
-
-  std::unique_ptr<MojoHandle[]> handles;
-  if (num_handles)
-    handles.reset(new MojoHandle[num_handles]);
-
-  rv = MojoReadMessage(
-      state_->handle().get().value(), num_bytes ? &buffer[0] : nullptr,
-      &num_bytes, handles.get(), &num_handles, MOJO_READ_MESSAGE_FLAG_NONE);
+  mojo::ScopedMessageHandle message;
+  MojoResult rv = mojo::ReadMessageNew(state_->handle().get(), &message,
+                                       MOJO_READ_MESSAGE_FLAG_NONE);
   if (rv != MOJO_RESULT_OK)
     return false;
 
-  buffer.swap(*encoded_message);
+  uint32_t num_bytes = 0;
+  uint32_t num_handles = 0;
+  void* buffer;
+  std::vector<mojo::ScopedHandle> handles;
+  rv = MojoGetSerializedMessageContents(
+      message->value(), &buffer, &num_bytes, nullptr, &num_handles,
+      MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_NONE);
+  if (rv == MOJO_RESULT_RESOURCE_EXHAUSTED) {
+    handles.resize(num_handles);
+    rv = MojoGetSerializedMessageContents(
+        message->value(), &buffer, &num_bytes,
+        reinterpret_cast<MojoHandle*>(handles.data()), &num_handles,
+        MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_NONE);
+  }
+  if (rv != MOJO_RESULT_OK)
+    return false;
 
-  if (num_handles) {
-    ports->resize(static_cast<size_t>(num_handles));
+  DCHECK_EQ(0u, num_bytes % sizeof(base::char16));
+  encoded_message->resize(num_bytes / sizeof(base::char16));
+  if (num_bytes)
+    memcpy(&encoded_message->at(0), buffer, num_bytes);
+
+  if (!handles.empty()) {
+    ports->resize(handles.size());
     for (uint32_t i = 0; i < num_handles; ++i) {
       ports->at(i) = MessagePort(
-          mojo::ScopedMessagePipeHandle(mojo::MessagePipeHandle(handles[i])));
+          mojo::ScopedMessagePipeHandle::From(std::move(handles[i])));
     }
   }
+
   return true;
 }
 
diff --git a/content/common/service_worker/service_worker_event_dispatcher.mojom b/content/common/service_worker/service_worker_event_dispatcher.mojom
index 0ff3dc0..93832a5 100644
--- a/content/common/service_worker/service_worker_event_dispatcher.mojom
+++ b/content/common/service_worker/service_worker_event_dispatcher.mojom
@@ -4,12 +4,12 @@
 
 module content.mojom;
 
-import "components/payments/mojom/payment_app.mojom";
 import "content/common/url_loader.mojom";
 import "mojo/common/string16.mojom";
 import "mojo/common/time.mojom";
 import "third_party/WebKit/public/platform/modules/background_sync/background_sync.mojom";
 import "third_party/WebKit/public/platform/modules/fetch/fetch_api_request.mojom";
+import "third_party/WebKit/public/platform/modules/payments/payment_app.mojom";
 import "third_party/WebKit/public/platform/modules/serviceworker/service_worker_event_status.mojom";
 import "third_party/WebKit/public/platform/modules/serviceworker/service_worker_stream_handle.mojom";
 import "url/mojo/origin.mojom";
diff --git a/content/public/browser/DEPS b/content/public/browser/DEPS
index ca1ff35..6a7e2125 100644
--- a/content/public/browser/DEPS
+++ b/content/public/browser/DEPS
@@ -1,5 +1,4 @@
 include_rules = [
-  "+components/payments/mojom/payment_app.mojom.h",
   "+content/common/input/input_handler.mojom.h",
   "+device/screen_orientation/public/interfaces",
   "+device/wake_lock/public/interfaces",
diff --git a/content/public/browser/payment_app_provider.h b/content/public/browser/payment_app_provider.h
index 8f44e4e9..6572aea 100644
--- a/content/public/browser/payment_app_provider.h
+++ b/content/public/browser/payment_app_provider.h
@@ -11,9 +11,9 @@
 #include <vector>
 
 #include "base/callback_forward.h"
-#include "components/payments/mojom/payment_app.mojom.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/stored_payment_instrument.h"
+#include "third_party/WebKit/public/platform/modules/payments/payment_app.mojom.h"
 
 namespace content {
 
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index cd09e6c..f2f14ba 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -442,7 +442,6 @@
     "//components/discardable_memory/client",
     "//components/metrics",
     "//components/metrics:single_sample_metrics",
-    "//components/payments/mojom:mojom_payment_app",
     "//components/ukm/public",
     "//components/url_formatter",
     "//components/variations",
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 063ca757..f0dc616 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -1803,12 +1803,6 @@
   proxy = RenderFrameProxy::CreateProxyToReplaceFrame(
       this, proxy_routing_id, replicated_frame_state.scope);
 
-  // Synchronously run the unload handler before sending the ACK.
-  // TODO(creis): Call dispatchUnloadEvent unconditionally here to support
-  // unload on subframes as well.
-  if (is_main_frame_)
-    frame_->DispatchUnloadEvent();
-
   // Swap out and stop sending any IPC messages that are not ACKs.
   if (is_main_frame_)
     render_view_->SetSwappedOut(true);
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index ef8a812..9aa953a 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -7,6 +7,7 @@
 #include <algorithm>
 #include <cmath>
 #include <memory>
+#include <utility>
 
 #include "base/auto_reset.h"
 #include "base/bind.h"
@@ -2089,6 +2090,9 @@
 }
 
 void RenderViewImpl::OnClosePage() {
+  // ViewMsg_ClosePage should only be sent to active, non-swapped-out views.
+  DCHECK(webview()->MainFrame()->IsWebLocalFrame());
+
   // TODO(creis): We'd rather use webview()->Close() here, but that currently
   // sets the WebView's delegate_ to NULL, preventing any JavaScript dialogs
   // in the onunload handler from appearing.  For now, we're bypassing that and
@@ -2096,7 +2100,7 @@
   // revisited to avoid having two ways to close a page.  Having a single way
   // to close that can run onunload is also useful for fixing
   // http://b/issue?id=753080.
-  webview()->MainFrame()->DispatchUnloadEvent();
+  webview()->MainFrame()->ToWebLocalFrame()->DispatchUnloadEvent();
 
   Send(new ViewHostMsg_ClosePage_ACK(GetRoutingID()));
 }
diff --git a/content/renderer/service_worker/service_worker_context_client.h b/content/renderer/service_worker/service_worker_context_client.h
index 5b5cac9..2d39dcba 100644
--- a/content/renderer/service_worker/service_worker_context_client.h
+++ b/content/renderer/service_worker/service_worker_context_client.h
@@ -19,7 +19,6 @@
 #include "base/memory/ref_counted.h"
 #include "base/strings/string16.h"
 #include "base/time/time.h"
-#include "components/payments/mojom/payment_app.mojom.h"
 #include "content/child/webmessageportchannel_impl.h"
 #include "content/common/service_worker/embedded_worker.mojom.h"
 #include "content/common/service_worker/service_worker_event_dispatcher.mojom.h"
@@ -28,6 +27,7 @@
 #include "ipc/ipc_listener.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "third_party/WebKit/public/platform/WebMessagePortChannel.h"
+#include "third_party/WebKit/public/platform/modules/payments/payment_app.mojom.h"
 #include "third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerError.h"
 #include "third_party/WebKit/public/platform/modules/serviceworker/service_worker_event_status.mojom.h"
 #include "third_party/WebKit/public/web/modules/serviceworker/WebServiceWorkerContextClient.h"
@@ -302,8 +302,8 @@
       const PlatformNotificationData& notification_data);
 
   void OnDidGetClient(int request_id, const ServiceWorkerClientInfo& client);
-  void OnDidGetClients(
-      int request_id, const std::vector<ServiceWorkerClientInfo>& clients);
+  void OnDidGetClients(int request_id,
+                       const std::vector<ServiceWorkerClientInfo>& clients);
   void OnOpenWindowResponse(int request_id,
                             const ServiceWorkerClientInfo& client);
   void OnOpenWindowError(int request_id, const std::string& message);
diff --git a/content/renderer/service_worker/service_worker_type_converters.h b/content/renderer/service_worker/service_worker_type_converters.h
index 61ad863..36d320f 100644
--- a/content/renderer/service_worker/service_worker_type_converters.h
+++ b/content/renderer/service_worker/service_worker_type_converters.h
@@ -5,10 +5,10 @@
 #ifndef CONTENT_RENDERER_SERVICE_WORKER_SERVICE_WORKER_TYPE_CONVERTERS_H_
 #define CONTENT_RENDERER_SERVICE_WORKER_SERVICE_WORKER_TYPE_CONVERTERS_H_
 
-#include "components/payments/mojom/payment_app.mojom.h"
 #include "content/common/service_worker/service_worker_event_dispatcher.mojom.h"
 #include "content/common/service_worker/service_worker_status_code.h"
 #include "third_party/WebKit/public/platform/modules/payments/WebPaymentRequestEventData.h"
+#include "third_party/WebKit/public/platform/modules/payments/payment_app.mojom.h"
 #include "third_party/WebKit/public/platform/modules/serviceworker/service_worker_event_status.mojom.h"
 #include "third_party/WebKit/public/web/modules/serviceworker/WebServiceWorkerContextProxy.h"
 
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 079f0d1..62016ee 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -248,7 +248,6 @@
     "//cc/ipc:interfaces",
     "//cc/surfaces",
     "//components/leveldb/public/interfaces",
-    "//components/payments/mojom:mojom_payment_app",
     "//components/viz/display_compositor",
     "//components/viz/frame_sinks",
     "//components/viz/host",
@@ -758,7 +757,6 @@
     "//components/discardable_memory/common",
     "//components/discardable_memory/service",
     "//components/network_session_configurator/common",
-    "//components/payments/mojom:mojom_payment_app",
     "//content:resources",
     "//content/app:both_for_content_tests",
     "//content/browser:for_content_tests",
@@ -1498,8 +1496,6 @@
     "//cc/surfaces",
     "//components/leveldb/public/cpp",
     "//components/metrics/proto",
-    "//components/payments/mojom",
-    "//components/payments/mojom:mojom_payment_app",
     "//components/rappor:test_support",
     "//components/ukm:test_support",
     "//components/ukm/public/interfaces",
diff --git a/content/test/data/media/image_capture_test.html b/content/test/data/media/image_capture_test.html
index da8da8c8..f1b294c 100644
--- a/content/test/data/media/image_capture_test.html
+++ b/content/test/data/media/image_capture_test.html
@@ -16,8 +16,8 @@
   });
 }
 
-// Runs an ImageCapture.getPhotoCapabilities().
-function testCreateAndGetPhotoCapabilities() {
+// Runs an ImageCapture.getPhotoCapabilities() and expects it to succeed.
+function testCreateAndGetPhotoCapabilitiesSucceeds() {
   navigator.mediaDevices.getUserMedia({"video" : CONSTRAINTS})
       .then(stream => {
         assertEquals('video', stream.getVideoTracks()[0].kind);
@@ -35,8 +35,8 @@
       });
 }
 
-// Runs an ImageCapture.takePhoto().
-function testCreateAndTakePhoto() {
+// Runs an ImageCapture.takePhoto() and expects it to succeed.
+function testCreateAndTakePhotoSucceeds() {
   navigator.mediaDevices.getUserMedia({"video" : CONSTRAINTS})
       .then(stream => {
         assertEquals('video', stream.getVideoTracks()[0].kind);
@@ -56,8 +56,28 @@
       });
 }
 
-// Runs an ImageCapture.grabFrame().
-function testCreateAndGrabFrame() {
+// Runs an ImageCapture.takePhoto() and expects it to get rejected.
+function testCreateAndTakePhotoIsRejected() {
+  navigator.mediaDevices.getUserMedia({"video" : CONSTRAINTS})
+      .then(stream => {
+        assertEquals('video', stream.getVideoTracks()[0].kind);
+        return new ImageCapture(stream.getVideoTracks()[0]);
+      })
+      .then(capturer => {
+        return capturer.takePhoto();
+      })
+      .then(blob => {
+        failTest('Expected promise to get rejected but it was fulfilled');
+      }, err => {
+        reportTestSuccess();
+      })
+      .catch(err => {
+        return failTest(err.toString());
+      });
+}
+
+// Runs an ImageCapture.grabFrame() and expects it to succeed.
+function testCreateAndGrabFrameSucceeds() {
   navigator.mediaDevices.getUserMedia({"video" : CONSTRAINTS})
       .then(stream => {
         assertEquals('video', stream.getVideoTracks()[0].kind);
diff --git a/device/vr/vr_math.cc b/device/vr/vr_math.cc
index 8acf64d..c918042b 100644
--- a/device/vr/vr_math.cc
+++ b/device/vr/vr_math.cc
@@ -296,4 +296,24 @@
   return {q.qx, q.qy, q.qz, q.qw};
 }
 
+Mat4f ToMat4F(const gfx::Transform& t) {
+  Mat4f result;
+  for (int i = 0; i < 4; ++i) {
+    for (int j = 0; j < 4; ++j) {
+      result[i][j] = t.matrix().get(i, j);
+    }
+  }
+  return result;
+}
+
+gfx::Transform DEVICE_VR_EXPORT ToTransform(const vr::Mat4f& m) {
+  gfx::Transform result;
+  for (int i = 0; i < 4; ++i) {
+    for (int j = 0; j < 4; ++j) {
+      result.matrix().set(i, j, m[i][j]);
+    }
+  }
+  return result;
+}
+
 }  // namespace vr
diff --git a/device/vr/vr_math.h b/device/vr/vr_math.h
index a48c723..1d14ad61 100644
--- a/device/vr/vr_math.h
+++ b/device/vr/vr_math.h
@@ -9,6 +9,7 @@
 #include "device/vr/vr_types.h"
 
 #include "ui/gfx/geometry/quaternion.h"
+#include "ui/gfx/transform.h"
 
 namespace vr {
 
@@ -89,6 +90,10 @@
 
 gfx::Quaternion DEVICE_VR_EXPORT ToQuaternion(const vr::Quatf& q);
 
+Mat4f DEVICE_VR_EXPORT ToMat4F(const gfx::Transform& t);
+
+gfx::Transform DEVICE_VR_EXPORT ToTransform(const vr::Mat4f& m);
+
 }  // namespace vr
 
 #endif  // DEVICE_VR_VR_MATH_H_
diff --git a/docs/memory/bad_jokes.md b/docs/memory/bad_jokes.md
new file mode 100644
index 0000000..f5e7d54
--- /dev/null
+++ b/docs/memory/bad_jokes.md
@@ -0,0 +1,13 @@
+# Bad Memory Jokes
+
+## What is this?
+
+Stress relief.
+
+## Jokes
+
+  * "A library loads into a process and asks for some pages." -- @wez
+  * "A dynamic linker, a debugger, and an aslr implementation walk into a
+    bar..." -- @awong
+  * "A dynamic linker, a debugger, and an aslr implementation walk into a
+    **PCI** bar..." -- @primiano
diff --git a/docs/memory/key_concepts.md b/docs/memory/key_concepts.md
index 4baea37..e8c4942 100644
--- a/docs/memory/key_concepts.md
+++ b/docs/memory/key_concepts.md
@@ -1,5 +1,103 @@
 # Key Concepts in Chrome Memory
 
+## What's so hard about memory? Isn't it just malloc and free?
+
+Not really. There are lots of differences and subtleties that change per
+operating system and even per operating system configuration.
+
+Fortunately, these differences mostly disappear when a program is running
+with sufficient resources.
+
+Unfortunately, the distinctions end up being very relevant when
+working near out of memory conditions or analyzing overall performance
+when there is any amount of memory pressure; this makes crafting and
+interpreting memory statistics hard.
+
+Fortunately, the point of this doc is to give succinct background that
+will help you ramp up on the subtleties to work in this space. Yes, this
+is complicated stuff...but don't despair. You work on a multi-process
+browser implementing the web platform with high security guarantees.
+Compared to the rest the system, memory is not THAT complicated.
+
+## An you give specific examples of how it's harder than malloc/free?
+
+Here are some example questions that require a more complex
+view of memory than malloc/free.
+
+  * When Chrome allocates memory, when does it take up swap space?
+  * When memory is `free()`d, when is it made usable by other applications?
+  * Is it always safe to touch the memory returned by malloc()?
+  * How many heaps does Chrome have?
+  * How are memory resources used by the GPU and drivers accounted for?
+  * Is that the same on systems where GPU memory isn't shared with main memory?
+  * How are shared libraries accounted for? How big of a penalty is there for
+    each process that shares the memory?
+  * What types of memory does Task Manager/Activity Monitor/top report?
+  * What about the UMA stats?
+
+In many of the above, the answer actually changes per operating system variant.
+There is at least one major schism between Windows-based machines and more
+unixy systems. For example, it is impossible to return all resources (physical
+ram as well as swap space) to the OS in a way brings them back on demand which
+drastically changes the way one can handle free lists.
+
+However, even in macOS, Android, CrOS, and "standard desktop linux" each
+also have enough divergences (compressed memory, pagefile vs swap partition
+vs no swap, overcommit settings, memory perssure signals etc) that even
+answering "how much memory is Chromium using" is hard to do in a uniform
+manner.
+
+The goal of this document is to give a common set of vocabulary
+and concepts such that Chromium developers can more discuss questions like
+the ones above without misunderstanding each other.
+
+
+## Key gotchas
+
+### Windows allocation uses resources immediately; other OSes use it on first touch.
+
+Arguably the biggest difference for Windows and other OSes is memory granted to
+a process is always "committed" on allocation. Pragmatically this means that in
+Windows, `malloc(10*1024*1024*1024)` will immediately prevent other applications
+from being able to successfully allocate memory thereby causing them to crash
+or not be able to open. In Unix variants, usage usually only consumes system
+resources [TODO(awong): Link to overcommit] when pages are touched.
+
+Not being aware of this difference can cause architecture choices that have a
+larger than expected resource impact on Windows and incorrect interpretation for metrics on Windows
+
+See the following section on "discardable" memory for more info.
+
+
+### Because of the commit guarantee difference, "discarding" memory has completely different meanings across platforms.
+
+In Unix systems, there is an `madvise()` function via which pages that have
+been committed via usage can be returned to the non-resource consuming state.
+Such a page will then be recommitted on demand making it a tempting optimization
+for data structures with freelists. However, there is no such API on Windows.
+The `VirtualAlloc(MEM_RESET)`, `DiscardVirtualMemory()`, and
+`OfferVirtualMemory()` look temptingly similar and on first glance they even
+look like they work because they will immediately reduce the amount of physical
+ram (aka Working Set) a processes uses. However, they do NOT release swap
+meaning they will not help prevent OOM scenarios.
+
+Designing a freelist structure that conflates this behavior (see this
+[PartitionAlloc bug](https://bugs.chromium.org/p/chromium/issues/detail?id=726077))
+will result in a system that only truly reduces resource usage on Unix-like
+systems.
+
+
+## Terms and definitions
+
+TODO(awong): To through Erik's Consistent Memory Metrics doc and pull out bits
+that reconcile with this.
+
+### Commited Memory
+### Discardable memory
+### Proportional Set Size
+### Image memory
+### Shared Memory.
+
 TODO(awong): Write overview of our platform diversity, windows vs \*nix memory models (eg,
 "committed" memory), what "discardable" memory is, GPU memory, zram, overcommit,
 the various Chrome heaps (pageheap, partitionalloc, oilpan, v8, malloc...per
diff --git a/ios/web/webui/mojo_facade.mm b/ios/web/webui/mojo_facade.mm
index 0761888..32b64335 100644
--- a/ios/web/webui/mojo_facade.mm
+++ b/ios/web/webui/mojo_facade.mm
@@ -4,7 +4,10 @@
 
 #import "ios/web/webui/mojo_facade.h"
 
+#include <stdint.h>
+
 #include <utility>
+#include <vector>
 
 #import <Foundation/Foundation.h>
 
@@ -213,27 +216,21 @@
     flags = MOJO_READ_MESSAGE_FLAG_NONE;
   }
 
-  uint32_t num_bytes = 0;
-  uint32_t num_handles = 0;
+  std::vector<uint8_t> bytes;
+  std::vector<mojo::ScopedHandle> handles;
   mojo::MessagePipeHandle handle(static_cast<MojoHandle>(handle_as_int));
-  MojoResult mojo_result = mojo::ReadMessageRaw(handle, nullptr, &num_bytes,
-                                                nullptr, &num_handles, flags);
+  MojoResult mojo_result =
+      mojo::ReadMessageRaw(handle, &bytes, &handles, flags);
   std::unique_ptr<base::DictionaryValue> result(new base::DictionaryValue);
-
-  if (mojo_result == MOJO_RESULT_RESOURCE_EXHAUSTED) {
-    std::vector<uint8_t> bytes(num_bytes);
-    std::vector<MojoHandle> handles(num_handles);
-    mojo_result = mojo::ReadMessageRaw(handle, bytes.data(), &num_bytes,
-                                       handles.data(), &num_handles, flags);
-
+  if (mojo_result == MOJO_RESULT_OK) {
     base::ListValue* handles_list = new base::ListValue;
-    for (uint32_t i = 0; i < num_handles; i++) {
-      handles_list->AppendInteger(handles[i]);
+    for (uint32_t i = 0; i < handles.size(); i++) {
+      handles_list->AppendInteger(handles[i].release().value());
     }
     result->Set("handles", std::unique_ptr<base::Value>(handles_list));
 
     base::ListValue* buffer = new base::ListValue;
-    for (uint32_t i = 0; i < num_bytes; i++) {
+    for (uint32_t i = 0; i < bytes.size(); i++) {
       buffer->AppendInteger(bytes[i]);
     }
     result->Set("buffer", std::unique_ptr<base::Value>(buffer));
diff --git a/ios/web_view/test/web_view_interaction_test_util.h b/ios/web_view/test/web_view_interaction_test_util.h
index 830b002..eba3c51b 100644
--- a/ios/web_view/test/web_view_interaction_test_util.h
+++ b/ios/web_view/test/web_view_interaction_test_util.h
@@ -5,8 +5,9 @@
 #ifndef IOS_WEB_VIEW_TEST_WEB_VIEW_INTERACTION_TEST_UTIL_H_
 #define IOS_WEB_VIEW_TEST_WEB_VIEW_INTERACTION_TEST_UTIL_H_
 
+#import <Foundation/Foundation.h>
+
 @class CWVWebView;
-@class NSString;
 
 namespace ios_web_view {
 namespace test {
@@ -15,6 +16,15 @@
 // been tapped using a JavaScript click() event.
 bool TapChromeWebViewElementWithId(CWVWebView* web_view, NSString* element_id);
 
+// Waits until |script| is executed and synchronously returns the evaluation
+// result.
+id EvaluateJavaScript(CWVWebView* web_view, NSString* script, NSError** error);
+
+// Waits for |web_view| to contain |text|. Returns false if the condition is not
+// met within a timeout.
+bool WaitForWebViewContainingTextOrTimeout(CWVWebView* web_view,
+                                           NSString* text);
+
 }  // namespace test
 }  // namespace ios_web_view
 
diff --git a/ios/web_view/test/web_view_interaction_test_util.mm b/ios/web_view/test/web_view_interaction_test_util.mm
index 205e45c..56572335 100644
--- a/ios/web_view/test/web_view_interaction_test_util.mm
+++ b/ios/web_view/test/web_view_interaction_test_util.mm
@@ -12,6 +12,8 @@
 #error "This file requires ARC support."
 #endif
 
+using testing::WaitUntilConditionOrTimeout;
+
 namespace ios_web_view {
 namespace test {
 
@@ -27,19 +29,37 @@
                         "  return false;"
                         "})();",
                        element_id];
+  return [EvaluateJavaScript(web_view, script, nil) boolValue];
+}
+
+id EvaluateJavaScript(CWVWebView* web_view, NSString* script, NSError** error) {
   __block bool did_complete = false;
-  __block bool element_found = false;
+  __block id evaluation_result = nil;
+  __block id evaluation_error = nil;
   [web_view evaluateJavaScript:script
-             completionHandler:^(id result, NSError*) {
+             completionHandler:^(id local_result, NSError* local_error) {
                did_complete = true;
-               element_found = [result boolValue];
+               evaluation_result = [local_result copy];
+               evaluation_error = [local_error copy];
              }];
 
-  testing::WaitUntilConditionOrTimeout(testing::kWaitForJSCompletionTimeout, ^{
+  WaitUntilConditionOrTimeout(testing::kWaitForJSCompletionTimeout, ^{
     return did_complete;
   });
 
-  return element_found;
+  if (error)
+    *error = evaluation_error;
+
+  return evaluation_result;
+}
+
+bool WaitForWebViewContainingTextOrTimeout(CWVWebView* web_view,
+                                           NSString* text) {
+  return WaitUntilConditionOrTimeout(testing::kWaitForUIElementTimeout, ^{
+    id body = ios_web_view::test::EvaluateJavaScript(
+        web_view, @"document.body ? document.body.textContent : null", nil);
+    return [body isKindOfClass:[NSString class]] && [body containsString:text];
+  });
 }
 
 }  // namespace test
diff --git a/ipc/BUILD.gn b/ipc/BUILD.gn
index 99e93cf..e062e8f 100644
--- a/ipc/BUILD.gn
+++ b/ipc/BUILD.gn
@@ -141,9 +141,6 @@
   sources = [
     "ipc_test.mojom",
   ]
-
-  # TODO(crbug.com/714018): Convert the implementation to use OnceCallback.
-  use_once_callback = false
 }
 
 # This is provided as a separate target so other targets can provide param
diff --git a/ipc/ipc_channel_mojo_unittest.cc b/ipc/ipc_channel_mojo_unittest.cc
index f3499b7..f0fc0c30 100644
--- a/ipc/ipc_channel_mojo_unittest.cc
+++ b/ipc/ipc_channel_mojo_unittest.cc
@@ -244,15 +244,14 @@
     mojo::ScopedMessagePipeHandle pipe;
     EXPECT_TRUE(
         IPC::MojoMessageHelper::ReadMessagePipeFrom(&message, iter, &pipe));
-    std::string content(GetSendingFileContent().size(), ' ');
+    std::vector<uint8_t> content;
 
-    uint32_t num_bytes = static_cast<uint32_t>(content.size());
     ASSERT_EQ(MOJO_RESULT_OK,
               mojo::Wait(pipe.get(), MOJO_HANDLE_SIGNAL_READABLE));
     EXPECT_EQ(MOJO_RESULT_OK,
-              mojo::ReadMessageRaw(pipe.get(), &content[0], &num_bytes, nullptr,
-                                   nullptr, 0));
-    EXPECT_EQ(content, GetSendingFileContent());
+              mojo::ReadMessageRaw(pipe.get(), &content, nullptr, 0));
+    EXPECT_EQ(std::string(content.begin(), content.end()),
+              GetSendingFileContent());
   }
 
 #if defined(OS_POSIX)
@@ -354,13 +353,11 @@
 }
 
 void ReadOK(mojo::MessagePipeHandle pipe) {
-  std::string should_be_ok("xx");
-  uint32_t num_bytes = static_cast<uint32_t>(should_be_ok.size());
+  std::vector<uint8_t> should_be_ok;
   CHECK_EQ(MOJO_RESULT_OK, mojo::Wait(pipe, MOJO_HANDLE_SIGNAL_READABLE));
   CHECK_EQ(MOJO_RESULT_OK,
-           mojo::ReadMessageRaw(pipe, &should_be_ok[0], &num_bytes, nullptr,
-                                nullptr, 0));
-  EXPECT_EQ(should_be_ok, std::string("OK"));
+           mojo::ReadMessageRaw(pipe, &should_be_ok, nullptr, 0));
+  EXPECT_EQ("OK", std::string(should_be_ok.begin(), should_be_ok.end()));
 }
 
 void WriteOK(mojo::MessagePipeHandle pipe) {
@@ -548,18 +545,16 @@
     next_expected_value_ = value;
   }
 
-  void GetExpectedValue(const GetExpectedValueCallback& callback) override {
+  void GetExpectedValue(GetExpectedValueCallback callback) override {
     NOTREACHED();
   }
 
-  void RequestValue(const RequestValueCallback& callback) override {
-    NOTREACHED();
-  }
+  void RequestValue(RequestValueCallback callback) override { NOTREACHED(); }
 
-  void RequestQuit(const RequestQuitCallback& callback) override {
+  void RequestQuit(RequestQuitCallback callback) override {
     EXPECT_EQ(kNumMessages, num_messages_received_);
     received_quit_ = true;
-    callback.Run();
+    std::move(callback).Run();
     base::MessageLoop::current()->QuitWhenIdle();
   }
 
@@ -738,17 +733,15 @@
     next_expected_value_ = value;
   }
 
-  void GetExpectedValue(const GetExpectedValueCallback& callback) override {
-    callback.Run(next_expected_value_);
+  void GetExpectedValue(GetExpectedValueCallback callback) override {
+    std::move(callback).Run(next_expected_value_);
   }
 
-  void RequestValue(const RequestValueCallback& callback) override {
-    NOTREACHED();
-  }
+  void RequestValue(RequestValueCallback callback) override { NOTREACHED(); }
 
-  void RequestQuit(const RequestQuitCallback& callback) override {
+  void RequestQuit(RequestQuitCallback callback) override {
     received_quit_ = true;
-    callback.Run();
+    std::move(callback).Run();
     binding_.Close();
     base::MessageLoop::current()->QuitWhenIdle();
   }
@@ -871,8 +864,8 @@
   }
 
   // IPC::mojom::PingReceiver:
-  void Ping(const PingCallback& callback) override {
-    callback.Run();
+  void Ping(PingCallback callback) override {
+    std::move(callback).Run();
     ping_handler_.Run();
   }
 
@@ -953,17 +946,17 @@
     next_expected_value_ = value;
   }
 
-  void GetExpectedValue(const GetExpectedValueCallback& callback) override {
-    callback.Run(next_expected_value_);
+  void GetExpectedValue(GetExpectedValueCallback callback) override {
+    std::move(callback).Run(next_expected_value_);
   }
 
-  void RequestValue(const RequestValueCallback& callback) override {
-    callback.Run(response_value_);
+  void RequestValue(RequestValueCallback callback) override {
+    std::move(callback).Run(response_value_);
   }
 
-  void RequestQuit(const RequestQuitCallback& callback) override {
+  void RequestQuit(RequestQuitCallback callback) override {
     quit_closure_.Run();
-    callback.Run();
+    std::move(callback).Run();
   }
 
   // IPC::Listener:
@@ -1080,7 +1073,7 @@
 
  private:
   // IPC::mojom::SimpleTestClient:
-  void RequestValue(const RequestValueCallback& callback) override {
+  void RequestValue(RequestValueCallback callback) override {
     int32_t response = 0;
     if (use_sync_sender_) {
       std::unique_ptr<IPC::SyncMessage> reply(new IPC::SyncMessage(
@@ -1091,7 +1084,7 @@
       EXPECT_TRUE(driver_->RequestValue(&response));
     }
 
-    callback.Run(response);
+    std::move(callback).Run(response);
 
     DCHECK(run_loop_);
     run_loop_->Quit();
diff --git a/ipc/ipc_mojo_perftest.cc b/ipc/ipc_mojo_perftest.cc
index d02acda..836a1df 100644
--- a/ipc/ipc_mojo_perftest.cc
+++ b/ipc/ipc_mojo_perftest.cc
@@ -479,8 +479,8 @@
 
  private:
   // IPC::mojom::Reflector:
-  void Ping(const std::string& value, const PingCallback& callback) override {
-    callback.Run(value);
+  void Ping(const std::string& value, PingCallback callback) override {
+    std::move(callback).Run(value);
   }
 
   void Quit() override {
diff --git a/media/capture/video/fake_video_capture_device.cc b/media/capture/video/fake_video_capture_device.cc
index 3091ad2..7c7eca2 100644
--- a/media/capture/video/fake_video_capture_device.cc
+++ b/media/capture/video/fake_video_capture_device.cc
@@ -365,14 +365,19 @@
 
 FakePhotoDevice::FakePhotoDevice(
     std::unique_ptr<PacmanFramePainter> sk_n32_painter,
-    const FakeDeviceState* fake_device_state)
+    const FakeDeviceState* fake_device_state,
+    const FakePhotoDeviceConfig& config)
     : sk_n32_painter_(std::move(sk_n32_painter)),
-      fake_device_state_(fake_device_state) {}
+      fake_device_state_(fake_device_state),
+      config_(config) {}
 
 FakePhotoDevice::~FakePhotoDevice() = default;
 
 void FakePhotoDevice::TakePhoto(VideoCaptureDevice::TakePhotoCallback callback,
                                 base::TimeDelta elapsed_time) {
+  if (config_.should_fail_take_photo)
+    return;
+
   // Create a PNG-encoded frame and send it back to |callback|.
   auto required_sk_n32_buffer_size = VideoFrame::AllocationSize(
       PIXEL_FORMAT_ARGB, fake_device_state_->format.frame_size);
@@ -445,6 +450,9 @@
 
 void FakePhotoDevice::GetPhotoState(
     VideoCaptureDevice::GetPhotoStateCallback callback) {
+  if (config_.should_fail_get_photo_capabilities)
+    return;
+
   mojom::PhotoStatePtr photo_state = mojom::PhotoState::New();
 
   photo_state->current_white_balance_mode = mojom::MeteringMode::NONE;
@@ -491,8 +499,19 @@
 void FakeVideoCaptureDevice::SetPhotoOptions(mojom::PhotoSettingsPtr settings,
                                              SetPhotoOptionsCallback callback) {
   DCHECK(thread_checker_.CalledOnValidThread());
+  photo_device_->SetPhotoOptions(std::move(settings), std::move(callback),
+                                 device_state_.get());
+}
+
+void FakePhotoDevice::SetPhotoOptions(
+    mojom::PhotoSettingsPtr settings,
+    VideoCaptureDevice::SetPhotoOptionsCallback callback,
+    FakeDeviceState* device_state_write_access) {
+  if (config_.should_fail_set_photo_options)
+    return;
+
   if (settings->has_zoom) {
-    device_state_->zoom =
+    device_state_write_access->zoom =
         std::max(kMinZoom, std::min(settings->zoom, kMaxZoom));
   }
 
diff --git a/media/capture/video/fake_video_capture_device.h b/media/capture/video/fake_video_capture_device.h
index ffc0e8d..eefbea3 100644
--- a/media/capture/video/fake_video_capture_device.h
+++ b/media/capture/video/fake_video_capture_device.h
@@ -120,20 +120,36 @@
   const FakeDeviceState* device_state_ = nullptr;
 };
 
+struct FakePhotoDeviceConfig {
+  FakePhotoDeviceConfig()
+      : should_fail_get_photo_capabilities(false),
+        should_fail_set_photo_options(false),
+        should_fail_take_photo(false) {}
+
+  bool should_fail_get_photo_capabilities;
+  bool should_fail_set_photo_options;
+  bool should_fail_take_photo;
+};
+
 // Implements the photo functionality of a FakeVideoCaptureDevice
 class FakePhotoDevice {
  public:
   FakePhotoDevice(std::unique_ptr<PacmanFramePainter> sk_n32_painter,
-                  const FakeDeviceState* fake_device_state);
+                  const FakeDeviceState* fake_device_state,
+                  const FakePhotoDeviceConfig& config);
   ~FakePhotoDevice();
 
   void GetPhotoState(VideoCaptureDevice::GetPhotoStateCallback callback);
+  void SetPhotoOptions(mojom::PhotoSettingsPtr settings,
+                       VideoCaptureDevice::SetPhotoOptionsCallback callback,
+                       FakeDeviceState* device_state_write_access);
   void TakePhoto(VideoCaptureDevice::TakePhotoCallback callback,
                  base::TimeDelta elapsed_time);
 
  private:
   const std::unique_ptr<PacmanFramePainter> sk_n32_painter_;
   const FakeDeviceState* const fake_device_state_;
+  const FakePhotoDeviceConfig config_;
 };
 
 }  // namespace media
diff --git a/media/capture/video/fake_video_capture_device_factory.cc b/media/capture/video/fake_video_capture_device_factory.cc
index b6e6cee..6043c89 100644
--- a/media/capture/video/fake_video_capture_device_factory.cc
+++ b/media/capture/video/fake_video_capture_device_factory.cc
@@ -93,6 +93,12 @@
 FakeVideoCaptureDeviceSettings::FakeVideoCaptureDeviceSettings(
     const FakeVideoCaptureDeviceSettings& other) = default;
 
+constexpr char
+    FakeVideoCaptureDeviceFactory::kDeviceConfigForGetPhotoStateFails[];
+constexpr char
+    FakeVideoCaptureDeviceFactory::kDeviceConfigForSetPhotoOptionsFails[];
+constexpr char FakeVideoCaptureDeviceFactory::kDeviceConfigForTakePhotoFails[];
+
 FakeVideoCaptureDeviceFactory::FakeVideoCaptureDeviceFactory() {
   // The default |devices_config_| is the one obtained from an empty options
   // string.
@@ -103,13 +109,12 @@
 
 // static
 std::unique_ptr<VideoCaptureDevice>
-FakeVideoCaptureDeviceFactory::CreateDeviceWithSupportedFormats(
-    FakeVideoCaptureDevice::DeliveryMode delivery_mode,
-    const VideoCaptureFormats& formats) {
-  if (formats.empty())
+FakeVideoCaptureDeviceFactory::CreateDeviceWithSettings(
+    const FakeVideoCaptureDeviceSettings& settings) {
+  if (settings.supported_formats.empty())
     return CreateErrorDevice();
 
-  for (const auto& entry : formats) {
+  for (const auto& entry : settings.supported_formats) {
     bool pixel_format_supported = false;
     for (const auto& supported_pixel_format : kSupportedPixelFormats) {
       if (entry.pixel_format == supported_pixel_format) {
@@ -124,18 +129,19 @@
     }
   }
 
-  const VideoCaptureFormat& initial_format = formats.front();
+  const VideoCaptureFormat& initial_format = settings.supported_formats.front();
   auto device_state = base::MakeUnique<FakeDeviceState>(
       kInitialZoom, initial_format.frame_rate, initial_format.pixel_format);
 
   auto photo_frame_painter = base::MakeUnique<PacmanFramePainter>(
       PacmanFramePainter::Format::SK_N32, device_state.get());
   auto photo_device = base::MakeUnique<FakePhotoDevice>(
-      std::move(photo_frame_painter), device_state.get());
+      std::move(photo_frame_painter), device_state.get(),
+      settings.photo_device_config);
 
   return base::MakeUnique<FakeVideoCaptureDevice>(
-      formats,
-      base::MakeUnique<FrameDelivererFactory>(delivery_mode,
+      settings.supported_formats,
+      base::MakeUnique<FrameDelivererFactory>(settings.delivery_mode,
                                               device_state.get()),
       std::move(photo_device), std::move(device_state));
 }
@@ -146,10 +152,12 @@
     VideoPixelFormat pixel_format,
     FakeVideoCaptureDevice::DeliveryMode delivery_mode,
     float frame_rate) {
-  VideoCaptureFormats formats;
+  FakeVideoCaptureDeviceSettings settings;
+  settings.delivery_mode = delivery_mode;
   for (const gfx::Size& resolution : kDefaultResolutions)
-    formats.emplace_back(resolution, frame_rate, pixel_format);
-  return CreateDeviceWithSupportedFormats(delivery_mode, formats);
+    settings.supported_formats.emplace_back(resolution, frame_rate,
+                                            pixel_format);
+  return CreateDeviceWithSettings(settings);
 }
 
 // static
@@ -177,8 +185,7 @@
   for (const auto& entry : devices_config_) {
     if (device_descriptor.device_id != entry.device_id)
       continue;
-    return CreateDeviceWithSupportedFormats(entry.delivery_mode,
-                                            entry.supported_formats);
+    return CreateDeviceWithSettings(entry);
   }
   return nullptr;
 }
@@ -278,6 +285,34 @@
             kFakeCaptureMaxDeviceCount,
             std::max(kFakeCaptureMinDeviceCount, static_cast<int>(count)));
       }
+    } else if (base::EqualsCaseInsensitiveASCII(param.front(), "config")) {
+      const int device_index = 0;
+      std::vector<VideoPixelFormat> pixel_formats;
+      pixel_formats.push_back(GetPixelFormatFromDeviceIndex(device_index));
+      FakeVideoCaptureDeviceSettings settings;
+      settings.delivery_mode = delivery_mode;
+      settings.device_id =
+          base::StringPrintf(kDefaultDeviceIdMask, device_index);
+      AppendAllCombinationsToFormatsContainer(
+          pixel_formats, resolutions, frame_rates, &settings.supported_formats);
+
+      if (param.back() == kDeviceConfigForGetPhotoStateFails) {
+        settings.photo_device_config.should_fail_get_photo_capabilities = true;
+        config->push_back(settings);
+        return;
+      }
+      if (param.back() == kDeviceConfigForSetPhotoOptionsFails) {
+        settings.photo_device_config.should_fail_set_photo_options = true;
+        config->push_back(settings);
+        return;
+      }
+      if (param.back() == kDeviceConfigForTakePhotoFails) {
+        settings.photo_device_config.should_fail_take_photo = true;
+        config->push_back(settings);
+        return;
+      }
+      LOG(WARNING) << "Unknown config " << param.back();
+      return;
     }
   }
 
diff --git a/media/capture/video/fake_video_capture_device_factory.h b/media/capture/video/fake_video_capture_device_factory.h
index 06ed42ed..fdfe942 100644
--- a/media/capture/video/fake_video_capture_device_factory.h
+++ b/media/capture/video/fake_video_capture_device_factory.h
@@ -18,6 +18,7 @@
   std::string device_id;
   FakeVideoCaptureDevice::DeliveryMode delivery_mode;
   VideoCaptureFormats supported_formats;
+  FakePhotoDeviceConfig photo_device_config;
 };
 
 // Implementation of VideoCaptureDeviceFactory that creates fake devices
@@ -30,12 +31,18 @@
 class CAPTURE_EXPORT FakeVideoCaptureDeviceFactory
     : public VideoCaptureDeviceFactory {
  public:
+  static constexpr const char kDeviceConfigForGetPhotoStateFails[] =
+      "GetPhotoStateFails";
+  static constexpr const char kDeviceConfigForSetPhotoOptionsFails[] =
+      "SetPhotoOptionsFails";
+  static constexpr const char kDeviceConfigForTakePhotoFails[] =
+      "TakePhotoFails";
+
   FakeVideoCaptureDeviceFactory();
   ~FakeVideoCaptureDeviceFactory() override;
 
-  static std::unique_ptr<VideoCaptureDevice> CreateDeviceWithSupportedFormats(
-      FakeVideoCaptureDevice::DeliveryMode delivery_mode,
-      const VideoCaptureFormats& formats);
+  static std::unique_ptr<VideoCaptureDevice> CreateDeviceWithSettings(
+      const FakeVideoCaptureDeviceSettings& settings);
 
   static std::unique_ptr<VideoCaptureDevice> CreateDeviceWithDefaultResolutions(
       VideoPixelFormat pixel_format,
diff --git a/media/capture/video/video_capture_device.h b/media/capture/video/video_capture_device.h
index bbdf3bd..888462a 100644
--- a/media/capture/video/video_capture_device.h
+++ b/media/capture/video/video_capture_device.h
@@ -272,11 +272,14 @@
   virtual void StopAndDeAllocate() = 0;
 
   // Retrieve the photo capabilities and settings of the device (e.g. zoom
-  // levels etc).
+  // levels etc). On success, invokes |callback|. On failure, drops callback
+  // without invoking it.
   using GetPhotoStateCallback =
       ScopedResultCallback<base::Callback<void(mojom::PhotoStatePtr)>>;
   virtual void GetPhotoState(GetPhotoStateCallback callback);
 
+  // On success, invokes |callback| with value |true|. On failure, drops
+  // callback without invoking it.
   using SetPhotoOptionsCallback =
       ScopedResultCallback<base::Callback<void(bool)>>;
   virtual void SetPhotoOptions(mojom::PhotoSettingsPtr settings,
@@ -284,7 +287,8 @@
 
   // Asynchronously takes a photo, possibly reconfiguring the capture objects
   // and/or interrupting the capture flow. Runs |callback| on the thread
-  // where TakePhoto() is called, if the photo was successfully taken.
+  // where TakePhoto() is called, if the photo was successfully taken. On
+  // failure, drops callback without invoking it.
   using TakePhotoCallback =
       ScopedResultCallback<base::Callback<void(mojom::BlobPtr blob)>>;
   virtual void TakePhoto(TakePhotoCallback callback);
diff --git a/mojo/edk/js/core.cc b/mojo/edk/js/core.cc
index 4b557db..a04a960 100644
--- a/mojo/edk/js/core.cc
+++ b/mojo/edk/js/core.cc
@@ -118,13 +118,12 @@
   std::vector<MojoHandle> raw_handles(handles.size());
   for (size_t i = 0; i < handles.size(); ++i)
     raw_handles[i] = handles[i]->get().value();
-  MojoResult rv = MojoWriteMessage(handle.value(),
-                          buffer.bytes(),
-                          static_cast<uint32_t>(buffer.num_bytes()),
-                          raw_handles.empty() ? NULL : &raw_handles[0],
-                          static_cast<uint32_t>(raw_handles.size()),
-                          flags);
-  // MojoWriteMessage takes ownership of the handles, so release them here.
+  MojoResult rv =
+      WriteMessageRaw(MessagePipeHandle(handle.value()), buffer.bytes(),
+                      static_cast<uint32_t>(buffer.num_bytes()),
+                      raw_handles.empty() ? NULL : &raw_handles[0],
+                      static_cast<uint32_t>(raw_handles.size()), flags);
+  // WriteMessageRaw takes ownership of the handles, so release them here.
   for (size_t i = 0; i < handles.size(); ++i)
     ignore_result(handles[i]->release());
 
@@ -134,34 +133,55 @@
 gin::Dictionary ReadMessage(const gin::Arguments& args,
                             mojo::Handle handle,
                             MojoReadMessageFlags flags) {
-  uint32_t num_bytes = 0;
-  uint32_t num_handles = 0;
-  MojoResult result = MojoReadMessage(
-      handle.value(), NULL, &num_bytes, NULL, &num_handles, flags);
-  if (result != MOJO_RESULT_RESOURCE_EXHAUSTED) {
+  MojoMessageHandle message;
+  MojoResult result =
+      MojoReadMessageNew(handle.value(), &message, MOJO_READ_MESSAGE_FLAG_NONE);
+  if (result != MOJO_RESULT_OK) {
     gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate());
     dictionary.Set("result", result);
     return dictionary;
   }
 
+  result = MojoSerializeMessage(message);
+  if (result != MOJO_RESULT_OK && result != MOJO_RESULT_FAILED_PRECONDITION) {
+    MojoFreeMessage(message);
+    gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate());
+    dictionary.Set("result", MOJO_RESULT_ABORTED);
+    return dictionary;
+  }
+
+  uint32_t num_bytes = 0;
+  void* bytes;
+  uint32_t num_handles = 0;
+  std::vector<mojo::Handle> handles;
+  result = MojoGetSerializedMessageContents(
+      message, &bytes, &num_bytes, nullptr, &num_handles,
+      MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_NONE);
+  if (result == MOJO_RESULT_RESOURCE_EXHAUSTED) {
+    handles.resize(num_handles);
+    result = MojoGetSerializedMessageContents(
+        message, &bytes, &num_bytes,
+        reinterpret_cast<MojoHandle*>(handles.data()), &num_handles,
+        MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_NONE);
+  }
+
+  if (result != MOJO_RESULT_OK) {
+    MojoFreeMessage(message);
+    gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate());
+    dictionary.Set("result", MOJO_RESULT_ABORTED);
+    return dictionary;
+  }
+
   v8::Local<v8::ArrayBuffer> array_buffer =
       v8::ArrayBuffer::New(args.isolate(), num_bytes);
-  std::vector<mojo::Handle> handles(num_handles);
+  if (num_bytes) {
+    gin::ArrayBuffer buffer;
+    ConvertFromV8(args.isolate(), array_buffer, &buffer);
+    DCHECK_EQ(buffer.num_bytes(), num_bytes);
+    memcpy(buffer.bytes(), bytes, num_bytes);
+  }
 
-  gin::ArrayBuffer buffer;
-  ConvertFromV8(args.isolate(), array_buffer, &buffer);
-  CHECK(buffer.num_bytes() == num_bytes);
-
-  result = MojoReadMessage(handle.value(),
-                           buffer.bytes(),
-                           &num_bytes,
-                           handles.empty() ? NULL :
-                               reinterpret_cast<MojoHandle*>(&handles[0]),
-                           &num_handles,
-                           flags);
-
-  CHECK(buffer.num_bytes() == num_bytes);
-  CHECK(handles.size() == num_handles);
+  MojoFreeMessage(message);
 
   gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate());
   dictionary.Set("result", result);
diff --git a/mojo/edk/js/tests/js_to_cpp_tests.cc b/mojo/edk/js/tests/js_to_cpp_tests.cc
index d193ffc..0ce95e8 100644
--- a/mojo/edk/js/tests/js_to_cpp_tests.cc
+++ b/mojo/edk/js/tests/js_to_cpp_tests.cc
@@ -7,6 +7,7 @@
 
 #include <string>
 #include <utility>
+#include <vector>
 
 #include "base/at_exit.h"
 #include "base/files/file_path.h"
@@ -75,16 +76,15 @@
 }
 
 void CheckMessagePipe(MessagePipeHandle message_pipe_handle) {
-  unsigned char buffer[100];
-  uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer));
   MojoResult result = Wait(message_pipe_handle, MOJO_HANDLE_SIGNAL_READABLE);
   EXPECT_EQ(MOJO_RESULT_OK, result);
-  result = ReadMessageRaw(
-      message_pipe_handle, buffer, &buffer_size, 0, 0, 0);
+  std::vector<uint8_t> bytes;
+  std::vector<ScopedHandle> handles;
+  result = ReadMessageRaw(message_pipe_handle, &bytes, &handles, 0);
   EXPECT_EQ(MOJO_RESULT_OK, result);
-  EXPECT_EQ(64u, buffer_size);
+  EXPECT_EQ(64u, bytes.size());
   for (int i = 0; i < 64; ++i) {
-    EXPECT_EQ(255 - i, buffer[i]);
+    EXPECT_EQ(255 - i, bytes[i]);
   }
 }
 
@@ -176,14 +176,14 @@
 }
 
 void CheckCorruptedMessagePipe(MojoHandle message_pipe_handle) {
-  unsigned char buffer[100];
-  uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer));
-  MojoResult result = MojoReadMessage(
-      message_pipe_handle, buffer, &buffer_size, 0, 0, 0);
+  std::vector<uint8_t> bytes;
+  std::vector<ScopedHandle> handles;
+  MojoResult result = ReadMessageRaw(MessagePipeHandle(message_pipe_handle),
+                                     &bytes, &handles, 0);
   if (result != MOJO_RESULT_OK)
     return;
-  for (uint32_t i = 0; i < buffer_size; ++i)
-    g_waste_accumulator += buffer[i];
+  for (uint32_t i = 0; i < bytes.size(); ++i)
+    g_waste_accumulator += bytes[i];
 }
 
 void CheckCorruptedEchoArgs(const js_to_cpp::EchoArgsPtr& arg) {
diff --git a/mojo/edk/system/core_test_base.cc b/mojo/edk/system/core_test_base.cc
index 148d82e..86e484f3 100644
--- a/mojo/edk/system/core_test_base.cc
+++ b/mojo/edk/system/core_test_base.cc
@@ -45,14 +45,6 @@
       std::unique_ptr<ports::UserMessageEvent> message_event,
       MojoWriteMessageFlags /*flags*/) override {
     info_->IncrementWriteMessageCallCount();
-
-    auto* message = message_event->GetMessage<UserMessageImpl>();
-    if (message->user_payload_size() > GetConfiguration().max_message_num_bytes)
-      return MOJO_RESULT_RESOURCE_EXHAUSTED;
-
-    if (message->num_handles())
-      return MOJO_RESULT_UNIMPLEMENTED;
-
     return MOJO_RESULT_OK;
   }
 
diff --git a/mojo/edk/system/core_unittest.cc b/mojo/edk/system/core_unittest.cc
index 36b965b..60057e3 100644
--- a/mojo/edk/system/core_unittest.cc
+++ b/mojo/edk/system/core_unittest.cc
@@ -50,21 +50,18 @@
   ASSERT_NE(h, MOJO_HANDLE_INVALID);
 
   ASSERT_EQ(0u, info.GetWriteMessageCallCount());
+  MojoMessageHandle message;
+  ASSERT_EQ(MOJO_RESULT_OK, core()->CreateMessage(42, nullptr, &message));
   ASSERT_EQ(MOJO_RESULT_OK,
-            core()->WriteMessage(h, nullptr, 0, nullptr, 0,
-                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
+            core()->WriteMessageNew(h, message, MOJO_WRITE_MESSAGE_FLAG_NONE));
   ASSERT_EQ(1u, info.GetWriteMessageCallCount());
 
   ASSERT_EQ(0u, info.GetReadMessageCallCount());
-  uint32_t num_bytes = 0;
-  ASSERT_EQ(
-      MOJO_RESULT_OK,
-      core()->ReadMessage(h, nullptr, &num_bytes, nullptr, nullptr,
-                          MOJO_READ_MESSAGE_FLAG_NONE));
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->ReadMessageNew(h, &message, MOJO_READ_MESSAGE_FLAG_NONE));
   ASSERT_EQ(1u, info.GetReadMessageCallCount());
   ASSERT_EQ(MOJO_RESULT_OK,
-            core()->ReadMessage(h, nullptr, nullptr, nullptr, nullptr,
-                                MOJO_READ_MESSAGE_FLAG_NONE));
+            core()->ReadMessageNew(h, &message, MOJO_READ_MESSAGE_FLAG_NONE));
   ASSERT_EQ(2u, info.GetReadMessageCallCount());
 
   ASSERT_EQ(0u, info.GetWriteDataCallCount());
@@ -123,137 +120,28 @@
   // |CreateMessagePipe()|: Nothing to check (apart from things that cause
   // death).
 
-  // |WriteMessage()|:
+  // |WriteMessageNew()|:
   // Only check arguments checked by |Core|, namely |handle|, |handles|, and
   // |num_handles|.
   {
     ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
-              core()->WriteMessage(MOJO_HANDLE_INVALID, nullptr, 0,
-                                   nullptr, 0, MOJO_WRITE_MESSAGE_FLAG_NONE));
-
-    MockHandleInfo info;
-    MojoHandle h = CreateMockHandle(&info);
-    MojoHandle handles[2] = {MOJO_HANDLE_INVALID, MOJO_HANDLE_INVALID};
-
-    // Huge handle count (implausibly big on some systems -- more than can be
-    // stored in a 32-bit address space).
-    // Note: This may return either |MOJO_RESULT_INVALID_ARGUMENT| or
-    // |MOJO_RESULT_RESOURCE_EXHAUSTED|, depending on whether it's plausible or
-    // not.
-    ASSERT_NE(
-        MOJO_RESULT_OK,
-        core()->WriteMessage(h, nullptr, 0, handles,
-                             std::numeric_limits<uint32_t>::max(),
-                             MOJO_WRITE_MESSAGE_FLAG_NONE));
-    ASSERT_EQ(0u, info.GetWriteMessageCallCount());
-
-    // Null |bytes| with non-zero message size.
-    ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
-              core()->WriteMessage(h, nullptr, 1, nullptr, 0,
-                                   MOJO_WRITE_MESSAGE_FLAG_NONE));
-    ASSERT_EQ(0u, info.GetWriteMessageCallCount());
-
-    // Null |handles| with non-zero handle count.
-    ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
-              core()->WriteMessage(h, nullptr, 0, nullptr, 1,
-                                   MOJO_WRITE_MESSAGE_FLAG_NONE));
-    ASSERT_EQ(0u, info.GetWriteMessageCallCount());
-
-    // Huge handle count (plausibly big).
-    ASSERT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED,
-              core()->WriteMessage(
-                  h, nullptr, 0, handles,
-                  std::numeric_limits<uint32_t>::max() / sizeof(handles[0]),
-                  MOJO_WRITE_MESSAGE_FLAG_NONE));
-    ASSERT_EQ(0u, info.GetWriteMessageCallCount());
-
-    // Invalid handle in |handles|.
-    ASSERT_EQ(
-        MOJO_RESULT_INVALID_ARGUMENT,
-        core()->WriteMessage(h, nullptr, 0, handles, 1,
-                             MOJO_WRITE_MESSAGE_FLAG_NONE));
-    ASSERT_EQ(0u, info.GetWriteMessageCallCount());
-
-    // Two invalid handles in |handles|.
-    ASSERT_EQ(
-        MOJO_RESULT_INVALID_ARGUMENT,
-        core()->WriteMessage(h, nullptr, 0, handles, 2,
-                             MOJO_WRITE_MESSAGE_FLAG_NONE));
-    ASSERT_EQ(0u, info.GetWriteMessageCallCount());
-
-    // Can't send a handle over itself. Note that this will also cause |h| to be
-    // closed.
-    handles[0] = h;
-    ASSERT_EQ(
-        MOJO_RESULT_INVALID_ARGUMENT,
-        core()->WriteMessage(h, nullptr, 0, handles, 1,
-                             MOJO_WRITE_MESSAGE_FLAG_NONE));
-    ASSERT_EQ(0u, info.GetWriteMessageCallCount());
-
-    h = CreateMockHandle(&info);
-
-    MockHandleInfo info2;
-
-    // This is "okay", but |MockDispatcher| doesn't implement it.
-    handles[0] = CreateMockHandle(&info2);
-    ASSERT_EQ(
-        MOJO_RESULT_UNIMPLEMENTED,
-        core()->WriteMessage(h, nullptr, 0, handles, 1,
-                             MOJO_WRITE_MESSAGE_FLAG_NONE));
-    ASSERT_EQ(1u, info.GetWriteMessageCallCount());
-
-    // One of the |handles| is still invalid.
-    handles[0] = CreateMockHandle(&info2);
-    ASSERT_EQ(
-        MOJO_RESULT_INVALID_ARGUMENT,
-        core()->WriteMessage(h, nullptr, 0, handles, 2,
-                             MOJO_WRITE_MESSAGE_FLAG_NONE));
-    ASSERT_EQ(1u, info.GetWriteMessageCallCount());
-
-    // One of the |handles| is the same as |h|. Both handles are closed.
-    handles[0] = CreateMockHandle(&info2);
-    handles[1] = h;
-    ASSERT_EQ(
-        MOJO_RESULT_INVALID_ARGUMENT,
-        core()->WriteMessage(h, nullptr, 0, handles, 2,
-                             MOJO_WRITE_MESSAGE_FLAG_NONE));
-    ASSERT_EQ(1u, info.GetWriteMessageCallCount());
-
-    h = CreateMockHandle(&info);
-
-    // Can't send a handle twice in the same message.
-    handles[0] = CreateMockHandle(&info2);
-    handles[1] = handles[0];
-    ASSERT_EQ(
-        MOJO_RESULT_BUSY,
-        core()->WriteMessage(h, nullptr, 0, handles, 2,
-                             MOJO_WRITE_MESSAGE_FLAG_NONE));
-    ASSERT_EQ(1u, info.GetWriteMessageCallCount());
-
-    ASSERT_EQ(MOJO_RESULT_OK, core()->Close(h));
+              core()->WriteMessageNew(MOJO_HANDLE_INVALID, 0,
+                                      MOJO_WRITE_MESSAGE_FLAG_NONE));
   }
 
-  // |ReadMessage()|:
+  // |ReadMessageNew()|:
   // Only check arguments checked by |Core|, namely |handle|, |handles|, and
   // |num_handles|.
   {
-    ASSERT_EQ(
-        MOJO_RESULT_INVALID_ARGUMENT,
-        core()->ReadMessage(MOJO_HANDLE_INVALID, nullptr, nullptr, nullptr,
-                            nullptr, MOJO_READ_MESSAGE_FLAG_NONE));
-
+    ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+              core()->ReadMessageNew(MOJO_HANDLE_INVALID, nullptr,
+                                     MOJO_READ_MESSAGE_FLAG_NONE));
     MockHandleInfo info;
     MojoHandle h = CreateMockHandle(&info);
-
-    // Okay.
-    uint32_t handle_count = 0;
-    ASSERT_EQ(MOJO_RESULT_OK,
-              core()->ReadMessage(
-                  h, nullptr, nullptr, nullptr, &handle_count,
-                  MOJO_READ_MESSAGE_FLAG_NONE));
+    ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+              core()->ReadMessageNew(h, nullptr, MOJO_READ_MESSAGE_FLAG_NONE));
     // Checked by |Core|, shouldn't go through to the dispatcher.
-    ASSERT_EQ(1u, info.GetReadMessageCallCount());
-
+    ASSERT_EQ(0u, info.GetReadMessageCallCount());
     ASSERT_EQ(MOJO_RESULT_OK, core()->Close(h));
   }
 }
@@ -279,44 +167,29 @@
   ASSERT_EQ(kAllSignals, hss[1].satisfiable_signals);
 
   // Try to read anyway.
-  char buffer[1] = {'a'};
-  uint32_t buffer_size = 1;
+  MojoMessageHandle message;
   ASSERT_EQ(
       MOJO_RESULT_SHOULD_WAIT,
-      core()->ReadMessage(h[0], buffer, &buffer_size, nullptr, nullptr,
-                          MOJO_READ_MESSAGE_FLAG_NONE));
-  // Check that it left its inputs alone.
-  ASSERT_EQ('a', buffer[0]);
-  ASSERT_EQ(1u, buffer_size);
+      core()->ReadMessageNew(h[0], &message, MOJO_READ_MESSAGE_FLAG_NONE));
 
   // Write to |h[1]|.
-  buffer[0] = 'b';
-  ASSERT_EQ(
-      MOJO_RESULT_OK,
-      core()->WriteMessage(h[1], buffer, 1, nullptr, 0,
-                           MOJO_WRITE_MESSAGE_FLAG_NONE));
+  const uintptr_t kTestMessageContext = 123;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->CreateMessage(kTestMessageContext, nullptr, &message));
+  ASSERT_EQ(MOJO_RESULT_OK, core()->WriteMessageNew(
+                                h[1], message, MOJO_WRITE_MESSAGE_FLAG_NONE));
 
   // Wait for |h[0]| to become readable.
   EXPECT_EQ(MOJO_RESULT_OK, mojo::Wait(mojo::Handle(h[0]),
                                        MOJO_HANDLE_SIGNAL_READABLE, &hss[0]));
 
   // Read from |h[0]|.
-  // First, get only the size.
-  buffer_size = 0;
-  ASSERT_EQ(
-      MOJO_RESULT_RESOURCE_EXHAUSTED,
-      core()->ReadMessage(h[0], nullptr, &buffer_size, nullptr, nullptr,
-                          MOJO_READ_MESSAGE_FLAG_NONE));
-  ASSERT_EQ(1u, buffer_size);
-  // Then actually read it.
-  buffer[0] = 'c';
-  buffer_size = 1;
-  ASSERT_EQ(
-      MOJO_RESULT_OK,
-      core()->ReadMessage(h[0], buffer, &buffer_size, nullptr, nullptr,
-                          MOJO_READ_MESSAGE_FLAG_NONE));
-  ASSERT_EQ('b', buffer[0]);
-  ASSERT_EQ(1u, buffer_size);
+  ASSERT_EQ(MOJO_RESULT_OK, core()->ReadMessageNew(
+                                h[0], &message, MOJO_READ_MESSAGE_FLAG_NONE));
+  uintptr_t context;
+  ASSERT_EQ(MOJO_RESULT_OK, core()->ReleaseMessageContext(message, &context));
+  ASSERT_EQ(kTestMessageContext, context);
+  ASSERT_EQ(MOJO_RESULT_OK, core()->FreeMessage(message));
 
   // |h[0]| should no longer be readable.
   hss[0] = kEmptyMojoHandleSignalsState;
@@ -325,11 +198,10 @@
   ASSERT_EQ(kAllSignals, hss[0].satisfiable_signals);
 
   // Write to |h[0]|.
-  buffer[0] = 'd';
-  ASSERT_EQ(
-      MOJO_RESULT_OK,
-      core()->WriteMessage(h[0], buffer, 1, nullptr, 0,
-                           MOJO_WRITE_MESSAGE_FLAG_NONE));
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->CreateMessage(kTestMessageContext, nullptr, &message));
+  ASSERT_EQ(MOJO_RESULT_OK, core()->WriteMessageNew(
+                                h[0], message, MOJO_WRITE_MESSAGE_FLAG_NONE));
 
   // Close |h[0]|.
   ASSERT_EQ(MOJO_RESULT_OK, core()->Close(h[0]));
@@ -352,9 +224,9 @@
             hss[1].satisfiable_signals);
 
   // Discard a message from |h[1]|.
-  ASSERT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED,
-            core()->ReadMessage(h[1], nullptr, nullptr, nullptr, nullptr,
-                                MOJO_READ_MESSAGE_FLAG_MAY_DISCARD));
+  ASSERT_EQ(MOJO_RESULT_OK, core()->ReadMessageNew(
+                                h[1], &message, MOJO_READ_MESSAGE_FLAG_NONE));
+  ASSERT_EQ(MOJO_RESULT_OK, core()->FreeMessage(message));
 
   // |h[1]| is no longer readable (and will never be).
   hss[1] = kFullMojoHandleSignalsState;
@@ -363,150 +235,48 @@
   EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss[1].satisfiable_signals);
 
   // Try writing to |h[1]|.
-  buffer[0] = 'e';
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->CreateMessage(kTestMessageContext, nullptr, &message));
   ASSERT_EQ(
       MOJO_RESULT_FAILED_PRECONDITION,
-      core()->WriteMessage(h[1], buffer, 1, nullptr, 0,
-                           MOJO_WRITE_MESSAGE_FLAG_NONE));
+      core()->WriteMessageNew(h[1], message, MOJO_WRITE_MESSAGE_FLAG_NONE));
 
   ASSERT_EQ(MOJO_RESULT_OK, core()->Close(h[1]));
 }
 
 // Tests passing a message pipe handle.
 TEST_F(CoreTest, MessagePipeBasicLocalHandlePassing1) {
-  const char kHello[] = "hello";
-  const uint32_t kHelloSize = static_cast<uint32_t>(sizeof(kHello));
-  const char kWorld[] = "world!!!";
-  const uint32_t kWorldSize = static_cast<uint32_t>(sizeof(kWorld));
-  char buffer[100];
-  const uint32_t kBufferSize = static_cast<uint32_t>(sizeof(buffer));
-  uint32_t num_bytes;
-  MojoHandle handles[10];
-  uint32_t num_handles;
   MojoHandleSignalsState hss;
-  MojoHandle h_received;
-
   MojoHandle h_passing[2];
   ASSERT_EQ(MOJO_RESULT_OK,
             core()->CreateMessagePipe(nullptr, &h_passing[0], &h_passing[1]));
 
   // Make sure that |h_passing[]| work properly.
+  const uintptr_t kTestMessageContext = 42;
+  MojoMessageHandle message;
   ASSERT_EQ(MOJO_RESULT_OK,
-            core()->WriteMessage(h_passing[0], kHello, kHelloSize, nullptr, 0,
-                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
+            core()->CreateMessage(kTestMessageContext, nullptr, &message));
+  ASSERT_EQ(MOJO_RESULT_OK,
+            core()->WriteMessageNew(h_passing[0], message,
+                                    MOJO_WRITE_MESSAGE_FLAG_NONE));
   hss = kEmptyMojoHandleSignalsState;
   EXPECT_EQ(MOJO_RESULT_OK, mojo::Wait(mojo::Handle(h_passing[1]),
                                        MOJO_HANDLE_SIGNAL_READABLE, &hss));
   ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
             hss.satisfied_signals);
   ASSERT_EQ(kAllSignals, hss.satisfiable_signals);
-  num_bytes = kBufferSize;
-  num_handles = arraysize(handles);
+  MojoMessageHandle message_handle;
   ASSERT_EQ(MOJO_RESULT_OK,
-            core()->ReadMessage(
-                h_passing[1], buffer, &num_bytes, handles, &num_handles,
-                MOJO_READ_MESSAGE_FLAG_NONE));
-  ASSERT_EQ(kHelloSize, num_bytes);
-  ASSERT_STREQ(kHello, buffer);
-  ASSERT_EQ(0u, num_handles);
-
-  // Make sure that you can't pass either of the message pipe's handles over
-  // itself.
-  ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
-            core()->WriteMessage(h_passing[0], kHello, kHelloSize,
-                                 &h_passing[0], 1,
-                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
+            core()->ReadMessageNew(h_passing[1], &message_handle,
+                                   MOJO_READ_MESSAGE_FLAG_NONE));
+  uintptr_t context;
   ASSERT_EQ(MOJO_RESULT_OK,
-            core()->CreateMessagePipe(nullptr, &h_passing[0], &h_passing[1]));
-
-  ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
-            core()->WriteMessage(h_passing[0], kHello, kHelloSize,
-                                 &h_passing[1], 1,
-                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
-  ASSERT_EQ(MOJO_RESULT_OK,
-            core()->CreateMessagePipe(nullptr, &h_passing[0], &h_passing[1]));
-
-  MojoHandle h_passed[2];
-  ASSERT_EQ(MOJO_RESULT_OK,
-            core()->CreateMessagePipe(nullptr, &h_passed[0], &h_passed[1]));
-
-  // Make sure that |h_passed[]| work properly.
-  ASSERT_EQ(MOJO_RESULT_OK,
-            core()->WriteMessage(h_passed[0], kHello, kHelloSize, nullptr, 0,
-                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
-  hss = kEmptyMojoHandleSignalsState;
-  ASSERT_EQ(MOJO_RESULT_OK, mojo::Wait(mojo::Handle(h_passed[1]),
-                                       MOJO_HANDLE_SIGNAL_READABLE, &hss));
-  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
-            hss.satisfied_signals);
-  ASSERT_EQ(kAllSignals, hss.satisfiable_signals);
-  num_bytes = kBufferSize;
-  num_handles = arraysize(handles);
-  ASSERT_EQ(MOJO_RESULT_OK,
-            core()->ReadMessage(
-                h_passed[1], buffer, &num_bytes, handles, &num_handles,
-                MOJO_READ_MESSAGE_FLAG_NONE));
-  ASSERT_EQ(kHelloSize, num_bytes);
-  ASSERT_STREQ(kHello, buffer);
-  ASSERT_EQ(0u, num_handles);
-
-  // Send |h_passed[1]| from |h_passing[0]| to |h_passing[1]|.
-  ASSERT_EQ(MOJO_RESULT_OK,
-            core()->WriteMessage(h_passing[0], kWorld, kWorldSize,
-                                 &h_passed[1], 1,
-                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
-  hss = kEmptyMojoHandleSignalsState;
-  ASSERT_EQ(MOJO_RESULT_OK, mojo::Wait(mojo::Handle(h_passing[1]),
-                                       MOJO_HANDLE_SIGNAL_READABLE, &hss));
-  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
-            hss.satisfied_signals);
-  ASSERT_EQ(kAllSignals, hss.satisfiable_signals);
-  num_bytes = kBufferSize;
-  num_handles = arraysize(handles);
-  ASSERT_EQ(MOJO_RESULT_OK,
-            core()->ReadMessage(
-                h_passing[1], buffer, &num_bytes, handles, &num_handles,
-                MOJO_READ_MESSAGE_FLAG_NONE));
-  ASSERT_EQ(kWorldSize, num_bytes);
-  ASSERT_STREQ(kWorld, buffer);
-  ASSERT_EQ(1u, num_handles);
-  h_received = handles[0];
-  ASSERT_NE(h_received, MOJO_HANDLE_INVALID);
-  ASSERT_NE(h_received, h_passing[0]);
-  ASSERT_NE(h_received, h_passing[1]);
-  ASSERT_NE(h_received, h_passed[0]);
-
-  // Note: We rely on the Mojo system not re-using handle values very often.
-  ASSERT_NE(h_received, h_passed[1]);
-
-  // |h_passed[1]| should no longer be valid; check that trying to close it
-  // fails. See above note.
-  ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->Close(h_passed[1]));
-
-  // Write to |h_passed[0]|. Should receive on |h_received|.
-  ASSERT_EQ(MOJO_RESULT_OK,
-            core()->WriteMessage(h_passed[0], kHello, kHelloSize, nullptr, 0,
-                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
-  hss = kEmptyMojoHandleSignalsState;
-  ASSERT_EQ(MOJO_RESULT_OK, mojo::Wait(mojo::Handle(h_received),
-                                       MOJO_HANDLE_SIGNAL_READABLE, &hss));
-  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
-            hss.satisfied_signals);
-  ASSERT_EQ(kAllSignals, hss.satisfiable_signals);
-  num_bytes = kBufferSize;
-  num_handles = arraysize(handles);
-  ASSERT_EQ(MOJO_RESULT_OK,
-            core()->ReadMessage(
-                h_received, buffer, &num_bytes, handles, &num_handles,
-                MOJO_READ_MESSAGE_FLAG_NONE));
-  ASSERT_EQ(kHelloSize, num_bytes);
-  ASSERT_STREQ(kHello, buffer);
-  ASSERT_EQ(0u, num_handles);
+            core()->ReleaseMessageContext(message_handle, &context));
+  ASSERT_EQ(kTestMessageContext, context);
+  ASSERT_EQ(MOJO_RESULT_OK, MojoFreeMessage(message_handle));
 
   ASSERT_EQ(MOJO_RESULT_OK, core()->Close(h_passing[0]));
   ASSERT_EQ(MOJO_RESULT_OK, core()->Close(h_passing[1]));
-  ASSERT_EQ(MOJO_RESULT_OK, core()->Close(h_passed[0]));
-  ASSERT_EQ(MOJO_RESULT_OK, core()->Close(h_received));
 }
 
 TEST_F(CoreTest, DataPipe) {
@@ -695,233 +465,6 @@
   ASSERT_EQ(MOJO_RESULT_OK, core()->Close(ch));
 }
 
-// Tests passing data pipe producer and consumer handles.
-TEST_F(CoreTest, MessagePipeBasicLocalHandlePassing2) {
-  const char kHello[] = "hello";
-  const uint32_t kHelloSize = static_cast<uint32_t>(sizeof(kHello));
-  const char kWorld[] = "world!!!";
-  const uint32_t kWorldSize = static_cast<uint32_t>(sizeof(kWorld));
-  char buffer[100];
-  const uint32_t kBufferSize = static_cast<uint32_t>(sizeof(buffer));
-  uint32_t num_bytes;
-  MojoHandle handles[10];
-  uint32_t num_handles;
-  MojoHandleSignalsState hss;
-
-  MojoHandle h_passing[2];
-  ASSERT_EQ(MOJO_RESULT_OK,
-            core()->CreateMessagePipe(nullptr, &h_passing[0], &h_passing[1]));
-
-  MojoHandle ph, ch;
-  ASSERT_EQ(MOJO_RESULT_OK,
-            core()->CreateDataPipe(nullptr, &ph, &ch));
-
-  // Send |ch| from |h_passing[0]| to |h_passing[1]|.
-  ASSERT_EQ(MOJO_RESULT_OK,
-            core()->WriteMessage(h_passing[0], kHello, kHelloSize, &ch, 1,
-                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
-  hss = kEmptyMojoHandleSignalsState;
-  ASSERT_EQ(MOJO_RESULT_OK, mojo::Wait(mojo::Handle(h_passing[1]),
-                                       MOJO_HANDLE_SIGNAL_READABLE, &hss));
-  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
-            hss.satisfied_signals);
-  ASSERT_EQ(kAllSignals, hss.satisfiable_signals);
-  num_bytes = kBufferSize;
-  num_handles = arraysize(handles);
-  ASSERT_EQ(MOJO_RESULT_OK,
-            core()->ReadMessage(
-                h_passing[1], buffer, &num_bytes, handles, &num_handles,
-                MOJO_READ_MESSAGE_FLAG_NONE));
-  ASSERT_EQ(kHelloSize, num_bytes);
-  ASSERT_STREQ(kHello, buffer);
-  ASSERT_EQ(1u, num_handles);
-  MojoHandle ch_received = handles[0];
-  ASSERT_NE(ch_received, MOJO_HANDLE_INVALID);
-  ASSERT_NE(ch_received, h_passing[0]);
-  ASSERT_NE(ch_received, h_passing[1]);
-  ASSERT_NE(ch_received, ph);
-
-  // Note: We rely on the Mojo system not re-using handle values very often.
-  ASSERT_NE(ch_received, ch);
-
-  // |ch| should no longer be valid; check that trying to close it fails. See
-  // above note.
-  ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->Close(ch));
-
-  // Write to |ph|. Should receive on |ch_received|.
-  num_bytes = kWorldSize;
-  ASSERT_EQ(MOJO_RESULT_OK,
-            core()->WriteData(ph, kWorld, &num_bytes,
-                              MOJO_WRITE_DATA_FLAG_ALL_OR_NONE));
-  hss = kEmptyMojoHandleSignalsState;
-  EXPECT_EQ(MOJO_RESULT_OK, mojo::Wait(mojo::Handle(ch_received),
-                                       MOJO_HANDLE_SIGNAL_READABLE, &hss));
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
-            hss.satisfied_signals);
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
-                MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
-            hss.satisfiable_signals);
-  num_bytes = kBufferSize;
-  ASSERT_EQ(MOJO_RESULT_OK,
-            core()->ReadData(ch_received, buffer, &num_bytes,
-                             MOJO_READ_MESSAGE_FLAG_NONE));
-  ASSERT_EQ(kWorldSize, num_bytes);
-  ASSERT_STREQ(kWorld, buffer);
-
-  // Now pass |ph| in the same direction.
-  ASSERT_EQ(MOJO_RESULT_OK,
-            core()->WriteMessage(h_passing[0], kWorld, kWorldSize, &ph, 1,
-                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
-  hss = kEmptyMojoHandleSignalsState;
-  ASSERT_EQ(MOJO_RESULT_OK, mojo::Wait(mojo::Handle(h_passing[1]),
-                                       MOJO_HANDLE_SIGNAL_READABLE, &hss));
-  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
-            hss.satisfied_signals);
-  ASSERT_EQ(kAllSignals, hss.satisfiable_signals);
-  num_bytes = kBufferSize;
-  num_handles = arraysize(handles);
-  ASSERT_EQ(MOJO_RESULT_OK,
-            core()->ReadMessage(
-                h_passing[1], buffer, &num_bytes, handles, &num_handles,
-                MOJO_READ_MESSAGE_FLAG_NONE));
-  ASSERT_EQ(kWorldSize, num_bytes);
-  ASSERT_STREQ(kWorld, buffer);
-  ASSERT_EQ(1u, num_handles);
-  MojoHandle ph_received = handles[0];
-  ASSERT_NE(ph_received, MOJO_HANDLE_INVALID);
-  ASSERT_NE(ph_received, h_passing[0]);
-  ASSERT_NE(ph_received, h_passing[1]);
-  ASSERT_NE(ph_received, ch_received);
-
-  // Again, rely on the Mojo system not re-using handle values very often.
-  ASSERT_NE(ph_received, ph);
-
-  // |ph| should no longer be valid; check that trying to close it fails. See
-  // above note.
-  ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->Close(ph));
-
-  // Write to |ph_received|. Should receive on |ch_received|.
-  num_bytes = kHelloSize;
-  ASSERT_EQ(MOJO_RESULT_OK,
-            core()->WriteData(ph_received, kHello, &num_bytes,
-                              MOJO_WRITE_DATA_FLAG_ALL_OR_NONE));
-  hss = kEmptyMojoHandleSignalsState;
-  EXPECT_EQ(MOJO_RESULT_OK, mojo::Wait(mojo::Handle(ch_received),
-                                       MOJO_HANDLE_SIGNAL_READABLE, &hss));
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
-            hss.satisfied_signals);
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
-                MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
-            hss.satisfiable_signals);
-  num_bytes = kBufferSize;
-  ASSERT_EQ(MOJO_RESULT_OK,
-            core()->ReadData(ch_received, buffer, &num_bytes,
-                             MOJO_READ_MESSAGE_FLAG_NONE));
-  ASSERT_EQ(kHelloSize, num_bytes);
-  ASSERT_STREQ(kHello, buffer);
-
-  ph = ph_received;
-  ph_received = MOJO_HANDLE_INVALID;
-  ch = ch_received;
-  ch_received = MOJO_HANDLE_INVALID;
-
-  // Make sure that |ph| can't be sent if it's in a two-phase write.
-  void* write_ptr = nullptr;
-  num_bytes = 0;
-  ASSERT_EQ(MOJO_RESULT_OK,
-            core()->BeginWriteData(ph, &write_ptr, &num_bytes,
-                                   MOJO_WRITE_DATA_FLAG_NONE));
-  ASSERT_GE(num_bytes, 1u);
-  ASSERT_EQ(MOJO_RESULT_BUSY,
-            core()->WriteMessage(h_passing[0], kHello, kHelloSize, &ph, 1,
-                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
-
-  // But |ch| can, even if |ph| is in a two-phase write.
-  ASSERT_EQ(MOJO_RESULT_OK,
-            core()->WriteMessage(h_passing[0], kHello, kHelloSize, &ch, 1,
-                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
-  ch = MOJO_HANDLE_INVALID;
-  EXPECT_EQ(MOJO_RESULT_OK, mojo::Wait(mojo::Handle(h_passing[1]),
-                                       MOJO_HANDLE_SIGNAL_READABLE));
-  num_bytes = kBufferSize;
-  num_handles = arraysize(handles);
-  ASSERT_EQ(MOJO_RESULT_OK,
-            core()->ReadMessage(
-                h_passing[1], buffer, &num_bytes, handles, &num_handles,
-                MOJO_READ_MESSAGE_FLAG_NONE));
-  ASSERT_EQ(kHelloSize, num_bytes);
-  ASSERT_STREQ(kHello, buffer);
-  ASSERT_EQ(1u, num_handles);
-  ch = handles[0];
-  ASSERT_NE(ch, MOJO_HANDLE_INVALID);
-
-  // Complete the two-phase write.
-  static_cast<char*>(write_ptr)[0] = 'x';
-  ASSERT_EQ(MOJO_RESULT_OK, core()->EndWriteData(ph, 1));
-
-  // Wait for |ch| to be readable.
-  hss = kEmptyMojoHandleSignalsState;
-  EXPECT_EQ(MOJO_RESULT_OK,
-            mojo::Wait(mojo::Handle(ch), MOJO_HANDLE_SIGNAL_READABLE, &hss));
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
-            hss.satisfied_signals);
-  EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
-                MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
-            hss.satisfiable_signals);
-
-  // Make sure that |ch| can't be sent if it's in a two-phase read.
-  const void* read_ptr = nullptr;
-  num_bytes = 1;
-  ASSERT_EQ(MOJO_RESULT_OK,
-            core()->BeginReadData(ch, &read_ptr, &num_bytes,
-                                  MOJO_READ_DATA_FLAG_ALL_OR_NONE));
-  ASSERT_EQ(MOJO_RESULT_BUSY,
-            core()->WriteMessage(h_passing[0], kHello, kHelloSize, &ch, 1,
-                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
-
-  // But |ph| can, even if |ch| is in a two-phase read.
-  ASSERT_EQ(MOJO_RESULT_OK,
-            core()->WriteMessage(h_passing[0], kWorld, kWorldSize, &ph, 1,
-                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
-  ph = MOJO_HANDLE_INVALID;
-  hss = kEmptyMojoHandleSignalsState;
-  EXPECT_EQ(MOJO_RESULT_OK, mojo::Wait(mojo::Handle(h_passing[1]),
-                                       MOJO_HANDLE_SIGNAL_READABLE, &hss));
-  ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
-            hss.satisfied_signals);
-  ASSERT_EQ(kAllSignals, hss.satisfiable_signals);
-  num_bytes = kBufferSize;
-  num_handles = arraysize(handles);
-  ASSERT_EQ(MOJO_RESULT_OK,
-            core()->ReadMessage(
-                h_passing[1], buffer, &num_bytes, handles, &num_handles,
-                MOJO_READ_MESSAGE_FLAG_NONE));
-  ASSERT_EQ(kWorldSize, num_bytes);
-  ASSERT_STREQ(kWorld, buffer);
-  ASSERT_EQ(1u, num_handles);
-  ph = handles[0];
-  ASSERT_NE(ph, MOJO_HANDLE_INVALID);
-
-  // Complete the two-phase read.
-  ASSERT_EQ('x', static_cast<const char*>(read_ptr)[0]);
-  ASSERT_EQ(MOJO_RESULT_OK, core()->EndReadData(ch, 1));
-
-  ASSERT_EQ(MOJO_RESULT_OK, core()->Close(h_passing[0]));
-  ASSERT_EQ(MOJO_RESULT_OK, core()->Close(h_passing[1]));
-  ASSERT_EQ(MOJO_RESULT_OK, core()->Close(ph));
-  ASSERT_EQ(MOJO_RESULT_OK, core()->Close(ch));
-}
-
-struct TestAsyncWaiter {
-  TestAsyncWaiter() : result(MOJO_RESULT_UNKNOWN) {}
-
-  void Awake(MojoResult r) { result = r; }
-
-  MojoResult result;
-};
-
-// TODO(vtl): Test |DuplicateBufferHandle()| and |MapBuffer()|.
-
 }  // namespace
 }  // namespace edk
 }  // namespace mojo
diff --git a/mojo/edk/system/data_pipe_unittest.cc b/mojo/edk/system/data_pipe_unittest.cc
index 79c1f75..377b129 100644
--- a/mojo/edk/system/data_pipe_unittest.cc
+++ b/mojo/edk/system/data_pipe_unittest.cc
@@ -20,6 +20,7 @@
 #include "mojo/public/c/system/data_pipe.h"
 #include "mojo/public/c/system/functions.h"
 #include "mojo/public/c/system/message_pipe.h"
+#include "mojo/public/cpp/system/message_pipe.h"
 #include "mojo/public/cpp/system/simple_watcher.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -56,6 +57,22 @@
       CHECK_EQ(MOJO_RESULT_OK, MojoClose(consumer_));
   }
 
+  MojoResult ReadEmptyMessageWithHandles(MojoHandle pipe,
+                                         MojoHandle* out_handles,
+                                         uint32_t num_handles) {
+    std::vector<uint8_t> bytes;
+    std::vector<ScopedHandle> handles;
+    MojoResult rv = ReadMessageRaw(MessagePipeHandle(pipe), &bytes, &handles,
+                                   MOJO_READ_MESSAGE_FLAG_NONE);
+    if (rv == MOJO_RESULT_OK) {
+      CHECK_EQ(0u, bytes.size());
+      CHECK_EQ(num_handles, handles.size());
+      for (size_t i = 0; i < num_handles; ++i)
+        out_handles[i] = handles[i].release().value();
+    }
+    return rv;
+  }
+
   MojoResult Create(const MojoCreateDataPipeOptions* options) {
     return MojoCreateDataPipe(options, &producer_, &consumer_);
   }
@@ -1530,16 +1547,12 @@
             MojoCreateMessagePipe(nullptr, &pipe0, &pipe1));
 
   ASSERT_EQ(MOJO_RESULT_OK,
-            MojoWriteMessage(pipe0, nullptr, 0, &producer_, 1,
-                             MOJO_WRITE_MESSAGE_FLAG_NONE));
+            WriteMessageRaw(MessagePipeHandle(pipe0), nullptr, 0, &producer_, 1,
+                            MOJO_WRITE_MESSAGE_FLAG_NONE));
   producer_ = MOJO_HANDLE_INVALID;
   ASSERT_EQ(MOJO_RESULT_OK,
             WaitForSignals(pipe1, MOJO_HANDLE_SIGNAL_READABLE, &hss));
-  uint32_t num_handles = 1;
-  ASSERT_EQ(MOJO_RESULT_OK,
-            MojoReadMessage(pipe1, nullptr, 0, &producer_, &num_handles,
-                            MOJO_READ_MESSAGE_FLAG_NONE));
-  ASSERT_EQ(num_handles, 1u);
+  ASSERT_EQ(MOJO_RESULT_OK, ReadEmptyMessageWithHandles(pipe1, &producer_, 1));
 
   // Write more data.
   const char kExtraData[] = "bye world";
@@ -1605,16 +1618,12 @@
             MojoCreateMessagePipe(nullptr, &pipe0, &pipe1));
 
   ASSERT_EQ(MOJO_RESULT_OK,
-            MojoWriteMessage(pipe0, nullptr, 0, &consumer_, 1,
-                             MOJO_WRITE_MESSAGE_FLAG_NONE));
+            WriteMessageRaw(MessagePipeHandle(pipe0), nullptr, 0, &consumer_, 1,
+                            MOJO_WRITE_MESSAGE_FLAG_NONE));
   consumer_ = MOJO_HANDLE_INVALID;
   ASSERT_EQ(MOJO_RESULT_OK,
             WaitForSignals(pipe1, MOJO_HANDLE_SIGNAL_READABLE, &state));
-  uint32_t num_handles = 1;
-  ASSERT_EQ(MOJO_RESULT_OK,
-            MojoReadMessage(pipe1, nullptr, 0, &consumer_, &num_handles,
-                            MOJO_READ_MESSAGE_FLAG_NONE));
-  ASSERT_EQ(num_handles, 1u);
+  ASSERT_EQ(MOJO_RESULT_OK, ReadEmptyMessageWithHandles(pipe1, &consumer_, 1));
 
   ASSERT_EQ(MOJO_RESULT_OK,
             WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, &state));
@@ -1724,8 +1733,8 @@
 
     // Send child process the data pipe.
     ASSERT_EQ(MOJO_RESULT_OK,
-              MojoWriteMessage(server_mp, nullptr, 0, &consumer_, 1,
-                               MOJO_WRITE_MESSAGE_FLAG_NONE));
+              WriteMessageRaw(MessagePipeHandle(server_mp), nullptr, 0,
+                              &consumer_, 1, MOJO_WRITE_MESSAGE_FLAG_NONE));
 
     // Send a bunch of data of varying sizes.
     uint8_t buffer[100];
@@ -1744,21 +1753,16 @@
 
     // Swap ends.
     ASSERT_EQ(MOJO_RESULT_OK,
-              MojoWriteMessage(server_mp, nullptr, 0, &producer_, 1,
-                               MOJO_WRITE_MESSAGE_FLAG_NONE));
+              WriteMessageRaw(MessagePipeHandle(server_mp), nullptr, 0,
+                              &producer_, 1, MOJO_WRITE_MESSAGE_FLAG_NONE));
 
     // Receive the consumer from the other side.
     producer_ = MOJO_HANDLE_INVALID;
     MojoHandleSignalsState hss = MojoHandleSignalsState();
     ASSERT_EQ(MOJO_RESULT_OK,
               WaitForSignals(server_mp, MOJO_HANDLE_SIGNAL_READABLE, &hss));
-    MojoHandle handles[2];
-    uint32_t num_handles = arraysize(handles);
     ASSERT_EQ(MOJO_RESULT_OK,
-              MojoReadMessage(server_mp, nullptr, 0, handles, &num_handles,
-                              MOJO_READ_MESSAGE_FLAG_NONE));
-    ASSERT_EQ(1u, num_handles);
-    consumer_ = handles[0];
+              ReadEmptyMessageWithHandles(server_mp, &consumer_, 1));
 
     // Read the test string twice. Once for when we sent it, and once for the
     // other end sending it.
@@ -1782,13 +1786,8 @@
   MojoHandleSignalsState hss = MojoHandleSignalsState();
   ASSERT_EQ(MOJO_RESULT_OK,
             WaitForSignals(client_mp, MOJO_HANDLE_SIGNAL_READABLE, &hss));
-  MojoHandle handles[2];
-  uint32_t num_handles = arraysize(handles);
   ASSERT_EQ(MOJO_RESULT_OK,
-            MojoReadMessage(client_mp, nullptr, 0, handles, &num_handles,
-                            MOJO_READ_MESSAGE_FLAG_NONE));
-  ASSERT_EQ(1u, num_handles);
-  consumer = handles[0];
+            ReadEmptyMessageWithHandles(client_mp, &consumer, 1));
 
   // Read the initial string that was sent.
   int32_t buffer[100];
@@ -1810,20 +1809,17 @@
   }
 
   // Swap ends.
-  ASSERT_EQ(MOJO_RESULT_OK, MojoWriteMessage(client_mp, nullptr, 0, &consumer,
-                                             1, MOJO_WRITE_MESSAGE_FLAG_NONE));
+  ASSERT_EQ(MOJO_RESULT_OK,
+            WriteMessageRaw(MessagePipeHandle(client_mp), nullptr, 0, &consumer,
+                            1, MOJO_WRITE_MESSAGE_FLAG_NONE));
 
   // Receive the producer from the other side.
   MojoHandle producer = MOJO_HANDLE_INVALID;
   hss = MojoHandleSignalsState();
   ASSERT_EQ(MOJO_RESULT_OK,
             WaitForSignals(client_mp, MOJO_HANDLE_SIGNAL_READABLE, &hss));
-  num_handles = arraysize(handles);
   ASSERT_EQ(MOJO_RESULT_OK,
-            MojoReadMessage(client_mp, nullptr, 0, handles, &num_handles,
-                            MOJO_READ_MESSAGE_FLAG_NONE));
-  ASSERT_EQ(1u, num_handles);
-  producer = handles[0];
+            ReadEmptyMessageWithHandles(client_mp, &producer, 1));
 
   // Write the test string one more time.
   EXPECT_TRUE(WriteAllData(producer, kMultiprocessTestData, kTestDataSize));
diff --git a/mojo/edk/system/message_pipe_perftest.cc b/mojo/edk/system/message_pipe_perftest.cc
index 9866c47..73e788c 100644
--- a/mojo/edk/system/message_pipe_perftest.cc
+++ b/mojo/edk/system/message_pipe_perftest.cc
@@ -42,23 +42,22 @@
 
  protected:
   void WriteWaitThenRead(MojoHandle mp) {
-    CHECK_EQ(MojoWriteMessage(mp, payload_.data(),
-                              static_cast<uint32_t>(payload_.size()), nullptr,
-                              0, MOJO_WRITE_MESSAGE_FLAG_NONE),
-             MOJO_RESULT_OK);
+    CHECK_EQ(
+        WriteMessageRaw(MessagePipeHandle(mp), payload_.data(), payload_.size(),
+                        nullptr, 0, MOJO_WRITE_MESSAGE_FLAG_NONE),
+        MOJO_RESULT_OK);
     HandleSignalsState hss;
     CHECK_EQ(WaitForSignals(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss),
              MOJO_RESULT_OK);
-    uint32_t read_buffer_size = static_cast<uint32_t>(read_buffer_.size());
-    CHECK_EQ(MojoReadMessage(mp, &read_buffer_[0], &read_buffer_size, nullptr,
-                             nullptr, MOJO_READ_MESSAGE_FLAG_NONE),
+    CHECK_EQ(ReadMessageRaw(MessagePipeHandle(mp), &read_buffer_, nullptr,
+                            MOJO_READ_MESSAGE_FLAG_NONE),
              MOJO_RESULT_OK);
-    CHECK_EQ(read_buffer_size, static_cast<uint32_t>(payload_.size()));
+    CHECK_EQ(read_buffer_.size(), payload_.size());
   }
 
   void SendQuitMessage(MojoHandle mp) {
-    CHECK_EQ(MojoWriteMessage(mp, "", 0, nullptr, 0,
-                              MOJO_WRITE_MESSAGE_FLAG_NONE),
+    CHECK_EQ(WriteMessageRaw(MessagePipeHandle(mp), "", 0, nullptr, 0,
+                             MOJO_WRITE_MESSAGE_FLAG_NONE),
              MOJO_RESULT_OK);
   }
 
@@ -92,7 +91,7 @@
   }
 
   static int RunPingPongClient(MojoHandle mp) {
-    std::string buffer(1000000, '\0');
+    std::vector<uint8_t> buffer;
     int rv = 0;
     while (true) {
       // Wait for our end of the message pipe to be readable.
@@ -103,20 +102,18 @@
         break;
       }
 
-      uint32_t read_size = static_cast<uint32_t>(buffer.size());
-      CHECK_EQ(MojoReadMessage(mp, &buffer[0],
-                               &read_size, nullptr,
-                               0, MOJO_READ_MESSAGE_FLAG_NONE),
+      CHECK_EQ(ReadMessageRaw(MessagePipeHandle(mp), &buffer, nullptr,
+                              MOJO_READ_MESSAGE_FLAG_NONE),
                MOJO_RESULT_OK);
 
       // Empty message indicates quit.
-      if (read_size == 0)
+      if (buffer.empty())
         break;
 
-      CHECK_EQ(MojoWriteMessage(mp, &buffer[0],
-                                read_size,
-                                nullptr, 0, MOJO_WRITE_MESSAGE_FLAG_NONE),
-               MOJO_RESULT_OK);
+      CHECK_EQ(
+          WriteMessageRaw(MessagePipeHandle(mp), buffer.data(), buffer.size(),
+                          nullptr, 0, MOJO_WRITE_MESSAGE_FLAG_NONE),
+          MOJO_RESULT_OK);
     }
 
     return rv;
@@ -126,7 +123,7 @@
   int message_count_;
   size_t message_size_;
   std::string payload_;
-  std::string read_buffer_;
+  std::vector<uint8_t> read_buffer_;
   std::unique_ptr<base::PerfTimeLogger> perf_logger_;
 
   DISALLOW_COPY_AND_ASSIGN(MessagePipePerfTest);
diff --git a/mojo/edk/system/message_pipe_unittest.cc b/mojo/edk/system/message_pipe_unittest.cc
index 71b0195..fb7c6365 100644
--- a/mojo/edk/system/message_pipe_unittest.cc
+++ b/mojo/edk/system/message_pipe_unittest.cc
@@ -43,17 +43,37 @@
   MojoResult WriteMessage(MojoHandle message_pipe_handle,
                           const void* bytes,
                           uint32_t num_bytes) {
-    return MojoWriteMessage(message_pipe_handle, bytes, num_bytes, nullptr, 0,
-                            MOJO_WRITE_MESSAGE_FLAG_NONE);
+    return mojo::WriteMessageRaw(MessagePipeHandle(message_pipe_handle), bytes,
+                                 num_bytes, nullptr, 0,
+                                 MOJO_WRITE_MESSAGE_FLAG_NONE);
   }
 
   MojoResult ReadMessage(MojoHandle message_pipe_handle,
                          void* bytes,
                          uint32_t* num_bytes,
                          bool may_discard = false) {
-    return MojoReadMessage(message_pipe_handle, bytes, num_bytes, nullptr, 0,
-                           may_discard ? MOJO_READ_MESSAGE_FLAG_MAY_DISCARD :
-                                         MOJO_READ_MESSAGE_FLAG_NONE);
+    MojoMessageHandle message_handle;
+    MojoResult rv = MojoReadMessageNew(message_pipe_handle, &message_handle,
+                                       MOJO_READ_MESSAGE_FLAG_NONE);
+    if (rv != MOJO_RESULT_OK)
+      return rv;
+
+    const uint32_t expected_num_bytes = *num_bytes;
+    void* buffer;
+    rv = MojoGetSerializedMessageContents(
+        message_handle, &buffer, num_bytes, nullptr, nullptr,
+        MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_NONE);
+
+    if (rv == MOJO_RESULT_RESOURCE_EXHAUSTED) {
+      CHECK(may_discard);
+    } else if (*num_bytes) {
+      CHECK_EQ(MOJO_RESULT_OK, rv);
+      CHECK_GE(expected_num_bytes, *num_bytes);
+      CHECK(bytes);
+      memcpy(bytes, buffer, *num_bytes);
+    }
+    CHECK_EQ(MOJO_RESULT_OK, MojoFreeMessage(message_handle));
+    return rv;
   }
 
   MojoHandle pipe0_, pipe1_;
@@ -134,24 +154,6 @@
   ASSERT_EQ(MOJO_RESULT_OK,
             WaitForSignals(pipe1_, MOJO_HANDLE_SIGNAL_READABLE, &state));
 
-  // Read from port 1 with buffer size 0 (should get the size of next message).
-  // Also test that giving a null buffer is okay when the buffer size is 0.
-  buffer_size = 0;
-  ASSERT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED,
-            ReadMessage(pipe1_, nullptr, &buffer_size));
-  ASSERT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size);
-
-  // Read from port 1 with buffer size 1 (too small; should get the size of next
-  // message).
-  buffer[0] = 123;
-  buffer[1] = 456;
-  buffer_size = 1;
-  ASSERT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED,
-            ReadMessage(pipe1_, buffer, &buffer_size));
-  ASSERT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size);
-  ASSERT_EQ(123, buffer[0]);
-  ASSERT_EQ(456, buffer[1]);
-
   // Read from port 1.
   buffer[0] = 123;
   buffer[1] = 456;
@@ -226,9 +228,8 @@
             WaitForSignals(pipe0_, MOJO_HANDLE_SIGNAL_READABLE, &state));
 
   // Port 0 shouldn't be empty.
-  buffer_size = 0;
-  ASSERT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED,
-            ReadMessage(pipe0_, nullptr, &buffer_size));
+  buffer_size = kBufferSize;
+  ASSERT_EQ(MOJO_RESULT_OK, ReadMessage(pipe0_, buffer, &buffer_size));
   ASSERT_EQ(kBufferSize, buffer_size);
 
   // Close port 0 first, which should have outstanding (incoming) messages.
@@ -237,92 +238,6 @@
   pipe0_ = pipe1_ = MOJO_HANDLE_INVALID;
 }
 
-TEST_F(MessagePipeTest, DiscardMode) {
-  int32_t buffer[2];
-  const uint32_t kBufferSize = static_cast<uint32_t>(sizeof(buffer));
-  uint32_t buffer_size;
-
-  // Write from port 1 (to port 0).
-  buffer[0] = 789012345;
-  buffer[1] = 0;
-  ASSERT_EQ(MOJO_RESULT_OK, WriteMessage(pipe1_, buffer, sizeof(buffer[0])));
-
-  MojoHandleSignalsState state;
-  ASSERT_EQ(MOJO_RESULT_OK,
-            WaitForSignals(pipe0_, MOJO_HANDLE_SIGNAL_READABLE, &state));
-
-  // Read/discard from port 0 (no buffer); get size.
-  buffer_size = 0;
-  ASSERT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED,
-            ReadMessage(pipe0_, nullptr, &buffer_size, true));
-  ASSERT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size);
-
-  // Read again from port 0 -- it should be empty.
-  buffer_size = kBufferSize;
-  ASSERT_EQ(MOJO_RESULT_SHOULD_WAIT,
-            ReadMessage(pipe0_, buffer, &buffer_size, true));
-
-  // Write from port 1 (to port 0).
-  buffer[0] = 890123456;
-  buffer[1] = 0;
-  ASSERT_EQ(MOJO_RESULT_OK,
-            WriteMessage(pipe1_, buffer, sizeof(buffer[0])));
-
-  ASSERT_EQ(MOJO_RESULT_OK,
-            WaitForSignals(pipe0_, MOJO_HANDLE_SIGNAL_READABLE, &state));
-
-  // Read from port 0 (buffer big enough).
-  buffer[0] = 123;
-  buffer[1] = 456;
-  buffer_size = kBufferSize;
-  ASSERT_EQ(MOJO_RESULT_OK, ReadMessage(pipe0_, buffer, &buffer_size, true));
-  ASSERT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size);
-  ASSERT_EQ(890123456, buffer[0]);
-  ASSERT_EQ(456, buffer[1]);
-
-  // Read again from port 0 -- it should be empty.
-  buffer_size = kBufferSize;
-  ASSERT_EQ(MOJO_RESULT_SHOULD_WAIT,
-            ReadMessage(pipe0_, buffer, &buffer_size, true));
-
-  // Write from port 1 (to port 0).
-  buffer[0] = 901234567;
-  buffer[1] = 0;
-  ASSERT_EQ(MOJO_RESULT_OK, WriteMessage(pipe1_, buffer, sizeof(buffer[0])));
-
-  ASSERT_EQ(MOJO_RESULT_OK,
-            WaitForSignals(pipe0_, MOJO_HANDLE_SIGNAL_READABLE, &state));
-
-  // Read/discard from port 0 (buffer too small); get size.
-  buffer_size = 1;
-  ASSERT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED,
-            ReadMessage(pipe0_, buffer, &buffer_size, true));
-  ASSERT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size);
-
-  // Read again from port 0 -- it should be empty.
-  buffer_size = kBufferSize;
-  ASSERT_EQ(MOJO_RESULT_SHOULD_WAIT,
-            ReadMessage(pipe0_, buffer, &buffer_size, true));
-
-  // Write from port 1 (to port 0).
-  buffer[0] = 123456789;
-  buffer[1] = 0;
-  ASSERT_EQ(MOJO_RESULT_OK, WriteMessage(pipe1_, buffer, sizeof(buffer[0])));
-
-  ASSERT_EQ(MOJO_RESULT_OK,
-            WaitForSignals(pipe0_, MOJO_HANDLE_SIGNAL_READABLE, &state));
-
-  // Discard from port 0.
-  buffer_size = 1;
-  ASSERT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED,
-            ReadMessage(pipe0_, nullptr, 0, true));
-
-  // Read again from port 0 -- it should be empty.
-  buffer_size = kBufferSize;
-  ASSERT_EQ(MOJO_RESULT_SHOULD_WAIT,
-            ReadMessage(pipe0_, buffer, &buffer_size, true));
-}
-
 TEST_F(MessagePipeTest, BasicWaiting) {
   MojoHandleSignalsState hss;
 
@@ -401,60 +316,6 @@
   ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals);
 }
 
-TEST_F(MessagePipeTest, AllocAndFreeMessage) {
-  const std::string kMessage = "Hello, world.";
-  MojoMessageHandle message = MOJO_MESSAGE_HANDLE_INVALID;
-  ASSERT_EQ(MOJO_RESULT_OK,
-            MojoAllocMessage(static_cast<uint32_t>(kMessage.size()), nullptr, 0,
-                             MOJO_ALLOC_MESSAGE_FLAG_NONE, &message));
-  ASSERT_NE(MOJO_MESSAGE_HANDLE_INVALID, message);
-  ASSERT_EQ(MOJO_RESULT_OK, MojoFreeMessage(message));
-}
-
-TEST_F(MessagePipeTest, WriteAndReadMessageObject) {
-  const std::string kMessage = "Hello, world.";
-  MojoMessageHandle message = MOJO_MESSAGE_HANDLE_INVALID;
-  EXPECT_EQ(MOJO_RESULT_OK,
-            MojoAllocMessage(static_cast<uint32_t>(kMessage.size()), nullptr, 0,
-                             MOJO_ALLOC_MESSAGE_FLAG_NONE, &message));
-  ASSERT_NE(MOJO_MESSAGE_HANDLE_INVALID, message);
-
-  uint32_t num_bytes;
-  void* buffer = nullptr;
-  EXPECT_EQ(MOJO_RESULT_OK,
-            MojoGetSerializedMessageContents(
-                message, &buffer, &num_bytes, nullptr, nullptr,
-                MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_NONE));
-  ASSERT_TRUE(buffer);
-  EXPECT_EQ(kMessage.size(), num_bytes);
-  memcpy(buffer, kMessage.data(), kMessage.size());
-
-  MojoHandle a, b;
-  CreateMessagePipe(&a, &b);
-  EXPECT_EQ(MOJO_RESULT_OK,
-            MojoWriteMessageNew(a, message, MOJO_WRITE_MESSAGE_FLAG_NONE));
-
-  EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(b, MOJO_HANDLE_SIGNAL_READABLE));
-  uint32_t num_handles = 0;
-  EXPECT_EQ(MOJO_RESULT_OK,
-            MojoReadMessageNew(b, &message, MOJO_READ_MESSAGE_FLAG_NONE));
-  ASSERT_NE(MOJO_MESSAGE_HANDLE_INVALID, message);
-  EXPECT_EQ(MOJO_RESULT_OK,
-            MojoGetSerializedMessageContents(
-                message, &buffer, &num_bytes, nullptr, &num_handles,
-                MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_NONE));
-  EXPECT_EQ(static_cast<uint32_t>(kMessage.size()), num_bytes);
-  EXPECT_EQ(0u, num_handles);
-  ASSERT_TRUE(buffer);
-
-  EXPECT_EQ(0, strncmp(static_cast<const char*>(buffer), kMessage.data(),
-                       num_bytes));
-
-  EXPECT_EQ(MOJO_RESULT_OK, MojoFreeMessage(message));
-  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a));
-  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b));
-}
-
 #if !defined(OS_IOS)
 
 const size_t kPingPongHandlesPerIteration = 50;
diff --git a/mojo/edk/system/message_unittest.cc b/mojo/edk/system/message_unittest.cc
index 5d01243..b9b4398 100644
--- a/mojo/edk/system/message_unittest.cc
+++ b/mojo/edk/system/message_unittest.cc
@@ -354,21 +354,13 @@
                 MOJO_WRITE_MESSAGE_FLAG_NONE));
   EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(b, MOJO_HANDLE_SIGNAL_READABLE));
 
-  // Attempt to read an unserialized message into serialized buffers. The
-  // request should fail.
-  uint32_t num_bytes = 0;
-  EXPECT_EQ(MOJO_RESULT_NOT_FOUND,
-            MojoReadMessage(b, nullptr, &num_bytes, nullptr, nullptr,
-                            MOJO_READ_MESSAGE_FLAG_NONE));
-  EXPECT_FALSE(message_was_destroyed);
-
-  // Try again, this time reading into a message object.
   MojoMessageHandle message_handle;
   EXPECT_EQ(MOJO_RESULT_OK, MojoReadMessageNew(b, &message_handle,
                                                MOJO_READ_MESSAGE_FLAG_NONE));
   EXPECT_FALSE(message_was_destroyed);
 
   // Not a serialized message, so we can't get serialized contents.
+  uint32_t num_bytes = 0;
   void* buffer;
   uint32_t num_handles = 0;
   EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
diff --git a/mojo/edk/system/multiprocess_message_pipe_unittest.cc b/mojo/edk/system/multiprocess_message_pipe_unittest.cc
index 3bf7979..0087cc5 100644
--- a/mojo/edk/system/multiprocess_message_pipe_unittest.cc
+++ b/mojo/edk/system/multiprocess_message_pipe_unittest.cc
@@ -31,15 +31,60 @@
 #include "mojo/public/c/system/buffer.h"
 #include "mojo/public/c/system/functions.h"
 #include "mojo/public/c/system/types.h"
+#include "mojo/public/cpp/system/message_pipe.h"
 #include "mojo/public/cpp/system/simple_watcher.h"
 #include "mojo/public/cpp/system/wait.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-
 namespace mojo {
 namespace edk {
 namespace {
 
+// Temporary helpers to avoid tons of churn as old APIs are removed. These
+// support only enough of a subset of the old APIs to satisfy the usage in these
+// tests.
+//
+// TODO(rockot): Remove these.
+MojoResult MojoReadMessage(MojoHandle pipe,
+                           void* out_bytes,
+                           uint32_t* num_bytes,
+                           MojoHandle* out_handles,
+                           uint32_t* num_handles,
+                           MojoReadMessageFlags flags) {
+  std::vector<uint8_t> bytes;
+  std::vector<ScopedHandle> handles;
+  MojoResult rv =
+      ReadMessageRaw(MessagePipeHandle(pipe), &bytes, &handles, flags);
+  if (rv != MOJO_RESULT_OK)
+    return rv;
+
+  if (num_bytes)
+    *num_bytes = static_cast<uint32_t>(bytes.size());
+  if (!bytes.empty()) {
+    CHECK(out_bytes && num_bytes && *num_bytes >= bytes.size());
+    memcpy(out_bytes, bytes.data(), bytes.size());
+  }
+
+  if (num_handles)
+    *num_handles = static_cast<uint32_t>(handles.size());
+  if (!handles.empty()) {
+    CHECK(out_handles && num_handles && *num_handles >= handles.size());
+    for (size_t i = 0; i < handles.size(); ++i)
+      out_handles[i] = handles[i].release().value();
+  }
+  return MOJO_RESULT_OK;
+}
+
+MojoResult MojoWriteMessage(MojoHandle pipe,
+                            const void* bytes,
+                            uint32_t num_bytes,
+                            const MojoHandle* handles,
+                            uint32_t num_handles,
+                            MojoWriteMessageFlags flags) {
+  return WriteMessageRaw(MessagePipeHandle(pipe), bytes, num_bytes, handles,
+                         num_handles, flags);
+}
+
 class MultiprocessMessagePipeTest : public test::MojoTestBase {
  protected:
   // Convenience class for tests which will control command-driven children.
diff --git a/mojo/edk/test/mojo_test_base.cc b/mojo/edk/test/mojo_test_base.cc
index 71a5e3b..cb1bbae 100644
--- a/mojo/edk/test/mojo_test_base.cc
+++ b/mojo/edk/test/mojo_test_base.cc
@@ -15,6 +15,8 @@
 #include "mojo/public/c/system/data_pipe.h"
 #include "mojo/public/c/system/functions.h"
 #include "mojo/public/c/system/watcher.h"
+#include "mojo/public/cpp/system/handle.h"
+#include "mojo/public/cpp/system/message_pipe.h"
 #include "mojo/public/cpp/system/wait.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -117,9 +119,9 @@
                                            const std::string& message,
                                            const MojoHandle *handles,
                                            uint32_t num_handles) {
-  CHECK_EQ(MojoWriteMessage(mp, message.data(),
-                            static_cast<uint32_t>(message.size()),
-                            handles, num_handles, MOJO_WRITE_MESSAGE_FLAG_NONE),
+  CHECK_EQ(WriteMessageRaw(MessagePipeHandle(mp), message.data(),
+                           static_cast<uint32_t>(message.size()), handles,
+                           num_handles, MOJO_WRITE_MESSAGE_FLAG_NONE),
            MOJO_RESULT_OK);
 }
 
@@ -131,25 +133,20 @@
 // static
 std::string MojoTestBase::ReadMessageWithHandles(
     MojoHandle mp,
-    MojoHandle* handles,
+    MojoHandle* out_handles,
     uint32_t expected_num_handles) {
   CHECK_EQ(WaitForSignals(mp, MOJO_HANDLE_SIGNAL_READABLE), MOJO_RESULT_OK);
 
-  uint32_t message_size = 0;
-  uint32_t num_handles = 0;
-  CHECK_EQ(MojoReadMessage(mp, nullptr, &message_size, nullptr, &num_handles,
-                           MOJO_READ_MESSAGE_FLAG_NONE),
-           MOJO_RESULT_RESOURCE_EXHAUSTED);
-  CHECK_EQ(expected_num_handles, num_handles);
+  std::vector<uint8_t> bytes;
+  std::vector<ScopedHandle> handles;
+  CHECK_EQ(MOJO_RESULT_OK,
+           ReadMessageRaw(MessagePipeHandle(mp), &bytes, &handles,
+                          MOJO_READ_MESSAGE_FLAG_NONE));
+  CHECK_EQ(expected_num_handles, handles.size());
+  for (size_t i = 0; i < handles.size(); ++i)
+    out_handles[i] = handles[i].release().value();
 
-  std::string message(message_size, 'x');
-  CHECK_EQ(MojoReadMessage(mp, &message[0], &message_size, handles,
-                           &num_handles, MOJO_READ_MESSAGE_FLAG_NONE),
-           MOJO_RESULT_OK);
-  CHECK_EQ(message_size, message.size());
-  CHECK_EQ(num_handles, expected_num_handles);
-
-  return message;
+  return std::string(bytes.begin(), bytes.end());
 }
 
 // static
@@ -157,28 +154,20 @@
                                                         MojoHandle* handle) {
   CHECK_EQ(WaitForSignals(mp, MOJO_HANDLE_SIGNAL_READABLE), MOJO_RESULT_OK);
 
-  uint32_t message_size = 0;
-  uint32_t num_handles = 0;
-  CHECK_EQ(MojoReadMessage(mp, nullptr, &message_size, nullptr, &num_handles,
-                           MOJO_READ_MESSAGE_FLAG_NONE),
-           MOJO_RESULT_RESOURCE_EXHAUSTED);
-  CHECK(num_handles == 0 || num_handles == 1);
-
+  std::vector<uint8_t> bytes;
+  std::vector<ScopedHandle> handles;
+  CHECK_EQ(MOJO_RESULT_OK,
+           ReadMessageRaw(MessagePipeHandle(mp), &bytes, &handles,
+                          MOJO_READ_MESSAGE_FLAG_NONE));
+  CHECK(handles.size() == 0 || handles.size() == 1);
   CHECK(handle);
 
-  std::string message(message_size, 'x');
-  CHECK_EQ(MojoReadMessage(mp, &message[0], &message_size, handle,
-                           &num_handles, MOJO_READ_MESSAGE_FLAG_NONE),
-           MOJO_RESULT_OK);
-  CHECK_EQ(message_size, message.size());
-  CHECK(num_handles == 0 || num_handles == 1);
-
-  if (num_handles)
-    CHECK_NE(*handle, MOJO_HANDLE_INVALID);
+  if (handles.size() == 1)
+    *handle = handles[0].release().value();
   else
     *handle = MOJO_HANDLE_INVALID;
 
-  return message;
+  return std::string(bytes.begin(), bytes.end());
 }
 
 // static
@@ -192,19 +181,14 @@
                                size_t num_bytes) {
   CHECK_EQ(WaitForSignals(mp, MOJO_HANDLE_SIGNAL_READABLE), MOJO_RESULT_OK);
 
-  uint32_t message_size = 0;
-  uint32_t num_handles = 0;
-  CHECK_EQ(MojoReadMessage(mp, nullptr, &message_size, nullptr, &num_handles,
-                           MOJO_READ_MESSAGE_FLAG_NONE),
-           MOJO_RESULT_RESOURCE_EXHAUSTED);
-  CHECK_EQ(num_handles, 0u);
-  CHECK_EQ(message_size, num_bytes);
-
-  CHECK_EQ(MojoReadMessage(mp, data, &message_size, nullptr, &num_handles,
-                           MOJO_READ_MESSAGE_FLAG_NONE),
-           MOJO_RESULT_OK);
-  CHECK_EQ(num_handles, 0u);
-  CHECK_EQ(message_size, num_bytes);
+  std::vector<uint8_t> bytes;
+  std::vector<ScopedHandle> handles;
+  CHECK_EQ(MOJO_RESULT_OK,
+           ReadMessageRaw(MessagePipeHandle(mp), &bytes, &handles,
+                          MOJO_READ_MESSAGE_FLAG_NONE));
+  CHECK_EQ(0u, handles.size());
+  CHECK_EQ(num_bytes, bytes.size());
+  memcpy(data, bytes.data(), bytes.size());
 }
 
 // static
@@ -305,7 +289,7 @@
            MOJO_RESULT_OK);
   CHECK_EQ(num_bytes, static_cast<uint32_t>(size));
 
-  return std::string(buffer.data(), buffer.size());
+  return std::string(buffer.begin(), buffer.end());
 }
 
 // static
diff --git a/mojo/public/c/system/tests/core_perftest.cc b/mojo/public/c/system/tests/core_perftest.cc
index cab465b..54b9d81 100644
--- a/mojo/public/c/system/tests/core_perftest.cc
+++ b/mojo/public/c/system/tests/core_perftest.cc
@@ -12,6 +12,7 @@
 
 #include "base/macros.h"
 #include "base/threading/simple_thread.h"
+#include "mojo/public/cpp/system/message_pipe.h"
 #include "mojo/public/cpp/system/wait.h"
 #include "mojo/public/cpp/test_support/test_support.h"
 #include "mojo/public/cpp/test_support/test_utils.h"
@@ -37,10 +38,10 @@
     char buffer[10000];
     assert(num_bytes_ <= sizeof(buffer));
 
-    // TODO(vtl): Should I throttle somehow?
     for (;;) {
-      MojoResult result = MojoWriteMessage(handle_, buffer, num_bytes_, nullptr,
-                                           0, MOJO_WRITE_MESSAGE_FLAG_NONE);
+      MojoResult result = mojo::WriteMessageRaw(
+          mojo::MessagePipeHandle(handle_), buffer, num_bytes_, nullptr, 0,
+          MOJO_WRITE_MESSAGE_FLAG_NONE);
       if (result == MOJO_RESULT_OK) {
         num_writes_++;
         continue;
@@ -74,12 +75,11 @@
   ~MessagePipeReaderThread() override {}
 
   void Run() override {
-    char buffer[10000];
-
     for (;;) {
-      uint32_t num_bytes = static_cast<uint32_t>(sizeof(buffer));
-      MojoResult result = MojoReadMessage(handle_, buffer, &num_bytes, nullptr,
-                                          nullptr, MOJO_READ_MESSAGE_FLAG_NONE);
+      std::vector<uint8_t> bytes;
+      MojoResult result =
+          mojo::ReadMessageRaw(mojo::MessagePipeHandle(handle_), &bytes,
+                               nullptr, MOJO_READ_MESSAGE_FLAG_NONE);
       if (result == MOJO_RESULT_OK) {
         num_reads_++;
         continue;
@@ -114,7 +114,7 @@
 
 class CorePerftest : public testing::Test {
  public:
-  CorePerftest() : buffer_(nullptr), num_bytes_(0) {}
+  CorePerftest() {}
   ~CorePerftest() override {}
 
   static void NoOp(void* /*closure*/) {}
@@ -132,22 +132,22 @@
 
   static void MessagePipe_WriteAndRead(void* closure) {
     CorePerftest* self = static_cast<CorePerftest*>(closure);
-    MojoResult result =
-        MojoWriteMessage(self->h0_, self->buffer_, self->num_bytes_, nullptr, 0,
-                         MOJO_WRITE_MESSAGE_FLAG_NONE);
+    MojoResult result = mojo::WriteMessageRaw(
+        mojo::MessagePipeHandle(self->h0_), self->buffer_.data(),
+        self->buffer_.size(), nullptr, 0, MOJO_WRITE_MESSAGE_FLAG_NONE);
     ALLOW_UNUSED_LOCAL(result);
     assert(result == MOJO_RESULT_OK);
-    uint32_t read_bytes = self->num_bytes_;
-    result = MojoReadMessage(self->h1_, self->buffer_, &read_bytes, nullptr,
+    result =
+        mojo::ReadMessageRaw(mojo::MessagePipeHandle(self->h1_), &self->buffer_,
                              nullptr, MOJO_READ_MESSAGE_FLAG_NONE);
     assert(result == MOJO_RESULT_OK);
   }
 
   static void MessagePipe_EmptyRead(void* closure) {
     CorePerftest* self = static_cast<CorePerftest*>(closure);
+    MojoMessageHandle message;
     MojoResult result =
-        MojoReadMessage(self->h0_, nullptr, nullptr, nullptr, nullptr,
-                        MOJO_READ_MESSAGE_FLAG_MAY_DISCARD);
+        MojoReadMessageNew(self->h0_, &message, MOJO_READ_MESSAGE_FLAG_NONE);
     ALLOW_UNUSED_LOCAL(result);
     assert(result == MOJO_RESULT_SHOULD_WAIT);
   }
@@ -233,8 +233,7 @@
   MojoHandle h0_;
   MojoHandle h1_;
 
-  void* buffer_;
-  uint32_t num_bytes_;
+  std::vector<uint8_t> buffer_;
 
  private:
 #if !defined(WIN32)
@@ -268,21 +267,19 @@
   MojoResult result = MojoCreateMessagePipe(nullptr, &h0_, &h1_);
   ALLOW_UNUSED_LOCAL(result);
   assert(result == MOJO_RESULT_OK);
-  char buffer[10000] = {0};
-  buffer_ = buffer;
-  num_bytes_ = 10u;
+  buffer_.resize(10);
   mojo::test::IterateAndReportPerf("MessagePipe_WriteAndRead", "10bytes",
                                    &CorePerftest::MessagePipe_WriteAndRead,
                                    this);
-  num_bytes_ = 100u;
+  buffer_.resize(100);
   mojo::test::IterateAndReportPerf("MessagePipe_WriteAndRead", "100bytes",
                                    &CorePerftest::MessagePipe_WriteAndRead,
                                    this);
-  num_bytes_ = 1000u;
+  buffer_.resize(1000);
   mojo::test::IterateAndReportPerf("MessagePipe_WriteAndRead", "1000bytes",
                                    &CorePerftest::MessagePipe_WriteAndRead,
                                    this);
-  num_bytes_ = 10000u;
+  buffer_.resize(10000);
   mojo::test::IterateAndReportPerf("MessagePipe_WriteAndRead", "10000bytes",
                                    &CorePerftest::MessagePipe_WriteAndRead,
                                    this);
diff --git a/mojo/public/c/system/tests/core_unittest.cc b/mojo/public/c/system/tests/core_unittest.cc
index a9da255..7f99097 100644
--- a/mojo/public/c/system/tests/core_unittest.cc
+++ b/mojo/public/c/system/tests/core_unittest.cc
@@ -9,6 +9,7 @@
 #include <stdint.h>
 #include <string.h>
 
+#include "mojo/public/cpp/system/message_pipe.h"
 #include "mojo/public/cpp/system/wait.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -43,12 +44,10 @@
   // Message pipe:
   h0 = MOJO_HANDLE_INVALID;
   EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
-            MojoWriteMessage(h0, buffer, 3, nullptr, 0,
-                             MOJO_WRITE_MESSAGE_FLAG_NONE));
-  buffer_size = static_cast<uint32_t>(sizeof(buffer));
+            MojoWriteMessageNew(h0, MOJO_MESSAGE_HANDLE_INVALID,
+                                MOJO_WRITE_MESSAGE_FLAG_NONE));
   EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
-            MojoReadMessage(h0, buffer, &buffer_size, nullptr, nullptr,
-                            MOJO_READ_MESSAGE_FLAG_NONE));
+            MojoReadMessageNew(h0, nullptr, MOJO_READ_MESSAGE_FLAG_NONE));
 
   // Data pipe:
   buffer_size = static_cast<uint32_t>(sizeof(buffer));
@@ -79,8 +78,6 @@
 TEST(CoreTest, BasicMessagePipe) {
   MojoHandle h0, h1;
   MojoHandleSignals sig;
-  char buffer[10] = {0};
-  uint32_t buffer_size;
 
   h0 = MOJO_HANDLE_INVALID;
   h1 = MOJO_HANDLE_INVALID;
@@ -95,16 +92,16 @@
   EXPECT_EQ(kSignalAll, state.satisfiable_signals);
 
   // Try to read.
-  buffer_size = static_cast<uint32_t>(sizeof(buffer));
+  MojoMessageHandle message;
   EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
-            MojoReadMessage(h0, buffer, &buffer_size, nullptr, nullptr,
-                            MOJO_READ_MESSAGE_FLAG_NONE));
+            MojoReadMessageNew(h0, &message, MOJO_READ_MESSAGE_FLAG_NONE));
 
   // Write to |h1|.
-  static const char kHello[] = "hello";
-  buffer_size = static_cast<uint32_t>(sizeof(kHello));
-  EXPECT_EQ(MOJO_RESULT_OK, MojoWriteMessage(h1, kHello, buffer_size, nullptr,
-                                             0, MOJO_WRITE_MESSAGE_FLAG_NONE));
+  const uintptr_t kTestMessageContext = 1234;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoCreateMessage(kTestMessageContext, nullptr, &message));
+  EXPECT_EQ(MOJO_RESULT_OK,
+            MojoWriteMessageNew(h1, message, MOJO_WRITE_MESSAGE_FLAG_NONE));
 
   // |h0| should be readable.
   size_t result_index = 1;
@@ -119,12 +116,12 @@
   EXPECT_EQ(kSignalAll, states[0].satisfiable_signals);
 
   // Read from |h0|.
-  buffer_size = static_cast<uint32_t>(sizeof(buffer));
   EXPECT_EQ(MOJO_RESULT_OK,
-            MojoReadMessage(h0, buffer, &buffer_size, nullptr, nullptr,
-                            MOJO_READ_MESSAGE_FLAG_NONE));
-  EXPECT_EQ(static_cast<uint32_t>(sizeof(kHello)), buffer_size);
-  EXPECT_STREQ(kHello, buffer);
+            MojoReadMessageNew(h0, &message, MOJO_READ_MESSAGE_FLAG_NONE));
+  uintptr_t context;
+  EXPECT_EQ(MOJO_RESULT_OK, MojoReleaseMessageContext(message, &context));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoFreeMessage(message));
+  EXPECT_EQ(kTestMessageContext, context);
 
   // |h0| should no longer be readable.
   EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(h0, &state));
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 3164649..de16790 100644
--- a/mojo/public/c/system/tests/core_unittest_pure_c.c
+++ b/mojo/public/c/system/tests/core_unittest_pure_c.c
@@ -42,9 +42,6 @@
   // at the top. (MSVS 2013 is more reasonable.)
   MojoTimeTicks ticks;
   MojoHandle handle0, handle1;
-  const char kHello[] = "hello";
-  char buffer[200] = {0};
-  uint32_t num_bytes;
 
   ticks = MojoGetTimeTicksNow();
   EXPECT_NE(ticks, 0);
@@ -58,20 +55,19 @@
   handle1 = MOJO_HANDLE_INVALID;
   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,
-            MojoWriteMessage(handle0, kHello, (uint32_t)sizeof(kHello), NULL,
-                             0u, MOJO_WRITE_DATA_FLAG_NONE));
+            MojoWriteMessageNew(handle0, message, MOJO_WRITE_DATA_FLAG_NONE));
 
-  num_bytes = (uint32_t)sizeof(buffer);
-  EXPECT_EQ(MOJO_RESULT_OK, MojoReadMessage(handle1, buffer, &num_bytes, NULL,
-                                            NULL, MOJO_READ_MESSAGE_FLAG_NONE));
-  EXPECT_EQ((uint32_t)sizeof(kHello), num_bytes);
-  EXPECT_EQ(0, memcmp(buffer, kHello, sizeof(kHello)));
+  EXPECT_EQ(MOJO_RESULT_OK, MojoReadMessageNew(handle1, &message,
+                                               MOJO_READ_MESSAGE_FLAG_NONE));
+  uintptr_t context;
+  EXPECT_EQ(MOJO_RESULT_OK, MojoReleaseMessageContext(message, &context));
+  EXPECT_EQ(42, context);
 
   EXPECT_EQ(MOJO_RESULT_OK, MojoClose(handle0));
   EXPECT_EQ(MOJO_RESULT_OK, MojoClose(handle1));
 
-  // TODO(vtl): data pipe
-
   return NULL;
 }
diff --git a/mojo/public/cpp/bindings/tests/struct_traits_unittest.cc b/mojo/public/cpp/bindings/tests/struct_traits_unittest.cc
index b48284b..7e4bcd12 100644
--- a/mojo/public/cpp/bindings/tests/struct_traits_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/struct_traits_unittest.cc
@@ -396,13 +396,12 @@
 
   EXPECT_EQ(MOJO_RESULT_OK, Wait(received.get(), MOJO_HANDLE_SIGNAL_READABLE));
 
-  char buffer[10] = {0};
-  uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer));
-  EXPECT_EQ(MOJO_RESULT_OK,
-            ReadMessageRaw(received.get(), buffer, &buffer_size, nullptr,
-                           nullptr, MOJO_READ_MESSAGE_FLAG_NONE));
-  EXPECT_EQ(kHelloSize, buffer_size);
-  EXPECT_STREQ(kHello, buffer);
+  std::vector<uint8_t> bytes;
+  std::vector<ScopedHandle> handles;
+  EXPECT_EQ(MOJO_RESULT_OK, ReadMessageRaw(received.get(), &bytes, &handles,
+                                           MOJO_READ_MESSAGE_FLAG_NONE));
+  EXPECT_EQ(kHelloSize, bytes.size());
+  EXPECT_STREQ(kHello, reinterpret_cast<char*>(bytes.data()));
 }
 
 void CaptureNullableMoveOnlyStructWithTraitsImpl(
diff --git a/mojo/public/cpp/system/BUILD.gn b/mojo/public/cpp/system/BUILD.gn
index 35087ef..9513aeb 100644
--- a/mojo/public/cpp/system/BUILD.gn
+++ b/mojo/public/cpp/system/BUILD.gn
@@ -31,6 +31,7 @@
     "handle.h",
     "handle_signals_state.h",
     "message.h",
+    "message_pipe.cc",
     "message_pipe.h",
     "platform_handle.cc",
     "platform_handle.h",
diff --git a/mojo/public/cpp/system/message.h b/mojo/public/cpp/system/message.h
index a8078a1..e06d6a7 100644
--- a/mojo/public/cpp/system/message.h
+++ b/mojo/public/cpp/system/message.h
@@ -50,6 +50,18 @@
 
 using ScopedMessageHandle = ScopedHandleBase<MessageHandle>;
 
+inline MojoResult CreateMessage(uintptr_t context,
+                                const MojoMessageOperationThunks* thunks,
+                                ScopedMessageHandle* handle) {
+  MojoMessageHandle raw_handle;
+  MojoResult rv = MojoCreateMessage(context, thunks, &raw_handle);
+  if (rv != MOJO_RESULT_OK)
+    return rv;
+
+  handle->reset(MessageHandle(raw_handle));
+  return MOJO_RESULT_OK;
+}
+
 inline MojoResult AllocMessage(size_t num_bytes,
                                const MojoHandle* handles,
                                size_t num_handles,
diff --git a/mojo/public/cpp/system/message_pipe.cc b/mojo/public/cpp/system/message_pipe.cc
new file mode 100644
index 0000000..1254e2e
--- /dev/null
+++ b/mojo/public/cpp/system/message_pipe.cc
@@ -0,0 +1,134 @@
+// 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 "mojo/public/cpp/system/message_pipe.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);
+  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.
+    return MOJO_RESULT_ABORTED;
+  }
+
+  return MojoWriteMessageNew(message_pipe.value(),
+                             message_handle.release().value(), flags);
+}
+
+MojoResult ReadMessageRaw(MessagePipeHandle message_pipe,
+                          std::vector<uint8_t>* payload,
+                          std::vector<ScopedHandle>* handles,
+                          MojoReadMessageFlags flags) {
+  ScopedMessageHandle message_handle;
+  int rv = ReadMessageNew(message_pipe, &message_handle, flags);
+  if (rv != MOJO_RESULT_OK)
+    return rv;
+
+  rv = MojoSerializeMessage(message_handle->value());
+  if (rv != MOJO_RESULT_OK && rv != MOJO_RESULT_FAILED_PRECONDITION)
+    return MOJO_RESULT_ABORTED;
+
+  void* buffer = nullptr;
+  uint32_t num_bytes = 0;
+  uint32_t num_handles = 0;
+  rv = MojoGetSerializedMessageContents(
+      message_handle->value(), &buffer, &num_bytes, nullptr, &num_handles,
+      MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_NONE);
+  if (rv == MOJO_RESULT_RESOURCE_EXHAUSTED) {
+    DCHECK(handles);
+    handles->resize(num_handles);
+    rv = MojoGetSerializedMessageContents(
+        message_handle->value(), &buffer, &num_bytes,
+        reinterpret_cast<MojoHandle*>(handles->data()), &num_handles,
+        MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_NONE);
+  }
+
+  if (num_bytes) {
+    DCHECK(buffer);
+    uint8_t* payload_data = reinterpret_cast<uint8_t*>(buffer);
+    payload->resize(num_bytes);
+    std::copy(payload_data, payload_data + num_bytes, payload->begin());
+  } else if (payload) {
+    payload->clear();
+  }
+
+  if (handles && !num_handles)
+    handles->clear();
+
+  if (rv != MOJO_RESULT_OK)
+    return MOJO_RESULT_ABORTED;
+
+  return MOJO_RESULT_OK;
+}
+
+}  // namespace mojo
diff --git a/mojo/public/cpp/system/message_pipe.h b/mojo/public/cpp/system/message_pipe.h
index acb5bcd0..7a421746 100644
--- a/mojo/public/cpp/system/message_pipe.h
+++ b/mojo/public/cpp/system/message_pipe.h
@@ -14,11 +14,14 @@
 
 #include <stdint.h>
 
+#include <vector>
+
 #include "base/compiler_specific.h"
 #include "base/logging.h"
 #include "mojo/public/c/system/message_pipe.h"
 #include "mojo/public/cpp/system/handle.h"
 #include "mojo/public/cpp/system/message.h"
+#include "mojo/public/cpp/system/system_export.h"
 
 namespace mojo {
 
@@ -57,37 +60,31 @@
   return rv;
 }
 
-// The following "...Raw" versions fully expose the underlying API, and don't
-// help with ownership of handles (especially when writing messages). It is
-// expected that in most cases these methods will be called through generated
-// bindings anyway.
-// TODO(vtl): Write friendlier versions of these functions (using scoped
-// handles and/or vectors) if there is a demonstrated need for them.
+// A helper for writing a serialized message to a message pipe. Use this for
+// convenience in lieu of the lower-level MojoWriteMessage API, but beware that
+// it does incur an extra copy of the message payload.
+//
+// See documentation for MojoWriteMessage for return code details.
+MOJO_CPP_SYSTEM_EXPORT MojoResult
+WriteMessageRaw(MessagePipeHandle message_pipe,
+                const void* bytes,
+                size_t num_bytes,
+                const MojoHandle* handles,
+                size_t num_handles,
+                MojoWriteMessageFlags flags);
 
-// Writes to a message pipe.  If handles are attached, on success the handles
-// will no longer be valid (the receiver will receive equivalent, but logically
-// different, handles). See |MojoWriteMessage()| for complete documentation.
-inline MojoResult WriteMessageRaw(MessagePipeHandle message_pipe,
-                                  const void* bytes,
-                                  uint32_t num_bytes,
-                                  const MojoHandle* handles,
-                                  uint32_t num_handles,
-                                  MojoWriteMessageFlags flags) {
-  return MojoWriteMessage(
-      message_pipe.value(), bytes, num_bytes, handles, num_handles, flags);
-}
-
-// Reads from a message pipe. See |MojoReadMessage()| for complete
-// documentation.
-inline MojoResult ReadMessageRaw(MessagePipeHandle message_pipe,
-                                 void* bytes,
-                                 uint32_t* num_bytes,
-                                 MojoHandle* handles,
-                                 uint32_t* num_handles,
-                                 MojoReadMessageFlags flags) {
-  return MojoReadMessage(
-      message_pipe.value(), bytes, num_bytes, handles, num_handles, flags);
-}
+// A helper for reading serialized messages from a pipe. Use this for
+// convenience in lieu of the lower-level MojoReadMessage API, but beware that
+// it does incur an extra copy of the message payload.
+//
+// See documentation for MojoReadMessage for return code details. In addition to
+// those return codes, this may return |MOJO_RESULT_ABORTED| if the message was
+// unable to be serialized into the provided containers.
+MOJO_CPP_SYSTEM_EXPORT MojoResult
+ReadMessageRaw(MessagePipeHandle message_pipe,
+               std::vector<uint8_t>* payload,
+               std::vector<ScopedHandle>* handles,
+               MojoReadMessageFlags flags);
 
 // Writes to a message pipe. Takes ownership of |message| and any attached
 // handles.
diff --git a/mojo/public/cpp/system/tests/core_unittest.cc b/mojo/public/cpp/system/tests/core_unittest.cc
index 40a94f0..eacb5930 100644
--- a/mojo/public/cpp/system/tests/core_unittest.cc
+++ b/mojo/public/cpp/system/tests/core_unittest.cc
@@ -149,16 +149,6 @@
               ReadMessageRaw(h_invalid,
                              nullptr,
                              nullptr,
-                             nullptr,
-                             nullptr,
-                             MOJO_READ_MESSAGE_FLAG_NONE));
-    uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer));
-    EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
-              ReadMessageRaw(h_invalid,
-                             buffer,
-                             &buffer_size,
-                             nullptr,
-                             nullptr,
                              MOJO_READ_MESSAGE_FLAG_NONE));
 
     // Basic tests of waiting and closing.
@@ -227,11 +217,7 @@
       const char kHello[] = "hello";
       const uint32_t kHelloSize = static_cast<uint32_t>(sizeof(kHello));
       EXPECT_EQ(MOJO_RESULT_OK,
-                WriteMessageRaw(h0.get(),
-                                kHello,
-                                kHelloSize,
-                                nullptr,
-                                0,
+                WriteMessageRaw(h0.get(), kHello, kHelloSize - 1, nullptr, 0,
                                 MOJO_WRITE_MESSAGE_FLAG_NONE));
 
       MojoHandleSignalsState state;
@@ -240,17 +226,10 @@
       EXPECT_EQ(kSignalReadableWritable, state.satisfied_signals);
       EXPECT_EQ(kSignalAll, state.satisfiable_signals);
 
-      char buffer[10] = {0};
-      uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer));
-      EXPECT_EQ(MOJO_RESULT_OK,
-                ReadMessageRaw(h1.get(),
-                               buffer,
-                               &buffer_size,
-                               nullptr,
-                               nullptr,
-                               MOJO_READ_MESSAGE_FLAG_NONE));
-      EXPECT_EQ(kHelloSize, buffer_size);
-      EXPECT_STREQ(kHello, buffer);
+      std::vector<uint8_t> bytes;
+      EXPECT_EQ(MOJO_RESULT_OK, ReadMessageRaw(h1.get(), &bytes, nullptr,
+                                               MOJO_READ_MESSAGE_FLAG_NONE));
+      EXPECT_EQ(kHello, std::string(bytes.begin(), bytes.end()));
 
       // Send a handle over the previously-establish message pipe. Use the
       // |MessagePipe| wrapper (to test it), which automatically creates a
@@ -261,12 +240,8 @@
       const char kWorld[] = "world!";
       const uint32_t kWorldSize = static_cast<uint32_t>(sizeof(kWorld));
       EXPECT_EQ(MOJO_RESULT_OK,
-                WriteMessageRaw(mp.handle0.get(),
-                                kWorld,
-                                kWorldSize,
-                                nullptr,
-                                0,
-                                MOJO_WRITE_MESSAGE_FLAG_NONE));
+                WriteMessageRaw(mp.handle0.get(), kWorld, kWorldSize - 1,
+                                nullptr, 0, MOJO_WRITE_MESSAGE_FLAG_NONE));
 
       // Send |mp.handle1| over |h1| to |h0|.
       MojoHandle handles[5];
@@ -275,12 +250,8 @@
       EXPECT_FALSE(mp.handle1.get().is_valid());
       uint32_t handles_count = 1;
       EXPECT_EQ(MOJO_RESULT_OK,
-                WriteMessageRaw(h1.get(),
-                                kHello,
-                                kHelloSize,
-                                handles,
-                                handles_count,
-                                MOJO_WRITE_MESSAGE_FLAG_NONE));
+                WriteMessageRaw(h1.get(), kHello, kHelloSize - 1, handles,
+                                handles_count, MOJO_WRITE_MESSAGE_FLAG_NONE));
       // |handles[0]| should actually be invalid now.
       EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(handles[0]));
 
@@ -290,54 +261,32 @@
       EXPECT_EQ(kSignalReadableWritable, state.satisfied_signals);
       EXPECT_EQ(kSignalAll, state.satisfiable_signals);
 
-      memset(buffer, 0, sizeof(buffer));
-      buffer_size = static_cast<uint32_t>(sizeof(buffer));
-      for (size_t i = 0; i < arraysize(handles); i++)
-        handles[i] = kInvalidHandleValue;
-      handles_count = static_cast<uint32_t>(arraysize(handles));
-      EXPECT_EQ(MOJO_RESULT_OK,
-                ReadMessageRaw(h0.get(),
-                               buffer,
-                               &buffer_size,
-                               handles,
-                               &handles_count,
-                               MOJO_READ_MESSAGE_FLAG_NONE));
-      EXPECT_EQ(kHelloSize, buffer_size);
-      EXPECT_STREQ(kHello, buffer);
-      EXPECT_EQ(1u, handles_count);
-      EXPECT_NE(kInvalidHandleValue, handles[0]);
+      std::vector<ScopedHandle> read_handles;
+      EXPECT_EQ(MOJO_RESULT_OK, ReadMessageRaw(h0.get(), &bytes, &read_handles,
+                                               MOJO_READ_MESSAGE_FLAG_NONE));
+      EXPECT_EQ(kHello, std::string(bytes.begin(), bytes.end()));
+      EXPECT_EQ(1u, read_handles.size());
+      EXPECT_NE(kInvalidHandleValue, read_handles[0]->value());
 
       // Read from the sent/received handle.
-      mp.handle1.reset(MessagePipeHandle(handles[0]));
+      mp.handle1.reset(MessagePipeHandle(read_handles[0]->value()));
       // Save |handles[0]| to check that it gets properly closed.
-      hv0 = handles[0];
+      hv0 = read_handles[0].release().value();
 
       EXPECT_EQ(MOJO_RESULT_OK,
                 Wait(mp.handle1.get(), MOJO_HANDLE_SIGNAL_READABLE, &state));
       EXPECT_EQ(kSignalReadableWritable, state.satisfied_signals);
       EXPECT_EQ(kSignalAll, state.satisfiable_signals);
 
-      memset(buffer, 0, sizeof(buffer));
-      buffer_size = static_cast<uint32_t>(sizeof(buffer));
-      for (size_t i = 0; i < arraysize(handles); i++)
-        handles[i] = kInvalidHandleValue;
-      handles_count = static_cast<uint32_t>(arraysize(handles));
+      read_handles.clear();
       EXPECT_EQ(MOJO_RESULT_OK,
-                ReadMessageRaw(mp.handle1.get(),
-                               buffer,
-                               &buffer_size,
-                               handles,
-                               &handles_count,
+                ReadMessageRaw(mp.handle1.get(), &bytes, &read_handles,
                                MOJO_READ_MESSAGE_FLAG_NONE));
-      EXPECT_EQ(kWorldSize, buffer_size);
-      EXPECT_STREQ(kWorld, buffer);
-      EXPECT_EQ(0u, handles_count);
+      EXPECT_EQ(kWorld, std::string(bytes.begin(), bytes.end()));
+      EXPECT_TRUE(read_handles.empty());
     }
     EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(hv0));
   }
-
-  // TODO(vtl): Test |CloseRaw()|.
-  // TODO(vtl): Test |reset()| more thoroughly?
 }
 
 TEST(CoreCppTest, TearDownWithMessagesEnqueued) {
diff --git a/mojo/public/cpp/system/tests/wait_set_unittest.cc b/mojo/public/cpp/system/tests/wait_set_unittest.cc
index d60cb459..8e8f00f 100644
--- a/mojo/public/cpp/system/tests/wait_set_unittest.cc
+++ b/mojo/public/cpp/system/tests/wait_set_unittest.cc
@@ -31,18 +31,11 @@
 }
 
 std::string ReadMessage(const ScopedMessagePipeHandle& handle) {
-  uint32_t num_bytes = 0;
-  uint32_t num_handles = 0;
-  MojoResult rv = ReadMessageRaw(handle.get(), nullptr, &num_bytes, nullptr,
-                                 &num_handles, MOJO_READ_MESSAGE_FLAG_NONE);
-  CHECK_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED, rv);
-  CHECK_EQ(0u, num_handles);
-
-  std::vector<char> buffer(num_bytes);
-  rv = ReadMessageRaw(handle.get(), buffer.data(), &num_bytes, nullptr,
-                      &num_handles, MOJO_READ_MESSAGE_FLAG_NONE);
+  std::vector<uint8_t> bytes;
+  MojoResult rv = ReadMessageRaw(handle.get(), &bytes, nullptr,
+                                 MOJO_READ_MESSAGE_FLAG_NONE);
   CHECK_EQ(MOJO_RESULT_OK, rv);
-  return std::string(buffer.data(), buffer.size());
+  return std::string(bytes.begin(), bytes.end());
 }
 
 class ThreadedRunner : public base::SimpleThread {
diff --git a/mojo/public/cpp/system/tests/wait_unittest.cc b/mojo/public/cpp/system/tests/wait_unittest.cc
index 1d9d3c6..24f1bee 100644
--- a/mojo/public/cpp/system/tests/wait_unittest.cc
+++ b/mojo/public/cpp/system/tests/wait_unittest.cc
@@ -33,18 +33,11 @@
 }
 
 std::string ReadMessage(const ScopedMessagePipeHandle& handle) {
-  uint32_t num_bytes = 0;
-  uint32_t num_handles = 0;
-  MojoResult rv = ReadMessageRaw(handle.get(), nullptr, &num_bytes, nullptr,
-                                 &num_handles, MOJO_READ_MESSAGE_FLAG_NONE);
-  CHECK_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED, rv);
-  CHECK_EQ(0u, num_handles);
-
-  std::vector<char> buffer(num_bytes);
-  rv = ReadMessageRaw(handle.get(), buffer.data(), &num_bytes, nullptr,
-                      &num_handles, MOJO_READ_MESSAGE_FLAG_NONE);
+  std::vector<uint8_t> bytes;
+  MojoResult rv = ReadMessageRaw(handle.get(), &bytes, nullptr,
+                                 MOJO_READ_MESSAGE_FLAG_NONE);
   CHECK_EQ(MOJO_RESULT_OK, rv);
-  return std::string(buffer.data(), buffer.size());
+  return std::string(bytes.begin(), bytes.end());
 }
 
 class ThreadedRunner : public base::SimpleThread {
diff --git a/mojo/public/cpp/test_support/lib/test_utils.cc b/mojo/public/cpp/test_support/lib/test_utils.cc
index 7fe6f02..b59e3116 100644
--- a/mojo/public/cpp/test_support/lib/test_utils.cc
+++ b/mojo/public/cpp/test_support/lib/test_utils.cc
@@ -7,6 +7,8 @@
 #include <stddef.h>
 #include <stdint.h>
 
+#include <vector>
+
 #include "mojo/public/cpp/system/core.h"
 #include "mojo/public/cpp/system/wait.h"
 #include "mojo/public/cpp/test_support/test_support.h"
@@ -26,50 +28,30 @@
 }
 
 bool ReadTextMessage(const MessagePipeHandle& handle, std::string* text) {
-  MojoResult rv;
-  bool did_wait = false;
+  if (Wait(handle, MOJO_HANDLE_SIGNAL_READABLE) != MOJO_RESULT_OK)
+    return false;
 
-  uint32_t num_bytes = 0, num_handles = 0;
-  for (;;) {
-    rv = ReadMessageRaw(handle,
-                        nullptr,
-                        &num_bytes,
-                        nullptr,
-                        &num_handles,
-                        MOJO_READ_MESSAGE_FLAG_NONE);
-    if (rv == MOJO_RESULT_SHOULD_WAIT) {
-      if (did_wait) {
-        assert(false);  // Looping endlessly!?
-        return false;
-      }
-      rv = Wait(handle, MOJO_HANDLE_SIGNAL_READABLE);
-      if (rv != MOJO_RESULT_OK)
-        return false;
-      did_wait = true;
-    } else {
-      assert(!num_handles);
-      break;
-    }
+  std::vector<uint8_t> bytes;
+  std::vector<ScopedHandle> handles;
+  if (ReadMessageRaw(handle, &bytes, &handles, MOJO_READ_MESSAGE_FLAG_NONE) !=
+      MOJO_RESULT_OK) {
+    return false;
   }
 
-  text->resize(num_bytes);
-  rv = ReadMessageRaw(handle,
-                      &text->at(0),
-                      &num_bytes,
-                      nullptr,
-                      &num_handles,
-                      MOJO_READ_MESSAGE_FLAG_NONE);
-  return rv == MOJO_RESULT_OK;
+  assert(handles.empty());
+  text->resize(bytes.size());
+  std::copy(bytes.begin(), bytes.end(), text->begin());
+  return true;
 }
 
 bool DiscardMessage(const MessagePipeHandle& handle) {
-  MojoResult rv = ReadMessageRaw(handle,
-                                 nullptr,
-                                 nullptr,
-                                 nullptr,
-                                 nullptr,
-                                 MOJO_READ_MESSAGE_FLAG_MAY_DISCARD);
-  return rv == MOJO_RESULT_OK;
+  MojoMessageHandle message;
+  int rv =
+      MojoReadMessageNew(handle.value(), &message, MOJO_READ_MESSAGE_FLAG_NONE);
+  if (rv != MOJO_RESULT_OK)
+    return false;
+  MojoFreeMessage(message);
+  return true;
 }
 
 void IterateAndReportPerf(const char* test_name,
diff --git a/services/resource_coordinator/coordination_unit/coordination_unit_impl.cc b/services/resource_coordinator/coordination_unit/coordination_unit_impl.cc
index 1547dba..b50d98fc 100644
--- a/services/resource_coordinator/coordination_unit/coordination_unit_impl.cc
+++ b/services/resource_coordinator/coordination_unit/coordination_unit_impl.cc
@@ -111,6 +111,9 @@
     case mojom::EventType::kOnProcessAudioStopped:
       state_flags_[kAudioPlaying] = false;
       break;
+    case mojom::EventType::kOnLocalFrameNetworkIdle:
+      state_flags_[kNetworkIdle] = true;
+      break;
     case mojom::EventType::kTestEvent:
       state_flags_[kTestState] = true;
       break;
diff --git a/services/resource_coordinator/coordination_unit/coordination_unit_impl.h b/services/resource_coordinator/coordination_unit/coordination_unit_impl.h
index d8b3fe24..94ebb044 100644
--- a/services/resource_coordinator/coordination_unit/coordination_unit_impl.h
+++ b/services/resource_coordinator/coordination_unit/coordination_unit_impl.h
@@ -81,6 +81,7 @@
     kTestState,
     kTabVisible,
     kAudioPlaying,
+    kNetworkIdle,
     kNumStateFlags
   };
   bool SelfOrParentHasFlagSet(StateFlags state);
diff --git a/services/resource_coordinator/public/interfaces/events.mojom b/services/resource_coordinator/public/interfaces/events.mojom
index 9a0edd4..ee46713 100644
--- a/services/resource_coordinator/public/interfaces/events.mojom
+++ b/services/resource_coordinator/public/interfaces/events.mojom
@@ -12,6 +12,7 @@
   kOnRendererFrameCreated,
   kOnProcessAudioStarted,
   kOnProcessAudioStopped,
+  kOnLocalFrameNetworkIdle,
 };
 
 struct Event {
diff --git a/services/video_capture/device_media_to_mojo_adapter.cc b/services/video_capture/device_media_to_mojo_adapter.cc
index 837dcd8c..a55d93d 100644
--- a/services/video_capture/device_media_to_mojo_adapter.cc
+++ b/services/video_capture/device_media_to_mojo_adapter.cc
@@ -6,6 +6,7 @@
 
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
+#include "media/base/bind_to_current_loop.h"
 #include "media/capture/video/video_capture_buffer_pool_impl.h"
 #include "media/capture/video/video_capture_buffer_tracker_factory_impl.h"
 #include "media/capture/video/video_capture_jpeg_decoder.h"
@@ -17,8 +18,37 @@
 // If all buffers are still in use by consumers when new frames are produced
 // those frames get dropped.
 static const int kMaxBufferCount = 3;
+
+void RunSuccessfulGetPhotoStateCallback(
+    video_capture::mojom::Device::GetPhotoStateCallback callback,
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+    media::mojom::PhotoStatePtr result) {
+  task_runner->PostTask(FROM_HERE, base::Bind(callback, base::Passed(&result)));
 }
 
+void RunFailedGetPhotoStateCallback(
+    base::Callback<void(media::mojom::PhotoStatePtr)> cb) {
+  cb.Run(nullptr);
+}
+
+void RunFailedSetOptionsCallback(base::Callback<void(bool)> cb) {
+  cb.Run(false);
+}
+
+void RunSuccessfulTakePhotoCallback(
+    video_capture::mojom::Device::TakePhotoCallback callback,
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+    media::mojom::BlobPtr blob) {
+  task_runner->PostTask(FROM_HERE, base::Bind(callback, base::Passed(&blob)));
+}
+
+void RunFailedTakePhotoCallback(
+    base::Callback<void(media::mojom::BlobPtr blob)> cb) {
+  cb.Run(nullptr);
+}
+
+}  // anonymous namespace
+
 namespace video_capture {
 
 DeviceMediaToMojoAdapter::DeviceMediaToMojoAdapter(
@@ -78,6 +108,56 @@
   device_->OnUtilizationReport(frame_feedback_id, utilization);
 }
 
+void DeviceMediaToMojoAdapter::RequestRefreshFrame() {
+  if (!device_started_)
+    return;
+  device_->RequestRefreshFrame();
+}
+
+void DeviceMediaToMojoAdapter::MaybeSuspend() {
+  if (!device_started_)
+    return;
+  device_->MaybeSuspend();
+}
+
+void DeviceMediaToMojoAdapter::Resume() {
+  if (!device_started_)
+    return;
+  device_->Resume();
+}
+
+void DeviceMediaToMojoAdapter::GetPhotoState(
+    const GetPhotoStateCallback& callback) {
+  media::VideoCaptureDevice::GetPhotoStateCallback scoped_callback(
+      // Cannot use BindToCurrentLoop() here, because it does not support
+      // callbacks with unbound move-only parameters.
+      base::Bind(&RunSuccessfulGetPhotoStateCallback, std::move(callback),
+                 base::ThreadTaskRunnerHandle::Get()),
+      media::BindToCurrentLoop(base::Bind(&RunFailedGetPhotoStateCallback)));
+  device_->GetPhotoState(std::move(scoped_callback));
+}
+
+void DeviceMediaToMojoAdapter::SetPhotoOptions(
+    media::mojom::PhotoSettingsPtr settings,
+    const SetPhotoOptionsCallback& callback) {
+  media::ScopedResultCallback<media::mojom::ImageCapture::SetOptionsCallback>
+      scoped_callback(
+          media::BindToCurrentLoop(std::move(callback)),
+          media::BindToCurrentLoop(base::Bind(&RunFailedSetOptionsCallback)));
+  device_->SetPhotoOptions(std::move(settings), std::move(scoped_callback));
+}
+
+void DeviceMediaToMojoAdapter::TakePhoto(const TakePhotoCallback& callback) {
+  media::ScopedResultCallback<media::mojom::ImageCapture::TakePhotoCallback>
+      scoped_callback(
+          // Cannot use BindToCurrentLoop() here, because it does not support
+          // callbacks with unbound move-only parameters.
+          base::Bind(&RunSuccessfulTakePhotoCallback, std::move(callback),
+                     base::ThreadTaskRunnerHandle::Get()),
+          media::BindToCurrentLoop(base::Bind(&RunFailedTakePhotoCallback)));
+  device_->TakePhoto(std::move(scoped_callback));
+}
+
 void DeviceMediaToMojoAdapter::Stop() {
   DCHECK(thread_checker_.CalledOnValidThread());
   if (device_started_ == false)
diff --git a/services/video_capture/device_media_to_mojo_adapter.h b/services/video_capture/device_media_to_mojo_adapter.h
index f0b7591e..80efe96 100644
--- a/services/video_capture/device_media_to_mojo_adapter.h
+++ b/services/video_capture/device_media_to_mojo_adapter.h
@@ -31,9 +31,15 @@
              mojom::ReceiverPtr receiver) override;
   void OnReceiverReportingUtilization(int32_t frame_feedback_id,
                                       double utilization) override;
+  void RequestRefreshFrame() override;
+  void MaybeSuspend() override;
+  void Resume() override;
+  void GetPhotoState(const GetPhotoStateCallback& callback) override;
+  void SetPhotoOptions(media::mojom::PhotoSettingsPtr settings,
+                       const SetPhotoOptionsCallback& callback) override;
+  void TakePhoto(const TakePhotoCallback& callback) override;
 
   void Stop();
-
   void OnClientConnectionErrorOrClose();
 
   // Returns the fixed maximum number of buffers passed to the constructor
diff --git a/services/video_capture/public/interfaces/BUILD.gn b/services/video_capture/public/interfaces/BUILD.gn
index 58034c9..01bcd97 100644
--- a/services/video_capture/public/interfaces/BUILD.gn
+++ b/services/video_capture/public/interfaces/BUILD.gn
@@ -14,6 +14,7 @@
 
   deps = [
     "//media/capture/mojo:capture_types",
+    "//media/capture/mojo:image_capture",
     "//media/mojo/interfaces",
     "//ui/gfx/geometry/mojo",
   ]
diff --git a/services/video_capture/public/interfaces/device.mojom b/services/video_capture/public/interfaces/device.mojom
index 46707a97..0e8ea39 100644
--- a/services/video_capture/public/interfaces/device.mojom
+++ b/services/video_capture/public/interfaces/device.mojom
@@ -5,6 +5,7 @@
 module video_capture.mojom;
 
 import "media/capture/mojo/video_capture_types.mojom";
+import "media/capture/mojo/image_capture.mojom";
 import "services/video_capture/public/interfaces/receiver.mojom";
 
 // Represents access to a video capture device available on the machine.
@@ -15,4 +16,13 @@
   Start(media.mojom.VideoCaptureParams requested_settings, Receiver receiver);
   OnReceiverReportingUtilization(int32 frame_feedback_id,
                                  double utilization);
+  RequestRefreshFrame();
+  MaybeSuspend();
+  Resume();
+  GetPhotoState()
+      => (media.mojom.PhotoState? capabilities);
+  SetPhotoOptions(media.mojom.PhotoSettings settings)
+      => (bool success);
+  TakePhoto()
+      => (media.mojom.Blob? blob);
 };
diff --git a/storage/browser/quota/quota_manager.cc b/storage/browser/quota/quota_manager.cc
index 518ca1d..74d2de6 100644
--- a/storage/browser/quota/quota_manager.cc
+++ b/storage/browser/quota/quota_manager.cc
@@ -1109,12 +1109,6 @@
   storage_monitor_->RemoveObserver(observer);
 }
 
-void QuotaManager::RemoveStorageObserverForFilter(
-    StorageObserver* observer, const StorageObserver::Filter& filter) {
-  DCHECK(observer);
-  storage_monitor_->RemoveObserverForFilter(observer, filter);
-}
-
 QuotaManager::~QuotaManager() {
   proxy_->manager_ = NULL;
   for (auto* client : clients_)
diff --git a/storage/browser/quota/quota_manager.h b/storage/browser/quota/quota_manager.h
index efe8d7d..524fe92 100644
--- a/storage/browser/quota/quota_manager.h
+++ b/storage/browser/quota/quota_manager.h
@@ -224,8 +224,6 @@
   void AddStorageObserver(StorageObserver* observer,
                           const StorageObserver::MonitorParams& params);
   void RemoveStorageObserver(StorageObserver* observer);
-  void RemoveStorageObserverForFilter(StorageObserver* observer,
-                                      const StorageObserver::Filter& filter);
 
   static const int64_t kPerHostPersistentQuotaLimit;
   static const char kDatabaseName[];
diff --git a/storage/browser/quota/storage_monitor.cc b/storage/browser/quota/storage_monitor.cc
index eafa5a6e..a978fd6e 100644
--- a/storage/browser/quota/storage_monitor.cc
+++ b/storage/browser/quota/storage_monitor.cc
@@ -277,18 +277,6 @@
   }
 }
 
-void StorageTypeObservers::RemoveObserverForFilter(
-    StorageObserver* observer, const StorageObserver::Filter& filter) {
-  std::string host = net::GetHostOrSpecFromURL(filter.origin);
-  auto it = host_observers_map_.find(host);
-  if (it == host_observers_map_.end())
-    return;
-
-  it->second->RemoveObserver(observer);
-  if (!it->second->ContainsObservers())
-    host_observers_map_.erase(it);
-}
-
 const HostStorageObservers* StorageTypeObservers::GetHostObservers(
     const std::string& host) const {
   auto it = host_observers_map_.find(host);
@@ -346,15 +334,6 @@
   }
 }
 
-void StorageMonitor::RemoveObserverForFilter(
-    StorageObserver* observer, const StorageObserver::Filter& filter) {
-  auto it = storage_type_observers_map_.find(filter.storage_type);
-  if (it == storage_type_observers_map_.end())
-    return;
-
-  it->second->RemoveObserverForFilter(observer, filter);
-}
-
 const StorageTypeObservers* StorageMonitor::GetStorageTypeObservers(
     StorageType storage_type) const {
   auto it = storage_type_observers_map_.find(storage_type);
diff --git a/storage/browser/quota/storage_monitor.h b/storage/browser/quota/storage_monitor.h
index 01cb447..85e3e3d 100644
--- a/storage/browser/quota/storage_monitor.h
+++ b/storage/browser/quota/storage_monitor.h
@@ -131,8 +131,6 @@
   void AddObserver(StorageObserver* observer,
                    const StorageObserver::MonitorParams& params);
   void RemoveObserver(StorageObserver* observer);
-  void RemoveObserverForFilter(StorageObserver* observer,
-                               const StorageObserver::Filter& filter);
 
   // Returns the observers of a specific host.
   const HostStorageObservers* GetHostObservers(const std::string& host) const;
diff --git a/storage/browser/quota/storage_monitor_unittest.cc b/storage/browser/quota/storage_monitor_unittest.cc
index f3a2074..5a1c2728 100644
--- a/storage/browser/quota/storage_monitor_unittest.cc
+++ b/storage/browser/quota/storage_monitor_unittest.cc
@@ -532,17 +532,17 @@
   EXPECT_EQ(2, GetObserverCount(*type_observers.GetHostObservers(host1)));
   EXPECT_EQ(3, GetObserverCount(*type_observers.GetHostObservers(host2)));
 
-  // Remove an observer for a specific filter.
-  type_observers.RemoveObserverForFilter(&mock_observer1, params1.filter);
+  // Remove all instances of observer1.
+  type_observers.RemoveObserver(&mock_observer1);
   ASSERT_TRUE(type_observers.GetHostObservers(host1));
   ASSERT_TRUE(type_observers.GetHostObservers(host2));
   EXPECT_EQ(1, GetObserverCount(*type_observers.GetHostObservers(host1)));
-  EXPECT_EQ(3, GetObserverCount(*type_observers.GetHostObservers(host2)));
+  EXPECT_EQ(2, GetObserverCount(*type_observers.GetHostObservers(host2)));
 
-  // Remove all instances of an observer.
+  // Remove all instances of observer2.
   type_observers.RemoveObserver(&mock_observer2);
   ASSERT_TRUE(type_observers.GetHostObservers(host2));
-  EXPECT_EQ(2, GetObserverCount(*type_observers.GetHostObservers(host2)));
+  EXPECT_EQ(1, GetObserverCount(*type_observers.GetHostObservers(host2)));
   // Observers of host1 has been deleted as it is empty.
   EXPECT_FALSE(type_observers.GetHostObservers(host1));
 }
@@ -636,12 +636,6 @@
   CheckObserverCount(1, 2);
 }
 
-// Test removing an observer for a specific filter.
-TEST_F(StorageMonitorTest, RemoveObserverForFilter) {
-  storage_monitor_->RemoveObserverForFilter(&mock_observer1_, params2_.filter);
-  CheckObserverCount(2, 2);
-}
-
 // Integration test for QuotaManager and StorageMonitor:
 
 class StorageMonitorIntegrationTest : public testing::Test {
diff --git a/testing/buildbot/chromium.perf.fyi.json b/testing/buildbot/chromium.perf.fyi.json
index 93a648a9..c08e95e 100644
--- a/testing/buildbot/chromium.perf.fyi.json
+++ b/testing/buildbot/chromium.perf.fyi.json
@@ -1,6 +1,2470 @@
 {
   "AAAAA1 AUTOGENERATED FILE DO NOT EDIT": {},
   "AAAAA2 See //tools/perf/generate_perf_data.py to make changes": {},
+  "Android Nexus5X WebView Perf": {
+    "isolated_scripts": [
+      {
+        "args": [
+          "battor.steady_state",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "battor.steady_state",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device7",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "battor.trivial_pages",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "battor.trivial_pages",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device5",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "blink_perf.bindings",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "blink_perf.bindings",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device4",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "blink_perf.css",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "blink_perf.css",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device5",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "blink_perf.dom",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "blink_perf.dom",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device4",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "blink_perf.events",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "blink_perf.events",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device3",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "blink_perf.layout",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "blink_perf.layout",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device6",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "blink_perf.paint",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "blink_perf.paint",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device5",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "blink_perf.parser",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "blink_perf.parser",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device5",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "blink_perf.shadow_dom",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "blink_perf.shadow_dom",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device6",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "blink_perf.svg",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "blink_perf.svg",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device3",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "dromaeo.domcoreattr",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "dromaeo.domcoreattr",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device3",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "dromaeo.domcoremodify",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "dromaeo.domcoremodify",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device6",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "dromaeo.domcorequery",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "dromaeo.domcorequery",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device4",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "dromaeo.domcoretraverse",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "dromaeo.domcoretraverse",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device4",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "dummy_benchmark.noisy_benchmark_1",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "dummy_benchmark.noisy_benchmark_1",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device5",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "dummy_benchmark.stable_benchmark_1",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "dummy_benchmark.stable_benchmark_1",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device4",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "image_decoding.image_decoding_measurement",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "image_decoding.image_decoding_measurement",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device7",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "jetstream",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "jetstream",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device6",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "kraken",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "kraken",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device3",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "loading.mobile",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "loading.mobile",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device4",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 16200,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "media.android.tough_video_cases",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "media.android.tough_video_cases",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device5",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "media.android.tough_video_cases_tbmv2",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "media.android.tough_video_cases_tbmv2",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device5",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "media.mse_cases",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "media.mse_cases",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device7",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "memory.blink_memory_mobile",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "memory.blink_memory_mobile",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device7",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "memory.desktop",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "memory.desktop",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device3",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "memory.long_running_idle_gmail_background_tbmv2",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "memory.long_running_idle_gmail_background_tbmv2",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device3",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "memory.long_running_idle_gmail_tbmv2",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "memory.long_running_idle_gmail_tbmv2",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device7",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "memory.top_10_mobile",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "memory.top_10_mobile",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device7",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "octane",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "octane",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device3",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "oortonline_tbmv2",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "oortonline_tbmv2",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device4",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "power.idle_platform",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "power.idle_platform",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device5",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "power.steady_state",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "power.steady_state",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device5",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "power.trivial_pages",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "power.trivial_pages",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device6",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "power.typical_10_mobile",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "power.typical_10_mobile",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device4",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "scheduler.tough_scheduling_cases",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "scheduler.tough_scheduling_cases",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device4",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "service_worker.service_worker",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "service_worker.service_worker",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device6",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "service_worker.service_worker_micro_benchmark",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "service_worker.service_worker_micro_benchmark",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device4",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "smoothness.desktop_tough_pinch_zoom_cases",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "smoothness.desktop_tough_pinch_zoom_cases",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device7",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "smoothness.gpu_rasterization.polymer",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "smoothness.gpu_rasterization.polymer",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device7",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "smoothness.gpu_rasterization.top_25_smooth",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "smoothness.gpu_rasterization.top_25_smooth",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device2",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "smoothness.gpu_rasterization.tough_filters_cases",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "smoothness.gpu_rasterization.tough_filters_cases",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device6",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "smoothness.gpu_rasterization.tough_path_rendering_cases",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "smoothness.gpu_rasterization.tough_path_rendering_cases",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device3",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "smoothness.key_mobile_sites_smooth",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "smoothness.key_mobile_sites_smooth",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device2",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "smoothness.key_silk_cases",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "smoothness.key_silk_cases",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device5",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "smoothness.maps",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "smoothness.maps",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device7",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "smoothness.pathological_mobile_sites",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "smoothness.pathological_mobile_sites",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device7",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "smoothness.simple_mobile_sites",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "smoothness.simple_mobile_sites",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device3",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "smoothness.sync_scroll.key_mobile_sites_smooth",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "smoothness.sync_scroll.key_mobile_sites_smooth",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device3",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "smoothness.top_25_smooth",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "smoothness.top_25_smooth",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device3",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "smoothness.tough_ad_cases",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "smoothness.tough_ad_cases",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device6",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "smoothness.tough_filters_cases",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "smoothness.tough_filters_cases",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device3",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "smoothness.tough_path_rendering_cases",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "smoothness.tough_path_rendering_cases",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device6",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "smoothness.tough_scrolling_cases",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "smoothness.tough_scrolling_cases",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device3",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "smoothness.tough_texture_upload_cases",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "smoothness.tough_texture_upload_cases",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device7",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "smoothness.tough_webgl_ad_cases",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "smoothness.tough_webgl_ad_cases",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device4",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "speedometer",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "speedometer",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device4",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "start_with_url.cold.startup_pages",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "start_with_url.cold.startup_pages",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device3",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "start_with_url.warm.startup_pages",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "start_with_url.warm.startup_pages",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device3",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "storage.indexeddb_endure",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "storage.indexeddb_endure",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device5",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "storage.indexeddb_endure_tracing",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "storage.indexeddb_endure_tracing",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device3",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "system_health.common_desktop",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "system_health.common_desktop",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device3",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "system_health.common_mobile",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "system_health.common_mobile",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device4",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "system_health.memory_desktop",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "system_health.memory_desktop",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device1",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "system_health.memory_mobile",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "system_health.memory_mobile",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device6",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "system_health.webview_startup",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "system_health.webview_startup",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device7",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "thread_times.key_hit_test_cases",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "thread_times.key_hit_test_cases",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device5",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "thread_times.key_idle_power_cases",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "thread_times.key_idle_power_cases",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device7",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "thread_times.key_mobile_sites_smooth",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "thread_times.key_mobile_sites_smooth",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device2",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "thread_times.key_noop_cases",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "thread_times.key_noop_cases",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device3",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "thread_times.key_silk_cases",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "thread_times.key_silk_cases",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device2",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "thread_times.polymer",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "thread_times.polymer",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device1",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "thread_times.simple_mobile_sites",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "thread_times.simple_mobile_sites",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device5",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "thread_times.tough_compositor_cases",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "thread_times.tough_compositor_cases",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device5",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "thread_times.tough_scrolling_cases",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "thread_times.tough_scrolling_cases",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device5",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "tracing.tracing_with_background_memory_infra",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "tracing.tracing_with_background_memory_infra",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device4",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "tracing.tracing_with_debug_overhead",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "tracing.tracing_with_debug_overhead",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device5",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "v8.browsing_mobile",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "v8.browsing_mobile",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device2",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "v8.detached_context_age_in_gc",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "v8.detached_context_age_in_gc",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device6",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "v8.mobile_infinite_scroll_tbmv2",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "v8.mobile_infinite_scroll_tbmv2",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device1",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "v8.runtimestats.browsing_mobile",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "v8.runtimestats.browsing_mobile",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device1",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "webrtc",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "webrtc",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build243-m1--device7",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      }
+    ]
+  },
   "Android Swarming N5X Tester": {
     "isolated_scripts": [
       {
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl
index a240a39..0fb3b19 100644
--- a/testing/buildbot/gn_isolate_map.pyl
+++ b/testing/buildbot/gn_isolate_map.pyl
@@ -912,6 +912,19 @@
       "../../tools/perf/run_benchmark",
     ],
   },
+  "telemetry_perf_webview_tests": {
+    "label": "//chrome/test:telemetry_perf_webview_tests",
+    "type": "script",
+    "script": "//third_party/catapult/devil/devil/android/tools/system_app.py",
+    "args": [
+      "remove",
+      "--package com.android.webview com.google.android.webview",
+      "-v",
+      "--",
+      "//testing/scripts/run_telemetry_benchmark_as_googletest.py",
+      "../../tools/perf/run_benchmark",
+    ],
+  },
   "telemetry_unittests": {
     "label": "//chrome/test:telemetry_unittests",
     "type": "script",
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-network-service b/third_party/WebKit/LayoutTests/FlagExpectations/enable-network-service
index 5b8a7ce..d05a662f 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-network-service
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-network-service
@@ -299,7 +299,7 @@
 Bug(none) external/wpt/fetch/api/policies/referrer-unsafe-url-service-worker.https.html [ Failure Timeout ]
 Bug(none) external/wpt/fetch/api/redirect/redirect-location-worker.html [ Failure Timeout ]
 Bug(none) external/wpt/fetch/api/request/request-consume-empty.html [ Failure Timeout ]
-Bug(none) external/wpt/fetch/api/request/request-consume.html [ Failure Timeout ]
+Bug(none) external/wpt/fetch/api/request/request-consume.html [ Crash Pass Failure Timeout ]
 Bug(none) external/wpt/fetch/api/request/request-init-002.html [ Failure Timeout ]
 Bug(none) external/wpt/fetch/api/response/response-cancel-stream.html [ Failure Timeout ]
 Bug(none) external/wpt/fetch/api/response/response-clone.html [ Failure Timeout ]
@@ -683,6 +683,7 @@
 Bug(none) external/wpt/payment-request/payment-request-onshippingoptionchange-attribute.https.html [ Failure Timeout ]
 Bug(none) external/wpt/payment-request/payment-request-show-method.https.html [ Failure Timeout ]
 Bug(none) external/wpt/payment-request/payment-request-update-event-constructor.https.html [ Failure Timeout ]
+Bug(none) external/wpt/payment-request/payment-request-update-event-updatewith-method.https.html [ Timeout ]
 Bug(none) external/wpt/pointerevents/pointerevent_touch-action-table-test_touch-manual.html [ Failure Timeout ]
 Bug(none) external/wpt/preload/delaying-onload-link-preload-after-discovery.html [ Failure Timeout ]
 Bug(none) external/wpt/preload/download-resources.html [ Failure Timeout ]
@@ -1048,39 +1049,66 @@
 Bug(none) external/wpt/referrer-policy/origin/meta-referrer/same-origin/http-https/xhr-request/generic.keep-origin-redirect.http.html [ Failure Timeout ]
 Bug(none) external/wpt/referrer-policy/origin/meta-referrer/same-origin/http-https/xhr-request/generic.no-redirect.http.html [ Failure Timeout ]
 Bug(none) external/wpt/referrer-policy/origin/meta-referrer/same-origin/http-https/xhr-request/generic.swap-origin-redirect.http.html [ Failure Timeout ]
+Bug(none) external/wpt/referrer-policy/same-origin/attr-referrer/cross-origin/http-https/iframe-tag/cross-origin.keep-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/same-origin/attr-referrer/cross-origin/http-https/iframe-tag/cross-origin.no-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/same-origin/attr-referrer/cross-origin/http-https/iframe-tag/cross-origin.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/same-origin/attr-referrer/cross-origin/http-https/img-tag/cross-origin.keep-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/same-origin/attr-referrer/cross-origin/http-https/img-tag/cross-origin.no-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/same-origin/attr-referrer/cross-origin/http-https/img-tag/cross-origin.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/same-origin/attr-referrer/same-origin/http-https/iframe-tag/same-origin-insecure.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/same-origin/attr-referrer/same-origin/http-https/img-tag/same-origin-insecure.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/same-origin/http-rp/cross-origin/http-https/fetch-request/cross-origin.keep-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/same-origin/http-rp/cross-origin/http-https/fetch-request/cross-origin.no-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/same-origin/http-rp/cross-origin/http-https/fetch-request/cross-origin.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/same-origin/http-rp/cross-origin/http-https/iframe-tag/cross-origin.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/same-origin/http-rp/cross-origin/http-https/iframe-tag/cross-origin.no-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/same-origin/http-rp/cross-origin/http-https/iframe-tag/cross-origin.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/same-origin/http-rp/cross-origin/http-https/img-tag/cross-origin.keep-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/same-origin/http-rp/cross-origin/http-https/img-tag/cross-origin.no-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/same-origin/http-rp/cross-origin/http-https/img-tag/cross-origin.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/same-origin/http-rp/cross-origin/http-https/script-tag/cross-origin.keep-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/same-origin/http-rp/cross-origin/http-https/script-tag/cross-origin.no-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/same-origin/http-rp/cross-origin/http-https/script-tag/cross-origin.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/same-origin/http-rp/cross-origin/http-https/xhr-request/cross-origin.keep-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/same-origin/http-rp/cross-origin/http-https/xhr-request/cross-origin.no-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/same-origin/http-rp/cross-origin/http-https/xhr-request/cross-origin.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/same-origin/http-rp/same-origin/http-https/fetch-request/same-origin-insecure.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/same-origin/http-rp/same-origin/http-https/iframe-tag/same-origin-insecure.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/same-origin/http-rp/same-origin/http-https/img-tag/same-origin-insecure.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/same-origin/http-rp/same-origin/http-https/script-tag/same-origin-insecure.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/same-origin/http-rp/same-origin/http-https/xhr-request/same-origin-insecure.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/same-origin/meta-referrer/cross-origin/http-https/fetch-request/cross-origin.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/same-origin/meta-referrer/cross-origin/http-https/fetch-request/cross-origin.no-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/same-origin/meta-referrer/cross-origin/http-https/fetch-request/cross-origin.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/same-origin/meta-referrer/cross-origin/http-https/iframe-tag/cross-origin.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/same-origin/meta-referrer/cross-origin/http-https/iframe-tag/cross-origin.no-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/same-origin/meta-referrer/cross-origin/http-https/iframe-tag/cross-origin.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/same-origin/meta-referrer/cross-origin/http-https/img-tag/cross-origin.keep-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/same-origin/meta-referrer/cross-origin/http-https/img-tag/cross-origin.no-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/same-origin/meta-referrer/cross-origin/http-https/img-tag/cross-origin.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/same-origin/meta-referrer/cross-origin/http-https/script-tag/cross-origin.keep-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/same-origin/meta-referrer/cross-origin/http-https/script-tag/cross-origin.no-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/same-origin/meta-referrer/cross-origin/http-https/script-tag/cross-origin.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/same-origin/meta-referrer/cross-origin/http-https/xhr-request/cross-origin.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/same-origin/meta-referrer/cross-origin/http-https/xhr-request/cross-origin.no-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/same-origin/meta-referrer/cross-origin/http-https/xhr-request/cross-origin.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/same-origin/meta-referrer/same-origin/http-https/fetch-request/same-origin-insecure.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/same-origin/meta-referrer/same-origin/http-https/iframe-tag/same-origin-insecure.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/same-origin/meta-referrer/same-origin/http-https/img-tag/same-origin-insecure.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/same-origin/meta-referrer/same-origin/http-https/script-tag/same-origin-insecure.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/same-origin/meta-referrer/same-origin/http-https/xhr-request/same-origin-insecure.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/attr-referrer/cross-origin/http-https/iframe-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/attr-referrer/cross-origin/http-https/iframe-tag/upgrade-protocol.no-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/attr-referrer/cross-origin/http-https/iframe-tag/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/attr-referrer/cross-origin/http-https/img-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/attr-referrer/cross-origin/http-https/img-tag/upgrade-protocol.no-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/attr-referrer/cross-origin/http-https/img-tag/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/attr-referrer/same-origin/http-https/iframe-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/attr-referrer/same-origin/http-https/iframe-tag/upgrade-protocol.no-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/attr-referrer/same-origin/http-https/iframe-tag/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/attr-referrer/same-origin/http-https/img-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/attr-referrer/same-origin/http-https/img-tag/upgrade-protocol.no-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/attr-referrer/same-origin/http-https/img-tag/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/cross-origin/http-https/fetch-request/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/cross-origin/http-https/fetch-request/upgrade-protocol.no-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/cross-origin/http-https/fetch-request/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
@@ -1089,58 +1117,102 @@
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/cross-origin/http-https/iframe-tag/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/cross-origin/http-https/img-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/cross-origin/http-https/img-tag/upgrade-protocol.no-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/cross-origin/http-https/img-tag/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/cross-origin/http-https/script-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/cross-origin/http-https/script-tag/upgrade-protocol.no-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/cross-origin/http-https/script-tag/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/cross-origin/http-https/xhr-request/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/cross-origin/http-https/xhr-request/upgrade-protocol.no-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/cross-origin/http-https/xhr-request/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/same-origin/http-https/fetch-request/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/same-origin/http-https/fetch-request/upgrade-protocol.no-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/same-origin/http-https/fetch-request/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/same-origin/http-https/iframe-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/same-origin/http-https/iframe-tag/upgrade-protocol.no-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/same-origin/http-https/iframe-tag/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/same-origin/http-https/img-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/same-origin/http-https/img-tag/upgrade-protocol.no-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/same-origin/http-https/img-tag/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/same-origin/http-https/script-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/same-origin/http-https/script-tag/upgrade-protocol.no-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/same-origin/http-https/script-tag/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/same-origin/http-https/xhr-request/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/same-origin/http-https/xhr-request/upgrade-protocol.no-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/same-origin/http-https/xhr-request/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/cross-origin/http-https/fetch-request/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/cross-origin/http-https/fetch-request/upgrade-protocol.no-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/cross-origin/http-https/fetch-request/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/cross-origin/http-https/iframe-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/cross-origin/http-https/iframe-tag/upgrade-protocol.no-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/cross-origin/http-https/iframe-tag/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/cross-origin/http-https/img-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/cross-origin/http-https/img-tag/upgrade-protocol.no-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/cross-origin/http-https/img-tag/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/cross-origin/http-https/script-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/cross-origin/http-https/script-tag/upgrade-protocol.no-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/cross-origin/http-https/script-tag/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/cross-origin/http-https/xhr-request/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/cross-origin/http-https/xhr-request/upgrade-protocol.no-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/cross-origin/http-https/xhr-request/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/same-origin/http-https/fetch-request/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/same-origin/http-https/fetch-request/upgrade-protocol.no-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/same-origin/http-https/fetch-request/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/same-origin/http-https/iframe-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/same-origin/http-https/iframe-tag/upgrade-protocol.no-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/same-origin/http-https/iframe-tag/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/same-origin/http-https/img-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/same-origin/http-https/img-tag/upgrade-protocol.no-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/same-origin/http-https/img-tag/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/same-origin/http-https/script-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/same-origin/http-https/script-tag/upgrade-protocol.no-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/same-origin/http-https/script-tag/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/same-origin/http-https/xhr-request/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/same-origin/http-https/xhr-request/upgrade-protocol.no-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/same-origin/http-https/xhr-request/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/attr-referrer/cross-origin/http-https/iframe-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/attr-referrer/cross-origin/http-https/iframe-tag/upgrade-protocol.no-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/attr-referrer/cross-origin/http-https/iframe-tag/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/attr-referrer/cross-origin/http-https/img-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/attr-referrer/cross-origin/http-https/img-tag/upgrade-protocol.no-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/attr-referrer/cross-origin/http-https/img-tag/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/attr-referrer/same-origin/http-https/iframe-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/attr-referrer/same-origin/http-https/iframe-tag/upgrade-protocol.no-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/attr-referrer/same-origin/http-https/iframe-tag/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/attr-referrer/same-origin/http-https/img-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/attr-referrer/same-origin/http-https/img-tag/upgrade-protocol.no-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/attr-referrer/same-origin/http-https/img-tag/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/http-rp/cross-origin/http-https/fetch-request/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/http-rp/cross-origin/http-https/fetch-request/upgrade-protocol.no-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/http-rp/cross-origin/http-https/fetch-request/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/http-rp/cross-origin/http-https/iframe-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/http-rp/cross-origin/http-https/iframe-tag/upgrade-protocol.no-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/http-rp/cross-origin/http-https/iframe-tag/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/http-rp/cross-origin/http-https/img-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/http-rp/cross-origin/http-https/img-tag/upgrade-protocol.no-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/http-rp/cross-origin/http-https/img-tag/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/http-rp/cross-origin/http-https/script-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/http-rp/cross-origin/http-https/script-tag/upgrade-protocol.no-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/http-rp/cross-origin/http-https/script-tag/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/http-rp/cross-origin/http-https/xhr-request/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/http-rp/cross-origin/http-https/xhr-request/upgrade-protocol.no-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/http-rp/cross-origin/http-https/xhr-request/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/http-rp/same-origin/http-https/fetch-request/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/http-rp/same-origin/http-https/fetch-request/upgrade-protocol.no-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/http-rp/same-origin/http-https/fetch-request/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/http-rp/same-origin/http-https/iframe-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/http-rp/same-origin/http-https/iframe-tag/upgrade-protocol.no-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/http-rp/same-origin/http-https/iframe-tag/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/http-rp/same-origin/http-https/img-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/http-rp/same-origin/http-https/img-tag/upgrade-protocol.no-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/http-rp/same-origin/http-https/img-tag/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/http-rp/same-origin/http-https/script-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/http-rp/same-origin/http-https/script-tag/upgrade-protocol.no-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/http-rp/same-origin/http-https/script-tag/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/http-rp/same-origin/http-https/xhr-request/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/http-rp/same-origin/http-https/xhr-request/upgrade-protocol.no-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/http-rp/same-origin/http-https/xhr-request/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/meta-referrer/cross-origin/http-https/fetch-request/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/meta-referrer/cross-origin/http-https/fetch-request/upgrade-protocol.no-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/meta-referrer/cross-origin/http-https/fetch-request/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/meta-referrer/cross-origin/http-https/iframe-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/meta-referrer/cross-origin/http-https/iframe-tag/upgrade-protocol.no-redirect.http.html [ Timeout ]
@@ -1151,8 +1223,16 @@
 Bug(none) external/wpt/referrer-policy/strict-origin/meta-referrer/cross-origin/http-https/script-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/meta-referrer/cross-origin/http-https/script-tag/upgrade-protocol.no-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/meta-referrer/cross-origin/http-https/script-tag/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/meta-referrer/cross-origin/http-https/xhr-request/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/meta-referrer/cross-origin/http-https/xhr-request/upgrade-protocol.no-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/meta-referrer/cross-origin/http-https/xhr-request/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/meta-referrer/same-origin/http-https/fetch-request/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/meta-referrer/same-origin/http-https/fetch-request/upgrade-protocol.no-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/meta-referrer/same-origin/http-https/fetch-request/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/meta-referrer/same-origin/http-https/iframe-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/meta-referrer/same-origin/http-https/iframe-tag/upgrade-protocol.no-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/meta-referrer/same-origin/http-https/iframe-tag/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/meta-referrer/same-origin/http-https/img-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/meta-referrer/same-origin/http-https/img-tag/upgrade-protocol.no-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/meta-referrer/same-origin/http-https/img-tag/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/meta-referrer/same-origin/http-https/script-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
@@ -1424,7 +1504,7 @@
 Bug(none) fast/canvas/fillrect-gradient-zero-stops.html [ Timeout ]
 Bug(none) fast/canvas/webgl/offscreenCanvas-context-lost-restored-worker.html [ Timeout ]
 Bug(none) fast/canvas/webgl/offscreenCanvas-context-lost-worker.html [ Timeout ]
-Bug(none) fast/canvas/webgl/offscreenCanvas-transferToImageBitmap-texImage2D.html [ Timeout ]
+Bug(none) fast/canvas/webgl/offscreenCanvas-transferToImageBitmap-texImage2D.html [ Crash Pass Timeout ]
 Bug(none) fast/canvas/webgl/texImage-imageBitmap-from-blob-resize.html [ Failure ]
 Bug(none) fast/canvas/webgl/texImage-imageBitmap-from-blob.html [ Failure ]
 Bug(none) fast/canvas/webgl/texImage-imageBitmap-from-imageBitmap-from-blob.html [ Failure ]
@@ -1508,7 +1588,7 @@
 Bug(none) fast/filesystem/workers/file-writer-sync-write-overlapped.html [ Failure ]
 Bug(none) fast/filesystem/workers/file-writer-truncate-extend.html [ Failure ]
 Bug(none) fast/filesystem/workers/file-writer-write-overlapped.html [ Failure ]
-Bug(none) fast/forms/file/recover-file-input-in-unposted-form.html [ Timeout ]
+Bug(none) fast/forms/file/recover-file-input-in-unposted-form.html [ Crash Pass Timeout ]
 Bug(none) fast/forms/select/input-select-after-resize.html [ Failure ]
 Bug(none) fast/forms/select/option-add-crash.html [ Timeout ]
 Bug(none) fast/forms/select/select-set-length-with-mutation-remove.html [ Timeout ]
@@ -2019,7 +2099,7 @@
 Bug(none) http/tests/inspector/extensions/extensions-audits-content-script.html [ Timeout ]
 Bug(none) http/tests/inspector/extensions/extensions-audits.html [ Timeout ]
 Bug(none) http/tests/inspector/extensions/extensions-events.html [ Timeout ]
-Bug(none) http/tests/inspector/extensions/extensions-network.html [ Timeout ]
+Bug(none) http/tests/inspector/extensions/extensions-network.html [ Failure Timeout ]
 Bug(none) http/tests/inspector/extensions/extensions-reload.html [ Timeout ]
 Bug(none) http/tests/inspector/extensions/extensions-resources.html [ Timeout ]
 Bug(none) http/tests/inspector/extensions/extensions-timeline-api.html [ Timeout ]
diff --git a/third_party/WebKit/LayoutTests/LeakExpectations b/third_party/WebKit/LayoutTests/LeakExpectations
index 35fa4ba..2b3e44b 100644
--- a/third_party/WebKit/LayoutTests/LeakExpectations
+++ b/third_party/WebKit/LayoutTests/LeakExpectations
@@ -45,10 +45,6 @@
 crbug.com/506754 http/tests/serviceworker/chromium/window-close-during-registration.html [ Leak ]
 crbug.com/506754 virtual/mojo-loading/http/tests/serviceworker/chromium/window-close-during-registration.html [ Leak ]
 crbug.com/506754 virtual/off-main-thread-fetch/http/tests/serviceworker/chromium/window-close-during-registration.html [ Leak ]
-crbug.com/506754 virtual/service-worker-navigation-preload/http/tests/serviceworker/chromium/resolve-after-window-close.html [ Leak ]
-crbug.com/506754 virtual/service-worker-navigation-preload/http/tests/serviceworker/chromium/window-close-during-registration.html [ Leak ]
-crbug.com/506754 virtual/service-worker-navigation-preload-disabled/http/tests/serviceworker/chromium/resolve-after-window-close.html [ Leak ]
-crbug.com/506754 virtual/service-worker-navigation-preload-disabled/http/tests/serviceworker/chromium/window-close-during-registration.html [ Leak ]
 
 # -----------------------------------------------------------------
 # Leaks in external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/*
@@ -56,7 +52,6 @@
 crbug.com/594309 [ Linux ] external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-align-self-baseline-vert-001.html [ Failure Pass Leak ]
 crbug.com/594309 [ Linux ] external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-empty-001a.html [ Leak ]
 crbug.com/594309 [ Linux ] external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-empty-001b.html [ Leak ]
-crbug.com/594309 [ Linux ] external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-item-vert-001.html [ Leak ]
 
 crbug.com/664874 http/tests/xmlhttprequest/workers/xmlhttprequest-allowed-with-disabled-web-security.html [ Leak ]
 crbug.com/664874 virtual/mojo-loading/http/tests/xmlhttprequest/workers/xmlhttprequest-allowed-with-disabled-web-security.html [ Leak ]
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 8cc8f3b..fb0de28 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -93,6 +93,11 @@
 # when we swith to swiftshader.
 crbug.com/549742 [ Linux Mac Win ] virtual/gpu/fast/canvas/canvas-drawImage-video-imageSmoothingEnabled.html [ Failure ]
 
+crbug.com/733428 virtual/gpu-rasterization/images/color-profile-background-image-repeat.html [ NeedsManualRebaseline ]
+crbug.com/733428 virtual/gpu-rasterization/images/color-profile-border-image.html [ NeedsManualRebaseline ]
+crbug.com/733428 virtual/gpu-rasterization/images/color-profile-svg.html [ NeedsManualRebaseline ]
+crbug.com/733428 virtual/gpu/fast/canvas/image-object-in-canvas.html [ NeedsManualRebaseline ]
+
 crbug.com/602110 hittesting/border-hittest-with-image-fallback.html [ Failure ]
 
 # Fails consistently on WebKit Mac10.10, WebKit Mac10.11 (retina) and mac10.10_blink_rel tryserver, but not on other Mac bots.
@@ -1560,21 +1565,6 @@
 crbug.com/501659 virtual/mojo-loading/http/tests/security/xss-DENIED-xml-external-entity.xhtml [ Failure ]
 crbug.com/501659 fast/css/stylesheet-candidate-nodes-crash.xhtml [ Failure ]
 
-# Change to higher precision for exotic blend modes
-crbug.com/732829 css3/blending/background-blend-mode-crossfade-image-gradient.html [ NeedsRebaseline ]
-crbug.com/732829 css3/blending/background-blend-mode-gif-color-2.html [ NeedsRebaseline ]
-crbug.com/732829 css3/blending/background-blend-mode-gif-color.html [ NeedsRebaseline ]
-crbug.com/732829 css3/blending/background-blend-mode-svg-color.html [ NeedsRebaseline ]
-crbug.com/732829 css3/blending/mix-blend-mode-isolated-group-1.html [ NeedsRebaseline ]
-crbug.com/732829 css3/blending/mix-blend-mode-isolated-group-2.html [ NeedsRebaseline ]
-crbug.com/732829 css3/blending/mix-blend-mode-isolated-group-3.html [ NeedsRebaseline ]
-crbug.com/732829 css3/blending/mix-blend-mode-simple-text.html [ NeedsRebaseline ]
-crbug.com/732829 svg/filters/feBlend-all-modes.html [ NeedsRebaseline ]
-crbug.com/732829 css3/blending/svg-blend-color.html [ NeedsRebaseline ]
-crbug.com/732829 css3/blending/svg-blend-hue.html [ NeedsRebaseline ]
-crbug.com/732829 css3/blending/svg-blend-luminosity.html [ NeedsRebaseline ]
-crbug.com/732829 css3/blending/svg-blend-saturation.html [ NeedsRebaseline ]
-
 # Mac10.10-specific failures that still need triaging.
 # Form controls need rebaseline because of the default font change.
 # If you see wider INPUT elements or narrower TEXTAREA elements, you may do just
@@ -1803,6 +1793,7 @@
 crbug.com/594639 external/wpt/html/semantics/scripting-1/the-script-element/module/evaluation-error-2.html [ Failure ]
 crbug.com/594639 external/wpt/html/semantics/scripting-1/the-script-element/module/evaluation-error-3.html [ Failure ]
 crbug.com/594639 external/wpt/html/semantics/scripting-1/the-script-element/module/evaluation-error-4.html [ Failure ]
+crbug.com/594639 external/wpt/html/semantics/scripting-1/the-script-element/module/slow-cycle.html [ Failure ]
 
 # This test has a failure console message with specific performance
 # numbers so a consistent baseline cannot be added. This test could be
diff --git a/third_party/WebKit/LayoutTests/css3/blending/background-blend-mode-crossfade-image-gradient-expected.png b/third_party/WebKit/LayoutTests/css3/blending/background-blend-mode-crossfade-image-gradient-expected.png
deleted file mode 100644
index 3d51b402..0000000
--- a/third_party/WebKit/LayoutTests/css3/blending/background-blend-mode-crossfade-image-gradient-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/css3/blending/background-blend-mode-gif-color-2-expected.png b/third_party/WebKit/LayoutTests/css3/blending/background-blend-mode-gif-color-2-expected.png
index 090aadc..60746df 100644
--- a/third_party/WebKit/LayoutTests/css3/blending/background-blend-mode-gif-color-2-expected.png
+++ b/third_party/WebKit/LayoutTests/css3/blending/background-blend-mode-gif-color-2-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/css3/blending/background-blend-mode-gif-color-expected.png b/third_party/WebKit/LayoutTests/css3/blending/background-blend-mode-gif-color-expected.png
index a3c9e41..ec694410 100644
--- a/third_party/WebKit/LayoutTests/css3/blending/background-blend-mode-gif-color-expected.png
+++ b/third_party/WebKit/LayoutTests/css3/blending/background-blend-mode-gif-color-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/css3/blending/background-blend-mode-svg-color-expected.png b/third_party/WebKit/LayoutTests/css3/blending/background-blend-mode-svg-color-expected.png
index 35fb98d..91d6f0b 100644
--- a/third_party/WebKit/LayoutTests/css3/blending/background-blend-mode-svg-color-expected.png
+++ b/third_party/WebKit/LayoutTests/css3/blending/background-blend-mode-svg-color-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/css3/blending/mix-blend-mode-isolated-group-1-expected.png b/third_party/WebKit/LayoutTests/css3/blending/mix-blend-mode-isolated-group-1-expected.png
deleted file mode 100644
index acefe2d2..0000000
--- a/third_party/WebKit/LayoutTests/css3/blending/mix-blend-mode-isolated-group-1-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/css3/blending/mix-blend-mode-isolated-group-2-expected.png b/third_party/WebKit/LayoutTests/css3/blending/mix-blend-mode-isolated-group-2-expected.png
deleted file mode 100644
index e284429..0000000
--- a/third_party/WebKit/LayoutTests/css3/blending/mix-blend-mode-isolated-group-2-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/css3/blending/mix-blend-mode-isolated-group-3-expected.png b/third_party/WebKit/LayoutTests/css3/blending/mix-blend-mode-isolated-group-3-expected.png
deleted file mode 100644
index a3fef31..0000000
--- a/third_party/WebKit/LayoutTests/css3/blending/mix-blend-mode-isolated-group-3-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/css3/blending/mix-blend-mode-simple-text-expected.png b/third_party/WebKit/LayoutTests/css3/blending/mix-blend-mode-simple-text-expected.png
index cb1963dc..edb5d05 100644
--- a/third_party/WebKit/LayoutTests/css3/blending/mix-blend-mode-simple-text-expected.png
+++ b/third_party/WebKit/LayoutTests/css3/blending/mix-blend-mode-simple-text-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/css3/blending/svg-blend-color-expected.png b/third_party/WebKit/LayoutTests/css3/blending/svg-blend-color-expected.png
index 4d05c45..d81319f 100644
--- a/third_party/WebKit/LayoutTests/css3/blending/svg-blend-color-expected.png
+++ b/third_party/WebKit/LayoutTests/css3/blending/svg-blend-color-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/css3/blending/svg-blend-hue-expected.png b/third_party/WebKit/LayoutTests/css3/blending/svg-blend-hue-expected.png
index 5e27b26..9178a40 100644
--- a/third_party/WebKit/LayoutTests/css3/blending/svg-blend-hue-expected.png
+++ b/third_party/WebKit/LayoutTests/css3/blending/svg-blend-hue-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/css3/blending/svg-blend-luminosity-expected.png b/third_party/WebKit/LayoutTests/css3/blending/svg-blend-luminosity-expected.png
index ee2bf76a..cd91b3e 100644
--- a/third_party/WebKit/LayoutTests/css3/blending/svg-blend-luminosity-expected.png
+++ b/third_party/WebKit/LayoutTests/css3/blending/svg-blend-luminosity-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/css3/blending/svg-blend-saturation-expected.png b/third_party/WebKit/LayoutTests/css3/blending/svg-blend-saturation-expected.png
index 0dd9148..e6272dff 100644
--- a/third_party/WebKit/LayoutTests/css3/blending/svg-blend-saturation-expected.png
+++ b/third_party/WebKit/LayoutTests/css3/blending/svg-blend-saturation-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/semantics/scripting-1/the-script-element/module/slow-cycle.html b/third_party/WebKit/LayoutTests/external/wpt/html/semantics/scripting-1/the-script-element/module/slow-cycle.html
new file mode 100644
index 0000000..3a42cf9e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/html/semantics/scripting-1/the-script-element/module/slow-cycle.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<title>Cyclic graph with slow imports</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script type="module">
+import { loaded } from "./slow-module-graph-a.js";
+
+test(() => {
+  assert_true(loaded);
+}, "module graph with cycles load even if part of the graph loads slow");
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/semantics/scripting-1/the-script-element/module/slow-module-graph-a.js b/third_party/WebKit/LayoutTests/external/wpt/html/semantics/scripting-1/the-script-element/module/slow-module-graph-a.js
new file mode 100644
index 0000000..48701aa
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/html/semantics/scripting-1/the-script-element/module/slow-module-graph-a.js
@@ -0,0 +1,3 @@
+import "./slow-module-graph-b.js";
+import "./resources/delayed-modulescript.py"
+export let loaded = true;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/semantics/scripting-1/the-script-element/module/slow-module-graph-b.js b/third_party/WebKit/LayoutTests/external/wpt/html/semantics/scripting-1/the-script-element/module/slow-module-graph-b.js
new file mode 100644
index 0000000..53a8f202
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/html/semantics/scripting-1/the-script-element/module/slow-module-graph-b.js
@@ -0,0 +1 @@
+import "./slow-module-graph-a.js";
diff --git a/third_party/WebKit/LayoutTests/http/tests/csspaint/border-color-expected.png b/third_party/WebKit/LayoutTests/http/tests/csspaint/border-color-expected.png
new file mode 100644
index 0000000..8649167d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/csspaint/border-color-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/http/tests/csspaint/border-color-expected.txt b/third_party/WebKit/LayoutTests/http/tests/csspaint/border-color-expected.txt
new file mode 100644
index 0000000..e5a88d7a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/csspaint/border-color-expected.txt
@@ -0,0 +1,6 @@
+layer at (0,0) size 800x600
+  LayoutView at (0,0) size 800x600
+layer at (0,0) size 800x56
+  LayoutBlockFlow {HTML} at (0,0) size 800x56
+    LayoutBlockFlow {BODY} at (8,8) size 784x40
+      LayoutBlockFlow {DIV} at (0,0) size 160x40 [border: (10px solid #000000) (20px solid #000000) (30px solid #000000) (40px solid #000000)]
diff --git a/third_party/WebKit/LayoutTests/http/tests/csspaint/border-color.html b/third_party/WebKit/LayoutTests/http/tests/csspaint/border-color.html
new file mode 100644
index 0000000..2520a9d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/csspaint/border-color.html
@@ -0,0 +1,165 @@
+<!DOCTYPE html>
+<html>
+<script src="../resources/run-after-layout-and-paint.js"></script>
+<script src="resources/test-runner-paint-worklet.js"></script>
+
+<style>
+#example {
+  max-width: 100px;
+  --border-top-width: 10;
+  --border-right-width: 20;
+  --border-bottom-width: 30;
+  --border-left-width: 40;
+  --border-top-color: red;
+  --border-right-color: green;
+  --border-bottom-color: cyan;
+  --border-left-color: blue;
+}
+.multi-border {
+  border-style: solid;
+  border-image: paint(border-colors);
+  border-image-slice:
+    var(--border-top-width)
+    var(--border-right-width)
+    var(--border-bottom-width)
+    var(--border-left-width);
+  border-width:
+    calc(var(--border-top-width) * 1px)
+    calc(var(--border-right-width) * 1px)
+    calc(var(--border-bottom-width) * 1px)
+    calc(var(--border-left-width) * 1px);
+}
+</style>
+
+<body>
+<div id=example class=multi-border></div>
+<script>
+[
+  '--border-top-color',
+  '--border-right-color',
+  '--border-bottom-color',
+  '--border-left-color',
+].map((name) => {
+  CSS.registerProperty({
+    name: name,
+    syntax: '<color>+',
+    inherits: false,
+    initialValue: 'currentcolor',
+  });
+});
+[
+  '--border-top-width',
+  '--border-right-width',
+  '--border-bottom-width',
+  '--border-left-width',
+].map((name) => {
+  CSS.registerProperty({
+    name: name,
+    syntax: '<number>',
+    inherits: false,
+    initialValue: '0',
+  });
+});
+</script>
+
+<script id="code" type="text/worklet">
+registerPaint('border-colors', class {
+  static get inputProperties() {
+    return [
+      '--border-top-width',
+      '--border-right-width',
+      '--border-bottom-width',
+      '--border-left-width',
+      '--border-top-color',
+      '--border-right-color',
+      '--border-bottom-color',
+      '--border-left-color',
+    ];
+  }
+
+  paint(ctx, size, styleMap) {
+    const t = 0;
+    const r = size.width;
+    const b = size.height;
+    const l = 0;
+
+    const tw = parseFloat(styleMap.get('--border-top-width'));
+    const rw = parseFloat(styleMap.get('--border-right-width'));
+    const bw = parseFloat(styleMap.get('--border-bottom-width'));
+    const lw = parseFloat(styleMap.get('--border-left-width'));
+
+    const ti = tw;
+    const ri = size.width - rw;
+    const bi = size.height - bw;
+    const li = lw;
+
+    let tp, rp, bp, lp, colors;
+    const updateProgression = function() {
+      tp = tw / colors.length;
+      rp = rw / colors.length;
+      bp = bw / colors.length;
+      lp = lw / colors.length;
+    }
+
+    colors = styleMap.getAll('--border-top-color');
+    updateProgression();
+    for (let i = 0; i < colors.length; i++) {
+      ctx.fillStyle = colors[i];
+      this.fillQuad(ctx,
+          li - lp * i, ti - tp * i,
+          li - lp * (i+1), ti - tp * (i+1),
+          ri + lp * (i+1), ti - tp * (i+1),
+          ri + lp * i, ti - tp * i);
+    }
+
+    colors = styleMap.getAll('--border-right-color');
+    updateProgression();
+    for (let i = 0; i < colors.length; i++) {
+      ctx.fillStyle = colors[i];
+      this.fillQuad(ctx,
+          ri + rp * i, ti - tp * i,
+          ri + rp * (i+1), ti - tp * (i+1),
+          ri + rp * (i+1), bi + bp * (i+1),
+          ri + rp * i, bi + bp * i);
+    }
+
+    colors = styleMap.getAll('--border-bottom-color');
+    updateProgression();
+    for (let i = 0; i < colors.length; i++) {
+      ctx.fillStyle = colors[i];
+      this.fillQuad(ctx,
+          ri + rp * i, bi + bp * i,
+          ri + rp * (i+1), bi + bp * (i+1),
+          li - lp * (i+1), bi + bp * (i+1),
+          li - lp * i, bi + bp * i);
+    }
+
+    colors = styleMap.getAll('--border-left-color');
+    updateProgression();
+    for (let i = 0; i < colors.length; i++) {
+      ctx.fillStyle = colors[i];
+      this.fillQuad(ctx,
+          li - lp * i, bi + bp * i,
+          li - lp * (i+1), bi + bp * (i+1),
+          li - lp * (i+1), ti - tp * (i+1),
+          li - lp * i, ti - tp * i);
+    }
+  }
+
+  fillQuad(ctx, x1, y1, x2, y2, x3, y3, x4, y4) {
+    ctx.beginPath();
+    ctx.moveTo(x1, y1);
+    ctx.lineTo(x2, y2);
+    ctx.lineTo(x3, y3);
+    ctx.lineTo(x4, y4);
+    ctx.lineTo(x1, y1);
+    ctx.fill();
+  }
+});
+</script>
+
+<script>
+    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+</script>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/mojo/message-pipe.html b/third_party/WebKit/LayoutTests/mojo/message-pipe.html
index a8e51b7f..52b496b7 100644
--- a/third_party/WebKit/LayoutTests/mojo/message-pipe.html
+++ b/third_party/WebKit/LayoutTests/mojo/message-pipe.html
@@ -58,8 +58,8 @@
     {
       let {result, buffer, handles} = handle0.readMessage();
       assert_equals(result, Mojo.RESULT_OK);
-      assert_equals(buffer, undefined);
-      assert_equals(handles, undefined);
+      assert_equals(buffer.byteLength, 0);
+      assert_array_equals(handles, []);
     }
   }));
   let result = handle1.writeMessage(new ArrayBuffer(0), []);
diff --git a/third_party/WebKit/LayoutTests/payments/resources/payment-request-mock.js b/third_party/WebKit/LayoutTests/payments/resources/payment-request-mock.js
index a50b8dd0..6ea089b1 100644
--- a/third_party/WebKit/LayoutTests/payments/resources/payment-request-mock.js
+++ b/third_party/WebKit/LayoutTests/payments/resources/payment-request-mock.js
@@ -5,11 +5,10 @@
 "use strict";
 
 let paymentRequestMock = loadMojoModules(
-    'paymentRequestMock',
-    ['components/payments/mojom/payment_request.mojom',
-     'mojo/public/js/bindings',
-    ]).then(mojo => {
-  let [paymentRequest, bindings] =  mojo.modules;
+  'paymentRequestMock', ['third_party/WebKit/public/platform/modules/payments/payment_request.mojom',
+    'mojo/public/js/bindings',
+  ]).then(mojo => {
+  let [paymentRequest, bindings] = mojo.modules;
 
   class PaymentRequestMock {
     constructor(interfaceProvider) {
@@ -31,14 +30,11 @@
       }
     }
 
-    show() {
-    }
+    show() {}
 
-    updateWith(details) {
-    }
+    updateWith(details) {}
 
-    complete(success) {
-    }
+    complete(success) {}
 
     onPaymentResponse(data) {
       if (!this.client_) {
diff --git a/third_party/WebKit/LayoutTests/platform/linux/css3/blending/background-blend-mode-crossfade-image-gradient-expected.png b/third_party/WebKit/LayoutTests/platform/linux/css3/blending/background-blend-mode-crossfade-image-gradient-expected.png
new file mode 100644
index 0000000..c311df0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/linux/css3/blending/background-blend-mode-crossfade-image-gradient-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/css3/blending/mix-blend-mode-isolated-group-1-expected.png b/third_party/WebKit/LayoutTests/platform/linux/css3/blending/mix-blend-mode-isolated-group-1-expected.png
new file mode 100644
index 0000000..11079957
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/linux/css3/blending/mix-blend-mode-isolated-group-1-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/css3/blending/mix-blend-mode-isolated-group-2-expected.png b/third_party/WebKit/LayoutTests/platform/linux/css3/blending/mix-blend-mode-isolated-group-2-expected.png
new file mode 100644
index 0000000..158b093
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/linux/css3/blending/mix-blend-mode-isolated-group-2-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/css3/blending/mix-blend-mode-isolated-group-3-expected.png b/third_party/WebKit/LayoutTests/platform/linux/css3/blending/mix-blend-mode-isolated-group-3-expected.png
new file mode 100644
index 0000000..be5be67
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/linux/css3/blending/mix-blend-mode-isolated-group-3-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/filters/feBlend-all-modes-expected.png b/third_party/WebKit/LayoutTests/platform/linux/svg/filters/feBlend-all-modes-expected.png
deleted file mode 100644
index 6dbca7ae..0000000
--- a/third_party/WebKit/LayoutTests/platform/linux/svg/filters/feBlend-all-modes-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/css3/blending/background-blend-mode-gif-color-2-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/css3/blending/background-blend-mode-gif-color-2-expected.png
new file mode 100644
index 0000000..4a7c8a9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/css3/blending/background-blend-mode-gif-color-2-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/css3/blending/background-blend-mode-gif-color-2-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/css3/blending/background-blend-mode-gif-color-2-expected.png
new file mode 100644
index 0000000..60746df
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/css3/blending/background-blend-mode-gif-color-2-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-retina/css3/blending/background-blend-mode-gif-color-2-expected.png b/third_party/WebKit/LayoutTests/platform/mac-retina/css3/blending/background-blend-mode-gif-color-2-expected.png
index 3126f0c..4a7c8a9 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-retina/css3/blending/background-blend-mode-gif-color-2-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-retina/css3/blending/background-blend-mode-gif-color-2-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/css3/blending/background-blend-mode-crossfade-image-gradient-expected.png b/third_party/WebKit/LayoutTests/platform/mac/css3/blending/background-blend-mode-crossfade-image-gradient-expected.png
new file mode 100644
index 0000000..c311df0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/css3/blending/background-blend-mode-crossfade-image-gradient-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/css3/blending/mix-blend-mode-isolated-group-1-expected.png b/third_party/WebKit/LayoutTests/platform/mac/css3/blending/mix-blend-mode-isolated-group-1-expected.png
new file mode 100644
index 0000000..11079957
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/css3/blending/mix-blend-mode-isolated-group-1-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/css3/blending/mix-blend-mode-isolated-group-2-expected.png b/third_party/WebKit/LayoutTests/platform/mac/css3/blending/mix-blend-mode-isolated-group-2-expected.png
new file mode 100644
index 0000000..158b093
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/css3/blending/mix-blend-mode-isolated-group-2-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/css3/blending/mix-blend-mode-isolated-group-3-expected.png b/third_party/WebKit/LayoutTests/platform/mac/css3/blending/mix-blend-mode-isolated-group-3-expected.png
new file mode 100644
index 0000000..be5be67
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/css3/blending/mix-blend-mode-isolated-group-3-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/filters/feBlend-all-modes-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/filters/feBlend-all-modes-expected.png
new file mode 100644
index 0000000..a6a4649
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/svg/filters/feBlend-all-modes-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/css3/blending/background-blend-mode-crossfade-image-gradient-expected.png b/third_party/WebKit/LayoutTests/platform/win/css3/blending/background-blend-mode-crossfade-image-gradient-expected.png
new file mode 100644
index 0000000..0fe9b13
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win/css3/blending/background-blend-mode-crossfade-image-gradient-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/css3/blending/mix-blend-mode-isolated-group-1-expected.png b/third_party/WebKit/LayoutTests/platform/win/css3/blending/mix-blend-mode-isolated-group-1-expected.png
new file mode 100644
index 0000000..2b058d5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win/css3/blending/mix-blend-mode-isolated-group-1-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/css3/blending/mix-blend-mode-isolated-group-2-expected.png b/third_party/WebKit/LayoutTests/platform/win/css3/blending/mix-blend-mode-isolated-group-2-expected.png
new file mode 100644
index 0000000..e88ebcf5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win/css3/blending/mix-blend-mode-isolated-group-2-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/css3/blending/mix-blend-mode-isolated-group-3-expected.png b/third_party/WebKit/LayoutTests/platform/win/css3/blending/mix-blend-mode-isolated-group-3-expected.png
new file mode 100644
index 0000000..a347de2a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win/css3/blending/mix-blend-mode-isolated-group-3-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/filters/feBlend-all-modes-expected.png b/third_party/WebKit/LayoutTests/platform/win/svg/filters/feBlend-all-modes-expected.png
index 6dbca7ae..1be1491 100644
--- a/third_party/WebKit/LayoutTests/platform/win/svg/filters/feBlend-all-modes-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/svg/filters/feBlend-all-modes-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/svg/filters/feBlend-all-modes-expected.png b/third_party/WebKit/LayoutTests/svg/filters/feBlend-all-modes-expected.png
deleted file mode 100644
index c46c5bb..0000000
--- a/third_party/WebKit/LayoutTests/svg/filters/feBlend-all-modes-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/Source/core/BUILD.gn b/third_party/WebKit/Source/core/BUILD.gn
index 7584311..d7ec4d5 100644
--- a/third_party/WebKit/Source/core/BUILD.gn
+++ b/third_party/WebKit/Source/core/BUILD.gn
@@ -1369,6 +1369,7 @@
     "loader/FrameFetchContextTest.cpp",
     "loader/LinkLoaderTest.cpp",
     "loader/MixedContentCheckerTest.cpp",
+    "loader/NetworkQuietDetectorTest.cpp",
     "loader/PingLoaderTest.cpp",
     "loader/ProgressTrackerTest.cpp",
     "loader/TextResourceDecoderBuilderTest.cpp",
diff --git a/third_party/WebKit/Source/core/editing/serializers/MarkupAccumulator.cpp b/third_party/WebKit/Source/core/editing/serializers/MarkupAccumulator.cpp
index c8cbc31..d942727 100644
--- a/third_party/WebKit/Source/core/editing/serializers/MarkupAccumulator.cpp
+++ b/third_party/WebKit/Source/core/editing/serializers/MarkupAccumulator.cpp
@@ -151,6 +151,11 @@
   return formatter_.SerializeAsHTMLDocument(node);
 }
 
+std::pair<Node*, Element*> MarkupAccumulator::GetAuxiliaryDOMTree(
+    const Element& element) const {
+  return std::pair<Node*, Element*>();
+}
+
 template <typename Strategy>
 static void SerializeNodesWithNamespaces(MarkupAccumulator& accumulator,
                                          Node& target_node,
@@ -177,6 +182,25 @@
     for (; current; current = Strategy::NextSibling(*current))
       SerializeNodesWithNamespaces<Strategy>(accumulator, *current,
                                              kIncludeNode, &namespace_hash);
+
+    // Traverses other DOM tree, i.e., shadow tree.
+    if (target_node.IsElementNode()) {
+      std::pair<Node*, Element*> auxiliary_pair =
+          accumulator.GetAuxiliaryDOMTree(ToElement(target_node));
+      Node* auxiliary_tree = auxiliary_pair.first;
+      Element* enclosing_element = auxiliary_pair.second;
+      if (auxiliary_tree) {
+        if (auxiliary_pair.second)
+          accumulator.AppendStartTag(*enclosing_element);
+        current = Strategy::FirstChild(*auxiliary_tree);
+        for (; current; current = Strategy::NextSibling(*current)) {
+          SerializeNodesWithNamespaces<Strategy>(accumulator, *current,
+                                                 kIncludeNode, &namespace_hash);
+        }
+        if (enclosing_element)
+          accumulator.AppendEndTag(*enclosing_element);
+      }
+    }
   }
 
   if ((!children_only && target_node.IsElementNode()) &&
diff --git a/third_party/WebKit/Source/core/editing/serializers/MarkupAccumulator.h b/third_party/WebKit/Source/core/editing/serializers/MarkupAccumulator.h
index d18c400..d04b5062 100644
--- a/third_party/WebKit/Source/core/editing/serializers/MarkupAccumulator.h
+++ b/third_party/WebKit/Source/core/editing/serializers/MarkupAccumulator.h
@@ -75,6 +75,15 @@
 
   EntityMask EntityMaskForText(const Text&) const;
 
+  // Returns an auxiliary DOM tree, i.e. shadow tree, that needs also to be
+  // serialized. The root of auxiliary DOM tree is returned as an 1st element
+  // in the pair. It can be null if no auxiliary DOM tree exists. An additional
+  // element used to enclose the serialized content of auxiliary DOM tree
+  // can be returned as 2nd element in the pair. It can be null if this is not
+  // needed. For shadow tree, a <template> element is needed to wrap the shadow
+  // tree content.
+  virtual std::pair<Node*, Element*> GetAuxiliaryDOMTree(const Element&) const;
+
  private:
   MarkupFormatter formatter_;
   StringBuilder markup_;
diff --git a/third_party/WebKit/Source/core/exported/WebRemoteFrameImpl.cpp b/third_party/WebKit/Source/core/exported/WebRemoteFrameImpl.cpp
index 144ddb8c..99a9749b 100644
--- a/third_party/WebKit/Source/core/exported/WebRemoteFrameImpl.cpp
+++ b/third_party/WebKit/Source/core/exported/WebRemoteFrameImpl.cpp
@@ -143,10 +143,6 @@
   return WebPerformance();
 }
 
-void WebRemoteFrameImpl::DispatchUnloadEvent() {
-  NOTREACHED();
-}
-
 void WebRemoteFrameImpl::Reload(WebFrameLoadType) {
   NOTREACHED();
 }
diff --git a/third_party/WebKit/Source/core/exported/WebRemoteFrameImpl.h b/third_party/WebKit/Source/core/exported/WebRemoteFrameImpl.h
index 9f21ca1..95cec3e6 100644
--- a/third_party/WebKit/Source/core/exported/WebRemoteFrameImpl.h
+++ b/third_party/WebKit/Source/core/exported/WebRemoteFrameImpl.h
@@ -44,7 +44,6 @@
   WebView* View() const override;
   WebDocument GetDocument() const override;
   WebPerformance Performance() const override;
-  void DispatchUnloadEvent() override;
   void Reload(WebFrameLoadType) override;
   void ReloadWithOverrideURL(const WebURL& override_url,
                              WebFrameLoadType) override;
diff --git a/third_party/WebKit/Source/core/frame/FrameSerializer.cpp b/third_party/WebKit/Source/core/frame/FrameSerializer.cpp
index 2b43599..272da66 100644
--- a/third_party/WebKit/Source/core/frame/FrameSerializer.cpp
+++ b/third_party/WebKit/Source/core/frame/FrameSerializer.cpp
@@ -102,6 +102,7 @@
                        Namespaces*) override;
   void AppendStartTag(Node&, Namespaces* = nullptr) override;
   void AppendEndTag(const Element&) override;
+  std::pair<Node*, Element*> GetAuxiliaryDOMTree(const Element&) const override;
 
  private:
   void AppendAttributeValue(StringBuilder& out, const String& attribute_value);
@@ -232,6 +233,11 @@
   MarkupAccumulator::AppendEndTag(element);
 }
 
+std::pair<Node*, Element*> SerializerMarkupAccumulator::GetAuxiliaryDOMTree(
+    const Element& element) const {
+  return delegate_.GetAuxiliaryDOMTree(element);
+}
+
 void SerializerMarkupAccumulator::AppendAttributeValue(
     StringBuilder& out,
     const String& attribute_value) {
diff --git a/third_party/WebKit/Source/core/frame/FrameSerializer.h b/third_party/WebKit/Source/core/frame/FrameSerializer.h
index 8a7b366..a42e43a7 100644
--- a/third_party/WebKit/Source/core/frame/FrameSerializer.h
+++ b/third_party/WebKit/Source/core/frame/FrameSerializer.h
@@ -108,6 +108,13 @@
       return Vector<Attribute>();
     }
 
+    // Returns an auxiliary DOM tree, i.e. shadow tree, that needs to be
+    // serialized.
+    virtual std::pair<Node*, Element*> GetAuxiliaryDOMTree(
+        const Element&) const {
+      return std::pair<Node*, Element*>();
+    }
+
     virtual bool ShouldCollectProblemMetric() { return false; }
   };
 
diff --git a/third_party/WebKit/Source/core/frame/LocalFrame.h b/third_party/WebKit/Source/core/frame/LocalFrame.h
index a0435f2..1c3e573 100644
--- a/third_party/WebKit/Source/core/frame/LocalFrame.h
+++ b/third_party/WebKit/Source/core/frame/LocalFrame.h
@@ -238,6 +238,7 @@
 
   ContentSettingsClient* GetContentSettingsClient();
   FrameResourceCoordinator* GetFrameResourceCoordinator() {
+    // can be null
     return frame_resource_coordinator_;
   }
 
diff --git a/third_party/WebKit/Source/core/frame/LocalFrameView.cpp b/third_party/WebKit/Source/core/frame/LocalFrameView.cpp
index 2da8988..2dc6209 100644
--- a/third_party/WebKit/Source/core/frame/LocalFrameView.cpp
+++ b/third_party/WebKit/Source/core/frame/LocalFrameView.cpp
@@ -130,6 +130,7 @@
 #include "platform/scroll/ScrollbarTheme.h"
 #include "platform/scroll/ScrollerSizeMetrics.h"
 #include "platform/text/TextStream.h"
+#include "platform/wtf/CheckedNumeric.h"
 #include "platform/wtf/CurrentTime.h"
 #include "platform/wtf/PtrUtil.h"
 #include "platform/wtf/StdLibExtras.h"
@@ -2209,14 +2210,15 @@
     PaintLayerScrollableArea* paint_layer_scrollable_area =
         ToPaintLayerScrollableArea(scrollable_area);
     if (paint_layer_scrollable_area->ScrollsOverflow() &&
-        !paint_layer_scrollable_area->Layer()->IsRootLayer()) {
-      DEFINE_STATIC_LOCAL(
-          CustomCountHistogram, scrollable_area_size_histogram,
-          ("Event.Scroll.ScrollerSize.OnLoad", 1, kScrollerSizeLargestBucket,
-           kScrollerSizeBucketCount));
-      scrollable_area_size_histogram.Count(
-          paint_layer_scrollable_area->VisibleContentRect().Width() *
-          paint_layer_scrollable_area->VisibleContentRect().Height());
+        !paint_layer_scrollable_area->Layer()->IsRootLayer() &&
+        paint_layer_scrollable_area->VisibleContentRect().Size().Area() > 0) {
+      CheckedNumeric<int> size =
+          paint_layer_scrollable_area->VisibleContentRect().Width();
+      size *= paint_layer_scrollable_area->VisibleContentRect().Height();
+      UMA_HISTOGRAM_CUSTOM_COUNTS(
+          "Event.Scroll.ScrollerSize.OnLoad",
+          size.ValueOrDefault(std::numeric_limits<int>::max()), 1,
+          kScrollerSizeLargestBucket, kScrollerSizeBucketCount);
     }
   }
 }
diff --git a/third_party/WebKit/Source/core/input/ScrollManager.cpp b/third_party/WebKit/Source/core/input/ScrollManager.cpp
index 309d87ca..027fda0 100644
--- a/third_party/WebKit/Source/core/input/ScrollManager.cpp
+++ b/third_party/WebKit/Source/core/input/ScrollManager.cpp
@@ -25,6 +25,7 @@
 #include "platform/Histogram.h"
 #include "platform/RuntimeEnabledFeatures.h"
 #include "platform/scroll/ScrollerSizeMetrics.h"
+#include "platform/wtf/CheckedNumeric.h"
 #include "platform/wtf/PtrUtil.h"
 
 namespace blink {
@@ -200,7 +201,8 @@
 
 void ScrollManager::ComputeScrollRelatedMetrics(
     uint32_t* non_composited_main_thread_scrolling_reasons,
-    int* scroller_size) {
+    int* scroller_size,
+    bool* scroller_size_updated) {
   // When scrolling on the main thread, the scrollableArea may or may not be
   // composited. Either way, we have recorded either the reasons stored in
   // its layer or the reason NonFastScrollableRegion from the compositor
@@ -211,8 +213,7 @@
 
   // When recording the size of the scroller, we only need to record the
   // first scrollable area that we find during the walk up.
-  bool set_scroller_size = false;
-
+  *scroller_size_updated = false;
   for (auto* cur_box =
            scroll_gesture_handling_node_->GetLayoutObject()->EnclosingBox();
        cur_box; cur_box = cur_box->ContainingBlock()) {
@@ -221,10 +222,11 @@
     if (!scrollable_area || !scrollable_area->ScrollsOverflow())
       continue;
 
-    if (!set_scroller_size && !cur_box->Layer()->IsRootLayer()) {
-      *scroller_size = scrollable_area->VisibleContentRect().Width() *
-                       scrollable_area->VisibleContentRect().Height();
-      set_scroller_size = true;
+    if (!*scroller_size_updated && !cur_box->Layer()->IsRootLayer()) {
+      CheckedNumeric<int> size = scrollable_area->VisibleContentRect().Width();
+      size *= scrollable_area->VisibleContentRect().Height();
+      *scroller_size = size.ValueOrDefault(std::numeric_limits<int>::max());
+      *scroller_size_updated = true;
     }
 
     DCHECK(!scrollable_area->UsesCompositedScrolling() ||
@@ -240,23 +242,21 @@
     return;
   }
 
-  int scroller_size = -1;
+  int scroller_size = 0;
+  bool scroller_size_updated = false;
   uint32_t non_composited_main_thread_scrolling_reasons = 0;
   ComputeScrollRelatedMetrics(&non_composited_main_thread_scrolling_reasons,
-                              &scroller_size);
-  if (scroller_size >= 0) {
+                              &scroller_size, &scroller_size_updated);
+  if (scroller_size_updated) {
+    DCHECK_GT(scroller_size, 0);
     if (device == kWebGestureDeviceTouchpad) {
-      DEFINE_STATIC_LOCAL(
-          CustomCountHistogram, size_histogram_wheel,
-          ("Event.Scroll.ScrollerSize.OnScroll_Wheel", 1,
-           kScrollerSizeLargestBucket, kScrollerSizeBucketCount));
-      size_histogram_wheel.Count(scroller_size);
+      UMA_HISTOGRAM_CUSTOM_COUNTS("Event.Scroll.ScrollerSize.OnScroll_Wheel",
+                                  scroller_size, 1, kScrollerSizeLargestBucket,
+                                  kScrollerSizeBucketCount);
     } else {
-      DEFINE_STATIC_LOCAL(
-          CustomCountHistogram, size_histogram_touch,
-          ("Event.Scroll.ScrollerSize.OnScroll_Touch", 1,
-           kScrollerSizeLargestBucket, kScrollerSizeBucketCount));
-      size_histogram_touch.Count(scroller_size);
+      UMA_HISTOGRAM_CUSTOM_COUNTS("Event.Scroll.ScrollerSize.OnScroll_Touch",
+                                  scroller_size, 1, kScrollerSizeLargestBucket,
+                                  kScrollerSizeBucketCount);
     }
   }
 
diff --git a/third_party/WebKit/Source/core/input/ScrollManager.h b/third_party/WebKit/Source/core/input/ScrollManager.h
index eb0451e..58e0d14 100644
--- a/third_party/WebKit/Source/core/input/ScrollManager.h
+++ b/third_party/WebKit/Source/core/input/ScrollManager.h
@@ -114,7 +114,8 @@
   // scroller_size is set only when scrolling non root scroller.
   void ComputeScrollRelatedMetrics(
       uint32_t* non_composited_main_thread_scrolling_reasons,
-      int* scroller_size);
+      int* scroller_size,
+      bool* scroller_size_updated);
   void RecordScrollRelatedMetrics(const WebGestureDevice);
 
   // NOTE: If adding a new field to this class please ensure that it is
diff --git a/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp b/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp
index 0ad5360..c43598b 100644
--- a/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp
+++ b/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp
@@ -461,13 +461,13 @@
 }
 
 void CompositedLayerMapping::UpdateRasterizationPolicy() {
-  bool allow_transformed_rasterization =
-      !RequiresCompositing(owning_layer_.GetCompositingReasons() &
-                           ~kCompositingReasonSquashingDisallowed);
-  graphics_layer_->ContentLayer()->SetAllowTransformedRasterization(
-      allow_transformed_rasterization);
+  bool transformed_rasterization_allowed =
+      !(owning_layer_.GetCompositingReasons() &
+        kCompositingReasonComboAllDirectReasons);
+  graphics_layer_->ContentLayer()->SetTransformedRasterizationAllowed(
+      transformed_rasterization_allowed);
   if (squashing_layer_)
-    squashing_layer_->ContentLayer()->SetAllowTransformedRasterization(true);
+    squashing_layer_->ContentLayer()->SetTransformedRasterizationAllowed(true);
 }
 
 void CompositedLayerMapping::UpdateCompositedBounds() {
diff --git a/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMappingTest.cpp b/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMappingTest.cpp
index a8209c7..8f6387e 100644
--- a/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMappingTest.cpp
+++ b/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMappingTest.cpp
@@ -1811,4 +1811,66 @@
   EXPECT_FLOAT_EQ(100, main_graphics_layer->GetPosition().Y());
 }
 
+TEST_P(CompositedLayerMappingTest,
+       TransformedRasterizationDisallowedForDirectReasons) {
+  // This test verifies layers with direct compositing reasons won't have
+  // transformed rasterization, i.e. should raster in local space.
+  SetBodyInnerHTML(
+      "<div id='target1' style='transform:translateZ(0);'>foo</div>"
+      "<div id='target2' style='will-change:opacity;'>bar</div>"
+      "<div id='target3' style='backface-visibility:hidden;'>ham</div>");
+
+  {
+    LayoutObject* target = GetLayoutObjectByElementId("target1");
+    ASSERT_TRUE(target && target->IsBox());
+    PaintLayer* target_layer = ToLayoutBox(target)->Layer();
+    GraphicsLayer* target_graphics_layer =
+        target_layer ? target_layer->GraphicsLayerBacking() : nullptr;
+    ASSERT_TRUE(target_graphics_layer);
+    EXPECT_FALSE(target_graphics_layer->ContentLayer()
+                     ->TransformedRasterizationAllowed());
+  }
+  {
+    LayoutObject* target = GetLayoutObjectByElementId("target2");
+    ASSERT_TRUE(target && target->IsBox());
+    PaintLayer* target_layer = ToLayoutBox(target)->Layer();
+    GraphicsLayer* target_graphics_layer =
+        target_layer ? target_layer->GraphicsLayerBacking() : nullptr;
+    ASSERT_TRUE(target_graphics_layer);
+    EXPECT_FALSE(target_graphics_layer->ContentLayer()
+                     ->TransformedRasterizationAllowed());
+  }
+  {
+    LayoutObject* target = GetLayoutObjectByElementId("target3");
+    ASSERT_TRUE(target && target->IsBox());
+    PaintLayer* target_layer = ToLayoutBox(target)->Layer();
+    GraphicsLayer* target_graphics_layer =
+        target_layer ? target_layer->GraphicsLayerBacking() : nullptr;
+    ASSERT_TRUE(target_graphics_layer);
+    EXPECT_FALSE(target_graphics_layer->ContentLayer()
+                     ->TransformedRasterizationAllowed());
+  }
+}
+
+TEST_P(CompositedLayerMappingTest, TransformedRasterizationForInlineTransform) {
+  // This test verifies we allow layers that are indirectly composited due to
+  // an inline transform (but no direct reason otherwise) to raster in the
+  // device space for higher quality.
+  SetBodyInnerHTML(
+      "<div style='will-change:transform; width:500px; "
+      "height:20px;'>composited</div>"
+      "<div id='target' style='transform:translate(1.5px,-10.5px); "
+      "width:500px; height:20px;'>indirectly composited due to inline "
+      "transform</div>");
+
+  LayoutObject* target = GetLayoutObjectByElementId("target");
+  ASSERT_TRUE(target && target->IsBox());
+  PaintLayer* target_layer = ToLayoutBox(target)->Layer();
+  GraphicsLayer* target_graphics_layer =
+      target_layer ? target_layer->GraphicsLayerBacking() : nullptr;
+  ASSERT_TRUE(target_graphics_layer);
+  EXPECT_TRUE(
+      target_graphics_layer->ContentLayer()->TransformedRasterizationAllowed());
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/layout/ng/BlockLayout.md b/third_party/WebKit/Source/core/layout/ng/BlockLayout.md
new file mode 100644
index 0000000..2357fba
--- /dev/null
+++ b/third_party/WebKit/Source/core/layout/ng/BlockLayout.md
@@ -0,0 +1,156 @@
+# Block Layout #
+
+This document can be viewed in formatted form [here](https://chromium.googlesource.com/chromium/src/+/master/third_party/WebKit/Source/core/layout/ng/BlockLayout.md).
+
+## Floats ##
+
+TODO.
+
+## An introduction to margin collapsing ##
+
+A simple way to think about [margin collapsing](https://www.w3.org/TR/CSS2/box.html#collapsing-margins)
+is that it takes the maximum margin between two elements. For example:
+
+```html
+<!-- The divs below are 20px apart -->
+<div style="margin-bottom: 10px;">Hi</div>
+<div style="margin-top: 20px;">there</div>
+```
+
+This is complicated by _negative_ margins. For example:
+
+```html
+<!-- The divs below are 10px apart -->
+<div style="margin-bottom: 20px;">Hi</div>
+<div style="margin-top: -10px;">there</div>
+
+<!-- The divs below are -20px apart -->
+<div style="margin-bottom: -20px;">Hi</div>
+<div style="margin-top: -10px;">there</div>
+```
+
+The rule here is: `max(pos_margins) + min(neg_margins)`. This rule we'll refer
+to as the _margin collapsing rule_. If this only happened between top level
+elements it would be pretty simple, however consider the following:
+
+```html
+<!-- The top-level divs below are -2px apart -->
+<div style="margin-bottom: 3px">
+  <div style="margin-bottom: -5">
+    <div style="margin-bottom: 7px">Hi</div>
+  </div>
+</div>
+<div style="margin-top: 11px">
+  <div style="margin-top: -13px">there</div>
+</div>
+```
+
+In the above example as there isn't **anything** separating the edges of two
+fragments the margins stack together (e.g. no borders or padding). There are
+known as **adjoining margins**.  If we apply our formula to the above we get:
+`max(3, 7, 11) + min(-5, -13) = -2`.
+
+A useful concept is a **margin strut**. This is a pair of margins consisting of
+one positive and one negative margin.
+
+A margin strut allows us to keep track of the largest positive and smallest
+negative margin. E.g.
+```cpp
+struct MarginStrut {
+  LayoutUnit pos_margin;
+  LayoutUnit neg_margin;
+
+  void Append(LayoutUnit margin) {
+    if (margin < 0)
+      neg_margin = std::min(margin, neg_margin);
+    else
+      pos_margin = std::max(margin, pos_margin);
+  }
+
+  LayoutUnit Sum() { return pos_margin + neg_margin; }
+}
+```
+
+A naïve algorithm for the adjoining margins case would be to _bubble_ up
+margins. For example each fragment would have a **margin strut** at the
+block-start and block-end edge. If the child fragment was **adjoining** to its
+parent, you simply keep track of the margins by calling `Append` on the margin
+strut. E.g.
+
+```cpp
+// fragment1 is the first child.
+MarginStrut s1 = fragment1.block_start_margin_strut;
+s1.Append(node1.style.margin_start);
+
+builder.SetStartMarginStrut(s1);
+
+// fragment2 is the last child.
+MarginStrut s2 = fragment2.block_end_margin_strut;
+s2.Append(node2.style.margin_start);
+
+builder.SetEndMarginStrut(s2);
+```
+
+When it comes time to collapse the margins you can use the margin collapsing
+rule, e.g.
+```cpp
+MarginStrut s1 = fragment1.block_end_margin_strut;
+MarginStrut s2 = fragment2.block_start_margin_strut;
+LayoutUnit distance =
+    std::max(s1.pos_margin, s2.pos_margin) +
+    std::min(s1.neg_margin, s2.neg_margin);
+```
+
+This would be pretty simple - however it doesn't work. As we discussed in the
+floats section a _child_ will position _itself_ within the BFC. If we did margin
+collapsing this way we'd create a circular dependency between layout and
+positioning. E.g. we need to perform layout in order to determine the
+block-start margin strut, which would allow us to position the fragment, which
+would allow us to perform layout.
+
+We **invert** the problem. A fragment now only produces an _end_ margin strut.
+The _start_ margin strut becomes an input as well as where the margin strut is
+currently positioned within the BFC.  For example:
+
+```cpp
+Fragment* Layout(LogicalOffset bfc_estimate, MarginStrut input_strut) {
+  MarginStrut curr_strut = input_strut;
+  LogicalOffset curr_bfc_estimate = bfc_estimate;
+  
+  // We collapse the margin strut which allows us to compute our BFC offset if
+  // we have border or padding. I.e. we don't have an adjoining margin.
+  if (border_padding.block_start) {
+    curr_bfc_estimate += curr_strut.Sum();
+    curr_strut = MarginStrut();
+
+    fragment_builder.SetBfcOffset(curr_bfc_estimate);
+    curr_bfc_estimate += border_padding.block_start;
+  }
+
+  for (const auto& child : children) {
+    curr_strut.Append(child.margins.block_start);
+    const auto* fragment = child.Layout(curr_bfc_estimate, curr_strut);
+
+    curr_strut = fragment->end_margin_strut;
+    curr_strut.Append(child.margins.block_end);
+
+    curr_bfc_estimate = fragment->BfcOffset() + fragment->BlockSize();
+  }
+
+  fragment_builder.SetEndMarginStrut(curr_strut);
+
+  return fragment_builder.ToFragment();
+}
+```
+
+It isn't immediately obvious that this works, but if you try and work through an
+example manually, it'll become clearer.
+
+There are lots of different things which can "resolve" the BFC offset of an
+element. For example inline content (text, atomic inlines), border and padding,
+if a child _might_ be affected by clearance.
+
+## Zero block-size fragments ##
+
+TODO.
+
diff --git a/third_party/WebKit/Source/core/loader/BUILD.gn b/third_party/WebKit/Source/core/loader/BUILD.gn
index 45e3cf7..13f291d7 100644
--- a/third_party/WebKit/Source/core/loader/BUILD.gn
+++ b/third_party/WebKit/Source/core/loader/BUILD.gn
@@ -48,6 +48,8 @@
     "NavigationScheduler.cpp",
     "NavigationScheduler.h",
     "NetworkHintsInterface.h",
+    "NetworkQuietDetector.cpp",
+    "NetworkQuietDetector.h",
     "PingLoader.cpp",
     "PingLoader.h",
     "PrerendererClient.cpp",
diff --git a/third_party/WebKit/Source/core/loader/FrameFetchContext.cpp b/third_party/WebKit/Source/core/loader/FrameFetchContext.cpp
index 96ec2377..2c7c2a1c 100644
--- a/third_party/WebKit/Source/core/loader/FrameFetchContext.cpp
+++ b/third_party/WebKit/Source/core/loader/FrameFetchContext.cpp
@@ -54,6 +54,7 @@
 #include "core/loader/FrameLoader.h"
 #include "core/loader/MixedContentChecker.h"
 #include "core/loader/NetworkHintsInterface.h"
+#include "core/loader/NetworkQuietDetector.h"
 #include "core/loader/PingLoader.h"
 #include "core/loader/ProgressTracker.h"
 #include "core/loader/SubresourceFilter.h"
@@ -68,6 +69,7 @@
 #include "core/timing/PerformanceBase.h"
 #include "platform/WebFrameScheduler.h"
 #include "platform/exported/WrappedResourceRequest.h"
+#include "platform/instrumentation/resource_coordinator/FrameResourceCoordinator.h"
 #include "platform/instrumentation/tracing/TracedValue.h"
 #include "platform/loader/fetch/ClientHintsPreferences.h"
 #include "platform/loader/fetch/FetchInitiatorTypeNames.h"
@@ -596,6 +598,9 @@
   if (!document_)
     return;
   FirstMeaningfulPaintDetector::From(*document_).CheckNetworkStable();
+  if (FrameResourceCoordinator::IsEnabled()) {
+    NetworkQuietDetector::From(*document_).CheckNetworkStable();
+  }
   if (resource->IsLoadEventBlockingResourceType())
     document_->CheckCompleted();
 }
diff --git a/third_party/WebKit/Source/core/loader/NetworkQuietDetector.cpp b/third_party/WebKit/Source/core/loader/NetworkQuietDetector.cpp
new file mode 100644
index 0000000..0b60839
--- /dev/null
+++ b/third_party/WebKit/Source/core/loader/NetworkQuietDetector.cpp
@@ -0,0 +1,84 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/loader/NetworkQuietDetector.h"
+
+#include "core/dom/TaskRunnerHelper.h"
+#include "core/frame/LocalFrame.h"
+#include "platform/instrumentation/resource_coordinator/FrameResourceCoordinator.h"
+#include "platform/loader/fetch/ResourceFetcher.h"
+
+namespace blink {
+
+static const char kSupplementName[] = "NetworkQuietDetector";
+
+NetworkQuietDetector& NetworkQuietDetector::From(Document& document) {
+  NetworkQuietDetector* detector = static_cast<NetworkQuietDetector*>(
+      Supplement<Document>::From(document, kSupplementName));
+  if (!detector) {
+    detector = new NetworkQuietDetector(document);
+    Supplement<Document>::ProvideTo(document, kSupplementName, detector);
+  }
+  return *detector;
+}
+
+// This function is called when the number of active connections is decreased.
+// Note that the number of active connections doesn't decrease monotonically.
+void NetworkQuietDetector::CheckNetworkStable() {
+  // Document finishes parsing after DomContentLoadedEventEnd is fired,
+  // check the status in order to avoid false signals.
+  if (!GetSupplementable()->HasFinishedParsing())
+    return;
+
+  SetNetworkQuietTimers(ActiveConnections());
+}
+
+NetworkQuietDetector::NetworkQuietDetector(Document& document)
+    : Supplement<Document>(document),
+      network_quiet_timer_(
+          TaskRunnerHelper::Get(TaskType::kUnspecedTimer, &document),
+          this,
+          &NetworkQuietDetector::NetworkQuietTimerFired) {}
+
+int NetworkQuietDetector::ActiveConnections() {
+  ResourceFetcher* fetcher = GetSupplementable()->Fetcher();
+  return fetcher->BlockingRequestCount() + fetcher->NonblockingRequestCount();
+}
+
+void NetworkQuietDetector::SetNetworkQuietTimers(int active_connections) {
+  if (network_quiet_reached_ ||
+      active_connections > kNetworkQuietMaximumConnections)
+    return;
+
+  // If activeConnections < 2 and the timer is already running, current
+  // quiet window continues; the timer shouldn't be restarted.
+  // The timer should be restarted when |active_connections| == 2 because it
+  // means the number of active connections has increased to more than 2 after
+  // the timer was started.
+  if (active_connections == kNetworkQuietMaximumConnections ||
+      !network_quiet_timer_.IsActive()) {
+    network_quiet_timer_.StartOneShot(kNetworkQuietWindowSeconds,
+                                      BLINK_FROM_HERE);
+  }
+}
+
+void NetworkQuietDetector::NetworkQuietTimerFired(TimerBase*) {
+  if (!GetSupplementable() || !GetSupplementable()->GetFrame() ||
+      network_quiet_reached_ ||
+      ActiveConnections() > kNetworkQuietMaximumConnections)
+    return;
+  network_quiet_reached_ = true;
+  auto frame_resource_coordinator =
+      GetSupplementable()->GetFrame()->GetFrameResourceCoordinator();
+  if (frame_resource_coordinator) {
+    frame_resource_coordinator->SendEvent(
+        resource_coordinator::mojom::EventType::kOnLocalFrameNetworkIdle);
+  }
+}
+
+DEFINE_TRACE(NetworkQuietDetector) {
+  Supplement<Document>::Trace(visitor);
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/core/loader/NetworkQuietDetector.h b/third_party/WebKit/Source/core/loader/NetworkQuietDetector.h
new file mode 100644
index 0000000..c9645f16
--- /dev/null
+++ b/third_party/WebKit/Source/core/loader/NetworkQuietDetector.h
@@ -0,0 +1,56 @@
+// 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 NetworkQuietDetector_h
+#define NetworkQuietDetector_h
+
+#include "core/CoreExport.h"
+#include "core/dom/Document.h"
+#include "platform/Supplementable.h"
+#include "platform/Timer.h"
+#include "platform/heap/Handle.h"
+#include "platform/wtf/Noncopyable.h"
+
+namespace blink {
+
+class Document;
+
+// NetworkQuietDetector observes network request count everytime a load is
+// finshed after DOMContentLoadedEventEnd is fired, and signals a network
+// idleness signal to GRC when there are no more than 2 network connection
+// active in 1 second.
+class CORE_EXPORT NetworkQuietDetector
+    : public GarbageCollectedFinalized<NetworkQuietDetector>,
+      public Supplement<Document> {
+  WTF_MAKE_NONCOPYABLE(NetworkQuietDetector);
+  USING_GARBAGE_COLLECTED_MIXIN(NetworkQuietDetector);
+
+ public:
+  static NetworkQuietDetector& From(Document&);
+  virtual ~NetworkQuietDetector() {}
+
+  void CheckNetworkStable();
+
+  DECLARE_VIRTUAL_TRACE();
+
+ private:
+  friend class NetworkQuietDetectorTest;
+
+  // The page is quiet if there are no more than 2 active network requests for
+  // this duration of time.
+  static constexpr double kNetworkQuietWindowSeconds = 1.0;
+  static constexpr int kNetworkQuietMaximumConnections = 2;
+
+  explicit NetworkQuietDetector(Document&);
+  int ActiveConnections();
+  void SetNetworkQuietTimers(int active_connections);
+  void NetworkQuietTimerFired(TimerBase*);
+
+  bool network_quiet_reached_ = false;
+  TaskRunnerTimer<NetworkQuietDetector> network_quiet_timer_;
+};
+
+}  // namespace blink
+
+#endif
diff --git a/third_party/WebKit/Source/core/loader/NetworkQuietDetectorTest.cpp b/third_party/WebKit/Source/core/loader/NetworkQuietDetectorTest.cpp
new file mode 100644
index 0000000..aaf5f0f
--- /dev/null
+++ b/third_party/WebKit/Source/core/loader/NetworkQuietDetectorTest.cpp
@@ -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.
+
+#include "core/loader/NetworkQuietDetector.h"
+
+#include "core/testing/DummyPageHolder.h"
+#include "platform/testing/TestingPlatformSupport.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+class NetworkQuietDetectorTest : public testing::Test {
+ protected:
+  void SetUp() override {
+    platform_->AdvanceClockSeconds(1);
+    dummy_page_holder_ = DummyPageHolder::Create(IntSize(800, 600));
+  }
+
+  double AdvanceClockAndGetTime() {
+    platform_->AdvanceClockSeconds(1);
+    return MonotonicallyIncreasingTime();
+  }
+
+  Document& GetDocument() { return dummy_page_holder_->GetDocument(); }
+  NetworkQuietDetector& Detector() {
+    return NetworkQuietDetector::From(GetDocument());
+  }
+
+  void SimulateNetworkStable() {
+    GetDocument().SetParsingState(Document::kFinishedParsing);
+    Detector().NetworkQuietTimerFired(nullptr);
+  }
+
+  void SimulateNetworkQuiet() {
+    GetDocument().SetParsingState(Document::kFinishedParsing);
+    Detector().NetworkQuietTimerFired(nullptr);
+  }
+
+  void SetActiveConnections(int connections) {
+    Detector().SetNetworkQuietTimers(connections);
+  }
+
+  bool IsNetworkQuietTimerActive() {
+    return Detector().network_quiet_timer_.IsActive();
+  }
+
+  bool HadNetworkQuiet() { return Detector().network_quiet_reached_; }
+
+ protected:
+  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
+      platform_;
+
+  static constexpr double kNetworkQuietWindowSeconds =
+      NetworkQuietDetector::kNetworkQuietWindowSeconds;
+
+ private:
+  std::unique_ptr<DummyPageHolder> dummy_page_holder_;
+};
+
+TEST_F(NetworkQuietDetectorTest, NetworkQuietTimer) {
+  SetActiveConnections(3);
+  EXPECT_FALSE(IsNetworkQuietTimerActive());
+
+  SetActiveConnections(2);
+  platform_->RunForPeriodSeconds(kNetworkQuietWindowSeconds - 0.1);
+  EXPECT_TRUE(IsNetworkQuietTimerActive());
+  EXPECT_FALSE(HadNetworkQuiet());
+
+  SetActiveConnections(2);  // This should reset the quiet timer.
+  platform_->RunForPeriodSeconds(kNetworkQuietWindowSeconds - 0.1);
+  EXPECT_TRUE(IsNetworkQuietTimerActive());
+  EXPECT_FALSE(HadNetworkQuiet());
+
+  SetActiveConnections(1);  // This should not reset the quiet timer.
+  platform_->RunForPeriodSeconds(0.1001);
+  EXPECT_TRUE(HadNetworkQuiet());
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/core/mojo/MojoHandle.cpp b/third_party/WebKit/Source/core/mojo/MojoHandle.cpp
index 36aa5e6..5d20750 100644
--- a/third_party/WebKit/Source/core/mojo/MojoHandle.cpp
+++ b/third_party/WebKit/Source/core/mojo/MojoHandle.cpp
@@ -19,6 +19,7 @@
 #include "core/mojo/MojoWatcher.h"
 #include "core/mojo/MojoWriteDataOptions.h"
 #include "core/mojo/MojoWriteDataResult.h"
+#include "mojo/public/cpp/system/message_pipe.h"
 #include "platform/bindings/ScriptState.h"
 
 // Mojo messages typically do not contain many handles. In fact most
@@ -53,7 +54,7 @@
 MojoResult MojoHandle::writeMessage(
     ArrayBufferOrArrayBufferView& buffer,
     const HeapVector<Member<MojoHandle>>& handles) {
-  // MojoWriteMessage takes ownership of the handles, so release them here.
+  // mojo::WriteMessageRaw takes ownership of the handles, so release them here.
   Vector<::MojoHandle, kHandleVectorInlineCapacity> raw_handles(handles.size());
   std::transform(
       handles.begin(), handles.end(), raw_handles.begin(),
@@ -71,41 +72,61 @@
     num_bytes = view->byteLength();
   }
 
-  return MojoWriteMessage(handle_->value(), bytes, num_bytes,
-                          raw_handles.data(), raw_handles.size(),
-                          MOJO_WRITE_MESSAGE_FLAG_NONE);
+  return mojo::WriteMessageRaw(
+      mojo::MessagePipeHandle(handle_->value()), bytes, num_bytes,
+      raw_handles.data(), raw_handles.size(), MOJO_WRITE_MESSAGE_FLAG_NONE);
 }
 
 void MojoHandle::readMessage(const MojoReadMessageFlags& flags_dict,
                              MojoReadMessageResult& result_dict) {
-  ::MojoReadMessageFlags flags = MOJO_READ_MESSAGE_FLAG_NONE;
-  if (flags_dict.mayDiscard())
-    flags |= MOJO_READ_MESSAGE_FLAG_MAY_DISCARD;
+  mojo::ScopedMessageHandle message;
+  MojoResult result =
+      mojo::ReadMessageNew(mojo::MessagePipeHandle(handle_->value()), &message,
+                           MOJO_READ_MESSAGE_FLAG_NONE);
+  if (result != MOJO_RESULT_OK) {
+    result_dict.setResult(result);
+    return;
+  }
+
+  result = MojoSerializeMessage(message->value());
+  if (result != MOJO_RESULT_OK && result != MOJO_RESULT_FAILED_PRECONDITION) {
+    result_dict.setResult(MOJO_RESULT_ABORTED);
+    return;
+  }
 
   uint32_t num_bytes = 0, num_handles = 0;
-  MojoResult result = MojoReadMessage(handle_->value(), nullptr, &num_bytes,
-                                      nullptr, &num_handles, flags);
-  if (result != MOJO_RESULT_RESOURCE_EXHAUSTED) {
-    result_dict.setResult(result);
+  void* bytes;
+  Vector<::MojoHandle, kHandleVectorInlineCapacity> raw_handles;
+  result = MojoGetSerializedMessageContents(
+      message->value(), &bytes, &num_bytes, nullptr, &num_handles,
+      MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_NONE);
+  if (result == MOJO_RESULT_RESOURCE_EXHAUSTED) {
+    raw_handles.resize(num_handles);
+    result = MojoGetSerializedMessageContents(
+        message->value(), &bytes, &num_bytes, raw_handles.data(), &num_handles,
+        MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_NONE);
+  }
+
+  if (result != MOJO_RESULT_OK) {
+    result_dict.setResult(MOJO_RESULT_ABORTED);
     return;
   }
 
   DOMArrayBuffer* buffer =
       DOMArrayBuffer::CreateUninitializedOrNull(num_bytes, 1);
-  CHECK(buffer);
-  Vector<::MojoHandle, kHandleVectorInlineCapacity> raw_handles(num_handles);
-  result = MojoReadMessage(handle_->value(), buffer->Data(), &num_bytes,
-                           raw_handles.data(), &num_handles, flags);
+  if (num_bytes) {
+    CHECK(buffer);
+    memcpy(buffer->Data(), bytes, num_bytes);
+  }
+  result_dict.setBuffer(buffer);
 
   HeapVector<Member<MojoHandle>> handles(num_handles);
   for (size_t i = 0; i < num_handles; ++i) {
     handles[i] = MojoHandle::Create(
         mojo::MakeScopedHandle(mojo::Handle(raw_handles[i])));
   }
-
-  result_dict.setResult(result);
-  result_dict.setBuffer(buffer);
   result_dict.setHandles(handles);
+  result_dict.setResult(result);
 }
 
 void MojoHandle::writeData(const ArrayBufferOrArrayBufferView& buffer,
diff --git a/third_party/WebKit/Source/devtools/front_end/console_model/ConsoleModel.js b/third_party/WebKit/Source/devtools/front_end/console_model/ConsoleModel.js
index bf9eab73..c3aafbe 100644
--- a/third_party/WebKit/Source/devtools/front_end/console_model/ConsoleModel.js
+++ b/third_party/WebKit/Source/devtools/front_end/console_model/ConsoleModel.js
@@ -84,8 +84,6 @@
     var resourceTreeModel = target.model(SDK.ResourceTreeModel);
     if (resourceTreeModel) {
       eventListeners.push(resourceTreeModel.addEventListener(
-          SDK.ResourceTreeModel.Events.MainFrameStartedLoading, this._mainFrameStartedLoading, this));
-      eventListeners.push(resourceTreeModel.addEventListener(
           SDK.ResourceTreeModel.Events.MainFrameNavigated, this._mainFrameNavigated, this));
     }
 
@@ -97,6 +95,8 @@
           SDK.RuntimeModel.Events.ExceptionRevoked, this._exceptionRevoked.bind(this, runtimeModel)));
       eventListeners.push(runtimeModel.addEventListener(
           SDK.RuntimeModel.Events.ConsoleAPICalled, this._consoleAPICalled.bind(this, runtimeModel)));
+      eventListeners.push(runtimeModel.debuggerModel().addEventListener(
+          SDK.DebuggerModel.Events.GlobalObjectCleared, this._clearIfNecessary, this));
     }
 
     var networkManager = target.model(SDK.NetworkManager);
@@ -253,7 +253,7 @@
   /**
    * @param {!Common.Event} event
    */
-  _mainFrameStartedLoading(event) {
+  _clearIfNecessary(event) {
     if (!Common.moduleSetting('preserveConsoleLog').get())
       this._clear();
   }
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/ResourceTreeModel.js b/third_party/WebKit/Source/devtools/front_end/sdk/ResourceTreeModel.js
index 5e0b1f6..a7a6b816 100644
--- a/third_party/WebKit/Source/devtools/front_end/sdk/ResourceTreeModel.js
+++ b/third_party/WebKit/Source/devtools/front_end/sdk/ResourceTreeModel.js
@@ -231,19 +231,6 @@
   }
 
   /**
-   * @param {!Protocol.Page.FrameId} frameId
-   */
-  _frameStartedLoading(frameId) {
-    // Do nothing unless cached resource tree is processed - it will overwrite everything.
-    if (!this._cachedResourcesProcessed)
-      return;
-
-    var frame = this._frames.get(frameId);
-    if (!frame || frame.isMainFrame())
-      this.dispatchEventToListeners(SDK.ResourceTreeModel.Events.MainFrameStartedLoading);
-  }
-
-  /**
    * @param {!Common.Event} event
    */
   _onRequestFinished(event) {
@@ -491,7 +478,6 @@
   FrameResized: Symbol('FrameResized'),
   FrameWillNavigate: Symbol('FrameWillNavigate'),
   MainFrameNavigated: Symbol('MainFrameNavigated'),
-  MainFrameStartedLoading: Symbol('MainFrameStartedLoading'),
   ResourceAdded: Symbol('ResourceAdded'),
   WillLoadCachedResources: Symbol('WillLoadCachedResources'),
   CachedResourcesLoaded: Symbol('CachedResourcesLoaded'),
@@ -813,7 +799,6 @@
    * @param {!Protocol.Page.FrameId} frameId
    */
   frameStartedLoading(frameId) {
-    this._resourceTreeModel._frameStartedLoading(frameId);
   }
 
   /**
diff --git a/third_party/WebKit/Source/modules/BUILD.gn b/third_party/WebKit/Source/modules/BUILD.gn
index cf07b1c..cf3443a 100644
--- a/third_party/WebKit/Source/modules/BUILD.gn
+++ b/third_party/WebKit/Source/modules/BUILD.gn
@@ -298,6 +298,7 @@
     "push_messaging/PushMessageDataTest.cpp",
     "remoteplayback/RemotePlaybackTest.cpp",
     "serviceworkers/ServiceWorkerContainerTest.cpp",
+    "serviceworkers/WebEmbeddedWorkerImplTest.cpp",
     "webaudio/AudioBasicProcessorHandlerTest.cpp",
     "webaudio/AudioContextTest.cpp",
     "webaudio/AudioWorkletGlobalScopeTest.cpp",
@@ -321,7 +322,6 @@
   deps = [
     ":modules",
     ":modules_testing",
-    "//components/payments/mojom:mojom_blink",
     "//skia",
     "//testing/gmock",
     "//testing/gtest",
diff --git a/third_party/WebKit/Source/modules/exported/BUILD.gn b/third_party/WebKit/Source/modules/exported/BUILD.gn
index 50ed446..de52e6d 100644
--- a/third_party/WebKit/Source/modules/exported/BUILD.gn
+++ b/third_party/WebKit/Source/modules/exported/BUILD.gn
@@ -10,6 +10,8 @@
     "WebDOMFileSystem.cpp",
     "WebDOMMediaStreamTrack.cpp",
     "WebDatabase.cpp",
+    "WebEmbeddedWorkerImpl.cpp",
+    "WebEmbeddedWorkerImpl.h",
     "WebMediaDeviceChangeObserver.cpp",
     "WebMediaDevicesRequest.cpp",
     "WebMediaStreamRegistry.cpp",
diff --git a/third_party/WebKit/Source/web/WebEmbeddedWorkerImpl.cpp b/third_party/WebKit/Source/modules/exported/WebEmbeddedWorkerImpl.cpp
similarity index 97%
rename from third_party/WebKit/Source/web/WebEmbeddedWorkerImpl.cpp
rename to third_party/WebKit/Source/modules/exported/WebEmbeddedWorkerImpl.cpp
index 031ced793..dd2347c 100644
--- a/third_party/WebKit/Source/web/WebEmbeddedWorkerImpl.cpp
+++ b/third_party/WebKit/Source/modules/exported/WebEmbeddedWorkerImpl.cpp
@@ -28,7 +28,7 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include "web/WebEmbeddedWorkerImpl.h"
+#include "modules/exported/WebEmbeddedWorkerImpl.h"
 
 #include <memory>
 #include "bindings/core/v8/SourceLocation.h"
@@ -36,6 +36,8 @@
 #include "core/dom/SecurityContext.h"
 #include "core/dom/TaskRunnerHelper.h"
 #include "core/exported/WebDataSourceImpl.h"
+#include "core/exported/WebFactory.h"
+#include "core/exported/WebViewBase.h"
 #include "core/frame/WebLocalFrameBase.h"
 #include "core/frame/csp/ContentSecurityPolicy.h"
 #include "core/inspector/ConsoleMessage.h"
@@ -52,6 +54,8 @@
 #include "core/workers/WorkerThreadStartupData.h"
 #include "modules/indexeddb/IndexedDBClientImpl.h"
 #include "modules/serviceworkers/ServiceWorkerContainerClient.h"
+#include "modules/serviceworkers/ServiceWorkerGlobalScopeClientImpl.h"
+#include "modules/serviceworkers/ServiceWorkerGlobalScopeProxy.h"
 #include "modules/serviceworkers/ServiceWorkerThread.h"
 #include "platform/Histogram.h"
 #include "platform/RuntimeEnabledFeatures.h"
@@ -75,8 +79,6 @@
 #include "public/web/WebSettings.h"
 #include "public/web/WebView.h"
 #include "public/web/modules/serviceworker/WebServiceWorkerContextClient.h"
-#include "web/ServiceWorkerGlobalScopeClientImpl.h"
-#include "web/ServiceWorkerGlobalScopeProxy.h"
 
 namespace blink {
 
@@ -225,9 +227,10 @@
   if (asked_to_terminate_)
     return;
   WebDevToolsAgent* devtools_agent = main_frame_->DevToolsAgent();
-  if (devtools_agent)
+  if (devtools_agent) {
     devtools_agent->DispatchOnInspectorBackend(session_id, call_id, method,
                                                message);
+  }
 }
 
 void WebEmbeddedWorkerImpl::AddMessageToConsole(
@@ -270,7 +273,8 @@
   // This code, and probably most of the code in this class should be shared
   // with SharedWorker.
   DCHECK(!web_view_);
-  web_view_ = WebView::Create(nullptr, kWebPageVisibilityStateVisible);
+  web_view_ = WebFactory::GetInstance().CreateWebViewBase(
+      nullptr, kWebPageVisibilityStateVisible);
   WebSettings* settings = web_view_->GetSettings();
   // FIXME: http://crbug.com/363843. This needs to find a better way to
   // not create graphics layers.
@@ -281,8 +285,8 @@
   settings->SetStrictMixedContentChecking(true);
   settings->SetAllowRunningOfInsecureContent(false);
   settings->SetDataSaverEnabled(worker_start_data_.data_saver_enabled);
-  main_frame_ = ToWebLocalFrameBase(WebLocalFrame::Create(
-      WebTreeScopeType::kDocument, this, nullptr, nullptr));
+  main_frame_ = WebFactory::GetInstance().CreateWebLocalFrameBase(
+      WebTreeScopeType::kDocument, this, nullptr, nullptr);
   web_view_->SetMainFrame(main_frame_.Get());
   main_frame_->SetDevToolsAgentClient(this);
 
diff --git a/third_party/WebKit/Source/web/WebEmbeddedWorkerImpl.h b/third_party/WebKit/Source/modules/exported/WebEmbeddedWorkerImpl.h
similarity index 100%
rename from third_party/WebKit/Source/web/WebEmbeddedWorkerImpl.h
rename to third_party/WebKit/Source/modules/exported/WebEmbeddedWorkerImpl.h
diff --git a/third_party/WebKit/Source/modules/payments/BUILD.gn b/third_party/WebKit/Source/modules/payments/BUILD.gn
index 8488065..9238edc5 100644
--- a/third_party/WebKit/Source/modules/payments/BUILD.gn
+++ b/third_party/WebKit/Source/modules/payments/BUILD.gn
@@ -34,8 +34,4 @@
     "PaymentsValidators.cpp",
     "PaymentsValidators.h",
   ]
-  deps = [
-    "//components/payments/mojom:mojom_blink",
-    "//components/payments/mojom:mojom_payment_app_blink",
-  ]
 }
diff --git a/third_party/WebKit/Source/modules/payments/DEPS b/third_party/WebKit/Source/modules/payments/DEPS
index 1c8bd343..962b0f1 100644
--- a/third_party/WebKit/Source/modules/payments/DEPS
+++ b/third_party/WebKit/Source/modules/payments/DEPS
@@ -1,5 +1,4 @@
 include_rules = [
-    "+components/payments",
     "+mojo/public/cpp/bindings/binding.h",
     "+mojo/public/cpp/bindings/interface_request.h",
 ]
diff --git a/third_party/WebKit/Source/modules/payments/PaymentAddress.h b/third_party/WebKit/Source/modules/payments/PaymentAddress.h
index d4abdda..0dc6403b9 100644
--- a/third_party/WebKit/Source/modules/payments/PaymentAddress.h
+++ b/third_party/WebKit/Source/modules/payments/PaymentAddress.h
@@ -6,13 +6,13 @@
 #define PaymentAddress_h
 
 #include "bindings/core/v8/ScriptValue.h"
-#include "components/payments/mojom/payment_request.mojom-blink.h"
 #include "modules/ModulesExport.h"
 #include "platform/bindings/ScriptWrappable.h"
 #include "platform/heap/Handle.h"
 #include "platform/wtf/Noncopyable.h"
 #include "platform/wtf/Vector.h"
 #include "platform/wtf/text/WTFString.h"
+#include "public/platform/modules/payments/payment_request.mojom-blink.h"
 
 namespace blink {
 
diff --git a/third_party/WebKit/Source/modules/payments/PaymentInstruments.h b/third_party/WebKit/Source/modules/payments/PaymentInstruments.h
index 993d474..4295247 100644
--- a/third_party/WebKit/Source/modules/payments/PaymentInstruments.h
+++ b/third_party/WebKit/Source/modules/payments/PaymentInstruments.h
@@ -5,12 +5,12 @@
 #ifndef PaymentInstruments_h
 #define PaymentInstruments_h
 
-#include "components/payments/mojom/payment_app.mojom-blink.h"
 #include "modules/ModulesExport.h"
 #include "platform/bindings/ScriptWrappable.h"
 #include "platform/heap/Handle.h"
 #include "platform/wtf/Noncopyable.h"
 #include "platform/wtf/text/WTFString.h"
+#include "public/platform/modules/payments/payment_app.mojom-blink.h"
 
 namespace blink {
 
diff --git a/third_party/WebKit/Source/modules/payments/PaymentManager.h b/third_party/WebKit/Source/modules/payments/PaymentManager.h
index b88e146f..1b32bb3 100644
--- a/third_party/WebKit/Source/modules/payments/PaymentManager.h
+++ b/third_party/WebKit/Source/modules/payments/PaymentManager.h
@@ -6,10 +6,10 @@
 #define PaymentManager_h
 
 #include "bindings/core/v8/ScriptPromise.h"
-#include "components/payments/mojom/payment_app.mojom-blink.h"
 #include "modules/ModulesExport.h"
 #include "platform/bindings/ScriptWrappable.h"
 #include "platform/heap/Handle.h"
+#include "public/platform/modules/payments/payment_app.mojom-blink.h"
 
 namespace blink {
 
diff --git a/third_party/WebKit/Source/modules/payments/PaymentRequest.h b/third_party/WebKit/Source/modules/payments/PaymentRequest.h
index 5778ac4b..cd1236d 100644
--- a/third_party/WebKit/Source/modules/payments/PaymentRequest.h
+++ b/third_party/WebKit/Source/modules/payments/PaymentRequest.h
@@ -7,7 +7,6 @@
 
 #include "bindings/core/v8/ScriptPromise.h"
 #include "bindings/core/v8/ScriptValue.h"
-#include "components/payments/mojom/payment_request.mojom-blink.h"
 #include "core/dom/ContextLifecycleObserver.h"
 #include "core/events/EventTarget.h"
 #include "modules/ModulesExport.h"
@@ -24,6 +23,7 @@
 #include "platform/wtf/RefPtr.h"
 #include "platform/wtf/Vector.h"
 #include "platform/wtf/text/WTFString.h"
+#include "public/platform/modules/payments/payment_request.mojom-blink.h"
 
 namespace blink {
 
diff --git a/third_party/WebKit/Source/modules/payments/PaymentResponse.h b/third_party/WebKit/Source/modules/payments/PaymentResponse.h
index c14f23e..ca0b9f7e 100644
--- a/third_party/WebKit/Source/modules/payments/PaymentResponse.h
+++ b/third_party/WebKit/Source/modules/payments/PaymentResponse.h
@@ -7,13 +7,13 @@
 
 #include "bindings/core/v8/ScriptPromise.h"
 #include "bindings/core/v8/ScriptValue.h"
-#include "components/payments/mojom/payment_request.mojom-blink.h"
 #include "modules/ModulesExport.h"
 #include "modules/payments/PaymentCurrencyAmount.h"
 #include "platform/bindings/ScriptWrappable.h"
 #include "platform/heap/Handle.h"
 #include "platform/wtf/Noncopyable.h"
 #include "platform/wtf/text/WTFString.h"
+#include "public/platform/modules/payments/payment_request.mojom-blink.h"
 
 namespace blink {
 
diff --git a/third_party/WebKit/Source/modules/payments/PaymentTestHelper.h b/third_party/WebKit/Source/modules/payments/PaymentTestHelper.h
index 19683ee..70c0306 100644
--- a/third_party/WebKit/Source/modules/payments/PaymentTestHelper.h
+++ b/third_party/WebKit/Source/modules/payments/PaymentTestHelper.h
@@ -7,7 +7,6 @@
 
 #include "bindings/core/v8/ScriptFunction.h"
 #include "bindings/core/v8/V8DOMException.h"
-#include "components/payments/mojom/payment_request.mojom-blink.h"
 #include "modules/payments/PaymentDetailsInit.h"
 #include "modules/payments/PaymentDetailsUpdate.h"
 #include "modules/payments/PaymentItem.h"
@@ -17,6 +16,7 @@
 #include "platform/wtf/Allocator.h"
 #include "platform/wtf/Vector.h"
 #include "platform/wtf/text/WTFString.h"
+#include "public/platform/modules/payments/payment_request.mojom-blink.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 namespace blink {
diff --git a/third_party/WebKit/Source/modules/payments/PaymentsValidators.h b/third_party/WebKit/Source/modules/payments/PaymentsValidators.h
index 100d799..21f3e8b 100644
--- a/third_party/WebKit/Source/modules/payments/PaymentsValidators.h
+++ b/third_party/WebKit/Source/modules/payments/PaymentsValidators.h
@@ -5,10 +5,10 @@
 #ifndef PaymentsValidators_h
 #define PaymentsValidators_h
 
-#include "components/payments/mojom/payment_request.mojom-blink.h"
 #include "modules/ModulesExport.h"
 #include "platform/wtf/Allocator.h"
 #include "platform/wtf/text/WTFString.h"
+#include "public/platform/modules/payments/payment_request.mojom-blink.h"
 
 namespace blink {
 
diff --git a/third_party/WebKit/Source/modules/serviceworkers/BUILD.gn b/third_party/WebKit/Source/modules/serviceworkers/BUILD.gn
index e06c648..109fc5f 100644
--- a/third_party/WebKit/Source/modules/serviceworkers/BUILD.gn
+++ b/third_party/WebKit/Source/modules/serviceworkers/BUILD.gn
@@ -44,6 +44,10 @@
     "ServiceWorkerGlobalScope.h",
     "ServiceWorkerGlobalScopeClient.cpp",
     "ServiceWorkerGlobalScopeClient.h",
+    "ServiceWorkerGlobalScopeClientImpl.cpp",
+    "ServiceWorkerGlobalScopeClientImpl.h",
+    "ServiceWorkerGlobalScopeProxy.cpp",
+    "ServiceWorkerGlobalScopeProxy.h",
     "ServiceWorkerLinkResource.cpp",
     "ServiceWorkerLinkResource.h",
     "ServiceWorkerRegistration.cpp",
diff --git a/third_party/WebKit/Source/modules/serviceworkers/DEPS b/third_party/WebKit/Source/modules/serviceworkers/DEPS
index 1421587..e85c09a 100644
--- a/third_party/WebKit/Source/modules/serviceworkers/DEPS
+++ b/third_party/WebKit/Source/modules/serviceworkers/DEPS
@@ -7,3 +7,14 @@
     "+modules/serviceworkers",
     "+mojo/public/cpp/system/data_pipe.h",
 ]
+
+specific_include_rules = {
+    "ServiceWorkerGlobalScopeProxy\.cpp": [
+        "+modules/background_fetch",
+        "+modules/background_sync",
+        "+modules/exported",
+        "+modules/notifications",
+        "+modules/payments",
+        "+modules/push_messaging",
+    ],
+}
diff --git a/third_party/WebKit/Source/web/ServiceWorkerGlobalScopeClientImpl.cpp b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerGlobalScopeClientImpl.cpp
similarity index 98%
rename from third_party/WebKit/Source/web/ServiceWorkerGlobalScopeClientImpl.cpp
rename to third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerGlobalScopeClientImpl.cpp
index b7d7dc8..ccaeaaa 100644
--- a/third_party/WebKit/Source/web/ServiceWorkerGlobalScopeClientImpl.cpp
+++ b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerGlobalScopeClientImpl.cpp
@@ -28,7 +28,7 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include "web/ServiceWorkerGlobalScopeClientImpl.h"
+#include "modules/serviceworkers/ServiceWorkerGlobalScopeClientImpl.h"
 
 #include <memory>
 #include <utility>
diff --git a/third_party/WebKit/Source/web/ServiceWorkerGlobalScopeClientImpl.h b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerGlobalScopeClientImpl.h
similarity index 99%
rename from third_party/WebKit/Source/web/ServiceWorkerGlobalScopeClientImpl.h
rename to third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerGlobalScopeClientImpl.h
index dd3174fb..2c26383 100644
--- a/third_party/WebKit/Source/web/ServiceWorkerGlobalScopeClientImpl.h
+++ b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerGlobalScopeClientImpl.h
@@ -31,10 +31,10 @@
 #ifndef ServiceWorkerGlobalScopeClientImpl_h
 #define ServiceWorkerGlobalScopeClientImpl_h
 
+#include <memory>
 #include "modules/serviceworkers/ServiceWorkerGlobalScopeClient.h"
 #include "public/platform/modules/serviceworker/WebServiceWorkerClientsInfo.h"
 #include "public/platform/modules/serviceworker/WebServiceWorkerSkipWaitingCallbacks.h"
-#include <memory>
 
 namespace blink {
 
diff --git a/third_party/WebKit/Source/web/ServiceWorkerGlobalScopeProxy.cpp b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerGlobalScopeProxy.cpp
similarity index 99%
rename from third_party/WebKit/Source/web/ServiceWorkerGlobalScopeProxy.cpp
rename to third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerGlobalScopeProxy.cpp
index 6d0ae20..45e215b 100644
--- a/third_party/WebKit/Source/web/ServiceWorkerGlobalScopeProxy.cpp
+++ b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerGlobalScopeProxy.cpp
@@ -28,7 +28,7 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include "web/ServiceWorkerGlobalScopeProxy.h"
+#include "modules/serviceworkers/ServiceWorkerGlobalScopeProxy.h"
 
 #include <memory>
 #include <utility>
@@ -50,6 +50,7 @@
 #include "modules/background_fetch/BackgroundFetchedEvent.h"
 #include "modules/background_fetch/BackgroundFetchedEventInit.h"
 #include "modules/background_sync/SyncEvent.h"
+#include "modules/exported/WebEmbeddedWorkerImpl.h"
 #include "modules/fetch/Headers.h"
 #include "modules/notifications/Notification.h"
 #include "modules/notifications/NotificationEvent.h"
@@ -80,7 +81,6 @@
 #include "public/platform/modules/serviceworker/WebServiceWorkerRequest.h"
 #include "public/web/WebSerializedScriptValue.h"
 #include "public/web/modules/serviceworker/WebServiceWorkerContextClient.h"
-#include "web/WebEmbeddedWorkerImpl.h"
 
 namespace blink {
 
diff --git a/third_party/WebKit/Source/web/ServiceWorkerGlobalScopeProxy.h b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerGlobalScopeProxy.h
similarity index 100%
rename from third_party/WebKit/Source/web/ServiceWorkerGlobalScopeProxy.h
rename to third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerGlobalScopeProxy.h
diff --git a/third_party/WebKit/Source/web/WebEmbeddedWorkerImplTest.cpp b/third_party/WebKit/Source/modules/serviceworkers/WebEmbeddedWorkerImplTest.cpp
similarity index 100%
rename from third_party/WebKit/Source/web/WebEmbeddedWorkerImplTest.cpp
rename to third_party/WebKit/Source/modules/serviceworkers/WebEmbeddedWorkerImplTest.cpp
diff --git a/third_party/WebKit/Source/platform/graphics/PaintGeneratedImage.cpp b/third_party/WebKit/Source/platform/graphics/PaintGeneratedImage.cpp
index a7fd314..5bdd82d6 100644
--- a/third_party/WebKit/Source/platform/graphics/PaintGeneratedImage.cpp
+++ b/third_party/WebKit/Source/platform/graphics/PaintGeneratedImage.cpp
@@ -23,7 +23,7 @@
     canvas->scale(dest_rect.Width() / src_rect.Width(),
                   dest_rect.Height() / src_rect.Height());
   canvas->translate(-src_rect.X(), -src_rect.Y());
-  SkRect bounds = SkRect::MakeWH(src_rect.Width(), src_rect.Height());
+  SkRect bounds = src_rect;
   canvas->saveLayer(&bounds, &flags);
   canvas->drawPicture(record_);
 }
diff --git a/third_party/WebKit/Source/platform/instrumentation/resource_coordinator/FrameResourceCoordinator.cpp b/third_party/WebKit/Source/platform/instrumentation/resource_coordinator/FrameResourceCoordinator.cpp
index becd3bf..2729c5f 100644
--- a/third_party/WebKit/Source/platform/instrumentation/resource_coordinator/FrameResourceCoordinator.cpp
+++ b/third_party/WebKit/Source/platform/instrumentation/resource_coordinator/FrameResourceCoordinator.cpp
@@ -43,6 +43,15 @@
 
 FrameResourceCoordinator::~FrameResourceCoordinator() = default;
 
+void FrameResourceCoordinator::SendEvent(
+    const resource_coordinator::mojom::blink::EventType& event_type) {
+  resource_coordinator::mojom::blink::EventPtr event =
+      resource_coordinator::mojom::blink::Event::New();
+  event->type = event_type;
+
+  service_->SendEvent(std::move(event));
+}
+
 DEFINE_TRACE(FrameResourceCoordinator) {}
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/instrumentation/resource_coordinator/FrameResourceCoordinator.h b/third_party/WebKit/Source/platform/instrumentation/resource_coordinator/FrameResourceCoordinator.h
index bbf37934..51e405dd 100644
--- a/third_party/WebKit/Source/platform/instrumentation/resource_coordinator/FrameResourceCoordinator.h
+++ b/third_party/WebKit/Source/platform/instrumentation/resource_coordinator/FrameResourceCoordinator.h
@@ -20,6 +20,7 @@
   static bool IsEnabled();
   static FrameResourceCoordinator* Create(InterfaceProvider*);
   virtual ~FrameResourceCoordinator();
+  void SendEvent(const resource_coordinator::mojom::blink::EventType&);
 
   DECLARE_TRACE();
 
diff --git a/third_party/WebKit/Source/web/BUILD.gn b/third_party/WebKit/Source/web/BUILD.gn
index 419aee9b..9053cf1 100644
--- a/third_party/WebKit/Source/web/BUILD.gn
+++ b/third_party/WebKit/Source/web/BUILD.gn
@@ -51,10 +51,6 @@
     "PageOverlay.h",
     "PrerendererClientImpl.cpp",
     "PrerendererClientImpl.h",
-    "ServiceWorkerGlobalScopeClientImpl.cpp",
-    "ServiceWorkerGlobalScopeClientImpl.h",
-    "ServiceWorkerGlobalScopeProxy.cpp",
-    "ServiceWorkerGlobalScopeProxy.h",
     "StorageClientImpl.cpp",
     "StorageClientImpl.h",
     "StorageQuotaClientImpl.cpp",
@@ -63,8 +59,6 @@
     "WebDevToolsAgentImpl.h",
     "WebDevToolsFrontendImpl.cpp",
     "WebDevToolsFrontendImpl.h",
-    "WebEmbeddedWorkerImpl.cpp",
-    "WebEmbeddedWorkerImpl.h",
     "WebExport.h",
     "WebFactoryImpl.cpp",
     "WebFactoryImpl.h",
@@ -153,7 +147,6 @@
     "WebAssociatedURLLoaderImplTest.cpp",
     "WebDragDataTest.cpp",
     "WebElementTest.cpp",
-    "WebEmbeddedWorkerImplTest.cpp",
     "WebNodeTest.cpp",
 
     # FIXME: Move the tests from web/tests/ to appropriate places.
diff --git a/third_party/WebKit/Source/web/LocalFrameClientImpl.cpp b/third_party/WebKit/Source/web/LocalFrameClientImpl.cpp
index 7bc97f1..b1e3d1e6c 100644
--- a/third_party/WebKit/Source/web/LocalFrameClientImpl.cpp
+++ b/third_party/WebKit/Source/web/LocalFrameClientImpl.cpp
@@ -212,6 +212,45 @@
 }
 
 void LocalFrameClientImpl::RunScriptsAtDocumentReady(bool document_is_empty) {
+  if (!document_is_empty && IsLoadedAsMHTMLArchive(web_frame_->GetFrame())) {
+    // For MHTML pages, recreate the shadow DOM contents from the templates that
+    // are captured from the shadow DOM trees at serialization.
+    // Note that the MHTML page is loaded in sandboxing mode with script
+    // execution disabled and thus only the following script will be executed.
+    // Any other scripts and event handlers outside the scope of the following
+    // script, including those that may be inserted in shadow DOM templates,
+    // will NOT be run.
+    String script = R"(
+function createShadowRootWithin(node) {
+  var nodes = node.querySelectorAll('template[shadowmode]');
+  for (var i = 0; i < nodes.length; ++i) {
+    var template = nodes[i];
+    var mode = template.getAttribute('shadowmode');
+    var parent = template.parentNode;
+    if (!parent)
+      continue;
+    parent.removeChild(template);
+    var shadowRoot;
+    if (mode == 'v0') {
+      shadowRoot = parent.createShadowRoot();
+    } else if (mode == 'open' || mode == 'closed') {
+      var delegatesFocus = template.hasAttribute('shadowdelegatesfocus');
+      shadowRoot = parent.attachShadow({'mode': mode,
+                                        'delegatesFocus': delegatesFocus});
+    }
+    if (!shadowRoot)
+      continue;
+    var clone = document.importNode(template.content, true);
+    shadowRoot.appendChild(clone);
+    createShadowRootWithin(shadowRoot);
+  }
+}
+createShadowRootWithin(document.body);
+)";
+    web_frame_->GetFrame()->GetScriptController().ExecuteScriptInMainWorld(
+        script, ScriptController::kExecuteScriptWhenScriptsDisabled);
+  }
+
   if (web_frame_->Client()) {
     web_frame_->Client()->RunScriptsAtDocumentReady(document_is_empty);
   }
diff --git a/third_party/WebKit/Source/web/WebFrameSerializer.cpp b/third_party/WebKit/Source/web/WebFrameSerializer.cpp
index 19e50a9..60af38cc 100644
--- a/third_party/WebKit/Source/web/WebFrameSerializer.cpp
+++ b/third_party/WebKit/Source/web/WebFrameSerializer.cpp
@@ -34,6 +34,7 @@
 #include "core/InputTypeNames.h"
 #include "core/dom/Document.h"
 #include "core/dom/Element.h"
+#include "core/dom/shadow/ElementShadow.h"
 #include "core/exported/WebRemoteFrameImpl.h"
 #include "core/frame/Frame.h"
 #include "core/frame/FrameSerializer.h"
@@ -81,13 +82,17 @@
 namespace {
 
 const int kPopupOverlayZIndexThreshold = 50;
+const char kShadowModeAttributeName[] = "shadowmode";
+const char kShadowDelegatesFocusAttributeName[] = "shadowdelegatesfocus";
 
 class MHTMLFrameSerializerDelegate final : public FrameSerializer::Delegate {
+  STACK_ALLOCATED();
   WTF_MAKE_NONCOPYABLE(MHTMLFrameSerializerDelegate);
 
  public:
-  explicit MHTMLFrameSerializerDelegate(
-      WebFrameSerializer::MHTMLPartsGenerationDelegate&);
+  MHTMLFrameSerializerDelegate(
+      WebFrameSerializer::MHTMLPartsGenerationDelegate&,
+      HeapHashSet<WeakMember<const Element>>&);
   ~MHTMLFrameSerializerDelegate() override;
   bool ShouldIgnoreElement(const Element&) override;
   bool ShouldIgnoreAttribute(const Element&, const Attribute&) override;
@@ -96,6 +101,7 @@
   bool ShouldSkipResource(
       FrameSerializer::ResourceHasCacheControlNoStoreHeader) override;
   Vector<Attribute> GetCustomAttributes(const Element&) override;
+  std::pair<Node*, Element*> GetAuxiliaryDOMTree(const Element&) const override;
   bool ShouldCollectProblemMetric() override;
 
  private:
@@ -108,12 +114,16 @@
                                                 Vector<Attribute>*);
 
   WebFrameSerializer::MHTMLPartsGenerationDelegate& web_delegate_;
+  HeapHashSet<WeakMember<const Element>>& shadow_template_elements_;
   bool popup_overlays_skipped_;
 };
 
 MHTMLFrameSerializerDelegate::MHTMLFrameSerializerDelegate(
-    WebFrameSerializer::MHTMLPartsGenerationDelegate& web_delegate)
-    : web_delegate_(web_delegate), popup_overlays_skipped_(false) {}
+    WebFrameSerializer::MHTMLPartsGenerationDelegate& web_delegate,
+    HeapHashSet<WeakMember<const Element>>& shadow_template_elements)
+    : web_delegate_(web_delegate),
+      shadow_template_elements_(shadow_template_elements),
+      popup_overlays_skipped_(false) {}
 
 MHTMLFrameSerializerDelegate::~MHTMLFrameSerializerDelegate() {
   if (web_delegate_.RemovePopupOverlay()) {
@@ -204,6 +214,16 @@
     return true;
   }
 
+  // The special attribute in a template element to denote the shadow DOM
+  // should only be generated from MHTML serialization. If it is found in the
+  // original page, it should be ignored.
+  if (isHTMLTemplateElement(element) &&
+      (attribute.LocalName() == kShadowModeAttributeName ||
+       attribute.LocalName() == kShadowDelegatesFocusAttributeName) &&
+      !shadow_template_elements_.Contains(&element)) {
+    return true;
+  }
+
   // If srcdoc attribute for frame elements will be rewritten as src attribute
   // containing link instead of html contents, don't ignore the attribute.
   // Bail out now to avoid the check in Element::isScriptingAttribute.
@@ -335,6 +355,48 @@
   attributes->push_back(disabled_attribute);
 }
 
+std::pair<Node*, Element*> MHTMLFrameSerializerDelegate::GetAuxiliaryDOMTree(
+    const Element& element) const {
+  const ElementShadow* shadow = element.Shadow();
+  if (!shadow)
+    return std::pair<Node*, Element*>();
+  ShadowRoot& shadow_root = shadow->OldestShadowRoot();
+
+  String shadow_mode;
+  switch (shadow_root.GetType()) {
+    case ShadowRootType::kUserAgent:
+      // No need to serialize.
+      return std::pair<Node*, Element*>();
+    case ShadowRootType::V0:
+      shadow_mode = "v0";
+      break;
+    case ShadowRootType::kOpen:
+      shadow_mode = "open";
+      break;
+    case ShadowRootType::kClosed:
+      shadow_mode = "closed";
+      break;
+  }
+
+  // Put the shadow DOM content inside a template element. A special attribute
+  // is set to tell the mode of the shadow DOM.
+  Element* template_element =
+      Element::Create(HTMLNames::templateTag, &(element.GetDocument()));
+  template_element->setAttribute(
+      QualifiedName(g_null_atom, kShadowModeAttributeName, g_null_atom),
+      AtomicString(shadow_mode));
+  if (shadow_root.GetType() != ShadowRootType::V0 &&
+      shadow_root.delegatesFocus()) {
+    template_element->setAttribute(
+        QualifiedName(g_null_atom, kShadowDelegatesFocusAttributeName,
+                      g_null_atom),
+        g_empty_atom);
+  }
+  shadow_template_elements_.insert(template_element);
+
+  return std::pair<Node*, Element*>(&shadow_root, template_element);
+}
+
 bool CacheControlNoStoreHeaderPresent(
     const WebLocalFrameBase& web_local_frame) {
   const ResourceResponse& response =
@@ -420,7 +482,9 @@
   {
     SCOPED_BLINK_UMA_HISTOGRAM_TIMER(
         "PageSerialization.MhtmlGeneration.SerializationTime.SingleFrame");
-    MHTMLFrameSerializerDelegate core_delegate(*web_delegate);
+    HeapHashSet<WeakMember<const Element>> shadow_template_elements;
+    MHTMLFrameSerializerDelegate core_delegate(*web_delegate,
+                                               shadow_template_elements);
     FrameSerializer serializer(resources, core_delegate);
     serializer.SerializeFrame(*frame);
   }
diff --git a/third_party/WebKit/Source/web/tests/MHTMLTest.cpp b/third_party/WebKit/Source/web/tests/MHTMLTest.cpp
index d8ef1b2..ce9a833 100644
--- a/third_party/WebKit/Source/web/tests/MHTMLTest.cpp
+++ b/third_party/WebKit/Source/web/tests/MHTMLTest.cpp
@@ -29,6 +29,8 @@
  */
 
 #include "core/dom/Document.h"
+#include "core/dom/Element.h"
+#include "core/dom/shadow/ElementShadow.h"
 #include "core/frame/FrameTestHelpers.h"
 #include "core/frame/LocalFrame.h"
 #include "core/frame/Location.h"
@@ -277,7 +279,7 @@
   const char kURL[] = "http://www.example.com";
 
   // Register the mocked frame and load it.
-  RegisterMockedURLLoad(kURL, "simple_test.mht");
+  RegisterMockedURLLoad(kURL, "page_with_javascript.mht");
   LoadURLInTopFrame(ToKURL(kURL));
   ASSERT_TRUE(GetPage());
   LocalFrame* frame = ToLocalFrame(GetPage()->MainFrame());
@@ -295,6 +297,43 @@
   EXPECT_TRUE(document->GetSecurityOrigin()->IsUnique());
   // Script execution should be disabled.
   EXPECT_FALSE(document->CanExecuteScripts(kNotAboutToExecuteScript));
+
+  // The element to be created by the script is not there.
+  EXPECT_FALSE(document->getElementById("mySpan"));
+}
+
+TEST_F(MHTMLTest, ShadowDom) {
+  const char kURL[] = "http://www.example.com";
+
+  // Register the mocked frame and load it.
+  RegisterMockedURLLoad(kURL, "shadow.mht");
+  LoadURLInTopFrame(ToKURL(kURL));
+  ASSERT_TRUE(GetPage());
+  LocalFrame* frame = ToLocalFrame(GetPage()->MainFrame());
+  ASSERT_TRUE(frame);
+  Document* document = frame->GetDocument();
+  ASSERT_TRUE(document);
+
+  EXPECT_TRUE(IsShadowHost(document->getElementById("h1")));
+  EXPECT_TRUE(IsShadowHost(document->getElementById("h2")));
+  // The nested shadow DOM tree is created.
+  EXPECT_TRUE(IsShadowHost(document->getElementById("h2")
+                               ->Shadow()
+                               ->OldestShadowRoot()
+                               .getElementById("h3")));
+
+  EXPECT_TRUE(IsShadowHost(document->getElementById("h4")));
+  // The static element in the shadow dom template is found.
+  EXPECT_TRUE(document->getElementById("h4")
+                  ->Shadow()
+                  ->OldestShadowRoot()
+                  .getElementById("s1"));
+  // The element to be created by the script in the shadow dom template is
+  // not found because the script is blocked.
+  EXPECT_FALSE(document->getElementById("h4")
+                   ->Shadow()
+                   ->OldestShadowRoot()
+                   .getElementById("s2"));
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/web/tests/WebFrameSerializerSanitizationTest.cpp b/third_party/WebKit/Source/web/tests/WebFrameSerializerSanitizationTest.cpp
index 52bf13d..040c8aeb95 100644
--- a/third_party/WebKit/Source/web/tests/WebFrameSerializerSanitizationTest.cpp
+++ b/third_party/WebKit/Source/web/tests/WebFrameSerializerSanitizationTest.cpp
@@ -102,21 +102,26 @@
   }
 
   String GenerateMHTMLFromHtml(const String& url, const String& file_name) {
-    return GenerateMHTML(url, file_name, "text/html", false);
+    LoadFrame(url, file_name, "text/html");
+    return GenerateMHTML(false);
   }
 
   String GenerateMHTMLPartsFromPng(const String& url, const String& file_name) {
-    return GenerateMHTML(url, file_name, "image/png", true);
+    LoadFrame(url, file_name, "image/png");
+    return GenerateMHTML(true);
   }
 
-  String GenerateMHTML(const String& url,
-                       const String& file_name,
-                       const String& mime_type,
-                       const bool only_body_parts) {
+  void LoadFrame(const String& url,
+                 const String& file_name,
+                 const String& mime_type) {
     KURL parsed_url(kParsedURLString, url);
     String file_path("frameserialization/" + file_name);
     RegisterMockedFileURLLoad(parsed_url, file_path, mime_type);
     FrameTestHelpers::LoadFrame(MainFrameImpl(), url.Utf8().data());
+    MainFrameImpl()->GetFrame()->View()->UpdateAllLifecyclePhases();
+  }
+
+  String GenerateMHTML(const bool only_body_parts) {
     // Boundaries are normally randomly generated but this one is predefined for
     // simplicity and as good as any other. Plus it gets used in almost all the
     // examples in the MHTML spec - RFC 2557.
@@ -150,6 +155,21 @@
     return mhtml_string;
   }
 
+  ShadowRoot* SetShadowContent(TreeScope& scope,
+                               const char* host,
+                               ShadowRootType shadow_type,
+                               const char* shadow_content,
+                               bool delegates_focus = false) {
+    ShadowRoot* shadow_root =
+        scope.getElementById(AtomicString::FromUTF8(host))
+            ->CreateShadowRootInternal(shadow_type, ASSERT_NO_EXCEPTION);
+    shadow_root->SetDelegatesFocus(delegates_focus);
+    shadow_root->setInnerHTML(String::FromUTF8(shadow_content),
+                              ASSERT_NO_EXCEPTION);
+    scope.GetDocument().View()->UpdateAllLifecyclePhases();
+    return shadow_root;
+  }
+
   void SetRemovePopupOverlay(bool remove_popup_overlay) {
     mhtml_delegate_.SetRemovePopupOverlay(remove_popup_overlay);
   }
@@ -347,4 +367,25 @@
   EXPECT_EQ(WTF::kNotFound, mhtml.Find("<option"));
 }
 
+TEST_F(WebFrameSerializerSanitizationTest, ShadowDOM) {
+  LoadFrame("http://www.test.com", "shadow_dom.html", "text/html");
+  Document* document = MainFrameImpl()->GetFrame()->GetDocument();
+  SetShadowContent(*document, "h1", ShadowRootType::V0, "V0 shadow");
+  ShadowRoot* shadowRoot =
+      SetShadowContent(*document, "h2", ShadowRootType::kOpen,
+                       "Parent shadow\n<p id=\"h3\">Foo</p>", true);
+  SetShadowContent(*shadowRoot, "h3", ShadowRootType::kClosed, "Nested shadow");
+  String mhtml = GenerateMHTML(false);
+
+  // Template with special attribute should be created for each shadow DOM tree.
+  EXPECT_NE(WTF::kNotFound, mhtml.Find("<template shadowmode=3D\"v0\">"));
+  EXPECT_NE(WTF::kNotFound,
+            mhtml.Find("<template shadowmode=3D\"open\" shadowdelegatesfocus"));
+  EXPECT_NE(WTF::kNotFound, mhtml.Find("<template shadowmode=3D\"closed\">"));
+
+  // The special attribute present in the original page should be removed.
+  EXPECT_EQ(WTF::kNotFound, mhtml.Find("shadowmode=3D\"foo\">"));
+  EXPECT_EQ(WTF::kNotFound, mhtml.Find("shadowdelegatesfocus=3D\"bar\">"));
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/web/tests/WebViewTest.cpp b/third_party/WebKit/Source/web/tests/WebViewTest.cpp
index 91ad8d7..fd27942 100644
--- a/third_party/WebKit/Source/web/tests/WebViewTest.cpp
+++ b/third_party/WebKit/Source/web/tests/WebViewTest.cpp
@@ -30,7 +30,9 @@
 
 #include "public/web/WebView.h"
 
+#include <limits>
 #include <memory>
+#include <string>
 
 #include "bindings/core/v8/V8Document.h"
 #include "core/dom/Document.h"
@@ -3675,7 +3677,7 @@
   RegisterMockedHttpURLLoad("add_frame_in_unload.html");
   web_view_helper_.InitializeAndLoad(base_url_ + "add_frame_in_unload.html",
                                      true, &frame_client);
-  web_view_helper_.WebView()->MainFrame()->DispatchUnloadEvent();
+  web_view_helper_.WebView()->MainFrameImpl()->DispatchUnloadEvent();
   EXPECT_EQ(0, frame_client.Count());
   web_view_helper_.Reset();
 }
diff --git a/third_party/WebKit/Source/web/tests/data/frameserialization/shadow_dom.html b/third_party/WebKit/Source/web/tests/data/frameserialization/shadow_dom.html
new file mode 100644
index 0000000..d1a9caf
--- /dev/null
+++ b/third_party/WebKit/Source/web/tests/data/frameserialization/shadow_dom.html
@@ -0,0 +1,18 @@
+<html>
+<head>
+<meta charset="utf8">
+<title>Test Shadow DOM</title>
+</head>
+<body>
+<p id="h1">
+  Foo
+  <span>Bar</span>
+</p>
+<div id="h2">
+  <span>Bar</span>
+</div>
+<template shadowmode="foo" shadowdelegatesfocus="bar">
+  To be removed
+</template>
+</body>
+</html>
diff --git a/third_party/WebKit/Source/web/tests/data/mhtml/.gitattributes b/third_party/WebKit/Source/web/tests/data/mhtml/.gitattributes
new file mode 100644
index 0000000..a9ebe50e
--- /dev/null
+++ b/third_party/WebKit/Source/web/tests/data/mhtml/.gitattributes
@@ -0,0 +1,6 @@
+# Set the default behavior, in case people don't have core.autocrlf set.
+* text=auto
+
+# MHTML files in general should only have CRLF line endings
+# (see RFC2557, section 10).
+*.mht text eol=crlf
diff --git a/third_party/WebKit/Source/web/tests/data/mhtml/page_with_javascript.mht b/third_party/WebKit/Source/web/tests/data/mhtml/page_with_javascript.mht
new file mode 100644
index 0000000..c75ecc5
--- /dev/null
+++ b/third_party/WebKit/Source/web/tests/data/mhtml/page_with_javascript.mht
@@ -0,0 +1,29 @@
+From: <Saved by UnMHT>
+Subject: =?iso-2022-jp?B?QSBzaW1wbGUgcGFnZQ==?=
+Date: Wed, May 11 2011 15:36:36 GMT-0700
+MIME-Version: 1.0
+Content-Type: multipart/related;
+	boundary="----=_NextPart_000_0000_87206557.D2C008B0";
+	type="text/html"
+
+------=_NextPart_000_0000_87206557.D2C008B0
+Content-Type: text/html; charset="ISO-8859-1"
+Content-Transfer-Encoding: quoted-printable
+Content-Location: http://localhost/page_with_javascript.html
+
+<html><head><meta http-equiv=3D"Content-Type" content=3D"text/html; charset=
+=3Diso-8859-1">
+
+
+<title>A page with JavaScript</title>
+<h1>This page has content generated by JavaScript</h1>
+Javascript is not run, so that dynamic content should not show.
+<div id=3D"myDiv"></div>
+
+<script>
+ document.getElementById('myDiv').innerHTML =3D
+      "<span id='mySpan'>Foo</span>";
+</script>
+
+</body></html>
+------=_NextPart_000_0000_87206557.D2C008B0--
diff --git a/third_party/WebKit/Source/web/tests/data/mhtml/shadow.mht b/third_party/WebKit/Source/web/tests/data/mhtml/shadow.mht
new file mode 100644
index 0000000..8669bcd
--- /dev/null
+++ b/third_party/WebKit/Source/web/tests/data/mhtml/shadow.mht
@@ -0,0 +1,40 @@
+From: <Saved by UnMHT>
+Subject: =?iso-2022-jp?B?QSBzaW1wbGUgcGFnZQ==?=
+Date: Wed, May 11 2011 15:36:36 GMT-0700
+MIME-Version: 1.0
+Content-Type: multipart/related;
+	boundary="----=_NextPart_000_0000_87206557.D2C008B0";
+	type="text/html"
+
+------=_NextPart_000_0000_87206557.D2C008B0
+Content-Type: text/html; charset="ISO-8859-1"
+Content-Transfer-Encoding: quoted-printable
+Content-Location: http://localhost/shadow.html
+
+<html><head><meta http-equiv=3D"Content-Type" content=3D"text/html; charset=
+=3Diso-8859-1">
+
+
+<title>A page with shadow DOM content</title>
+<h1>This page has shadow DOM content</h1>
+
+<p id=3D"h1">
+  <template shadowmode=3D"v0">V0</template>
+</p>
+<div id=3D"h2">
+  <template shadowmode=3D"open" shadowdelegatesfocus=3D"">Parent
+    <p id=3D"h3">
+      <template shadowmode=3D"closed">Nested</template>
+    </p>
+  </template>
+</div>
+<div id=3D"h4">
+  <template shadowmode=3D"open">
+    <span id='s1'>Foo</span>
+    <script>document.getElementById('s1').innerHTML=3D"<span id='s2'>Bar</span>";
+    </script>
+  </template>
+</div>
+
+</body></html>
+------=_NextPart_000_0000_87206557.D2C008B0--
diff --git a/third_party/WebKit/public/BUILD.gn b/third_party/WebKit/public/BUILD.gn
index 2d7b5f1..bbc508e8 100644
--- a/third_party/WebKit/public/BUILD.gn
+++ b/third_party/WebKit/public/BUILD.gn
@@ -701,7 +701,10 @@
 }
 
 mojom("mojo_bindings") {
-  visibility = [ "//content/*" ]
+  visibility = [
+    "//components/payments/content/*",
+    "//content/*",
+  ]
   visibility_blink = [
     "//content/common:mojo_bindings_blink",
     "//third_party/WebKit/Source/platform",
@@ -722,6 +725,7 @@
     "platform/modules/manifest/manifest_manager.mojom",
     "platform/modules/notifications/notification.mojom",
     "platform/modules/notifications/notification_service.mojom",
+    "platform/modules/payments/payment_app.mojom",
     "platform/modules/permissions/permission.mojom",
     "platform/modules/permissions/permission_status.mojom",
     "platform/modules/presentation/presentation.mojom",
@@ -775,10 +779,10 @@
     "platform/modules/installedapp/installed_app_provider.mojom",
     "platform/modules/installedapp/related_application.mojom",
     "platform/modules/mediasession/media_session.mojom",
+    "platform/modules/payments/payment_request.mojom",
     "platform/modules/webshare/webshare.mojom",
   ]
   public_deps = [
-    "//components/payments/mojom",
     "//mojo/common:common_custom_types",
     "//ui/gfx/geometry/mojo",
     "//url/mojo:url_mojom_gurl",
diff --git a/third_party/WebKit/public/platform/WebContentLayer.h b/third_party/WebKit/public/platform/WebContentLayer.h
index 3999c45..eedd739 100644
--- a/third_party/WebKit/public/platform/WebContentLayer.h
+++ b/third_party/WebKit/public/platform/WebContentLayer.h
@@ -42,7 +42,8 @@
   // origin aligned to local layer space, optionally with a uniform scale.
   // With this flag set to true, the compositor may raster contents in any
   // space, e.g. device pixel space.
-  virtual void SetAllowTransformedRasterization(bool) = 0;
+  virtual void SetTransformedRasterizationAllowed(bool) = 0;
+  virtual bool TransformedRasterizationAllowed() const = 0;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/public/platform/modules/payments/OWNERS b/third_party/WebKit/public/platform/modules/payments/OWNERS
index 05fdad3..4dd83e9 100644
--- a/third_party/WebKit/public/platform/modules/payments/OWNERS
+++ b/third_party/WebKit/public/platform/modules/payments/OWNERS
@@ -1,4 +1,9 @@
 jinho.bang@samsung.com
+mathp@chromium.org
+mek@chromium.org
 rouslan@chromium.org
 
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
+
 # COMPONENT: Blink>Payments
diff --git a/components/payments/mojom/payment_app.mojom b/third_party/WebKit/public/platform/modules/payments/payment_app.mojom
similarity index 96%
rename from components/payments/mojom/payment_app.mojom
rename to third_party/WebKit/public/platform/modules/payments/payment_app.mojom
index 6cb1980..e43a88d 100644
--- a/components/payments/mojom/payment_app.mojom
+++ b/third_party/WebKit/public/platform/modules/payments/payment_app.mojom
@@ -4,8 +4,8 @@
 
 module payments.mojom;
 
-import "components/payments/mojom/payment_request.mojom";
 import "mojo/common/time.mojom";
+import "third_party/WebKit/public/platform/modules/payments/payment_request.mojom";
 import "url/mojo/url.mojom";
 
 enum PaymentHandlerStatus {
diff --git a/components/payments/mojom/payment_request.mojom b/third_party/WebKit/public/platform/modules/payments/payment_request.mojom
similarity index 100%
rename from components/payments/mojom/payment_request.mojom
rename to third_party/WebKit/public/platform/modules/payments/payment_request.mojom
diff --git a/third_party/WebKit/public/web/WebFrame.h b/third_party/WebKit/public/web/WebFrame.h
index e2b93f6..3db02d3e 100644
--- a/third_party/WebKit/public/web/WebFrame.h
+++ b/third_party/WebKit/public/web/WebFrame.h
@@ -200,11 +200,6 @@
 
   virtual WebPerformance Performance() const = 0;
 
-  // Closing -------------------------------------------------------------
-
-  // Runs unload handlers for this frame.
-  virtual void DispatchUnloadEvent() = 0;
-
   // Scripting ----------------------------------------------------------
 
   // Returns the global proxy object.
diff --git a/third_party/WebKit/public/web/WebLocalFrame.h b/third_party/WebKit/public/web/WebLocalFrame.h
index 557cb8f..0eeb585 100644
--- a/third_party/WebKit/public/web/WebLocalFrame.h
+++ b/third_party/WebKit/public/web/WebLocalFrame.h
@@ -5,7 +5,9 @@
 #ifndef WebLocalFrame_h
 #define WebLocalFrame_h
 
+#include <memory>
 #include <set>
+
 #include "WebCompositionUnderline.h"
 #include "WebFrame.h"
 #include "WebFrameLoadType.h"
@@ -60,7 +62,7 @@
 // FIXME: Move lots of methods from WebFrame in here.
 class WebLocalFrame : public WebFrame {
  public:
-  // Creates a WebFrame. Delete this WebFrame by calling WebFrame::close().
+  // Creates a WebFrame. Delete this WebFrame by calling WebFrame::Close().
   // WebFrameClient may not be null.
   BLINK_EXPORT static WebLocalFrame* Create(WebTreeScopeType,
                                             WebFrameClient*,
@@ -123,6 +125,11 @@
   virtual void SetSharedWorkerRepositoryClient(
       WebSharedWorkerRepositoryClient*) = 0;
 
+  // Closing -------------------------------------------------------------
+
+  // Runs unload handlers for this frame.
+  virtual void DispatchUnloadEvent() = 0;
+
   // Basic properties ---------------------------------------------------
 
   // The urls of the given combination types of favicon (if any) specified by
diff --git a/tools/md_browser/md_browser.py b/tools/md_browser/md_browser.py
index ee9d9e2..e061333 100755
--- a/tools/md_browser/md_browser.py
+++ b/tools/md_browser/md_browser.py
@@ -31,15 +31,22 @@
   parser.add_argument('-p', '--port', type=int, default=8080,
                       help='port to run on (default = %(default)s)')
   parser.add_argument('-d', '--directory', type=str, default=SRC_DIR)
+  parser.add_argument('-e', '--external', action='store_true',
+                      help='whether to bind to external port')
   parser.add_argument('file', nargs='?',
                       help='open file in browser')
   args = parser.parse_args(argv)
 
   top_level = os.path.realpath(args.directory)
-  server_address = ('localhost', args.port)
+  hostname = '0.0.0.0' if args.external else 'localhost'
+  server_address = (hostname, args.port)
   s = Server(server_address, top_level)
 
-  print('Listening on http://%s:%s/' % server_address)
+  origin = 'http://' + hostname
+  if args.port != 80:
+    origin += ':%s' % args.port
+  print('Listening on %s/' % origin)
+
   thread = None
   if args.file:
     path = os.path.realpath(args.file)
@@ -47,15 +54,15 @@
       print('%s is not under %s' % (args.file, args.directory))
       return 1
     rpath = os.path.relpath(path, top_level)
-    url = 'http://localhost:%d/%s' % (args.port, rpath)
+    url = '%s/%s' % (origin, rpath)
     print('Opening %s' % url)
     thread = threading.Thread(target=_open_url, args=(url,))
     thread.start()
 
   elif os.path.isfile(os.path.join(top_level, 'docs', 'README.md')):
-    print(' Try loading http://localhost:%d/docs/README.md' % args.port)
+    print(' Try loading %s/docs/README.md' % origin)
   elif os.path.isfile(os.path.join(args.directory, 'README.md')):
-    print(' Try loading http://localhost:%d/README.md' % args.port)
+    print(' Try loading %s/README.md' % origin)
 
   retcode = 1
   try:
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index da75e4e..3fc7b7b 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -90209,7 +90209,14 @@
   <affected-histogram
       name="ServiceWorker.ActivatedWorkerPreparationForMainFrame.Time"/>
   <affected-histogram
-      name="ServiceWorker.ActivatedWorkerPreparationForMainFrame.Time_StartWorkerExistingProcess"/>
+      name="ServiceWorker.ActivatedWorkerPreparationForMainFrame.Time_StartWorkerExistingProcess">
+    <obsolete>
+      Deprecated as of June 2017, in favor of
+      ServiceWorker.ActivatedWorkerPreparationForMainFrame.Time_WorkerStartOccurred_NavigationPreloadEnabled.
+    </obsolete>
+  </affected-histogram>
+  <affected-histogram
+      name="ServiceWorker.ActivatedWorkerPreparationForMainFrame.Time_WorkerStartOccurred"/>
   <suffix name="NavigationPreloadEnabled" label="Navigation preload occurred."/>
   <affected-histogram
       name="ServiceWorker.ActivatedWorkerPreparationForMainFrame.Type"/>
@@ -94903,6 +94910,17 @@
   <affected-histogram name="ServiceWorker.EventDispatchingDelay_UNKNOWN"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="ServiceWorkerStartOccurred" separator="_">
+  <suffix name="WorkerStartOccurred"
+      label="The worker was not already running. Worker startup occurred."/>
+  <affected-histogram
+      name="ServiceWorker.ActivatedWorkerPreparationForMainFrame.Time"/>
+  <affected-histogram name="ServiceWorker.NavPreload.ConcurrentTime_MainFrame"/>
+  <affected-histogram name="ServiceWorker.NavPreload.FinishedFirst_MainFrame"/>
+  <affected-histogram name="ServiceWorker.NavPreload.ResponseTime_MainFrame"/>
+  <affected-histogram name="ServiceWorker.NavPreload.WorkerWaitTime_MainFrame"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="SessionRestoreTabCountMemoryPressure" separator="_">
   <suffix name="MemoryPressure"
       label="Total tabs involved in session restore that encountered memory
@@ -95312,6 +95330,9 @@
 </histogram_suffixes>
 
 <histogram_suffixes name="StartWorkerExistingProcess2" separator="_">
+  <obsolete>
+    Deprecated as of June 2017.
+  </obsolete>
   <suffix name="StartWorkerExistingProcess"
       label="The worker started up in an existing process"/>
   <affected-histogram name="ServiceWorker.NavPreload.ConcurrentTime_MainFrame"/>
diff --git a/tools/perf/benchmarks/blink_perf_unittest.py b/tools/perf/benchmarks/blink_perf_unittest.py
index b5cb03ab..67e506b 100644
--- a/tools/perf/benchmarks/blink_perf_unittest.py
+++ b/tools/perf/benchmarks/blink_perf_unittest.py
@@ -34,7 +34,7 @@
         serving_dirs={self._BLINK_PERF_TEST_DATA_DIR,
                       self._BLINK_PERF_RESOURCES_DIR})
     page = page_module.Page('file://' + test_file_name, story_set,
-        base_dir=story_set.base_dir)
+        base_dir=story_set.base_dir, name=test_file_name)
     story_set.AddStory(page)
     return story_set
 
diff --git a/tools/perf/contrib/cluster_telemetry/page_set.py b/tools/perf/contrib/cluster_telemetry/page_set.py
index d780a37..c92247a3 100644
--- a/tools/perf/contrib/cluster_telemetry/page_set.py
+++ b/tools/perf/contrib/cluster_telemetry/page_set.py
@@ -17,7 +17,8 @@
         url=url,
         page_set=page_set,
         shared_page_state_class=shared_page_state_class,
-        traffic_setting=traffic_setting)
+        traffic_setting=traffic_setting,
+        name=url)
     self.archive_data_file = archive_data_file
     self._run_page_interaction_callback = run_page_interaction_callback
 
diff --git a/tools/perf/contrib/cluster_telemetry/screenshot.py b/tools/perf/contrib/cluster_telemetry/screenshot.py
new file mode 100644
index 0000000..e5cc11d
--- /dev/null
+++ b/tools/perf/contrib/cluster_telemetry/screenshot.py
@@ -0,0 +1,56 @@
+# 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 logging
+import os
+import py_utils
+import time
+
+from telemetry.page import legacy_page_test
+from telemetry.util import image_util
+
+class Screenshot(legacy_page_test.LegacyPageTest):
+  """Takes a PNG screenshot of the page."""
+
+  def __init__(self, png_outdir, wait_time=0):
+    super(Screenshot, self).__init__()
+    self._png_outdir = png_outdir
+    self._wait_time = wait_time
+
+  def ValidateAndMeasurePage(self, page, tab, results):
+    if not tab.screenshot_supported:
+      raise legacy_page_test.MeasurementFailure(
+        'Screenshotting not supported on this platform')
+
+    try:
+      tab.WaitForDocumentReadyStateToBeComplete()
+    except py_utils.TimeoutException:
+      logging.warning("WaitForDocumentReadyStateToBeComplete() timeout, " +
+                      "page: %s", page.display_name)
+      return
+
+    time.sleep(self._wait_time)
+
+    if not os.path.exists(self._png_outdir):
+      logging.info("Creating directory %s", self._png_outdir)
+      try:
+        os.makedirs(self._png_outdir)
+      except OSError:
+        logging.warning("Directory %s could not be created", self._png_outdir)
+        raise
+
+    outpath = os.path.abspath(
+        os.path.join(self._png_outdir, page.file_safe_name)) + '.png'
+    # Replace win32 path separator char '\' with '\\'.
+    outpath = outpath.replace('\\', '\\\\')
+
+    screenshot = tab.Screenshot()
+
+    # TODO(lchoi): Add logging to image_util.py and/or augment error handling of
+    # image_util.WritePngFile
+    logging.info("Writing PNG file to %s. This may take awhile.", outpath)
+    start = time.time()
+    image_util.WritePngFile(screenshot, outpath)
+    logging.info("PNG file written successfully. (Took %f seconds)",
+                 time.time()-start)
diff --git a/tools/perf/contrib/cluster_telemetry/screenshot_ct.py b/tools/perf/contrib/cluster_telemetry/screenshot_ct.py
new file mode 100644
index 0000000..329a3d8
--- /dev/null
+++ b/tools/perf/contrib/cluster_telemetry/screenshot_ct.py
@@ -0,0 +1,42 @@
+# 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.
+
+from core import perf_benchmark
+
+from contrib.cluster_telemetry import ct_benchmarks_util
+from contrib.cluster_telemetry import page_set
+from contrib.cluster_telemetry import repaint_helpers
+from contrib.cluster_telemetry import screenshot
+
+class ScreenshotCT(perf_benchmark.PerfBenchmark):
+  """Captures PNG screenshots of web pages for Cluster Telemetry. Screenshots
+     written to local file with path-safe urls of pages as filenames. Cluster
+     Telemetry is then used for aggregation and analysis."""
+
+  @classmethod
+  def Name(cls):
+    return 'screenshot_ct'
+
+  @classmethod
+  def AddBenchmarkCommandLineArgs(cls, parser):
+    ct_benchmarks_util.AddBenchmarkCommandLineArgs(parser)
+    parser.add_option('--png-outdir', type='string',
+                      default=None,
+                      help='Output directory for the PNG files')
+    parser.add_option('--wait-time', type='float', default=0,
+                      help='Wait time before the benchmark is started')
+
+  @classmethod
+  def ProcessCommandLineArgs(cls, parser, args):
+    ct_benchmarks_util.ValidateCommandLineArgs(parser, args)
+    if not args.png_outdir:
+      parser.error('Please specify --png-outdir')
+
+  def CreatePageTest(self, options):
+    return screenshot.Screenshot(options.png_outdir)
+
+  def CreateStorySet(self, options):
+    return page_set.CTPageSet(
+        options.urls_list, options.user_agent, options.archive_data_file,
+        run_page_interaction_callback=repaint_helpers.WaitThenRepaint)
diff --git a/tools/perf/contrib/cluster_telemetry/screenshot_unittest.py b/tools/perf/contrib/cluster_telemetry/screenshot_unittest.py
new file mode 100644
index 0000000..7eef0b3f
--- /dev/null
+++ b/tools/perf/contrib/cluster_telemetry/screenshot_unittest.py
@@ -0,0 +1,41 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import shutil
+import tempfile
+
+from telemetry import decorators
+from telemetry.testing import options_for_unittests
+from telemetry.testing import page_test_test_case
+from telemetry.util import image_util
+from contrib.cluster_telemetry import screenshot
+
+class ScreenshotUnitTest(page_test_test_case.PageTestTestCase):
+
+  def setUp(self):
+    self._options = options_for_unittests.GetCopy()
+    self._png_outdir = tempfile.mkdtemp('_png_test')
+
+  def tearDown(self):
+    shutil.rmtree(self._png_outdir)
+
+  @decorators.Enabled('linux')
+  def testScreenshot(self):
+    # Screenshots for Cluster Telemetry purposes currently only supported on
+    # Linux platform.
+    page_set = self.CreateStorySetFromFileInUnittestDataDir(
+      'screenshot_test.html')
+    measurement = screenshot.Screenshot(self._png_outdir)
+    self.RunMeasurement(measurement, page_set, options=self._options)
+
+    path = self._png_outdir + '/' + page_set.stories[0].file_safe_name + '.png'
+    self.assertTrue(os.path.exists(path))
+    self.assertTrue(os.path.isfile(path))
+    self.assertTrue(os.access(path, os.R_OK))
+
+    image = image_util.FromPngFile(path)
+    screenshot_pixels = image_util.Pixels(image)
+    special_colored_pixel = bytearray([217, 115, 43])
+    self.assertTrue(special_colored_pixel in screenshot_pixels)
diff --git a/tools/perf/contrib/oilpan/oilpan_gc_times_unittest.py b/tools/perf/contrib/oilpan/oilpan_gc_times_unittest.py
index 5d02e645..0fe12c8e 100644
--- a/tools/perf/contrib/oilpan/oilpan_gc_times_unittest.py
+++ b/tools/perf/contrib/oilpan/oilpan_gc_times_unittest.py
@@ -18,7 +18,7 @@
 
   def __init__(self, page_set):
     super(TestOilpanGCTimesPage, self).__init__(
-        'file://blank.html', page_set, page_set.base_dir)
+        'file://blank.html', page_set, page_set.base_dir, name='blank.html')
 
   def RunPageInteractions(self, action_runner):
     with action_runner.CreateGestureInteraction('ScrollAction'):
diff --git a/tools/perf/core/benchmark_sharding_map.json b/tools/perf/core/benchmark_sharding_map.json
index 3a5f6d0..b78b77f 100644
--- a/tools/perf/core/benchmark_sharding_map.json
+++ b/tools/perf/core/benchmark_sharding_map.json
@@ -459,6 +459,376 @@
       ]
     }
   },
+  "Android Nexus5X WebView Perf": {
+    "build164-b1--device1": {
+      "benchmarks": [
+        "smoothness.key_silk_cases",
+        "system_health.memory_desktop",
+        "system_health.memory_mobile"
+      ]
+    },
+    "build164-b1--device2": {
+      "benchmarks": [
+        "loading.desktop",
+        "smoothness.simple_mobile_sites",
+        "v8.browsing_desktop_classic"
+      ]
+    },
+    "build164-b1--device3": {
+      "benchmarks": [
+        "system_health.common_desktop",
+        "v8.runtimestats.browsing_mobile_classic"
+      ]
+    },
+    "build164-b1--device4": {
+      "benchmarks": [
+        "smoothness.tough_animation_cases",
+        "v8.infinite_scroll-turbo_tbmv2"
+      ]
+    },
+    "build164-b1--device5": {
+      "benchmarks": [
+        "battor.trivial_pages",
+        "v8.browsing_mobile",
+        "v8.browsing_mobile_turbo"
+      ]
+    },
+    "build164-b1--device6": {
+      "benchmarks": [
+        "memory.desktop",
+        "power.typical_10_mobile",
+        "startup.large_profile.cold.blank_page",
+        "thread_times.key_mobile_sites_smooth",
+        "webrtc"
+      ]
+    },
+    "build164-b1--device7": {
+      "benchmarks": [
+        "battor.steady_state",
+        "blink_perf.canvas",
+        "blink_perf.paint",
+        "memory.blink_memory_mobile",
+        "power.idle_platform",
+        "smoothness.image_decoding_cases",
+        "start_with_ext.warm.blank_page",
+        "v8.browsing_mobile_classic"
+      ]
+    },
+    "build165-b1--device1": {
+      "benchmarks": [
+        "blink_perf.shadow_dom",
+        "dummy_benchmark.noisy_benchmark_1",
+        "media.tough_video_cases_tbmv2",
+        "memory.long_running_idle_gmail_background_tbmv2",
+        "smoothness.gpu_rasterization.tough_scrolling_cases",
+        "smoothness.tough_image_decode_cases",
+        "thread_times.key_idle_power_cases",
+        "v8.browsing_desktop",
+        "v8.infinite_scroll-classic_tbmv2",
+        "v8.runtimestats.browsing_mobile"
+      ]
+    },
+    "build165-b1--device2": {
+      "benchmarks": [
+        "dromaeo.domcorequery",
+        "dromaeo.domcoretraverse",
+        "oortonline",
+        "power.trivial_pages",
+        "tracing.tracing_with_debug_overhead"
+      ]
+    },
+    "build165-b1--device3": {
+      "benchmarks": [
+        "blink_perf.dom",
+        "jetstream",
+        "memory.top_10_mobile",
+        "smoothness.gpu_rasterization_and_decoding.image_decoding_cases",
+        "smoothness.pathological_mobile_sites",
+        "smoothness.tough_pinch_zoom_cases",
+        "startup.warm.blank_page",
+        "thread_times.tough_scrolling_cases"
+      ]
+    },
+    "build165-b1--device4": {
+      "benchmarks": [
+        "smoothness.gpu_rasterization.polymer",
+        "smoothness.scrolling_tough_ad_cases",
+        "smoothness.tough_canvas_cases",
+        "smoothness.tough_path_rendering_cases",
+        "start_with_url.cold.startup_pages",
+        "startup.large_profile.warm.blank_page",
+        "startup.warm.chrome_signin",
+        "system_health.webview_startup",
+        "v8.browsing_desktop_turbo"
+      ]
+    },
+    "build165-b1--device5": {
+      "benchmarks": [
+        "dromaeo.domcoreattr",
+        "oortonline_tbmv2",
+        "power.android_acceptance",
+        "smoothness.gpu_rasterization.tough_filters_cases",
+        "smoothness.maps",
+        "smoothness.tough_webgl_ad_cases",
+        "startup.cold.blank_page",
+        "thread_times.simple_mobile_sites"
+      ]
+    },
+    "build165-b1--device6": {
+      "benchmarks": [
+        "dromaeo.domcoremodify",
+        "media.android.tough_video_cases",
+        "media.media_cns_cases",
+        "rasterize_and_record_micro.key_mobile_sites",
+        "rasterize_and_record_micro.polymer",
+        "smoothness.gpu_rasterization.top_25_smooth",
+        "thread_times.key_noop_cases",
+        "thread_times.polymer",
+        "thread_times.tough_compositor_cases",
+        "v8.detached_context_age_in_gc",
+        "v8.mobile_infinite_scroll-turbo_tbmv2",
+        "v8.runtimestats.browsing_mobile_turbo"
+      ]
+    },
+    "build165-b1--device7": {
+      "benchmarks": [
+        "media.mse_cases",
+        "smoothness.key_desktop_move_cases",
+        "smoothness.tough_ad_cases",
+        "smoothness.tough_scrolling_cases",
+        "smoothness.tough_webgl_cases",
+        "speedometer-classic",
+        "system_health.common_mobile"
+      ]
+    },
+    "build166-b1--device1": {
+      "benchmarks": [
+        "image_decoding.image_decoding_measurement",
+        "kraken",
+        "loading.mobile",
+        "media.android.tough_video_cases_tbmv2",
+        "power.steady_state",
+        "smoothness.tough_filters_cases",
+        "storage.indexeddb_endure_tracing"
+      ]
+    },
+    "build166-b1--device2": {
+      "benchmarks": [
+        "blink_perf.bindings",
+        "memory.long_running_idle_gmail_tbmv2",
+        "octane",
+        "scheduler.tough_scheduling_cases",
+        "thread_times.key_hit_test_cases"
+      ]
+    },
+    "build166-b1--device3": {
+      "benchmarks": [
+        "memory.top_10_mobile_stress",
+        "page_cycler_v2.basic_oopif",
+        "service_worker.service_worker_micro_benchmark",
+        "smoothness.desktop_tough_pinch_zoom_cases",
+        "smoothness.sync_scroll.key_mobile_sites_smooth",
+        "speedometer",
+        "tab_switching.typical_25"
+      ]
+    },
+    "build166-b1--device4": {
+      "benchmarks": [
+        "blink_perf.events",
+        "blink_perf.parser",
+        "page_cycler_v2_site_isolation.basic_oopif",
+        "smoothness.tough_texture_upload_cases",
+        "speedometer-turbo",
+        "start_with_ext.cold.blank_page",
+        "thread_times.key_silk_cases"
+      ]
+    },
+    "build166-b1--device5": {
+      "benchmarks": [
+        "rasterize_and_record_micro.key_silk_cases",
+        "rasterize_and_record_micro.partial_invalidation",
+        "rasterize_and_record_micro.top_25",
+        "service_worker.service_worker",
+        "smoothness.gpu_rasterization.tough_pinch_zoom_cases",
+        "smoothness.top_25_smooth",
+        "start_with_url.warm.startup_pages",
+        "tracing.tracing_with_background_memory_infra",
+        "v8.mobile_infinite_scroll-classic_tbmv2",
+        "v8.mobile_infinite_scroll_tbmv2",
+        "v8.runtimestats.browsing_desktop_turbo"
+      ]
+    },
+    "build166-b1--device6": {
+      "benchmarks": [
+        "blink_perf.css",
+        "blink_perf.layout",
+        "media.tough_video_cases",
+        "smoothness.key_mobile_sites_smooth",
+        "storage.indexeddb_endure"
+      ]
+    },
+    "build166-b1--device7": {
+      "benchmarks": [
+        "blink_perf.svg",
+        "blob_storage.blob_storage",
+        "dummy_benchmark.stable_benchmark_1",
+        "smoothness.gpu_rasterization.tough_path_rendering_cases",
+        "v8.infinite_scroll_tbmv2",
+        "v8.runtime_stats.top_25",
+        "v8.runtimestats.browsing_desktop",
+        "v8.runtimestats.browsing_desktop_classic"
+      ]
+    }
+  },
+  "Android Nexus5X WebView Perf": {
+    "build243-m1--device1": {
+      "benchmarks": [
+        "system_health.memory_desktop",
+        "thread_times.polymer",
+        "v8.mobile_infinite_scroll_tbmv2",
+        "v8.runtime_stats.top_25",
+        "v8.runtimestats.browsing_mobile"
+      ]
+    },
+    "build243-m1--device2": {
+      "benchmarks": [
+        "loading.desktop",
+        "rasterize_and_record_micro.polymer",
+        "smoothness.gpu_rasterization.top_25_smooth",
+        "smoothness.key_mobile_sites_smooth",
+        "smoothness.tough_pinch_zoom_cases",
+        "startup.warm.chrome_signin",
+        "thread_times.key_mobile_sites_smooth",
+        "thread_times.key_silk_cases",
+        "v8.browsing_mobile"
+      ]
+    },
+    "build243-m1--device3": {
+      "benchmarks": [
+        "blink_perf.events",
+        "blink_perf.svg",
+        "dromaeo.domcoreattr",
+        "kraken",
+        "memory.desktop",
+        "memory.long_running_idle_gmail_background_tbmv2",
+        "octane",
+        "rasterize_and_record_micro.top_25",
+        "smoothness.gpu_rasterization.tough_path_rendering_cases",
+        "smoothness.simple_mobile_sites",
+        "smoothness.sync_scroll.key_mobile_sites_smooth",
+        "smoothness.top_25_smooth",
+        "smoothness.tough_filters_cases",
+        "smoothness.tough_scrolling_cases",
+        "start_with_url.cold.startup_pages",
+        "start_with_url.warm.startup_pages",
+        "startup.large_profile.warm.blank_page",
+        "storage.indexeddb_endure_tracing",
+        "system_health.common_desktop",
+        "thread_times.key_noop_cases"
+      ]
+    },
+    "build243-m1--device4": {
+      "benchmarks": [
+        "blink_perf.bindings",
+        "blink_perf.dom",
+        "dromaeo.domcorequery",
+        "dromaeo.domcoretraverse",
+        "dummy_benchmark.stable_benchmark_1",
+        "loading.mobile",
+        "oortonline_tbmv2",
+        "power.typical_10_mobile",
+        "rasterize_and_record_micro.key_silk_cases",
+        "scheduler.tough_scheduling_cases",
+        "service_worker.service_worker_micro_benchmark",
+        "smoothness.tough_animation_cases",
+        "smoothness.tough_webgl_ad_cases",
+        "smoothness.tough_webgl_cases",
+        "speedometer",
+        "startup.cold.blank_page",
+        "system_health.common_mobile",
+        "tracing.tracing_with_background_memory_infra",
+        "v8.infinite_scroll_tbmv2"
+      ]
+    },
+    "build243-m1--device5": {
+      "benchmarks": [
+        "battor.trivial_pages",
+        "blink_perf.css",
+        "blink_perf.paint",
+        "blink_perf.parser",
+        "dummy_benchmark.noisy_benchmark_1",
+        "media.android.tough_video_cases",
+        "media.android.tough_video_cases_tbmv2",
+        "media.tough_video_cases",
+        "media.tough_video_cases_tbmv2",
+        "oortonline",
+        "power.idle_platform",
+        "power.steady_state",
+        "rasterize_and_record_micro.partial_invalidation",
+        "smoothness.key_silk_cases",
+        "startup.warm.blank_page",
+        "storage.indexeddb_endure",
+        "tab_switching.typical_25",
+        "thread_times.key_hit_test_cases",
+        "thread_times.simple_mobile_sites",
+        "thread_times.tough_compositor_cases",
+        "thread_times.tough_scrolling_cases",
+        "tracing.tracing_with_debug_overhead",
+        "v8.runtimestats.browsing_desktop"
+      ]
+    },
+    "build243-m1--device6": {
+      "benchmarks": [
+        "blink_perf.layout",
+        "blink_perf.shadow_dom",
+        "dromaeo.domcoremodify",
+        "jetstream",
+        "media.media_cns_cases",
+        "page_cycler_v2_site_isolation.basic_oopif",
+        "power.trivial_pages",
+        "rasterize_and_record_micro.key_mobile_sites",
+        "service_worker.service_worker",
+        "smoothness.gpu_rasterization.tough_filters_cases",
+        "smoothness.gpu_rasterization.tough_scrolling_cases",
+        "smoothness.image_decoding_cases",
+        "smoothness.tough_ad_cases",
+        "smoothness.tough_path_rendering_cases",
+        "start_with_ext.warm.blank_page",
+        "startup.large_profile.cold.blank_page",
+        "system_health.memory_mobile",
+        "v8.detached_context_age_in_gc"
+      ]
+    },
+    "build243-m1--device7": {
+      "benchmarks": [
+        "battor.steady_state",
+        "blink_perf.canvas",
+        "blob_storage.blob_storage",
+        "image_decoding.image_decoding_measurement",
+        "media.mse_cases",
+        "memory.blink_memory_mobile",
+        "memory.long_running_idle_gmail_tbmv2",
+        "memory.top_10_mobile",
+        "page_cycler_v2.basic_oopif",
+        "smoothness.desktop_tough_pinch_zoom_cases",
+        "smoothness.gpu_rasterization.polymer",
+        "smoothness.gpu_rasterization.tough_pinch_zoom_cases",
+        "smoothness.gpu_rasterization_and_decoding.image_decoding_cases",
+        "smoothness.key_desktop_move_cases",
+        "smoothness.maps",
+        "smoothness.pathological_mobile_sites",
+        "smoothness.scrolling_tough_ad_cases",
+        "smoothness.tough_canvas_cases",
+        "smoothness.tough_image_decode_cases",
+        "smoothness.tough_texture_upload_cases",
+        "start_with_ext.cold.blank_page",
+        "system_health.webview_startup",
+        "thread_times.key_idle_power_cases",
+        "v8.browsing_desktop",
+        "webrtc"
+      ]
+    }
+  },
   "Android Nexus6 Perf": {
     "build15-b1--device1": {
       "benchmarks": [
@@ -689,6 +1059,227 @@
       ]
     }
   },
+  "Android Nexus6 WebView Perf": {
+    "build112-b1--device1": {
+      "benchmarks": [
+        "smoothness.key_silk_cases",
+        "system_health.memory_desktop",
+        "system_health.memory_mobile"
+      ]
+    },
+    "build112-b1--device2": {
+      "benchmarks": [
+        "loading.desktop",
+        "smoothness.simple_mobile_sites",
+        "v8.browsing_desktop_classic"
+      ]
+    },
+    "build112-b1--device3": {
+      "benchmarks": [
+        "system_health.common_desktop",
+        "v8.runtimestats.browsing_mobile_classic"
+      ]
+    },
+    "build112-b1--device4": {
+      "benchmarks": [
+        "smoothness.tough_animation_cases",
+        "v8.infinite_scroll-turbo_tbmv2"
+      ]
+    },
+    "build112-b1--device5": {
+      "benchmarks": [
+        "battor.trivial_pages",
+        "v8.browsing_mobile",
+        "v8.browsing_mobile_turbo"
+      ]
+    },
+    "build112-b1--device6": {
+      "benchmarks": [
+        "memory.desktop",
+        "power.typical_10_mobile",
+        "startup.large_profile.cold.blank_page",
+        "thread_times.key_mobile_sites_smooth",
+        "webrtc"
+      ]
+    },
+    "build112-b1--device7": {
+      "benchmarks": [
+        "battor.steady_state",
+        "blink_perf.canvas",
+        "blink_perf.paint",
+        "memory.blink_memory_mobile",
+        "power.idle_platform",
+        "smoothness.image_decoding_cases",
+        "start_with_ext.warm.blank_page",
+        "v8.browsing_mobile_classic"
+      ]
+    },
+    "build113-b1--device1": {
+      "benchmarks": [
+        "blink_perf.shadow_dom",
+        "dummy_benchmark.noisy_benchmark_1",
+        "media.tough_video_cases_tbmv2",
+        "memory.long_running_idle_gmail_background_tbmv2",
+        "smoothness.gpu_rasterization.tough_scrolling_cases",
+        "smoothness.tough_image_decode_cases",
+        "thread_times.key_idle_power_cases",
+        "v8.browsing_desktop",
+        "v8.infinite_scroll-classic_tbmv2",
+        "v8.runtimestats.browsing_mobile"
+      ]
+    },
+    "build113-b1--device2": {
+      "benchmarks": [
+        "dromaeo.domcorequery",
+        "dromaeo.domcoretraverse",
+        "oortonline",
+        "power.trivial_pages",
+        "tracing.tracing_with_debug_overhead"
+      ]
+    },
+    "build113-b1--device3": {
+      "benchmarks": [
+        "blink_perf.dom",
+        "jetstream",
+        "memory.top_10_mobile",
+        "smoothness.gpu_rasterization_and_decoding.image_decoding_cases",
+        "smoothness.pathological_mobile_sites",
+        "smoothness.tough_pinch_zoom_cases",
+        "startup.warm.blank_page",
+        "thread_times.tough_scrolling_cases"
+      ]
+    },
+    "build113-b1--device4": {
+      "benchmarks": [
+        "smoothness.gpu_rasterization.polymer",
+        "smoothness.scrolling_tough_ad_cases",
+        "smoothness.tough_canvas_cases",
+        "smoothness.tough_path_rendering_cases",
+        "start_with_url.cold.startup_pages",
+        "startup.large_profile.warm.blank_page",
+        "startup.warm.chrome_signin",
+        "system_health.webview_startup",
+        "v8.browsing_desktop_turbo"
+      ]
+    },
+    "build113-b1--device5": {
+      "benchmarks": [
+        "dromaeo.domcoreattr",
+        "oortonline_tbmv2",
+        "power.android_acceptance",
+        "smoothness.gpu_rasterization.tough_filters_cases",
+        "smoothness.maps",
+        "smoothness.tough_webgl_ad_cases",
+        "startup.cold.blank_page",
+        "thread_times.simple_mobile_sites"
+      ]
+    },
+    "build113-b1--device6": {
+      "benchmarks": [
+        "dromaeo.domcoremodify",
+        "media.android.tough_video_cases",
+        "media.media_cns_cases",
+        "rasterize_and_record_micro.key_mobile_sites",
+        "rasterize_and_record_micro.polymer",
+        "smoothness.gpu_rasterization.top_25_smooth",
+        "thread_times.key_noop_cases",
+        "thread_times.polymer",
+        "thread_times.tough_compositor_cases",
+        "v8.detached_context_age_in_gc",
+        "v8.mobile_infinite_scroll-turbo_tbmv2",
+        "v8.runtimestats.browsing_mobile_turbo"
+      ]
+    },
+    "build113-b1--device7": {
+      "benchmarks": [
+        "media.mse_cases",
+        "smoothness.key_desktop_move_cases",
+        "smoothness.tough_ad_cases",
+        "smoothness.tough_scrolling_cases",
+        "smoothness.tough_webgl_cases",
+        "speedometer-classic",
+        "system_health.common_mobile"
+      ]
+    },
+    "build114-b1--device1": {
+      "benchmarks": [
+        "image_decoding.image_decoding_measurement",
+        "kraken",
+        "loading.mobile",
+        "media.android.tough_video_cases_tbmv2",
+        "power.steady_state",
+        "smoothness.tough_filters_cases",
+        "storage.indexeddb_endure_tracing"
+      ]
+    },
+    "build114-b1--device2": {
+      "benchmarks": [
+        "blink_perf.bindings",
+        "memory.long_running_idle_gmail_tbmv2",
+        "octane",
+        "scheduler.tough_scheduling_cases",
+        "thread_times.key_hit_test_cases"
+      ]
+    },
+    "build114-b1--device3": {
+      "benchmarks": [
+        "memory.top_10_mobile_stress",
+        "page_cycler_v2.basic_oopif",
+        "service_worker.service_worker_micro_benchmark",
+        "smoothness.desktop_tough_pinch_zoom_cases",
+        "smoothness.sync_scroll.key_mobile_sites_smooth",
+        "speedometer",
+        "tab_switching.typical_25"
+      ]
+    },
+    "build114-b1--device4": {
+      "benchmarks": [
+        "blink_perf.events",
+        "blink_perf.parser",
+        "page_cycler_v2_site_isolation.basic_oopif",
+        "smoothness.tough_texture_upload_cases",
+        "speedometer-turbo",
+        "start_with_ext.cold.blank_page",
+        "thread_times.key_silk_cases"
+      ]
+    },
+    "build114-b1--device5": {
+      "benchmarks": [
+        "rasterize_and_record_micro.key_silk_cases",
+        "rasterize_and_record_micro.partial_invalidation",
+        "rasterize_and_record_micro.top_25",
+        "service_worker.service_worker",
+        "smoothness.gpu_rasterization.tough_pinch_zoom_cases",
+        "smoothness.top_25_smooth",
+        "start_with_url.warm.startup_pages",
+        "tracing.tracing_with_background_memory_infra",
+        "v8.mobile_infinite_scroll-classic_tbmv2",
+        "v8.mobile_infinite_scroll_tbmv2",
+        "v8.runtimestats.browsing_desktop_turbo"
+      ]
+    },
+    "build114-b1--device6": {
+      "benchmarks": [
+        "blink_perf.css",
+        "blink_perf.layout",
+        "media.tough_video_cases",
+        "smoothness.key_mobile_sites_smooth",
+        "storage.indexeddb_endure"
+      ]
+    },
+    "build114-b1--device7": {
+      "benchmarks": [
+        "blink_perf.svg",
+        "blob_storage.blob_storage",
+        "dummy_benchmark.stable_benchmark_1",
+        "smoothness.gpu_rasterization.tough_path_rendering_cases",
+        "v8.infinite_scroll_tbmv2",
+        "v8.runtime_stats.top_25",
+        "v8.runtimestats.browsing_desktop",
+        "v8.runtimestats.browsing_desktop_classic"
+      ]
+    }
+  },
   "Android Nexus7v2 Perf": {
     "build10-b1--device1": {
       "benchmarks": [
@@ -711,8 +1302,8 @@
         "page_cycler_v2.intl_hi_ru",
         "smoothness.tough_scrolling_cases",
         "smoothness.tough_texture_upload_cases",
-        "storage.indexeddb_endure_tracing",
-        "speedometer"
+        "speedometer",
+        "storage.indexeddb_endure_tracing"
       ]
     },
     "build10-b1--device3": {
@@ -2376,6 +2967,43 @@
     }
   },
   "Mac Retina Perf": {
+    "build30-b4": {
+      "benchmarks": [
+        "battor.steady_state",
+        "blink_perf.css",
+        "blink_perf.events",
+        "blink_perf.shadow_dom",
+        "kraken",
+        "loading.mobile",
+        "media.mse_cases",
+        "media.tough_video_cases_tbmv2",
+        "memory.blink_memory_mobile",
+        "memory.long_running_idle_gmail_tbmv2",
+        "memory.top_10_mobile_stress",
+        "page_cycler_v2_site_isolation.basic_oopif",
+        "rasterize_and_record_micro.key_mobile_sites",
+        "rasterize_and_record_micro.partial_invalidation",
+        "smoothness.desktop_tough_pinch_zoom_cases",
+        "smoothness.gpu_rasterization.tough_path_rendering_cases",
+        "smoothness.gpu_rasterization.tough_scrolling_cases",
+        "smoothness.maps",
+        "smoothness.sync_scroll.key_mobile_sites_smooth",
+        "smoothness.tough_animation_cases",
+        "smoothness.tough_texture_upload_cases",
+        "smoothness.tough_webgl_ad_cases",
+        "start_with_ext.warm.blank_page",
+        "start_with_url.warm.startup_pages",
+        "startup.large_profile.cold.blank_page",
+        "system_health.common_desktop",
+        "system_health.webview_startup",
+        "thread_times.key_idle_power_cases",
+        "thread_times.simple_mobile_sites",
+        "v8.browsing_mobile_turbo",
+        "v8.infinite_scroll-classic_tbmv2",
+        "v8.infinite_scroll_tbmv2",
+        "v8.runtimestats.browsing_mobile"
+      ]
+    },
     "build4-b1": {
       "benchmarks": [
         "blink_perf.dom",
@@ -2502,43 +3130,6 @@
         "v8.detached_context_age_in_gc",
         "v8.mobile_infinite_scroll-classic_tbmv2"
       ]
-    },
-    "build30-b4": {
-      "benchmarks": [
-        "battor.steady_state",
-        "blink_perf.css",
-        "blink_perf.events",
-        "blink_perf.shadow_dom",
-        "kraken",
-        "loading.mobile",
-        "media.mse_cases",
-        "media.tough_video_cases_tbmv2",
-        "memory.blink_memory_mobile",
-        "memory.long_running_idle_gmail_tbmv2",
-        "memory.top_10_mobile_stress",
-        "page_cycler_v2_site_isolation.basic_oopif",
-        "rasterize_and_record_micro.key_mobile_sites",
-        "rasterize_and_record_micro.partial_invalidation",
-        "smoothness.desktop_tough_pinch_zoom_cases",
-        "smoothness.gpu_rasterization.tough_path_rendering_cases",
-        "smoothness.gpu_rasterization.tough_scrolling_cases",
-        "smoothness.maps",
-        "smoothness.sync_scroll.key_mobile_sites_smooth",
-        "smoothness.tough_animation_cases",
-        "smoothness.tough_texture_upload_cases",
-        "smoothness.tough_webgl_ad_cases",
-        "start_with_ext.warm.blank_page",
-        "start_with_url.warm.startup_pages",
-        "startup.large_profile.cold.blank_page",
-        "system_health.common_desktop",
-        "system_health.webview_startup",
-        "thread_times.key_idle_power_cases",
-        "thread_times.simple_mobile_sites",
-        "v8.browsing_mobile_turbo",
-        "v8.infinite_scroll-classic_tbmv2",
-        "v8.infinite_scroll_tbmv2",
-        "v8.runtimestats.browsing_mobile"
-      ]
     }
   },
   "Win 10 4 Core Low-End Perf Tests": {
@@ -4341,7 +4932,6 @@
     "battor.steady_state",
     "battor.trivial_pages",
     "blink_perf.bindings",
-    "blink_perf.blink_gc",
     "blink_perf.canvas",
     "blink_perf.css",
     "blink_perf.dom",
@@ -4371,22 +4961,13 @@
     "media.tough_video_cases_tbmv2",
     "memory.blink_memory_mobile",
     "memory.desktop",
-    "memory.dual_browser_test",
     "memory.long_running_idle_gmail_background_tbmv2",
     "memory.long_running_idle_gmail_tbmv2",
     "memory.top_10_mobile",
-    "memory.top_10_mobile_stress",
     "octane",
     "oortonline",
     "oortonline_tbmv2",
     "page_cycler_v2.basic_oopif",
-    "page_cycler_v2.intl_ar_fa_he",
-    "page_cycler_v2.intl_es_fr_pt-BR",
-    "page_cycler_v2.intl_hi_ru",
-    "page_cycler_v2.intl_ja_zh",
-    "page_cycler_v2.intl_ko_th_vi",
-    "page_cycler_v2.top_10_mobile",
-    "page_cycler_v2.typical_25",
     "page_cycler_v2_site_isolation.basic_oopif",
     "power.idle_platform",
     "power.steady_state",
@@ -4430,8 +5011,6 @@
     "smoothness.tough_webgl_ad_cases",
     "smoothness.tough_webgl_cases",
     "speedometer",
-    "speedometer-classic",
-    "speedometer-turbo",
     "start_with_ext.cold.blank_page",
     "start_with_ext.warm.blank_page",
     "start_with_url.cold.startup_pages",
@@ -4448,7 +5027,6 @@
     "system_health.memory_desktop",
     "system_health.memory_mobile",
     "system_health.webview_startup",
-    "system_health.webview_startup_multiprocess",
     "tab_switching.typical_25",
     "thread_times.key_hit_test_cases",
     "thread_times.key_idle_power_cases",
@@ -4462,25 +5040,13 @@
     "tracing.tracing_with_background_memory_infra",
     "tracing.tracing_with_debug_overhead",
     "v8.browsing_desktop",
-    "v8.browsing_desktop_classic",
-    "v8.browsing_desktop_turbo",
     "v8.browsing_mobile",
-    "v8.browsing_mobile_classic",
-    "v8.browsing_mobile_turbo",
     "v8.detached_context_age_in_gc",
-    "v8.infinite_scroll-classic_tbmv2",
-    "v8.infinite_scroll-turbo_tbmv2",
     "v8.infinite_scroll_tbmv2",
-    "v8.mobile_infinite_scroll-classic_tbmv2",
-    "v8.mobile_infinite_scroll-turbo_tbmv2",
     "v8.mobile_infinite_scroll_tbmv2",
     "v8.runtime_stats.top_25",
     "v8.runtimestats.browsing_desktop",
-    "v8.runtimestats.browsing_desktop_classic",
-    "v8.runtimestats.browsing_desktop_turbo",
     "v8.runtimestats.browsing_mobile",
-    "v8.runtimestats.browsing_mobile_classic",
-    "v8.runtimestats.browsing_mobile_turbo",
     "webrtc"
   ]
 }
diff --git a/tools/perf/core/perf_data_generator.py b/tools/perf/core/perf_data_generator.py
index 135459f1..d70009a 100755
--- a/tools/perf/core/perf_data_generator.py
+++ b/tools/perf/core/perf_data_generator.py
@@ -37,13 +37,15 @@
   return waterfall
 
 def add_tester(waterfall, name, perf_id, platform, target_bits=64,
-              num_host_shards=1, num_device_shards=1, swarming=None):
+               num_host_shards=1, num_device_shards=1, swarming=None,
+               replace_system_webview=False):
   del perf_id # this will be needed
   waterfall['testers'][name] = {
     'platform': platform,
     'num_device_shards': num_device_shards,
     'num_host_shards': num_host_shards,
     'target_bits': target_bits,
+    'replace_system_webview': replace_system_webview,
   }
 
   if swarming:
@@ -119,6 +121,28 @@
         ]
       }
     ])
+
+  waterfall = add_tester(
+    waterfall, 'Android Nexus5X WebView Perf',
+    'fyi-android-webview-nexus5X', 'android', swarming=[
+      {
+       'os': 'Android',
+       'android_devices': '1',
+       'pool': 'Chrome-perf',
+       'device_ids': [
+           'build243-m1--device1', 'build243-m1--device2',
+           'build243-m1--device3', 'build243-m1--device4',
+           'build243-m1--device5', 'build243-m1--device6',
+           'build243-m1--device7',
+          ],
+       'perf_tests': [
+         # TODO(martiniss): implement these isolate targets
+         # ('tracing_webview_perftests', 'build112-b1--device2'),
+         # ('gpu_webview_perftests', 'build113-b1--device2'),
+         # ('cc_webview_perftests', 'build114-b1--device2'),
+        ]
+      }
+    ], replace_system_webview=True)
   return waterfall
 
 
@@ -270,6 +294,67 @@
       }
     ])
 
+  # TODO(martiniss): comment this back in once we're confident webview works
+  # waterfall = add_tester(
+  #   waterfall, 'Android Nexus5X WebView Perf', 'android-webview-nexus5X',
+  #   'android', swarming=[
+  #     {
+  #      'os': 'Android',
+  #      'android_devices': '1',
+  #      'pool': 'Chrome-perf',
+  #      'device_ids': [
+  #          'build164-b1--device1', 'build164-b1--device2',
+  #          'build164-b1--device3', 'build164-b1--device4',
+  #          'build164-b1--device5', 'build164-b1--device6',
+  #          'build164-b1--device7',
+  #          'build165-b1--device1', 'build165-b1--device2',
+  #          'build165-b1--device3', 'build165-b1--device4',
+  #          'build165-b1--device5', 'build165-b1--device6',
+  #          'build165-b1--device7',
+  #          'build166-b1--device1', 'build166-b1--device2',
+  #          'build166-b1--device3', 'build166-b1--device4',
+  #          'build166-b1--device5', 'build166-b1--device6',
+  #          'build166-b1--device7',
+  #         ],
+  #      'perf_tests': [
+  #        # TODO(martiniss): implement these isolate targets
+  #        # ('tracing_webview_perftests', 'build164-b1--device2'),
+  #        # ('gpu_webview_perftests', 'build165-b1--device2'),
+  #        # ('cc_webview_perftests', 'build166-b1--device2'),
+  #       ]
+  #     }
+  #   ], replace_system_webview=True)
+
+  # waterfall = add_tester(
+  #   waterfall, 'Android Nexus6 WebView Perf', 'android-webview-nexus6',
+  #   'android', swarming=[
+  #     {
+  #      'os': 'Android',
+  #      'android_devices': '1',
+  #      'pool': 'Chrome-perf',
+  #      'device_ids': [
+  #          'build112-b1--device1', 'build112-b1--device2',
+  #          'build112-b1--device3', 'build112-b1--device4',
+  #          'build112-b1--device5', 'build112-b1--device6',
+  #          'build112-b1--device7',
+  #          'build113-b1--device1', 'build113-b1--device2',
+  #          'build113-b1--device3', 'build113-b1--device4',
+  #          'build113-b1--device5', 'build113-b1--device6',
+  #          'build113-b1--device7',
+  #          'build114-b1--device1', 'build114-b1--device2',
+  #          'build114-b1--device3', 'build114-b1--device4',
+  #          'build114-b1--device5', 'build114-b1--device6',
+  #          'build114-b1--device7',
+  #         ],
+  #      'perf_tests': [
+  #        # TODO(martiniss): implement these isolate targets
+  #        # ('tracing_webview_perftests', 'build112-b1--device2'),
+  #        # ('gpu_webview_perftests', 'build113-b1--device2'),
+  #        # ('cc_webview_perftests', 'build114-b1--device2'),
+  #       ]
+  #     }
+  #   ], replace_system_webview=True)
+
   waterfall = add_tester(
     waterfall, 'Win 10 High-DPI Perf', 'win-high-dpi', 'win',
     swarming=[
@@ -577,10 +662,16 @@
     # to fix them except waiting for the reference build to update.
     ignore_task_failure = True
 
+  isolate_name = 'telemetry_perf_tests'
+  if browser == 'android-webview':
+    test_args.append(
+        '--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk')
+    isolate_name = 'telemetry_perf_webview_tests'
+
   return generate_isolate_script_entry(
-      swarming_dimensions, test_args, 'telemetry_perf_tests',
+      swarming_dimensions, test_args, isolate_name,
       step_name, ignore_task_failure=ignore_task_failure,
-      override_compile_targets=['telemetry_perf_tests'],
+      override_compile_targets=[isolate_name],
       swarming_timeout=BENCHMARK_SWARMING_TIMEOUTS.get(benchmark_name))
 
 
@@ -641,7 +732,10 @@
   # First determine the browser that you need based on the tester
   browser_name = ''
   if tester_config['platform'] == 'android':
-    browser_name = 'android-chromium'
+    if tester_config.get('replace_system_webview', False):
+      browser_name = 'android-webview'
+    else:
+      browser_name = 'android-chromium'
   elif (tester_config['platform'] == 'win'
     and tester_config['target_bits'] == 64):
     browser_name = 'release_x64'
@@ -674,7 +768,9 @@
     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.
-    if benchmark.Name() not in benchmark_ref_build_blacklist:
+    # Webview doesn't have a reference build right now.
+    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')
       isolated_scripts.append(reference_test)
diff --git a/tools/perf/core/perf_data_generator_unittest.py b/tools/perf/core/perf_data_generator_unittest.py
index 4ec50c0..5bf1e76 100644
--- a/tools/perf/core/perf_data_generator_unittest.py
+++ b/tools/perf/core/perf_data_generator_unittest.py
@@ -121,6 +121,33 @@
       }
     self.assertEquals(test, expected_generated_test)
 
+  def testGenerateTelemetryTestsWebView(self):
+    class RegularBenchmark(benchmark.Benchmark):
+      @classmethod
+      def Name(cls):
+        return 'regular'
+
+    swarming_dimensions = [
+        {'os': 'SkyNet', 'id': 'T-850', 'pool': 'T-RIP', 'device_ids': ['a']}
+    ]
+    test_config = {
+        'platform': 'android',
+        'swarming_dimensions': swarming_dimensions,
+        'replace_system_webview': True,
+    }
+    sharding_map = {'fake': {'regular': 'a'}}
+    benchmarks = [RegularBenchmark]
+    tests = perf_data_generator.generate_telemetry_tests(
+        'fake', test_config, benchmarks, sharding_map, ['blacklisted'])
+
+    self.assertEqual(len(tests), 1)
+    test = tests[0]
+    self.assertEquals(test['args'], [
+        'regular', '-v', '--upload-results', '--output-format=chartjson',
+        '--browser=android-webview',
+        '--webview-embedded-apk=../../out/Release/apks/SystemWebViewShell.apk'])
+    self.assertEquals(test['isolate_name'], 'telemetry_perf_webview_tests')
+
   def testGenerateTelemetryTestsBlacklistedReferenceBuildTest(self):
     class BlacklistedBenchmark(benchmark.Benchmark):
       @classmethod
diff --git a/tools/perf/docs/images/chrome_speed_color.png b/tools/perf/docs/images/chrome_speed_color.png
new file mode 100644
index 0000000..c9c634765
--- /dev/null
+++ b/tools/perf/docs/images/chrome_speed_color.png
Binary files differ
diff --git a/tools/perf/docs/navbar.md b/tools/perf/docs/navbar.md
new file mode 100644
index 0000000..517473d3
--- /dev/null
+++ b/tools/perf/docs/navbar.md
@@ -0,0 +1,6 @@
+# Chrome Speed
+
+* [Home][home]
+
+[home]: /README.md
+[logo]: /images/chrome_speed_color.png
diff --git a/tools/perf/measurements/task_execution_time_unittest.py b/tools/perf/measurements/task_execution_time_unittest.py
index 5bd95cb2..48e2310 100644
--- a/tools/perf/measurements/task_execution_time_unittest.py
+++ b/tools/perf/measurements/task_execution_time_unittest.py
@@ -18,7 +18,7 @@
 
   def __init__(self, page_set, base_dir):
     super(TestTaskExecutionTimePage, self).__init__(
-        'file://blank.html', page_set, base_dir)
+        'file://blank.html', page_set, base_dir, name='blank.html')
 
   def RunPageInteractions(self, action_runner):
     with action_runner.CreateGestureInteraction('ScrollAction'):
diff --git a/tools/perf/measurements/v8_detached_context_age_in_gc_unittest.py b/tools/perf/measurements/v8_detached_context_age_in_gc_unittest.py
index a559785..0f93ec8 100644
--- a/tools/perf/measurements/v8_detached_context_age_in_gc_unittest.py
+++ b/tools/perf/measurements/v8_detached_context_age_in_gc_unittest.py
@@ -14,7 +14,7 @@
 class FakePage(page_module.Page):
 
   def __init__(self, url):
-    super(FakePage, self).__init__(url=url)
+    super(FakePage, self).__init__(url=url, name=url)
 
   @property
   def is_file(self):
@@ -58,7 +58,7 @@
 
   def __init__(self, page_set):
     super(SimplePage, self).__init__(
-        'file://host.html', page_set, page_set.base_dir)
+        'file://host.html', page_set, page_set.base_dir, name='host.html')
 
   def RunPageInteractions(self, action_runner):
     # Reload the page to detach the previous context.
diff --git a/tools/perf/measurements/v8_gc_times_unittest.py b/tools/perf/measurements/v8_gc_times_unittest.py
index 5610fb4..b08226d 100644
--- a/tools/perf/measurements/v8_gc_times_unittest.py
+++ b/tools/perf/measurements/v8_gc_times_unittest.py
@@ -35,7 +35,7 @@
 
     def __init__(self, page_set):
       super(V8GCTimesTestPageHelper.MockV8GCTimesPage, self).__init__(
-          'file://blank.html', page_set, page_set.base_dir)
+          'file://blank.html', page_set, page_set.base_dir, name='blank.html')
 
   def MeasureFakePage(self):
     # Create a fake page and add it to the page set.
diff --git a/ui/ozone/platform/x11/BUILD.gn b/ui/ozone/platform/x11/BUILD.gn
index 7d194d1..2a0a6878 100644
--- a/ui/ozone/platform/x11/BUILD.gn
+++ b/ui/ozone/platform/x11/BUILD.gn
@@ -10,6 +10,8 @@
     "client_native_pixmap_factory_x11.h",
     "gl_ozone_glx.cc",
     "gl_ozone_glx.h",
+    "gl_surface_egl_ozone_x11.cc",
+    "gl_surface_egl_ozone_x11.h",
     "gl_surface_glx_ozone.cc",
     "gl_surface_glx_ozone.h",
     "ozone_platform_x11.cc",
diff --git a/ui/ozone/platform/x11/gl_surface_egl_ozone_x11.cc b/ui/ozone/platform/x11/gl_surface_egl_ozone_x11.cc
new file mode 100644
index 0000000..0fc232ee
--- /dev/null
+++ b/ui/ozone/platform/x11/gl_surface_egl_ozone_x11.cc
@@ -0,0 +1,100 @@
+// 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/platform/x11/gl_surface_egl_ozone_x11.h"
+
+#include <X11/Xlib.h>
+
+#include "ui/gfx/x/x11_types.h"
+#include "ui/gl/egl_util.h"
+
+namespace ui {
+
+GLSurfaceEGLOzoneX11::GLSurfaceEGLOzoneX11(EGLNativeWindowType window)
+    : NativeViewGLSurfaceEGL(window, nullptr) {}
+
+EGLConfig GLSurfaceEGLOzoneX11::GetConfig() {
+  // Try matching the window depth with an alpha channel, because we're worried
+  // the destination alpha width could constrain blending precision.
+  const int kBufferSizeOffset = 1;
+  const int kAlphaSizeOffset = 3;
+  EGLint config_attribs[] = {EGL_BUFFER_SIZE,
+                             ~0,  // To be replaced.
+                             EGL_ALPHA_SIZE,
+                             8,
+                             EGL_BLUE_SIZE,
+                             8,
+                             EGL_GREEN_SIZE,
+                             8,
+                             EGL_RED_SIZE,
+                             8,
+                             EGL_RENDERABLE_TYPE,
+                             EGL_OPENGL_ES2_BIT,
+                             EGL_SURFACE_TYPE,
+                             EGL_WINDOW_BIT,
+                             EGL_NONE};
+
+  // Get the depth of XWindow for surface.
+  XWindowAttributes win_attribs;
+  if (XGetWindowAttributes(gfx::GetXDisplay(), window_, &win_attribs)) {
+    config_attribs[kBufferSizeOffset] = win_attribs.depth;
+  }
+
+  EGLDisplay display = GetDisplay();
+
+  EGLConfig config;
+  EGLint num_configs;
+  if (!eglChooseConfig(display, config_attribs, &config, 1, &num_configs)) {
+    LOG(ERROR) << "eglChooseConfig failed with error "
+               << GetLastEGLErrorString();
+    return nullptr;
+  }
+
+  if (num_configs > 0) {
+    EGLint config_depth;
+    if (!eglGetConfigAttrib(display, config, EGL_BUFFER_SIZE, &config_depth)) {
+      LOG(ERROR) << "eglGetConfigAttrib failed with error "
+                 << GetLastEGLErrorString();
+      return nullptr;
+    }
+    if (config_depth == config_attribs[kBufferSizeOffset]) {
+      return config;
+    }
+  }
+
+  // Try without an alpha channel.
+  config_attribs[kAlphaSizeOffset] = 0;
+  if (!eglChooseConfig(display, config_attribs, &config, 1, &num_configs)) {
+    LOG(ERROR) << "eglChooseConfig failed with error "
+               << GetLastEGLErrorString();
+    return nullptr;
+  }
+
+  if (num_configs == 0) {
+    LOG(ERROR) << "No suitable EGL configs found.";
+    return nullptr;
+  }
+  return config;
+}
+
+bool GLSurfaceEGLOzoneX11::Resize(const gfx::Size& size,
+                                  float scale_factor,
+                                  bool has_alpha) {
+  if (size == GetSize())
+    return true;
+
+  size_ = size;
+
+  eglWaitGL();
+  XResizeWindow(gfx::GetXDisplay(), window_, size.width(), size.height());
+  eglWaitNative(EGL_CORE_NATIVE_ENGINE);
+
+  return true;
+}
+
+GLSurfaceEGLOzoneX11::~GLSurfaceEGLOzoneX11() {
+  Destroy();
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/x11/gl_surface_egl_ozone_x11.h b/ui/ozone/platform/x11/gl_surface_egl_ozone_x11.h
new file mode 100644
index 0000000..ec04465
--- /dev/null
+++ b/ui/ozone/platform/x11/gl_surface_egl_ozone_x11.h
@@ -0,0 +1,34 @@
+// 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_PLATFORM_X11_GL_SURFACE_EGL_OZONE_X11_H_
+#define UI_OZONE_PLATFORM_X11_GL_SURFACE_EGL_OZONE_X11_H_
+
+#include "base/macros.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gl/gl_surface_egl.h"
+
+namespace ui {
+
+// GLSurface implementation for Ozone X11 EGL. This does not create a new
+// XWindow or observe XExpose events like GLSurfaceEGLX11 does.
+class GLSurfaceEGLOzoneX11 : public gl::NativeViewGLSurfaceEGL {
+ public:
+  explicit GLSurfaceEGLOzoneX11(EGLNativeWindowType window);
+
+  // gl::NativeViewGLSurfaceEGL:
+  EGLConfig GetConfig() override;
+  bool Resize(const gfx::Size& size,
+              float scale_factor,
+              bool has_alpha) override;
+
+ private:
+  ~GLSurfaceEGLOzoneX11() override;
+
+  DISALLOW_COPY_AND_ASSIGN(GLSurfaceEGLOzoneX11);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_X11_GL_SURFACE_EGL_OZONE_X11_H_
diff --git a/ui/ozone/platform/x11/x11_surface_factory.cc b/ui/ozone/platform/x11/x11_surface_factory.cc
index e5cedcb..a78204f 100644
--- a/ui/ozone/platform/x11/x11_surface_factory.cc
+++ b/ui/ozone/platform/x11/x11_surface_factory.cc
@@ -7,127 +7,23 @@
 #include <X11/Xlib.h>
 
 #include "base/memory/ptr_util.h"
-#include "third_party/khronos/EGL/egl.h"
 #include "ui/gfx/x/x11_types.h"
-#include "ui/gl/egl_util.h"
 #include "ui/gl/gl_surface_egl.h"
 #include "ui/ozone/common/egl_util.h"
 #include "ui/ozone/common/gl_ozone_egl.h"
 #include "ui/ozone/common/gl_ozone_osmesa.h"
 #include "ui/ozone/platform/x11/gl_ozone_glx.h"
+#include "ui/ozone/platform/x11/gl_surface_egl_ozone_x11.h"
 
 namespace ui {
-
 namespace {
 
-// GLSurface implementation for Ozone X11 EGL.
-class GLSurfaceEGLOzoneX11 : public gl::NativeViewGLSurfaceEGL {
- public:
-  explicit GLSurfaceEGLOzoneX11(EGLNativeWindowType window);
-
-  // gl::NativeViewGLSurfaceEGL:
-  EGLConfig GetConfig() override;
-  bool Resize(const gfx::Size& size,
-              float scale_factor,
-              bool has_alpha) override;
-
- private:
-  ~GLSurfaceEGLOzoneX11() override;
-
-  DISALLOW_COPY_AND_ASSIGN(GLSurfaceEGLOzoneX11);
-};
-
-GLSurfaceEGLOzoneX11::GLSurfaceEGLOzoneX11(EGLNativeWindowType window)
-    : NativeViewGLSurfaceEGL(window, nullptr) {}
-
-EGLConfig GLSurfaceEGLOzoneX11::GetConfig() {
-  // Try matching the window depth with an alpha channel, because we're worried
-  // the destination alpha width could constrain blending precision.
-  const int kBufferSizeOffset = 1;
-  const int kAlphaSizeOffset = 3;
-  EGLint config_attribs[] = {EGL_BUFFER_SIZE,
-                             ~0,  // To be replaced.
-                             EGL_ALPHA_SIZE,
-                             8,
-                             EGL_BLUE_SIZE,
-                             8,
-                             EGL_GREEN_SIZE,
-                             8,
-                             EGL_RED_SIZE,
-                             8,
-                             EGL_RENDERABLE_TYPE,
-                             EGL_OPENGL_ES2_BIT,
-                             EGL_SURFACE_TYPE,
-                             EGL_WINDOW_BIT,
-                             EGL_NONE};
-
-  // Get the depth of XWindow for surface.
-  XWindowAttributes win_attribs;
-  if (XGetWindowAttributes(gfx::GetXDisplay(), window_, &win_attribs)) {
-    config_attribs[kBufferSizeOffset] = win_attribs.depth;
-  }
-
-  EGLDisplay display = GetDisplay();
-
-  EGLConfig config;
-  EGLint num_configs;
-  if (!eglChooseConfig(display, config_attribs, &config, 1, &num_configs)) {
-    LOG(ERROR) << "eglChooseConfig failed with error "
-               << GetLastEGLErrorString();
-    return nullptr;
-  }
-
-  if (num_configs > 0) {
-    EGLint config_depth;
-    if (!eglGetConfigAttrib(display, config, EGL_BUFFER_SIZE, &config_depth)) {
-      LOG(ERROR) << "eglGetConfigAttrib failed with error "
-                 << GetLastEGLErrorString();
-      return nullptr;
-    }
-    if (config_depth == config_attribs[kBufferSizeOffset]) {
-      return config;
-    }
-  }
-
-  // Try without an alpha channel.
-  config_attribs[kAlphaSizeOffset] = 0;
-  if (!eglChooseConfig(display, config_attribs, &config, 1, &num_configs)) {
-    LOG(ERROR) << "eglChooseConfig failed with error "
-               << GetLastEGLErrorString();
-    return nullptr;
-  }
-
-  if (num_configs == 0) {
-    LOG(ERROR) << "No suitable EGL configs found.";
-    return nullptr;
-  }
-  return config;
-}
-
-bool GLSurfaceEGLOzoneX11::Resize(const gfx::Size& size,
-                                  float scale_factor,
-                                  bool has_alpha) {
-  if (size == GetSize())
-    return true;
-
-  size_ = size;
-
-  eglWaitGL();
-  XResizeWindow(gfx::GetXDisplay(), window_, size.width(), size.height());
-  eglWaitNative(EGL_CORE_NATIVE_ENGINE);
-
-  return true;
-}
-
-GLSurfaceEGLOzoneX11::~GLSurfaceEGLOzoneX11() {
-  Destroy();
-}
-
 class GLOzoneEGLX11 : public GLOzoneEGL {
  public:
-  GLOzoneEGLX11() {}
-  ~GLOzoneEGLX11() override {}
+  GLOzoneEGLX11() = default;
+  ~GLOzoneEGLX11() override = default;
 
+  // GLOzone:
   scoped_refptr<gl::GLSurface> CreateViewGLSurface(
       gfx::AcceleratedWidget window) override {
     return gl::InitializeGLSurface(new GLSurfaceEGLOzoneX11(window));
@@ -139,6 +35,7 @@
   }
 
  protected:
+  // GLOzoneEGL:
   intptr_t GetNativeDisplay() override {
     return reinterpret_cast<intptr_t>(gfx::GetXDisplay());
   }