diff --git a/DEPS b/DEPS
index 6728ebe2d..fd93a5d4 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': 'ddebefb50ab52b968fed0f9957c419e7acd2326f',
+  'v8_revision': 'e9521a836dcd6a912141a999589bee67bab92f6d',
   # 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': 'd7403971a25c317671e92ccc676a5f600ed223eb',
+  'pdfium_revision': 'fb00ec2af390ccfe8f5e81f49b9113e044f5e5ad',
   # 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': '34ef95cb09ce13d370163e36179981f566356c68',
+  'catapult_revision': 'a5b1645083bd1cfad24a6bef274092407c196b1f',
   # 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/ash/public/interfaces/wallpaper.mojom b/ash/public/interfaces/wallpaper.mojom
index 7215c31..02edb35c 100644
--- a/ash/public/interfaces/wallpaper.mojom
+++ b/ash/public/interfaces/wallpaper.mojom
@@ -11,6 +11,10 @@
 
 // Used by Chrome to set the wallpaper displayed by ash.
 interface WallpaperController {
+  // Calling this method triggers an initial notification of the wallpaper
+  // state. Observers are automatically removed as their connections are closed.
+  AddObserver(associated WallpaperObserver observer);
+
   // Set the wallpaper picker interface, to let ash trigger Chrome's picker.
   SetWallpaperPicker(WallpaperPicker picker);
 
@@ -19,6 +23,18 @@
   // TODO(crbug.com/655875): Optimize ash wallpaper transport; avoid sending
   // large bitmaps over Mojo; use shared memory like BitmapUploader, etc.
   SetWallpaper(skia.mojom.Bitmap? wallpaper, WallpaperLayout layout);
+
+  // Runs to get wallpaper prominent colors.
+  GetWallpaperColors() => (array<uint32> prominent_colors);
+};
+
+// Used to listen for wallpaper state changed.
+interface WallpaperObserver {
+  // Called when the colors extracted from the current wallpaper change. May
+  // be called as a side effect of changing the wallpaper on the
+  // WallpaperController, e.g. WallpaperController::SetWallpaperImage().
+  // Colors are ordered and are referenced in wallpaper::ColorProfileType.
+  OnWallpaperColorsChanged(array<uint32> prominent_colors);
 };
 
 // Used by ash to trigger Chrome's wallpaper picker functionality.
diff --git a/ash/wallpaper/wallpaper_controller.cc b/ash/wallpaper/wallpaper_controller.cc
index 7d2e5ca..e594db7 100644
--- a/ash/wallpaper/wallpaper_controller.cc
+++ b/ash/wallpaper/wallpaper_controller.cc
@@ -23,6 +23,7 @@
 #include "base/sequenced_task_runner.h"
 #include "base/task_scheduler/post_task.h"
 #include "components/wallpaper/wallpaper_color_calculator.h"
+#include "components/wallpaper/wallpaper_color_profile.h"
 #include "components/wallpaper/wallpaper_resizer.h"
 #include "ui/display/manager/display_manager.h"
 #include "ui/display/manager/managed_display_info.h"
@@ -34,6 +35,7 @@
 using color_utils::ColorProfile;
 using color_utils::LumaRange;
 using color_utils::SaturationRange;
+using wallpaper::ColorProfileType;
 
 namespace ash {
 
@@ -45,17 +47,6 @@
 // How long to wait for resizing of the the wallpaper.
 constexpr int kCompositorLockTimeoutMs = 750;
 
-// This enum is used to get the corresponding prominent color from the
-// calculation results.
-enum ColorProfileIndex {
-  COLOR_PROFILE_INDEX_DARK_VIBRANT = 0,
-  COLOR_PROFILE_INDEX_NORMAL_VIBRANT,
-  COLOR_PROFILE_INDEX_LIGHT_VIBRANT,
-  COLOR_PROFILE_INDEX_DARK_MUTED,
-  COLOR_PROFILE_INDEX_NORMAL_MUTED,
-  COLOR_PROFILE_INDEX_LIGHT_MUTED,
-};
-
 // Returns true if a color should be extracted from the wallpaper based on the
 // command kAshShelfColor line arg.
 bool IsShelfColoringEnabled() {
@@ -90,30 +81,30 @@
           ColorProfile(LumaRange::LIGHT, SaturationRange::MUTED)};
 }
 
-// Gets the corresponding color profile index based on the given
+// Gets the corresponding color profile type based on the given
 // |color_profile|.
-ColorProfileIndex GetColorProfileIndex(ColorProfile color_profile) {
+ColorProfileType GetColorProfileType(ColorProfile color_profile) {
   if (color_profile.saturation == SaturationRange::VIBRANT) {
     switch (color_profile.luma) {
       case LumaRange::DARK:
-        return COLOR_PROFILE_INDEX_DARK_VIBRANT;
+        return ColorProfileType::DARK_VIBRANT;
       case LumaRange::NORMAL:
-        return COLOR_PROFILE_INDEX_NORMAL_VIBRANT;
+        return ColorProfileType::NORMAL_VIBRANT;
       case LumaRange::LIGHT:
-        return COLOR_PROFILE_INDEX_LIGHT_VIBRANT;
+        return ColorProfileType::LIGHT_VIBRANT;
     }
   } else {
     switch (color_profile.luma) {
       case LumaRange::DARK:
-        return COLOR_PROFILE_INDEX_DARK_MUTED;
+        return ColorProfileType::DARK_MUTED;
       case LumaRange::NORMAL:
-        return COLOR_PROFILE_INDEX_NORMAL_MUTED;
+        return ColorProfileType::NORMAL_MUTED;
       case LumaRange::LIGHT:
-        return COLOR_PROFILE_INDEX_LIGHT_MUTED;
+        return ColorProfileType::LIGHT_MUTED;
     }
   }
   NOTREACHED();
-  return COLOR_PROFILE_INDEX_DARK_MUTED;
+  return ColorProfileType::DARK_MUTED;
 }
 
 }  // namespace
@@ -173,8 +164,8 @@
 
 SkColor WallpaperController::GetProminentColor(
     ColorProfile color_profile) const {
-  ColorProfileIndex index = GetColorProfileIndex(color_profile);
-  return prominent_colors_[index];
+  ColorProfileType type = GetColorProfileType(color_profile);
+  return prominent_colors_[static_cast<int>(type)];
 }
 
 wallpaper::WallpaperLayout WallpaperController::GetWallpaperLayout() const {
@@ -305,6 +296,14 @@
   }
 }
 
+void WallpaperController::AddObserver(
+    mojom::WallpaperObserverAssociatedPtrInfo observer) {
+  mojom::WallpaperObserverAssociatedPtr observer_ptr;
+  observer_ptr.Bind(std::move(observer));
+  observer_ptr->OnWallpaperColorsChanged(prominent_colors_);
+  mojo_observers_.AddPtr(std::move(observer_ptr));
+}
+
 void WallpaperController::SetWallpaperPicker(mojom::WallpaperPickerPtr picker) {
   wallpaper_picker_ = std::move(picker);
 }
@@ -317,6 +316,11 @@
   SetWallpaperImage(gfx::ImageSkia::CreateFrom1xBitmap(wallpaper), layout);
 }
 
+void WallpaperController::GetWallpaperColors(
+    GetWallpaperColorsCallback callback) {
+  std::move(callback).Run(prominent_colors_);
+}
+
 void WallpaperController::OnWallpaperResized() {
   CalculateWallpaperColors();
   compositor_lock_.reset();
@@ -403,6 +407,9 @@
   prominent_colors_ = colors;
   for (auto& observer : observers_)
     observer.OnWallpaperColorsChanged();
+  mojo_observers_.ForAllPtrs([this](mojom::WallpaperObserver* observer) {
+    observer->OnWallpaperColorsChanged(prominent_colors_);
+  });
 }
 
 void WallpaperController::CalculateWallpaperColors() {
diff --git a/ash/wallpaper/wallpaper_controller.h b/ash/wallpaper/wallpaper_controller.h
index 74919b2..6c224f65 100644
--- a/ash/wallpaper/wallpaper_controller.h
+++ b/ash/wallpaper/wallpaper_controller.h
@@ -19,6 +19,7 @@
 #include "components/wallpaper/wallpaper_layout.h"
 #include "components/wallpaper/wallpaper_resizer_observer.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/interface_ptr_set.h"
 #include "ui/compositor/compositor_lock.h"
 
 namespace base {
@@ -122,9 +123,11 @@
   void OpenSetWallpaperPage();
 
   // mojom::WallpaperController overrides:
+  void AddObserver(mojom::WallpaperObserverAssociatedPtrInfo observer) override;
   void SetWallpaperPicker(mojom::WallpaperPickerPtr picker) override;
   void SetWallpaper(const SkBitmap& wallpaper,
                     wallpaper::WallpaperLayout layout) override;
+  void GetWallpaperColors(GetWallpaperColorsCallback callback) override;
 
   // WallpaperResizerObserver:
   void OnWallpaperResized() override;
@@ -195,6 +198,8 @@
 
   base::ObserverList<WallpaperControllerObserver> observers_;
 
+  mojo::AssociatedInterfacePtrSet<mojom::WallpaperObserver> mojo_observers_;
+
   std::unique_ptr<wallpaper::WallpaperResizer> current_wallpaper_;
 
   // Asynchronous task to extract colors from the wallpaper.
diff --git a/ash/wallpaper/wallpaper_controller_unittest.cc b/ash/wallpaper/wallpaper_controller_unittest.cc
index cf8c2d1..f8d1599 100644
--- a/ash/wallpaper/wallpaper_controller_unittest.cc
+++ b/ash/wallpaper/wallpaper_controller_unittest.cc
@@ -23,6 +23,7 @@
 #include "base/run_loop.h"
 #include "base/strings/stringprintf.h"
 #include "base/task_scheduler/task_scheduler.h"
+#include "mojo/public/cpp/bindings/associated_binding.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/aura/window.h"
@@ -106,6 +107,28 @@
   }
 }
 
+// A test implementation of the WallpaperObserver mojo interface.
+class TestWallpaperObserver : public mojom::WallpaperObserver {
+ public:
+  TestWallpaperObserver() = default;
+  ~TestWallpaperObserver() override = default;
+
+  // mojom::WallpaperObserver:
+  void OnWallpaperColorsChanged(
+      const std::vector<SkColor>& prominent_colors) override {
+    ++wallpaper_colors_changed_count_;
+  }
+
+  int wallpaper_colors_changed_count() const {
+    return wallpaper_colors_changed_count_;
+  }
+
+ private:
+  int wallpaper_colors_changed_count_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(TestWallpaperObserver);
+};
+
 }  // namespace
 
 class WallpaperControllerTest : public test::AshTestBase {
@@ -529,4 +552,25 @@
   EXPECT_FALSE(ShouldCalculateColors());
 }
 
+TEST_F(WallpaperControllerTest, MojoWallpaperObserverTest) {
+  TestWallpaperObserver observer;
+  mojom::WallpaperObserverAssociatedPtr observer_ptr;
+  mojo::AssociatedBinding<mojom::WallpaperObserver> binding(
+      &observer, mojo::MakeIsolatedRequest(&observer_ptr));
+  controller_->AddObserver(observer_ptr.PassInterface());
+
+  // Mojo observer will asynchronously receive the observed event, thus a run
+  // loop needs to be spinned.
+  base::RunLoop().RunUntilIdle();
+  // When adding observer, OnWallpaperColorsChanged() is fired so that we start
+  // with count equals 1.
+  EXPECT_EQ(1, observer.wallpaper_colors_changed_count());
+
+  // Enable shelf coloring will set a customized wallpaper image and change
+  // session state to ACTIVE, which will trigger wallpaper colors calculation.
+  EnableShelfColoring();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(2, observer.wallpaper_colors_changed_count());
+}
+
 }  // namespace ash
diff --git a/base/files/file_enumerator_posix.cc b/base/files/file_enumerator_posix.cc
index bc5c3cc5..ce1fe40a 100644
--- a/base/files/file_enumerator_posix.cc
+++ b/base/files/file_enumerator_posix.cc
@@ -125,7 +125,7 @@
     FileInfo dotdot;
     dotdot.stat_.st_mode = S_IFDIR;
     dotdot.filename_ = FilePath("..");
-    directory_entries_->push_back(dotdot);
+    directory_entries_.push_back(dotdot);
 #endif  // OS_FUCHSIA
 
     current_directory_entry_ = 0;
diff --git a/build/config/mac/BuildInfo.plist b/build/config/mac/BuildInfo.plist
index b0856e35..d32bf2ea7 100644
--- a/build/config/mac/BuildInfo.plist
+++ b/build/config/mac/BuildInfo.plist
@@ -14,13 +14,5 @@
   <string>${XCODE_VERSION}</string>
   <key>DTXcodeBuild</key>
   <string>${XCODE_BUILD}</string>
-  <key>CFBundleShortVersionString</key>
-  <string>${VERSION}</string>
-  <key>CFBundleVersion</key>
-  <string>${VERSION_BUILD}</string>
-  <key>CFBundleIdentifier</key>
-  <string>org.chromium.${PRODUCT_NAME:rfc1034identifier}</string>
-  <key>SCM_REVISION</key>
-  <string>${COMMIT_HASH}</string>
 </dict>
 </plist>
diff --git a/build/config/mac/plist_util.py b/build/config/mac/plist_util.py
index 0928fa13..dd6fe4c 100644
--- a/build/config/mac/plist_util.py
+++ b/build/config/mac/plist_util.py
@@ -21,25 +21,13 @@
 IDENT_RE = re.compile(r'[_/\s]')
 
 
-def InterpolateList(values, substitutions):
-  """Interpolates variable references into |value| using |substitutions|.
+class SubstitutionError(Exception):
+  def __init__(self, key):
+    super(SubstitutionError, self).__init__()
+    self.key = key
 
-  Inputs:
-    values: a list of values
-    substitutions: a mapping of variable names to values
-
-  Returns:
-    A new list of values with all variables references ${VARIABLE} replaced
-    by their value in |substitutions| or None if any of the variable has no
-    subsitution.
-  """
-  result = []
-  for value in values:
-    interpolated = InterpolateValue(value, substitutions)
-    if interpolated is None:
-      return None
-    result.append(interpolated)
-  return result
+  def __str__(self):
+    return "SubstitutionError: {}".format(self.key)
 
 
 def InterpolateString(value, substitutions):
@@ -51,29 +39,28 @@
 
   Returns:
     A new string with all variables references ${VARIABLES} replaced by their
-    value in |substitutions| or None if any of the variable has no substitution.
+    value in |substitutions|. Raises SubstitutionError if a variable has no
+    substitution.
   """
-  result = value
-  for match in reversed(list(SUBST_RE.finditer(value))):
+  def repl(match):
     variable = match.group('id')
     if variable not in substitutions:
-      return None
+      raise SubstitutionError(variable)
     # Some values need to be identifier and thus the variables references may
     # contains :modifier attributes to indicate how they should be converted
     # to identifiers ("identifier" replaces all invalid characters by '_' and
     # "rfc1034identifier" replaces them by "-" to make valid URI too).
     modifier = match.group('modifier')
     if modifier == ':identifier':
-      interpolated = IDENT_RE.sub('_', substitutions[variable])
+      return IDENT_RE.sub('_', substitutions[variable])
     elif modifier == ':rfc1034identifier':
-      interpolated = IDENT_RE.sub('-', substitutions[variable])
+      return IDENT_RE.sub('-', substitutions[variable])
     else:
-      interpolated = substitutions[variable]
-    result = result[:match.start()] + interpolated + result[match.end():]
-  return result
+      return substitutions[variable]
+  return SUBST_RE.sub(repl, value)
 
 
-def InterpolateValue(value, substitutions):
+def Interpolate(value, substitutions):
   """Interpolates variable references into |value| using |substitutions|.
 
   Inputs:
@@ -82,38 +69,18 @@
 
   Returns:
     A new value with all variables references ${VARIABLES} replaced by their
-    value in |substitutions| or None if any of the variable has no substitution.
+    value in |substitutions|. Raises SubstitutionError if a variable has no
+    substitution.
   """
   if isinstance(value, dict):
-    return Interpolate(value, substitutions)
+      return {k: Interpolate(v, substitutions) for k, v in value.iteritems()}
   if isinstance(value, list):
-    return InterpolateList(value, substitutions)
+    return [Interpolate(v, substitutions) for v in value]
   if isinstance(value, str):
     return InterpolateString(value, substitutions)
   return value
 
 
-def Interpolate(plist, substitutions):
-  """Interpolates variable references into |value| using |substitutions|.
-
-  Inputs:
-    plist: a dictionary representing a Property List (.plist) file
-    substitutions: a mapping of variable names to values
-
-  Returns:
-    A new plist with all variables references ${VARIABLES} replaced by their
-    value in |substitutions|. All values that contains references with no
-    substitutions will be removed and the corresponding key will be cleared
-    from the plist (not recursively).
-  """
-  result = {}
-  for key in plist:
-    value = InterpolateValue(plist[key], substitutions)
-    if value is not None:
-      result[key] = value
-  return result
-
-
 def LoadPList(path):
   """Loads Plist at |path| and returns it as a dictionary."""
   fd, name = tempfile.mkstemp()
@@ -153,19 +120,12 @@
     recursively, otherwise |plist2| value is used. If values are list, they
     are concatenated.
   """
-  if not isinstance(plist1, dict) or not isinstance(plist2, dict):
-    if plist2 is not None:
-      return plist2
-    else:
-      return plist1
-  result = {}
-  for key in set(plist1) | set(plist2):
-    if key in plist2:
-      value = plist2[key]
-    else:
-      value = plist1[key]
+  result = plist1.copy()
+  for key, value in plist2.iteritems():
     if isinstance(value, dict):
-      value = MergePList(plist1.get(key, None), plist2.get(key, None))
+      old_value = result.get(key)
+      if isinstance(old_value, dict):
+        value = MergePList(old_value, value)
     if isinstance(value, list):
       value = plist1.get(key, []) + plist2.get(key, [])
     result[key] = value
diff --git a/cc/paint/BUILD.gn b/cc/paint/BUILD.gn
index 57de616..1c53186 100644
--- a/cc/paint/BUILD.gn
+++ b/cc/paint/BUILD.gn
@@ -25,6 +25,10 @@
     "paint_image.h",
     "paint_op_buffer.cc",
     "paint_op_buffer.h",
+    "paint_op_reader.cc",
+    "paint_op_reader.h",
+    "paint_op_writer.cc",
+    "paint_op_writer.h",
     "paint_record.cc",
     "paint_record.h",
     "paint_recorder.cc",
diff --git a/cc/paint/paint_flags.h b/cc/paint/paint_flags.h
index 6954892d..940751c 100644
--- a/cc/paint/paint_flags.h
+++ b/cc/paint/paint_flags.h
@@ -224,6 +224,9 @@
   bool SupportsFoldingAlpha() const;
 
  private:
+  friend class PaintOpReader;
+  friend class PaintOpWriter;
+
   friend const SkPaint& ToSkPaint(const PaintFlags& flags);
   friend const SkPaint* ToSkPaint(const PaintFlags* flags);
 
diff --git a/cc/paint/paint_op_buffer.cc b/cc/paint/paint_op_buffer.cc
index 981622d..644b8550 100644
--- a/cc/paint/paint_op_buffer.cc
+++ b/cc/paint/paint_op_buffer.cc
@@ -6,6 +6,8 @@
 
 #include "base/containers/stack_container.h"
 #include "cc/paint/display_item_list.h"
+#include "cc/paint/paint_op_reader.h"
+#include "cc/paint/paint_op_writer.h"
 #include "cc/paint/paint_record.h"
 #include "third_party/skia/include/core/SkAnnotation.h"
 #include "third_party/skia/include/core/SkCanvas.h"
@@ -136,6 +138,24 @@
 static_assert(kNumOpTypes == TYPES(M), "Missing op in list");
 #undef M
 
+using SerializeFunction = size_t (*)(const PaintOp* op,
+                                     void* memory,
+                                     size_t size,
+                                     const PaintOp::SerializeOptions& options);
+#define M(T) &T::Serialize,
+static const SerializeFunction g_serialize_functions[kNumOpTypes] = {TYPES(M)};
+#undef M
+
+using DeserializeFunction = PaintOp* (*)(const void* input,
+                                         size_t input_size,
+                                         void* output,
+                                         size_t output_size);
+
+#define M(T) &T::Deserialize,
+static const DeserializeFunction g_deserialize_functions[kNumOpTypes] = {
+    TYPES(M)};
+#undef M
+
 using RasterFunction = void (*)(const PaintOp* op,
                                 SkCanvas* canvas,
                                 const SkMatrix& original_ctm);
@@ -187,6 +207,7 @@
 #undef TYPES
 
 SkRect PaintOp::kUnsetRect = {SK_ScalarInfinity, 0, 0, 0};
+const size_t PaintOp::kMaxSkip;
 
 std::string PaintOpTypeToString(PaintOpType type) {
   switch (type) {
@@ -254,6 +275,782 @@
   return "UNKNOWN";
 }
 
+template <typename T>
+size_t SimpleSerialize(const PaintOp* op, void* memory, size_t size) {
+  if (sizeof(T) > size)
+    return 0;
+  memcpy(memory, op, sizeof(T));
+  return sizeof(T);
+}
+
+size_t AnnotateOp::Serialize(const PaintOp* base_op,
+                             void* memory,
+                             size_t size,
+                             const SerializeOptions& options) {
+  auto* op = static_cast<const AnnotateOp*>(base_op);
+  PaintOpWriter helper(memory, size);
+  helper.Write(op->annotation_type);
+  helper.Write(op->rect);
+  helper.Write(op->data);
+  return helper.size();
+}
+
+size_t ClipPathOp::Serialize(const PaintOp* base_op,
+                             void* memory,
+                             size_t size,
+                             const SerializeOptions& options) {
+  auto* op = static_cast<const ClipPathOp*>(base_op);
+  PaintOpWriter helper(memory, size);
+  helper.Write(op->path);
+  helper.Write(op->op);
+  helper.Write(op->antialias);
+  return helper.size();
+}
+
+size_t ClipRectOp::Serialize(const PaintOp* op,
+                             void* memory,
+                             size_t size,
+                             const SerializeOptions& options) {
+  return SimpleSerialize<ClipRectOp>(op, memory, size);
+}
+
+size_t ClipRRectOp::Serialize(const PaintOp* op,
+                              void* memory,
+                              size_t size,
+                              const SerializeOptions& options) {
+  return SimpleSerialize<ClipRRectOp>(op, memory, size);
+}
+
+size_t ConcatOp::Serialize(const PaintOp* op,
+                           void* memory,
+                           size_t size,
+                           const SerializeOptions& options) {
+  return SimpleSerialize<ConcatOp>(op, memory, size);
+}
+
+size_t DrawArcOp::Serialize(const PaintOp* base_op,
+                            void* memory,
+                            size_t size,
+                            const SerializeOptions& options) {
+  auto* op = static_cast<const DrawArcOp*>(base_op);
+  PaintOpWriter helper(memory, size);
+  helper.Write(op->flags);
+  helper.Write(op->oval);
+  helper.Write(op->start_angle);
+  helper.Write(op->sweep_angle);
+  helper.Write(op->use_center);
+  return helper.size();
+}
+
+size_t DrawCircleOp::Serialize(const PaintOp* base_op,
+                               void* memory,
+                               size_t size,
+                               const SerializeOptions& options) {
+  auto* op = static_cast<const DrawCircleOp*>(base_op);
+  PaintOpWriter helper(memory, size);
+  helper.Write(op->flags);
+  helper.Write(op->cx);
+  helper.Write(op->cy);
+  helper.Write(op->radius);
+  return helper.size();
+}
+
+size_t DrawColorOp::Serialize(const PaintOp* op,
+                              void* memory,
+                              size_t size,
+                              const SerializeOptions& options) {
+  return SimpleSerialize<DrawColorOp>(op, memory, size);
+}
+
+size_t DrawDRRectOp::Serialize(const PaintOp* base_op,
+                               void* memory,
+                               size_t size,
+                               const SerializeOptions& options) {
+  auto* op = static_cast<const DrawDRRectOp*>(base_op);
+  PaintOpWriter helper(memory, size);
+  helper.Write(op->flags);
+  helper.Write(op->outer);
+  helper.Write(op->inner);
+  return helper.size();
+}
+
+size_t DrawImageOp::Serialize(const PaintOp* base_op,
+                              void* memory,
+                              size_t size,
+                              const SerializeOptions& options) {
+  auto* op = static_cast<const DrawImageOp*>(base_op);
+  PaintOpWriter helper(memory, size);
+  helper.Write(op->flags);
+  helper.Write(op->image, options.decode_cache);
+  helper.Write(op->left);
+  helper.Write(op->top);
+  return helper.size();
+}
+
+size_t DrawImageRectOp::Serialize(const PaintOp* base_op,
+                                  void* memory,
+                                  size_t size,
+                                  const SerializeOptions& options) {
+  auto* op = static_cast<const DrawImageRectOp*>(base_op);
+  PaintOpWriter helper(memory, size);
+  helper.Write(op->flags);
+  helper.Write(op->image, options.decode_cache);
+  helper.Write(op->src);
+  helper.Write(op->dst);
+  helper.Write(op->constraint);
+  return helper.size();
+}
+
+size_t DrawIRectOp::Serialize(const PaintOp* base_op,
+                              void* memory,
+                              size_t size,
+                              const SerializeOptions& options) {
+  auto* op = static_cast<const DrawIRectOp*>(base_op);
+  PaintOpWriter helper(memory, size);
+  helper.Write(op->flags);
+  helper.Write(op->rect);
+  return helper.size();
+}
+
+size_t DrawLineOp::Serialize(const PaintOp* base_op,
+                             void* memory,
+                             size_t size,
+                             const SerializeOptions& options) {
+  auto* op = static_cast<const DrawLineOp*>(base_op);
+  PaintOpWriter helper(memory, size);
+  helper.Write(op->flags);
+  helper.Write(op->x0);
+  helper.Write(op->y0);
+  helper.Write(op->x1);
+  helper.Write(op->y1);
+  return helper.size();
+}
+
+size_t DrawOvalOp::Serialize(const PaintOp* base_op,
+                             void* memory,
+                             size_t size,
+                             const SerializeOptions& options) {
+  auto* op = static_cast<const DrawOvalOp*>(base_op);
+  PaintOpWriter helper(memory, size);
+  helper.Write(op->flags);
+  helper.Write(op->oval);
+  return helper.size();
+}
+
+size_t DrawPathOp::Serialize(const PaintOp* base_op,
+                             void* memory,
+                             size_t size,
+                             const SerializeOptions& options) {
+  auto* op = static_cast<const DrawPathOp*>(base_op);
+  PaintOpWriter helper(memory, size);
+  helper.Write(op->flags);
+  helper.Write(op->path);
+  return helper.size();
+}
+
+size_t DrawPosTextOp::Serialize(const PaintOp* base_op,
+                                void* memory,
+                                size_t size,
+                                const SerializeOptions& options) {
+  auto* op = static_cast<const DrawPosTextOp*>(base_op);
+  PaintOpWriter helper(memory, size);
+  helper.Write(op->flags);
+  helper.Write(op->count);
+  helper.Write(op->bytes);
+  helper.WriteArray(op->count, op->GetArray());
+  helper.WriteData(op->bytes, op->GetData());
+  return helper.size();
+}
+
+size_t DrawRecordOp::Serialize(const PaintOp* op,
+                               void* memory,
+                               size_t size,
+                               const SerializeOptions& options) {
+  // TODO(enne): these must be flattened.  Serializing this will not do
+  // anything.
+  NOTREACHED();
+  return 0u;
+}
+
+size_t DrawRectOp::Serialize(const PaintOp* base_op,
+                             void* memory,
+                             size_t size,
+                             const SerializeOptions& options) {
+  auto* op = static_cast<const DrawRectOp*>(base_op);
+  PaintOpWriter helper(memory, size);
+  helper.Write(op->flags);
+  helper.Write(op->rect);
+  return helper.size();
+}
+
+size_t DrawRRectOp::Serialize(const PaintOp* base_op,
+                              void* memory,
+                              size_t size,
+                              const SerializeOptions& options) {
+  auto* op = static_cast<const DrawRRectOp*>(base_op);
+  PaintOpWriter helper(memory, size);
+  helper.Write(op->flags);
+  helper.Write(op->rrect);
+  return helper.size();
+}
+
+size_t DrawTextOp::Serialize(const PaintOp* base_op,
+                             void* memory,
+                             size_t size,
+                             const SerializeOptions& options) {
+  auto* op = static_cast<const DrawTextOp*>(base_op);
+  PaintOpWriter helper(memory, size);
+  helper.Write(op->flags);
+  helper.Write(op->x);
+  helper.Write(op->y);
+  helper.Write(op->bytes);
+  helper.WriteData(op->bytes, op->GetData());
+  return helper.size();
+}
+
+size_t DrawTextBlobOp::Serialize(const PaintOp* base_op,
+                                 void* memory,
+                                 size_t size,
+                                 const SerializeOptions& options) {
+  auto* op = static_cast<const DrawTextBlobOp*>(base_op);
+  PaintOpWriter helper(memory, size);
+  helper.Write(op->flags);
+  helper.Write(op->x);
+  helper.Write(op->y);
+  helper.Write(op->blob);
+  return helper.size();
+}
+
+size_t NoopOp::Serialize(const PaintOp* op,
+                         void* memory,
+                         size_t size,
+                         const SerializeOptions& options) {
+  return SimpleSerialize<NoopOp>(op, memory, size);
+}
+
+size_t RestoreOp::Serialize(const PaintOp* op,
+                            void* memory,
+                            size_t size,
+                            const SerializeOptions& options) {
+  return SimpleSerialize<RestoreOp>(op, memory, size);
+}
+
+size_t RotateOp::Serialize(const PaintOp* op,
+                           void* memory,
+                           size_t size,
+                           const SerializeOptions& options) {
+  return SimpleSerialize<RotateOp>(op, memory, size);
+}
+
+size_t SaveOp::Serialize(const PaintOp* op,
+                         void* memory,
+                         size_t size,
+                         const SerializeOptions& options) {
+  return SimpleSerialize<SaveOp>(op, memory, size);
+}
+
+size_t SaveLayerOp::Serialize(const PaintOp* base_op,
+                              void* memory,
+                              size_t size,
+                              const SerializeOptions& options) {
+  auto* op = static_cast<const SaveLayerOp*>(base_op);
+  PaintOpWriter helper(memory, size);
+  helper.Write(op->flags);
+  helper.Write(op->bounds);
+  return helper.size();
+}
+
+size_t SaveLayerAlphaOp::Serialize(const PaintOp* op,
+                                   void* memory,
+                                   size_t size,
+                                   const SerializeOptions& options) {
+  return SimpleSerialize<SaveLayerAlphaOp>(op, memory, size);
+}
+
+size_t ScaleOp::Serialize(const PaintOp* op,
+                          void* memory,
+                          size_t size,
+                          const SerializeOptions& options) {
+  return SimpleSerialize<ScaleOp>(op, memory, size);
+}
+
+size_t SetMatrixOp::Serialize(const PaintOp* op,
+                              void* memory,
+                              size_t size,
+                              const SerializeOptions& options) {
+  return SimpleSerialize<SetMatrixOp>(op, memory, size);
+}
+
+size_t TranslateOp::Serialize(const PaintOp* op,
+                              void* memory,
+                              size_t size,
+                              const SerializeOptions& options) {
+  return SimpleSerialize<TranslateOp>(op, memory, size);
+}
+
+template <typename T>
+void UpdateTypeAndSkip(T* op) {
+  op->type = static_cast<uint8_t>(T::kType);
+  op->skip = MathUtil::UncheckedRoundUp(sizeof(T), PaintOpBuffer::PaintOpAlign);
+}
+
+template <typename T>
+PaintOp* SimpleDeserialize(const void* input,
+                           size_t input_size,
+                           void* output,
+                           size_t output_size) {
+  if (input_size < sizeof(T))
+    return nullptr;
+  memcpy(output, input, sizeof(T));
+
+  T* op = reinterpret_cast<T*>(output);
+  // Type and skip were already read once, so could have been changed.
+  // Don't trust them and clobber them with something valid.
+  UpdateTypeAndSkip(op);
+  return op;
+}
+
+PaintOp* AnnotateOp::Deserialize(const void* input,
+                                 size_t input_size,
+                                 void* output,
+                                 size_t output_size) {
+  CHECK_GE(output_size, sizeof(AnnotateOp));
+  AnnotateOp* op = new (output) AnnotateOp;
+
+  PaintOpReader helper(input, input_size);
+  helper.Read(&op->annotation_type);
+  helper.Read(&op->rect);
+  helper.Read(&op->data);
+  if (!helper.valid()) {
+    op->~AnnotateOp();
+    return nullptr;
+  }
+
+  UpdateTypeAndSkip(op);
+  return op;
+}
+
+PaintOp* ClipPathOp::Deserialize(const void* input,
+                                 size_t input_size,
+                                 void* output,
+                                 size_t output_size) {
+  CHECK_GE(output_size, sizeof(ClipPathOp));
+  ClipPathOp* op = new (output) ClipPathOp;
+
+  PaintOpReader helper(input, input_size);
+  helper.Read(&op->path);
+  helper.Read(&op->op);
+  helper.Read(&op->antialias);
+  if (!helper.valid()) {
+    op->~ClipPathOp();
+    return nullptr;
+  }
+
+  UpdateTypeAndSkip(op);
+  return op;
+}
+
+PaintOp* ClipRectOp::Deserialize(const void* input,
+                                 size_t input_size,
+                                 void* output,
+                                 size_t output_size) {
+  return SimpleDeserialize<ClipRectOp>(input, input_size, output, output_size);
+}
+
+PaintOp* ClipRRectOp::Deserialize(const void* input,
+                                  size_t input_size,
+                                  void* output,
+                                  size_t output_size) {
+  return SimpleDeserialize<ClipRRectOp>(input, input_size, output, output_size);
+}
+
+PaintOp* ConcatOp::Deserialize(const void* input,
+                               size_t input_size,
+                               void* output,
+                               size_t output_size) {
+  return SimpleDeserialize<ConcatOp>(input, input_size, output, output_size);
+}
+
+PaintOp* DrawArcOp::Deserialize(const void* input,
+                                size_t input_size,
+                                void* output,
+                                size_t output_size) {
+  CHECK_GE(output_size, sizeof(DrawArcOp));
+  DrawArcOp* op = new (output) DrawArcOp;
+
+  PaintOpReader helper(input, input_size);
+  helper.Read(&op->flags);
+  helper.Read(&op->oval);
+  helper.Read(&op->start_angle);
+  helper.Read(&op->sweep_angle);
+  helper.Read(&op->use_center);
+  if (!helper.valid()) {
+    op->~DrawArcOp();
+    return nullptr;
+  }
+  UpdateTypeAndSkip(op);
+  return op;
+}
+
+PaintOp* DrawCircleOp::Deserialize(const void* input,
+                                   size_t input_size,
+                                   void* output,
+                                   size_t output_size) {
+  CHECK_GE(output_size, sizeof(DrawCircleOp));
+  DrawCircleOp* op = new (output) DrawCircleOp;
+
+  PaintOpReader helper(input, input_size);
+  helper.Read(&op->flags);
+  helper.Read(&op->cx);
+  helper.Read(&op->cy);
+  helper.Read(&op->radius);
+  if (!helper.valid()) {
+    op->~DrawCircleOp();
+    return nullptr;
+  }
+  UpdateTypeAndSkip(op);
+  return op;
+}
+
+PaintOp* DrawColorOp::Deserialize(const void* input,
+                                  size_t input_size,
+                                  void* output,
+                                  size_t output_size) {
+  return SimpleDeserialize<DrawColorOp>(input, input_size, output, output_size);
+}
+
+PaintOp* DrawDRRectOp::Deserialize(const void* input,
+                                   size_t input_size,
+                                   void* output,
+                                   size_t output_size) {
+  CHECK_GE(output_size, sizeof(DrawDRRectOp));
+  DrawDRRectOp* op = new (output) DrawDRRectOp;
+
+  PaintOpReader helper(input, input_size);
+  helper.Read(&op->flags);
+  helper.Read(&op->outer);
+  helper.Read(&op->inner);
+  if (!helper.valid()) {
+    op->~DrawDRRectOp();
+    return nullptr;
+  }
+  UpdateTypeAndSkip(op);
+  return op;
+}
+
+PaintOp* DrawImageOp::Deserialize(const void* input,
+                                  size_t input_size,
+                                  void* output,
+                                  size_t output_size) {
+  CHECK_GE(output_size, sizeof(DrawImageOp));
+  DrawImageOp* op = new (output) DrawImageOp;
+
+  PaintOpReader helper(input, input_size);
+  helper.Read(&op->flags);
+  helper.Read(&op->image);
+  helper.Read(&op->left);
+  helper.Read(&op->top);
+  if (!helper.valid()) {
+    op->~DrawImageOp();
+    return nullptr;
+  }
+  UpdateTypeAndSkip(op);
+  return op;
+}
+
+PaintOp* DrawImageRectOp::Deserialize(const void* input,
+                                      size_t input_size,
+                                      void* output,
+                                      size_t output_size) {
+  CHECK_GE(output_size, sizeof(DrawImageRectOp));
+  DrawImageRectOp* op = new (output) DrawImageRectOp;
+
+  PaintOpReader helper(input, input_size);
+  helper.Read(&op->flags);
+  helper.Read(&op->image);
+  helper.Read(&op->src);
+  helper.Read(&op->dst);
+  helper.Read(&op->constraint);
+  if (!helper.valid()) {
+    op->~DrawImageRectOp();
+    return nullptr;
+  }
+  UpdateTypeAndSkip(op);
+  return op;
+}
+
+PaintOp* DrawIRectOp::Deserialize(const void* input,
+                                  size_t input_size,
+                                  void* output,
+                                  size_t output_size) {
+  CHECK_GE(output_size, sizeof(DrawIRectOp));
+  DrawIRectOp* op = new (output) DrawIRectOp;
+
+  PaintOpReader helper(input, input_size);
+  helper.Read(&op->flags);
+  helper.Read(&op->rect);
+  if (!helper.valid()) {
+    op->~DrawIRectOp();
+    return nullptr;
+  }
+  UpdateTypeAndSkip(op);
+  return op;
+}
+
+PaintOp* DrawLineOp::Deserialize(const void* input,
+                                 size_t input_size,
+                                 void* output,
+                                 size_t output_size) {
+  CHECK_GE(output_size, sizeof(DrawLineOp));
+  DrawLineOp* op = new (output) DrawLineOp;
+
+  PaintOpReader helper(input, input_size);
+  helper.Read(&op->flags);
+  helper.Read(&op->x0);
+  helper.Read(&op->y0);
+  helper.Read(&op->x1);
+  helper.Read(&op->y1);
+  if (!helper.valid()) {
+    op->~DrawLineOp();
+    return nullptr;
+  }
+  UpdateTypeAndSkip(op);
+  return op;
+}
+
+PaintOp* DrawOvalOp::Deserialize(const void* input,
+                                 size_t input_size,
+                                 void* output,
+                                 size_t output_size) {
+  CHECK_GE(output_size, sizeof(DrawOvalOp));
+  DrawOvalOp* op = new (output) DrawOvalOp;
+
+  PaintOpReader helper(input, input_size);
+  helper.Read(&op->flags);
+  helper.Read(&op->oval);
+  if (!helper.valid()) {
+    op->~DrawOvalOp();
+    return nullptr;
+  }
+  UpdateTypeAndSkip(op);
+  return op;
+}
+
+PaintOp* DrawPathOp::Deserialize(const void* input,
+                                 size_t input_size,
+                                 void* output,
+                                 size_t output_size) {
+  CHECK_GE(output_size, sizeof(DrawPathOp));
+  DrawPathOp* op = new (output) DrawPathOp;
+
+  PaintOpReader helper(input, input_size);
+  helper.Read(&op->flags);
+  helper.Read(&op->path);
+  if (!helper.valid()) {
+    op->~DrawPathOp();
+    return nullptr;
+  }
+  UpdateTypeAndSkip(op);
+  return op;
+}
+
+PaintOp* DrawPosTextOp::Deserialize(const void* input,
+                                    size_t input_size,
+                                    void* output,
+                                    size_t output_size) {
+  // TODO(enne): This is a bit of a weird condition, but to avoid the code
+  // complexity of every Deserialize function being able to (re)allocate
+  // an aligned buffer of the right size, this function asserts that it
+  // will have enough size for the extra data.  It's guaranteed that any extra
+  // memory is at most |input_size| so that plus the op size is an upper bound.
+  // The caller has to awkwardly do this allocation though, sorry.
+  CHECK_GE(output_size, sizeof(DrawPosTextOp) + input_size);
+  DrawPosTextOp* op = new (output) DrawPosTextOp;
+
+  PaintOpReader helper(input, input_size);
+  helper.Read(&op->flags);
+  helper.Read(&op->count);
+  helper.Read(&op->bytes);
+  helper.ReadArray(op->count, op->GetArray());
+  helper.ReadData(op->bytes, op->GetData());
+  if (!helper.valid()) {
+    op->~DrawPosTextOp();
+    return nullptr;
+  }
+
+  op->type = static_cast<uint8_t>(PaintOpType::DrawPosText);
+  op->skip = MathUtil::UncheckedRoundUp(
+      sizeof(DrawPosTextOp) + op->bytes + sizeof(SkPoint) * op->count,
+      PaintOpBuffer::PaintOpAlign);
+
+  return op;
+}
+
+PaintOp* DrawRecordOp::Deserialize(const void* input,
+                                   size_t input_size,
+                                   void* output,
+                                   size_t output_size) {
+  // TODO(enne): these must be flattened and not sent directly.
+  // TODO(enne): could also consider caching these service side.
+  NOTREACHED();
+  return nullptr;
+}
+
+PaintOp* DrawRectOp::Deserialize(const void* input,
+                                 size_t input_size,
+                                 void* output,
+                                 size_t output_size) {
+  CHECK_GE(output_size, sizeof(DrawRectOp));
+  DrawRectOp* op = new (output) DrawRectOp;
+
+  PaintOpReader helper(input, input_size);
+  helper.Read(&op->flags);
+  helper.Read(&op->rect);
+  if (!helper.valid()) {
+    op->~DrawRectOp();
+    return nullptr;
+  }
+  UpdateTypeAndSkip(op);
+  return op;
+}
+
+PaintOp* DrawRRectOp::Deserialize(const void* input,
+                                  size_t input_size,
+                                  void* output,
+                                  size_t output_size) {
+  CHECK_GE(output_size, sizeof(DrawRRectOp));
+  DrawRRectOp* op = new (output) DrawRRectOp;
+
+  PaintOpReader helper(input, input_size);
+  helper.Read(&op->flags);
+  helper.Read(&op->rrect);
+  if (!helper.valid()) {
+    op->~DrawRRectOp();
+    return nullptr;
+  }
+  UpdateTypeAndSkip(op);
+  return op;
+}
+
+PaintOp* DrawTextOp::Deserialize(const void* input,
+                                 size_t input_size,
+                                 void* output,
+                                 size_t output_size) {
+  CHECK_GE(output_size, sizeof(DrawTextOp) + input_size);
+  DrawTextOp* op = new (output) DrawTextOp;
+
+  PaintOpReader helper(input, input_size);
+  helper.Read(&op->flags);
+  helper.Read(&op->x);
+  helper.Read(&op->y);
+  helper.Read(&op->bytes);
+  helper.ReadData(op->bytes, op->GetData());
+  if (!helper.valid()) {
+    op->~DrawTextOp();
+    return nullptr;
+  }
+
+  op->type = static_cast<uint8_t>(PaintOpType::DrawText);
+  op->skip = MathUtil::UncheckedRoundUp(sizeof(DrawTextOp) + op->bytes,
+                                        PaintOpBuffer::PaintOpAlign);
+  return op;
+}
+
+PaintOp* DrawTextBlobOp::Deserialize(const void* input,
+                                     size_t input_size,
+                                     void* output,
+                                     size_t output_size) {
+  CHECK_GE(output_size, sizeof(DrawTextBlobOp));
+  DrawTextBlobOp* op = new (output) DrawTextBlobOp;
+
+  PaintOpReader helper(input, input_size);
+  helper.Read(&op->flags);
+  helper.Read(&op->x);
+  helper.Read(&op->y);
+  helper.Read(&op->blob);
+  if (!helper.valid()) {
+    op->~DrawTextBlobOp();
+    return nullptr;
+  }
+  UpdateTypeAndSkip(op);
+  return op;
+}
+
+PaintOp* NoopOp::Deserialize(const void* input,
+                             size_t input_size,
+                             void* output,
+                             size_t output_size) {
+  return SimpleDeserialize<NoopOp>(input, input_size, output, output_size);
+}
+
+PaintOp* RestoreOp::Deserialize(const void* input,
+                                size_t input_size,
+                                void* output,
+                                size_t output_size) {
+  return SimpleDeserialize<RestoreOp>(input, input_size, output, output_size);
+}
+
+PaintOp* RotateOp::Deserialize(const void* input,
+                               size_t input_size,
+                               void* output,
+                               size_t output_size) {
+  return SimpleDeserialize<RotateOp>(input, input_size, output, output_size);
+}
+
+PaintOp* SaveOp::Deserialize(const void* input,
+                             size_t input_size,
+                             void* output,
+                             size_t output_size) {
+  return SimpleDeserialize<SaveOp>(input, input_size, output, output_size);
+}
+
+PaintOp* SaveLayerOp::Deserialize(const void* input,
+                                  size_t input_size,
+                                  void* output,
+                                  size_t output_size) {
+  CHECK_GE(output_size, sizeof(SaveLayerOp));
+  SaveLayerOp* op = new (output) SaveLayerOp;
+
+  PaintOpReader helper(input, input_size);
+  helper.Read(&op->flags);
+  helper.Read(&op->bounds);
+  if (!helper.valid()) {
+    op->~SaveLayerOp();
+    return nullptr;
+  }
+  UpdateTypeAndSkip(op);
+  return op;
+}
+
+PaintOp* SaveLayerAlphaOp::Deserialize(const void* input,
+                                       size_t input_size,
+                                       void* output,
+                                       size_t output_size) {
+  return SimpleDeserialize<SaveLayerAlphaOp>(input, input_size, output,
+                                             output_size);
+}
+
+PaintOp* ScaleOp::Deserialize(const void* input,
+                              size_t input_size,
+                              void* output,
+                              size_t output_size) {
+  return SimpleDeserialize<ScaleOp>(input, input_size, output, output_size);
+}
+
+PaintOp* SetMatrixOp::Deserialize(const void* input,
+                                  size_t input_size,
+                                  void* output,
+                                  size_t output_size) {
+  return SimpleDeserialize<SetMatrixOp>(input, input_size, output, output_size);
+}
+
+PaintOp* TranslateOp::Deserialize(const void* input,
+                                  size_t input_size,
+                                  void* output,
+                                  size_t output_size) {
+  return SimpleDeserialize<TranslateOp>(input, input_size, output, output_size);
+}
+
 void AnnotateOp::Raster(const PaintOp* base_op,
                         SkCanvas* canvas,
                         const SkMatrix& original_ctm) {
@@ -516,6 +1313,59 @@
   g_raster_alpha_functions[type](this, canvas, bounds, alpha);
 }
 
+size_t PaintOp::Serialize(void* memory,
+                          size_t size,
+                          const SerializeOptions& options) const {
+  // Need at least enough room for a skip/type header.
+  if (size < 4)
+    return 0u;
+
+  DCHECK_EQ(0u,
+            reinterpret_cast<uintptr_t>(memory) % PaintOpBuffer::PaintOpAlign);
+
+  size_t written = g_serialize_functions[type](this, memory, size, options);
+  DCHECK_LE(written, size);
+  if (written < 4)
+    return 0u;
+
+  size_t aligned_written =
+      MathUtil::UncheckedRoundUp(written, PaintOpBuffer::PaintOpAlign);
+  if (aligned_written >= kMaxSkip)
+    return 0u;
+  if (aligned_written > size)
+    return 0u;
+
+  // Update skip and type now that the size is known.
+  uint32_t skip = static_cast<uint32_t>(aligned_written);
+  static_cast<uint32_t*>(memory)[0] = type | skip << 8;
+  return skip;
+}
+
+PaintOp* PaintOp::Deserialize(const void* input,
+                              size_t input_size,
+                              void* output,
+                              size_t output_size) {
+  // TODO(enne): assert that output_size is big enough.
+  const PaintOp* serialized = reinterpret_cast<const PaintOp*>(input);
+  uint32_t skip = serialized->skip;
+  if (input_size < skip)
+    return nullptr;
+  if (skip % PaintOpBuffer::PaintOpAlign != 0)
+    return nullptr;
+  uint8_t type = serialized->type;
+  if (type > static_cast<uint8_t>(PaintOpType::LastPaintOpType))
+    return nullptr;
+
+  return g_deserialize_functions[serialized->type](input, skip, output,
+                                                   output_size);
+}
+
+void PaintOp::DestroyThis() {
+  auto func = g_destructor_functions[type];
+  if (func)
+    func(this);
+}
+
 int ClipPathOp::CountSlowPaths() const {
   return antialias && !path.isConvex() ? 1 : 0;
 }
@@ -563,6 +1413,8 @@
   return record->HasNonAAPaint();
 }
 
+AnnotateOp::AnnotateOp() = default;
+
 AnnotateOp::AnnotateOp(PaintCanvas::AnnotationType annotation_type,
                        const SkRect& rect,
                        sk_sp<SkData> data)
@@ -587,6 +1439,8 @@
 
 DrawImageOp::~DrawImageOp() = default;
 
+DrawImageRectOp::DrawImageRectOp() = default;
+
 DrawImageRectOp::DrawImageRectOp(const PaintImage& image,
                                  const SkRect& src,
                                  const SkRect& dst,
@@ -604,6 +1458,8 @@
 
 DrawImageRectOp::~DrawImageRectOp() = default;
 
+DrawPosTextOp::DrawPosTextOp() = default;
+
 DrawPosTextOp::DrawPosTextOp(size_t bytes,
                              size_t count,
                              const PaintFlags& flags)
@@ -611,6 +1467,8 @@
 
 DrawPosTextOp::~DrawPosTextOp() = default;
 
+DrawRecordOp::DrawRecordOp() = default;
+
 DrawRecordOp::DrawRecordOp(sk_sp<const PaintRecord> record)
     : record(std::move(record)) {}
 
@@ -624,6 +1482,8 @@
   return record->HasDiscardableImages();
 }
 
+DrawTextBlobOp::DrawTextBlobOp() = default;
+
 DrawTextBlobOp::DrawTextBlobOp(sk_sp<SkTextBlob> blob,
                                SkScalar x,
                                SkScalar y,
@@ -658,11 +1518,8 @@
 }
 
 void PaintOpBuffer::Reset() {
-  for (auto* op : Iterator(this)) {
-    auto func = g_destructor_functions[op->type];
-    if (func)
-      func(op);
-  }
+  for (auto* op : Iterator(this))
+    op->DestroyThis();
 
   // Leave data_ allocated, reserved_ unchanged.
   used_ = 0;
@@ -793,7 +1650,7 @@
   // Compute a skip such that all ops in the buffer are aligned to the
   // maximum required alignment of all ops.
   size_t skip = MathUtil::UncheckedRoundUp(sizeof_op + bytes, PaintOpAlign);
-  DCHECK_LT(skip, static_cast<size_t>(1) << 24);
+  DCHECK_LT(skip, PaintOp::kMaxSkip);
   if (used_ + skip > reserved_) {
     // Start reserved_ at kInitialBufferSize and then double.
     // ShrinkToFit can make this smaller afterwards.
diff --git a/cc/paint/paint_op_buffer.h b/cc/paint/paint_op_buffer.h
index d3cba68a2..fbbb65d6c 100644
--- a/cc/paint/paint_op_buffer.h
+++ b/cc/paint/paint_op_buffer.h
@@ -27,6 +27,8 @@
 
 namespace cc {
 
+class ImageDecodeCache;
+
 class CC_PAINT_EXPORT ThreadsafeMatrix : public SkMatrix {
  public:
   explicit ThreadsafeMatrix(const SkMatrix& matrix) : SkMatrix(matrix) {
@@ -39,8 +41,18 @@
   explicit ThreadsafePath(const SkPath& path) : SkPath(path) {
     updateBoundsCache();
   }
+  ThreadsafePath() { updateBoundsCache(); }
 };
 
+// See PaintOp::Serialize/Deserialize for comments.  Derived Serialize types
+// don't write the 4 byte type/skip header because they don't know how much
+// data they will need to write.  PaintOp::Serialize itself must update it.
+#define HAS_SERIALIZATION_FUNCTIONS()                                   \
+  static size_t Serialize(const PaintOp* op, void* memory, size_t size, \
+                          const SerializeOptions& options);             \
+  static PaintOp* Deserialize(const void* input, size_t input_size,     \
+                              void* output, size_t output_size);
+
 enum class PaintOpType : uint8_t {
   Annotate,
   ClipPath,
@@ -75,9 +87,10 @@
   LastPaintOpType = Translate,
 };
 
-std::string PaintOpTypeToString(PaintOpType type);
+CC_PAINT_EXPORT std::string PaintOpTypeToString(PaintOpType type);
 
-struct CC_PAINT_EXPORT PaintOp {
+class CC_PAINT_EXPORT PaintOp {
+ public:
   uint32_t type : 8;
   uint32_t skip : 24;
 
@@ -90,6 +103,28 @@
   void Raster(SkCanvas* canvas, const SkMatrix& original_ctm) const;
   bool IsDrawOp() const;
 
+  struct SerializeOptions {
+    ImageDecodeCache* decode_cache = nullptr;
+  };
+
+  // Subclasses should provide a static Serialize() method called from here.
+  // If the op can be serialized to |memory| in no more than |size| bytes,
+  // then return the number of bytes written.  If it won't fit, return 0.
+  size_t Serialize(void* memory,
+                   size_t size,
+                   const SerializeOptions& options) const;
+
+  // Deserializes a PaintOp of this type from a given buffer |input| of
+  // at most |input_size| bytes.  Returns null on any errors.
+  // The PaintOp is deserialized into the |output| buffer and returned
+  // if valid.  nullptr is returned if the deserialization fails.
+  // |output_size| must be at least LargestPaintOp + serialized->skip,
+  // to fit all ops.  The caller is responsible for destroying these ops.
+  static PaintOp* Deserialize(const void* input,
+                              size_t input_size,
+                              void* output,
+                              size_t output_size);
+
   // Only valid for draw ops.
   void RasterWithAlpha(SkCanvas* canvas,
                        const SkRect& bounds,
@@ -107,14 +142,20 @@
   // and display lists.  This doesn't count other objects like paths or blobs.
   size_t AdditionalBytesUsed() const { return 0; }
 
+  // Run the destructor for the derived op type.  Ops are usually contained in
+  // memory buffers and so don't have their destructors run automatically.
+  void DestroyThis();
+
   static constexpr bool kIsDrawOp = false;
   static constexpr bool kHasPaintFlags = false;
+  // Since skip and type fit in a uint32_t, this is the max size of skip.
+  static constexpr size_t kMaxSkip = static_cast<size_t>(1 << 24);
   static SkRect kUnsetRect;
 };
 
-struct CC_PAINT_EXPORT PaintOpWithFlags : PaintOp {
+class CC_PAINT_EXPORT PaintOpWithFlags : public PaintOp {
+ public:
   static constexpr bool kHasPaintFlags = true;
-
   explicit PaintOpWithFlags(const PaintFlags& flags) : flags(flags) {}
 
   int CountSlowPathsFromFlags() const { return flags.getPathEffect() ? 1 : 0; }
@@ -135,9 +176,13 @@
   // a const PaintOpWithFlags* parameter so that it can be used as a function
   // pointer.
   PaintFlags flags;
+
+ protected:
+  PaintOpWithFlags() = default;
 };
 
-struct CC_PAINT_EXPORT PaintOpWithData : PaintOpWithFlags {
+class CC_PAINT_EXPORT PaintOpWithData : public PaintOpWithFlags {
+ public:
   // Having data is just a helper for ops that have a varying amount of data and
   // want a way to store that inline.  This is for ops that pass in a
   // void* and a length.  The void* data is assumed to not have any alignment
@@ -150,6 +195,8 @@
   size_t bytes;
 
  protected:
+  PaintOpWithData() = default;
+
   // For some derived object T, return the internally stored data.
   // This needs the fully derived type to know how much to offset
   // from the start of the top to the data.
@@ -173,13 +220,18 @@
   }
 };
 
-struct CC_PAINT_EXPORT PaintOpWithArrayBase : PaintOpWithFlags {
+class CC_PAINT_EXPORT PaintOpWithArrayBase : public PaintOpWithFlags {
+ public:
   explicit PaintOpWithArrayBase(const PaintFlags& flags)
       : PaintOpWithFlags(flags) {}
+
+ protected:
+  PaintOpWithArrayBase() = default;
 };
 
 template <typename M>
-struct CC_PAINT_EXPORT PaintOpWithArray : PaintOpWithArrayBase {
+class CC_PAINT_EXPORT PaintOpWithArray : public PaintOpWithArrayBase {
+ public:
   // Paint op that has a M[count] and a char[bytes].
   // Array data is stored first so that it can be aligned with T's alignment
   // with the arbitrary unaligned char data after it.
@@ -192,6 +244,8 @@
   size_t count;
 
  protected:
+  PaintOpWithArray() = default;
+
   template <typename T>
   const void* GetDataForThis(const T* op) const {
     static_assert(std::is_convertible<T, PaintOpWithArrayBase>::value,
@@ -233,7 +287,8 @@
   }
 };
 
-struct CC_PAINT_EXPORT AnnotateOp final : PaintOp {
+class CC_PAINT_EXPORT AnnotateOp final : public PaintOp {
+ public:
   enum class AnnotationType {
     URL,
     LinkToDestination,
@@ -248,13 +303,18 @@
   static void Raster(const PaintOp* op,
                      SkCanvas* canvas,
                      const SkMatrix& original_ctm);
+  HAS_SERIALIZATION_FUNCTIONS();
 
   PaintCanvas::AnnotationType annotation_type;
   SkRect rect;
   sk_sp<SkData> data;
+
+ private:
+  AnnotateOp();
 };
 
-struct CC_PAINT_EXPORT ClipPathOp final : PaintOp {
+class CC_PAINT_EXPORT ClipPathOp final : public PaintOp {
+ public:
   static constexpr PaintOpType kType = PaintOpType::ClipPath;
   ClipPathOp(SkPath path, SkClipOp op, bool antialias)
       : path(path), op(op), antialias(antialias) {}
@@ -263,26 +323,36 @@
                      const SkMatrix& original_ctm);
   int CountSlowPaths() const;
   bool HasNonAAPaint() const { return !antialias; }
+  HAS_SERIALIZATION_FUNCTIONS();
 
   ThreadsafePath path;
   SkClipOp op;
   bool antialias;
+
+ private:
+  ClipPathOp() = default;
 };
 
-struct CC_PAINT_EXPORT ClipRectOp final : PaintOp {
+class CC_PAINT_EXPORT ClipRectOp final : public PaintOp {
+ public:
   static constexpr PaintOpType kType = PaintOpType::ClipRect;
   ClipRectOp(const SkRect& rect, SkClipOp op, bool antialias)
       : rect(rect), op(op), antialias(antialias) {}
   static void Raster(const PaintOp* op,
                      SkCanvas* canvas,
                      const SkMatrix& original_ctm);
+  HAS_SERIALIZATION_FUNCTIONS();
 
   SkRect rect;
   SkClipOp op;
   bool antialias;
+
+ private:
+  ClipRectOp() = default;
 };
 
-struct CC_PAINT_EXPORT ClipRRectOp final : PaintOp {
+class CC_PAINT_EXPORT ClipRRectOp final : public PaintOp {
+ public:
   static constexpr PaintOpType kType = PaintOpType::ClipRRect;
   ClipRRectOp(const SkRRect& rrect, SkClipOp op, bool antialias)
       : rrect(rrect), op(op), antialias(antialias) {}
@@ -290,23 +360,33 @@
                      SkCanvas* canvas,
                      const SkMatrix& original_ctm);
   bool HasNonAAPaint() const { return !antialias; }
+  HAS_SERIALIZATION_FUNCTIONS();
 
   SkRRect rrect;
   SkClipOp op;
   bool antialias;
+
+ private:
+  ClipRRectOp() = default;
 };
 
-struct CC_PAINT_EXPORT ConcatOp final : PaintOp {
+class CC_PAINT_EXPORT ConcatOp final : public PaintOp {
+ public:
   static constexpr PaintOpType kType = PaintOpType::Concat;
   explicit ConcatOp(const SkMatrix& matrix) : matrix(matrix) {}
   static void Raster(const PaintOp* op,
                      SkCanvas* canvas,
                      const SkMatrix& original_ctm);
+  HAS_SERIALIZATION_FUNCTIONS();
 
   ThreadsafeMatrix matrix;
+
+ private:
+  ConcatOp() = default;
 };
 
-struct CC_PAINT_EXPORT DrawArcOp final : PaintOpWithFlags {
+class CC_PAINT_EXPORT DrawArcOp final : public PaintOpWithFlags {
+ public:
   static constexpr PaintOpType kType = PaintOpType::DrawArc;
   static constexpr bool kIsDrawOp = true;
   DrawArcOp(const SkRect& oval,
@@ -329,14 +409,19 @@
                               const PaintFlags* flags,
                               SkCanvas* canvas,
                               const SkMatrix& original_ctm);
+  HAS_SERIALIZATION_FUNCTIONS();
 
   SkRect oval;
   SkScalar start_angle;
   SkScalar sweep_angle;
   bool use_center;
+
+ private:
+  DrawArcOp() = default;
 };
 
-struct CC_PAINT_EXPORT DrawCircleOp final : PaintOpWithFlags {
+class CC_PAINT_EXPORT DrawCircleOp final : public PaintOpWithFlags {
+ public:
   static constexpr PaintOpType kType = PaintOpType::DrawCircle;
   static constexpr bool kIsDrawOp = true;
   DrawCircleOp(SkScalar cx,
@@ -354,25 +439,35 @@
                               const PaintFlags* flags,
                               SkCanvas* canvas,
                               const SkMatrix& original_ctm);
+  HAS_SERIALIZATION_FUNCTIONS();
 
   SkScalar cx;
   SkScalar cy;
   SkScalar radius;
+
+ private:
+  DrawCircleOp() = default;
 };
 
-struct CC_PAINT_EXPORT DrawColorOp final : PaintOp {
+class CC_PAINT_EXPORT DrawColorOp final : public PaintOp {
+ public:
   static constexpr PaintOpType kType = PaintOpType::DrawColor;
   static constexpr bool kIsDrawOp = true;
   DrawColorOp(SkColor color, SkBlendMode mode) : color(color), mode(mode) {}
   static void Raster(const PaintOp* op,
                      SkCanvas* canvas,
                      const SkMatrix& original_ctm);
+  HAS_SERIALIZATION_FUNCTIONS();
 
   SkColor color;
   SkBlendMode mode;
+
+ private:
+  DrawColorOp() = default;
 };
 
-struct CC_PAINT_EXPORT DrawDRRectOp final : PaintOpWithFlags {
+class CC_PAINT_EXPORT DrawDRRectOp final : public PaintOpWithFlags {
+ public:
   static constexpr PaintOpType kType = PaintOpType::DrawDRRect;
   static constexpr bool kIsDrawOp = true;
   DrawDRRectOp(const SkRRect& outer,
@@ -389,12 +484,17 @@
                               const PaintFlags* flags,
                               SkCanvas* canvas,
                               const SkMatrix& original_ctm);
+  HAS_SERIALIZATION_FUNCTIONS();
 
   SkRRect outer;
   SkRRect inner;
+
+ private:
+  DrawDRRectOp() = default;
 };
 
-struct CC_PAINT_EXPORT DrawImageOp final : PaintOpWithFlags {
+class CC_PAINT_EXPORT DrawImageOp final : public PaintOpWithFlags {
+ public:
   static constexpr PaintOpType kType = PaintOpType::DrawImage;
   static constexpr bool kIsDrawOp = true;
   DrawImageOp(const PaintImage& image,
@@ -414,13 +514,18 @@
                               const SkMatrix& original_ctm);
   bool HasDiscardableImages() const;
   bool HasNonAAPaint() const { return false; }
+  HAS_SERIALIZATION_FUNCTIONS();
 
   PaintImage image;
   SkScalar left;
   SkScalar top;
+
+ private:
+  DrawImageOp() = default;
 };
 
-struct CC_PAINT_EXPORT DrawImageRectOp final : PaintOpWithFlags {
+class CC_PAINT_EXPORT DrawImageRectOp final : public PaintOpWithFlags {
+ public:
   static constexpr PaintOpType kType = PaintOpType::DrawImageRect;
   static constexpr bool kIsDrawOp = true;
   DrawImageRectOp(const PaintImage& image,
@@ -440,14 +545,19 @@
                               SkCanvas* canvas,
                               const SkMatrix& original_ctm);
   bool HasDiscardableImages() const;
+  HAS_SERIALIZATION_FUNCTIONS();
 
   PaintImage image;
   SkRect src;
   SkRect dst;
   PaintCanvas::SrcRectConstraint constraint;
+
+ private:
+  DrawImageRectOp();
 };
 
-struct CC_PAINT_EXPORT DrawIRectOp final : PaintOpWithFlags {
+class CC_PAINT_EXPORT DrawIRectOp final : public PaintOpWithFlags {
+ public:
   static constexpr PaintOpType kType = PaintOpType::DrawIRect;
   static constexpr bool kIsDrawOp = true;
   DrawIRectOp(const SkIRect& rect, const PaintFlags& flags)
@@ -463,11 +573,16 @@
                               SkCanvas* canvas,
                               const SkMatrix& original_ctm);
   bool HasNonAAPaint() const { return false; }
+  HAS_SERIALIZATION_FUNCTIONS();
 
   SkIRect rect;
+
+ private:
+  DrawIRectOp() = default;
 };
 
-struct CC_PAINT_EXPORT DrawLineOp final : PaintOpWithFlags {
+class CC_PAINT_EXPORT DrawLineOp final : public PaintOpWithFlags {
+ public:
   static constexpr PaintOpType kType = PaintOpType::DrawLine;
   static constexpr bool kIsDrawOp = true;
   DrawLineOp(SkScalar x0,
@@ -486,6 +601,7 @@
                               const PaintFlags* flags,
                               SkCanvas* canvas,
                               const SkMatrix& original_ctm);
+  HAS_SERIALIZATION_FUNCTIONS();
 
   int CountSlowPaths() const;
 
@@ -493,9 +609,13 @@
   SkScalar y0;
   SkScalar x1;
   SkScalar y1;
+
+ private:
+  DrawLineOp() = default;
 };
 
-struct CC_PAINT_EXPORT DrawOvalOp final : PaintOpWithFlags {
+class CC_PAINT_EXPORT DrawOvalOp final : public PaintOpWithFlags {
+ public:
   static constexpr PaintOpType kType = PaintOpType::DrawOval;
   static constexpr bool kIsDrawOp = true;
   DrawOvalOp(const SkRect& oval, const PaintFlags& flags)
@@ -510,11 +630,16 @@
                               const PaintFlags* flags,
                               SkCanvas* canvas,
                               const SkMatrix& original_ctm);
+  HAS_SERIALIZATION_FUNCTIONS();
 
   SkRect oval;
+
+ private:
+  DrawOvalOp() = default;
 };
 
-struct CC_PAINT_EXPORT DrawPathOp final : PaintOpWithFlags {
+class CC_PAINT_EXPORT DrawPathOp final : public PaintOpWithFlags {
+ public:
   static constexpr PaintOpType kType = PaintOpType::DrawPath;
   static constexpr bool kIsDrawOp = true;
   DrawPathOp(const SkPath& path, const PaintFlags& flags)
@@ -530,11 +655,16 @@
                               SkCanvas* canvas,
                               const SkMatrix& original_ctm);
   int CountSlowPaths() const;
+  HAS_SERIALIZATION_FUNCTIONS();
 
   ThreadsafePath path;
+
+ private:
+  DrawPathOp() = default;
 };
 
-struct CC_PAINT_EXPORT DrawPosTextOp final : PaintOpWithArray<SkPoint> {
+class CC_PAINT_EXPORT DrawPosTextOp final : public PaintOpWithArray<SkPoint> {
+ public:
   static constexpr PaintOpType kType = PaintOpType::DrawPosText;
   static constexpr bool kIsDrawOp = true;
   DrawPosTextOp(size_t bytes, size_t count, const PaintFlags& flags);
@@ -549,14 +679,19 @@
                               const PaintFlags* flags,
                               SkCanvas* canvas,
                               const SkMatrix& original_ctm);
+  HAS_SERIALIZATION_FUNCTIONS();
 
   const void* GetData() const { return GetDataForThis(this); }
   void* GetData() { return GetDataForThis(this); }
   const SkPoint* GetArray() const { return GetArrayForThis(this); }
   SkPoint* GetArray() { return GetArrayForThis(this); }
+
+ private:
+  DrawPosTextOp();
 };
 
-struct CC_PAINT_EXPORT DrawRecordOp final : PaintOp {
+class CC_PAINT_EXPORT DrawRecordOp final : public PaintOp {
+ public:
   static constexpr PaintOpType kType = PaintOpType::DrawRecord;
   static constexpr bool kIsDrawOp = true;
   explicit DrawRecordOp(sk_sp<const PaintRecord> record);
@@ -568,11 +703,16 @@
   bool HasDiscardableImages() const;
   int CountSlowPaths() const;
   bool HasNonAAPaint() const;
+  HAS_SERIALIZATION_FUNCTIONS();
 
   sk_sp<const PaintRecord> record;
+
+ private:
+  DrawRecordOp();
 };
 
-struct CC_PAINT_EXPORT DrawRectOp final : PaintOpWithFlags {
+class CC_PAINT_EXPORT DrawRectOp final : public PaintOpWithFlags {
+ public:
   static constexpr PaintOpType kType = PaintOpType::DrawRect;
   static constexpr bool kIsDrawOp = true;
   DrawRectOp(const SkRect& rect, const PaintFlags& flags)
@@ -587,11 +727,16 @@
                               const PaintFlags* flags,
                               SkCanvas* canvas,
                               const SkMatrix& original_ctm);
+  HAS_SERIALIZATION_FUNCTIONS();
 
   SkRect rect;
+
+ private:
+  DrawRectOp() = default;
 };
 
-struct CC_PAINT_EXPORT DrawRRectOp final : PaintOpWithFlags {
+class CC_PAINT_EXPORT DrawRRectOp final : public PaintOpWithFlags {
+ public:
   static constexpr PaintOpType kType = PaintOpType::DrawRRect;
   static constexpr bool kIsDrawOp = true;
   DrawRRectOp(const SkRRect& rrect, const PaintFlags& flags)
@@ -606,11 +751,16 @@
                               const PaintFlags* flags,
                               SkCanvas* canvas,
                               const SkMatrix& original_ctm);
+  HAS_SERIALIZATION_FUNCTIONS();
 
   SkRRect rrect;
+
+ private:
+  DrawRRectOp() = default;
 };
 
-struct CC_PAINT_EXPORT DrawTextOp final : PaintOpWithData {
+class CC_PAINT_EXPORT DrawTextOp final : public PaintOpWithData {
+ public:
   static constexpr PaintOpType kType = PaintOpType::DrawText;
   static constexpr bool kIsDrawOp = true;
   DrawTextOp(size_t bytes, SkScalar x, SkScalar y, const PaintFlags& flags)
@@ -625,15 +775,20 @@
                               const PaintFlags* flags,
                               SkCanvas* canvas,
                               const SkMatrix& original_ctm);
+  HAS_SERIALIZATION_FUNCTIONS();
 
   void* GetData() { return GetDataForThis(this); }
   const void* GetData() const { return GetDataForThis(this); }
 
   SkScalar x;
   SkScalar y;
+
+ private:
+  DrawTextOp() = default;
 };
 
-struct CC_PAINT_EXPORT DrawTextBlobOp final : PaintOpWithFlags {
+class CC_PAINT_EXPORT DrawTextBlobOp final : public PaintOpWithFlags {
+ public:
   static constexpr PaintOpType kType = PaintOpType::DrawTextBlob;
   static constexpr bool kIsDrawOp = true;
   DrawTextBlobOp(sk_sp<SkTextBlob> blob,
@@ -651,44 +806,60 @@
                               const PaintFlags* flags,
                               SkCanvas* canvas,
                               const SkMatrix& original_ctm);
+  HAS_SERIALIZATION_FUNCTIONS();
 
   sk_sp<SkTextBlob> blob;
   SkScalar x;
   SkScalar y;
+
+ private:
+  DrawTextBlobOp();
 };
 
-struct CC_PAINT_EXPORT NoopOp final : PaintOp {
+class CC_PAINT_EXPORT NoopOp final : public PaintOp {
+ public:
   static constexpr PaintOpType kType = PaintOpType::Noop;
   static void Raster(const PaintOp* op,
                      SkCanvas* canvas,
                      const SkMatrix& original_ctm) {}
+  HAS_SERIALIZATION_FUNCTIONS();
 };
 
-struct CC_PAINT_EXPORT RestoreOp final : PaintOp {
+class CC_PAINT_EXPORT RestoreOp final : public PaintOp {
+ public:
   static constexpr PaintOpType kType = PaintOpType::Restore;
   static void Raster(const PaintOp* op,
                      SkCanvas* canvas,
                      const SkMatrix& original_ctm);
+  HAS_SERIALIZATION_FUNCTIONS();
 };
 
-struct CC_PAINT_EXPORT RotateOp final : PaintOp {
+class CC_PAINT_EXPORT RotateOp final : public PaintOp {
+ public:
   static constexpr PaintOpType kType = PaintOpType::Rotate;
   explicit RotateOp(SkScalar degrees) : degrees(degrees) {}
   static void Raster(const PaintOp* op,
                      SkCanvas* canvas,
                      const SkMatrix& original_ctm);
+  HAS_SERIALIZATION_FUNCTIONS();
 
   SkScalar degrees;
+
+ private:
+  RotateOp() = default;
 };
 
-struct CC_PAINT_EXPORT SaveOp final : PaintOp {
+class CC_PAINT_EXPORT SaveOp final : public PaintOp {
+ public:
   static constexpr PaintOpType kType = PaintOpType::Save;
   static void Raster(const PaintOp* op,
                      SkCanvas* canvas,
                      const SkMatrix& original_ctm);
+  HAS_SERIALIZATION_FUNCTIONS();
 };
 
-struct CC_PAINT_EXPORT SaveLayerOp final : PaintOpWithFlags {
+class CC_PAINT_EXPORT SaveLayerOp final : public PaintOpWithFlags {
+ public:
   static constexpr PaintOpType kType = PaintOpType::SaveLayer;
   SaveLayerOp(const SkRect* bounds, const PaintFlags* flags)
       : PaintOpWithFlags(flags ? *flags : PaintFlags()),
@@ -704,11 +875,16 @@
                               SkCanvas* canvas,
                               const SkMatrix& original_ctm);
   bool HasNonAAPaint() const { return false; }
+  HAS_SERIALIZATION_FUNCTIONS();
 
   SkRect bounds;
+
+ private:
+  SaveLayerOp() = default;
 };
 
-struct CC_PAINT_EXPORT SaveLayerAlphaOp final : PaintOp {
+class CC_PAINT_EXPORT SaveLayerAlphaOp final : public PaintOp {
+ public:
   static constexpr PaintOpType kType = PaintOpType::SaveLayerAlpha;
   SaveLayerAlphaOp(const SkRect* bounds,
                    uint8_t alpha,
@@ -719,24 +895,34 @@
   static void Raster(const PaintOp* op,
                      SkCanvas* canvas,
                      const SkMatrix& original_ctm);
+  HAS_SERIALIZATION_FUNCTIONS();
 
   SkRect bounds;
   uint8_t alpha;
   bool preserve_lcd_text_requests;
+
+ private:
+  SaveLayerAlphaOp() = default;
 };
 
-struct CC_PAINT_EXPORT ScaleOp final : PaintOp {
+class CC_PAINT_EXPORT ScaleOp final : public PaintOp {
+ public:
   static constexpr PaintOpType kType = PaintOpType::Scale;
   ScaleOp(SkScalar sx, SkScalar sy) : sx(sx), sy(sy) {}
   static void Raster(const PaintOp* op,
                      SkCanvas* canvas,
                      const SkMatrix& original_ctm);
+  HAS_SERIALIZATION_FUNCTIONS();
 
   SkScalar sx;
   SkScalar sy;
+
+ private:
+  ScaleOp() = default;
 };
 
-struct CC_PAINT_EXPORT SetMatrixOp final : PaintOp {
+class CC_PAINT_EXPORT SetMatrixOp final : public PaintOp {
+ public:
   static constexpr PaintOpType kType = PaintOpType::SetMatrix;
   explicit SetMatrixOp(const SkMatrix& matrix) : matrix(matrix) {}
   // This is the only op that needs the original ctm of the SkCanvas
@@ -748,21 +934,32 @@
   static void Raster(const PaintOp* op,
                      SkCanvas* canvas,
                      const SkMatrix& original_ctm);
+  HAS_SERIALIZATION_FUNCTIONS();
 
   ThreadsafeMatrix matrix;
+
+ private:
+  SetMatrixOp() = default;
 };
 
-struct CC_PAINT_EXPORT TranslateOp final : PaintOp {
+class CC_PAINT_EXPORT TranslateOp final : public PaintOp {
+ public:
   static constexpr PaintOpType kType = PaintOpType::Translate;
   TranslateOp(SkScalar dx, SkScalar dy) : dx(dx), dy(dy) {}
   static void Raster(const PaintOp* op,
                      SkCanvas* canvas,
                      const SkMatrix& original_ctm);
+  HAS_SERIALIZATION_FUNCTIONS();
 
   SkScalar dx;
   SkScalar dy;
+
+ private:
+  TranslateOp() = default;
 };
 
+#undef HAS_SERIALIZATION_FUNCTIONS
+
 using LargestPaintOp = DrawDRRectOp;
 
 class CC_PAINT_EXPORT PaintOpBuffer : public SkRefCnt {
diff --git a/cc/paint/paint_op_buffer_unittest.cc b/cc/paint/paint_op_buffer_unittest.cc
index 913db86..ac1181ca 100644
--- a/cc/paint/paint_op_buffer_unittest.cc
+++ b/cc/paint/paint_op_buffer_unittest.cc
@@ -4,10 +4,12 @@
 
 #include "cc/paint/paint_op_buffer.h"
 #include "base/memory/ptr_util.h"
+#include "base/strings/stringprintf.h"
 #include "cc/paint/display_item_list.h"
 #include "cc/test/skia_common.h"
 #include "cc/test/test_skcanvas.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkWriteBuffer.h"
 #include "third_party/skia/include/effects/SkDashPathEffect.h"
 
 using testing::_;
@@ -916,4 +918,1148 @@
   buffer.Playback(&canvas);
 }
 
+std::vector<float> test_floats = {0.f,
+                                  1.f,
+                                  -1.f,
+                                  2384.981971f,
+                                  0.0001f,
+                                  std::numeric_limits<float>::min(),
+                                  std::numeric_limits<float>::max(),
+                                  std::numeric_limits<float>::infinity()};
+
+std::vector<uint8_t> test_uint8s = {
+    0, 255, 128, 10, 45,
+};
+
+std::vector<SkRect> test_rects = {
+    SkRect::MakeXYWH(1, 2.5, 3, 4), SkRect::MakeXYWH(0, 0, 0, 0),
+    SkRect::MakeLargest(),          SkRect::MakeXYWH(0.5f, 0.5f, 8.2f, 8.2f),
+    SkRect::MakeXYWH(-1, -1, 0, 0), SkRect::MakeXYWH(-100, -101, -102, -103)};
+
+std::vector<SkRRect> test_rrects = {
+    SkRRect::MakeEmpty(), SkRRect::MakeOval(SkRect::MakeXYWH(1, 2, 3, 4)),
+    SkRRect::MakeRect(SkRect::MakeXYWH(-10, 100, 5, 4)),
+    [] {
+      SkRRect rrect = SkRRect::MakeEmpty();
+      rrect.setNinePatch(SkRect::MakeXYWH(10, 20, 30, 40), 1, 2, 3, 4);
+      return rrect;
+    }(),
+};
+
+std::vector<SkIRect> test_irects = {
+    SkIRect::MakeXYWH(1, 2, 3, 4),   SkIRect::MakeXYWH(0, 0, 0, 0),
+    SkIRect::MakeLargest(),          SkIRect::MakeXYWH(0, 0, 10, 10),
+    SkIRect::MakeXYWH(-1, -1, 0, 0), SkIRect::MakeXYWH(-100, -101, -102, -103)};
+
+std::vector<SkMatrix> test_matrices = {
+    SkMatrix(),
+    SkMatrix::MakeScale(3.91f, 4.31f),
+    SkMatrix::MakeTrans(-5.2f, 8.7f),
+    [] {
+      SkMatrix matrix;
+      SkScalar buffer[] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
+      matrix.set9(buffer);
+      return matrix;
+    }(),
+    [] {
+      SkMatrix matrix;
+      SkScalar buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+      matrix.set9(buffer);
+      return matrix;
+    }(),
+};
+
+std::vector<SkPath> test_paths = {
+    [] {
+      SkPath path;
+      path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
+      path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
+      path.lineTo(SkIntToScalar(30), SkIntToScalar(30));
+      path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
+      return path;
+    }(),
+    [] {
+      SkPath path;
+      path.addCircle(2, 2, 5);
+      path.addCircle(3, 4, 2);
+      path.addArc(SkRect::MakeXYWH(1, 2, 3, 4), 5, 6);
+      return path;
+    }(),
+    SkPath(),
+};
+
+// TODO(enne): make this more real.
+std::vector<PaintFlags> test_flags = {
+    PaintFlags(), PaintFlags(), PaintFlags(), PaintFlags(), PaintFlags(),
+};
+
+std::vector<SkColor> test_colors = {
+    SkColorSetARGBInline(0, 0, 0, 0),
+    SkColorSetARGBInline(255, 255, 255, 255),
+    SkColorSetARGBInline(0, 255, 10, 255),
+    SkColorSetARGBInline(255, 0, 20, 255),
+    SkColorSetARGBInline(30, 255, 0, 255),
+    SkColorSetARGBInline(255, 40, 0, 0),
+};
+
+std::vector<std::string> test_strings = {
+    "", "foobar",
+    "blarbideeblarasdfaiousydfp234poiausdofiuapsodfjknla;sdfkjasd;f",
+};
+
+std::vector<std::vector<SkPoint>> test_point_arrays = {
+    std::vector<SkPoint>(),
+    {SkPoint::Make(1, 2)},
+    {SkPoint::Make(1, 2), SkPoint::Make(-5.4f, -3.8f)},
+    {SkPoint::Make(0, 0), SkPoint::Make(5, 6), SkPoint::Make(-1, -1),
+     SkPoint::Make(9, 9), SkPoint::Make(50, 50), SkPoint::Make(100, 100)},
+};
+
+std::vector<sk_sp<SkTextBlob>> test_blobs = {
+    [] {
+      SkPaint font;
+      font.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+
+      SkTextBlobBuilder builder;
+      builder.allocRun(font, 5, 1.2f, 2.3f, &test_rects[0]);
+      return builder.make();
+    }(),
+    [] {
+      SkPaint font;
+      font.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+
+      SkTextBlobBuilder builder;
+      builder.allocRun(font, 5, 1.2f, 2.3f, &test_rects[0]);
+      builder.allocRunPos(font, 16, &test_rects[1]);
+      builder.allocRunPosH(font, 8, 0, &test_rects[2]);
+      return builder.make();
+    }(),
+};
+
+// TODO(enne): In practice, probably all paint images need to be uploaded
+// ahead of time and not be bitmaps. These paint images should be fake
+// gpu resource paint images.
+std::vector<PaintImage> test_images = {
+    PaintImage(PaintImage::GetNextId(),
+               CreateDiscardableImage(gfx::Size(5, 10))),
+    PaintImage(PaintImage::GetNextId(),
+               CreateDiscardableImage(gfx::Size(1, 1))),
+    PaintImage(PaintImage::GetNextId(),
+               CreateDiscardableImage(gfx::Size(50, 50))),
+};
+
+// Writes as many ops in |buffer| as can fit in |output_size| to |output|.
+// Records the numbers of bytes written for each op.
+class SimpleSerializer {
+ public:
+  SimpleSerializer(void* output, size_t output_size)
+      : current_(static_cast<char*>(output)),
+        output_size_(output_size),
+        remaining_(output_size) {}
+
+  void Serialize(const PaintOpBuffer& buffer) {
+    bytes_written_.resize(buffer.size());
+    for (size_t i = 0; i < buffer.size(); ++i)
+      bytes_written_[i] = 0;
+
+    PaintOp::SerializeOptions options;
+
+    size_t op_idx = 0;
+    for (const auto* op : PaintOpBuffer::Iterator(&buffer)) {
+      size_t bytes_written = op->Serialize(current_, remaining_, options);
+      if (!bytes_written)
+        return;
+
+      PaintOp* written = reinterpret_cast<PaintOp*>(current_);
+      EXPECT_EQ(op->GetType(), written->GetType());
+      EXPECT_EQ(bytes_written, written->skip);
+
+      bytes_written_[op_idx] = bytes_written;
+      op_idx++;
+      current_ += bytes_written;
+      remaining_ -= bytes_written;
+
+      // Number of bytes bytes_written must be a multiple of PaintOpAlign
+      // unless the buffer is filled entirely.
+      if (remaining_ != 0u)
+        DCHECK_EQ(0u, bytes_written % PaintOpBuffer::PaintOpAlign);
+    }
+  }
+
+  const std::vector<size_t>& bytes_written() const { return bytes_written_; }
+  size_t TotalBytesWritten() const { return output_size_ - remaining_; }
+
+ private:
+  char* current_ = nullptr;
+  size_t output_size_ = 0u;
+  size_t remaining_ = 0u;
+  std::vector<size_t> bytes_written_;
+};
+
+class DeserializerIterator {
+ public:
+  DeserializerIterator(const void* input, size_t input_size)
+      : DeserializerIterator(input,
+                             static_cast<const char*>(input),
+                             input_size,
+                             input_size) {}
+
+  DeserializerIterator(DeserializerIterator&&) = default;
+  DeserializerIterator& operator=(DeserializerIterator&&) = default;
+
+  ~DeserializerIterator() { DestroyDeserializedOp(); }
+
+  DeserializerIterator begin() {
+    return DeserializerIterator(input_, static_cast<const char*>(input_),
+                                input_size_, input_size_);
+  }
+  DeserializerIterator end() {
+    return DeserializerIterator(
+        input_, static_cast<const char*>(input_) + input_size_, input_size_, 0);
+  }
+  bool operator!=(const DeserializerIterator& other) {
+    return input_ != other.input_ || current_ != other.current_ ||
+           input_size_ != other.input_size_ || remaining_ != other.remaining_;
+  }
+  DeserializerIterator& operator++() {
+    const PaintOp* serialized = reinterpret_cast<const PaintOp*>(current_);
+
+    CHECK_GE(remaining_, serialized->skip);
+    current_ += serialized->skip;
+    remaining_ -= serialized->skip;
+
+    if (remaining_ > 0)
+      CHECK_GE(remaining_, 4u);
+
+    DeserializeCurrentOp();
+
+    return *this;
+  }
+
+  operator bool() const { return remaining_ == 0u; }
+  const PaintOp* operator->() const { return deserialized_op_; }
+  const PaintOp* operator*() const { return deserialized_op_; }
+
+ private:
+  DeserializerIterator(const void* input,
+                       const char* current,
+                       size_t input_size,
+                       size_t remaining)
+      : input_(input),
+        current_(current),
+        input_size_(input_size),
+        remaining_(remaining) {
+    DeserializeCurrentOp();
+  }
+
+  void DestroyDeserializedOp() {
+    if (!deserialized_op_)
+      return;
+    deserialized_op_->DestroyThis();
+    deserialized_op_ = nullptr;
+  }
+
+  void DeserializeCurrentOp() {
+    DestroyDeserializedOp();
+
+    if (!remaining_)
+      return;
+
+    const PaintOp* serialized = reinterpret_cast<const PaintOp*>(current_);
+    size_t required = sizeof(LargestPaintOp) + serialized->skip;
+
+    if (data_size_ < required) {
+      data_.reset(static_cast<char*>(
+          base::AlignedAlloc(required, PaintOpBuffer::PaintOpAlign)));
+      data_size_ = required;
+    }
+    deserialized_op_ =
+        PaintOp::Deserialize(current_, remaining_, data_.get(), data_size_);
+  }
+
+  const void* input_ = nullptr;
+  const char* current_ = nullptr;
+  size_t input_size_ = 0u;
+  size_t remaining_ = 0u;
+  std::unique_ptr<char, base::AlignedFreeDeleter> data_;
+  size_t data_size_ = 0u;
+  PaintOp* deserialized_op_ = nullptr;
+};
+
+void PushAnnotateOps(PaintOpBuffer* buffer) {
+  buffer->push<AnnotateOp>(PaintCanvas::AnnotationType::URL, test_rects[0],
+                           SkData::MakeWithCString("thingerdoowhatchamagig"));
+  // Deliberately test both null and empty SkData.
+  buffer->push<AnnotateOp>(PaintCanvas::AnnotationType::LINK_TO_DESTINATION,
+                           test_rects[1], nullptr);
+  buffer->push<AnnotateOp>(PaintCanvas::AnnotationType::NAMED_DESTINATION,
+                           test_rects[2], SkData::MakeEmpty());
+}
+
+void PushClipPathOps(PaintOpBuffer* buffer) {
+  for (size_t i = 0; i < test_paths.size(); ++i) {
+    SkClipOp op = i % 3 ? SkClipOp::kDifference : SkClipOp::kIntersect;
+    buffer->push<ClipPathOp>(test_paths[i], op, !!(i % 2));
+  }
+}
+
+void PushClipRectOps(PaintOpBuffer* buffer) {
+  for (size_t i = 0; i < test_rects.size(); ++i) {
+    SkClipOp op = i % 2 ? SkClipOp::kIntersect : SkClipOp::kDifference;
+    bool antialias = !!(i % 3);
+    buffer->push<ClipRectOp>(test_rects[i], op, antialias);
+  }
+}
+
+void PushClipRRectOps(PaintOpBuffer* buffer) {
+  for (size_t i = 0; i < test_rrects.size(); ++i) {
+    SkClipOp op = i % 2 ? SkClipOp::kIntersect : SkClipOp::kDifference;
+    bool antialias = !!(i % 3);
+    buffer->push<ClipRRectOp>(test_rrects[i], op, antialias);
+  }
+}
+
+void PushConcatOps(PaintOpBuffer* buffer) {
+  for (size_t i = 0; i < test_matrices.size(); ++i)
+    buffer->push<ConcatOp>(test_matrices[i]);
+}
+
+void PushDrawArcOps(PaintOpBuffer* buffer) {
+  size_t len = std::min(std::min(test_floats.size() - 1, test_flags.size()),
+                        test_rects.size());
+  for (size_t i = 0; i < len; ++i) {
+    bool use_center = !!(i % 2);
+    buffer->push<DrawArcOp>(test_rects[i], test_floats[i], test_floats[i + 1],
+                            use_center, test_flags[i]);
+  }
+}
+
+void PushDrawCircleOps(PaintOpBuffer* buffer) {
+  size_t len = std::min(test_floats.size() - 2, test_flags.size());
+  for (size_t i = 0; i < len; ++i) {
+    buffer->push<DrawCircleOp>(test_floats[i], test_floats[i + 1],
+                               test_floats[i + 2], test_flags[i]);
+  }
+}
+
+void PushDrawColorOps(PaintOpBuffer* buffer) {
+  for (size_t i = 0; i < test_colors.size(); ++i) {
+    buffer->push<DrawColorOp>(test_colors[i], static_cast<SkBlendMode>(i));
+  }
+}
+
+void PushDrawDRRectOps(PaintOpBuffer* buffer) {
+  size_t len = std::min(test_rrects.size() - 1, test_flags.size());
+  for (size_t i = 0; i < len; ++i) {
+    buffer->push<DrawDRRectOp>(test_rrects[i], test_rrects[i + 1],
+                               test_flags[i]);
+  }
+}
+
+void PushDrawImageOps(PaintOpBuffer* buffer) {
+  size_t len = std::min(std::min(test_images.size(), test_flags.size()),
+                        test_floats.size() - 1);
+  for (size_t i = 0; i < len; ++i) {
+    buffer->push<DrawImageOp>(test_images[i], test_floats[i],
+                              test_floats[i + 1], &test_flags[i]);
+  }
+
+  // Test optional flags
+  // TODO(enne): maybe all these optional ops should not be optional.
+  buffer->push<DrawImageOp>(test_images[0], test_floats[0], test_floats[1],
+                            nullptr);
+}
+
+void PushDrawImageRectOps(PaintOpBuffer* buffer) {
+  size_t len = std::min(std::min(test_images.size(), test_flags.size()),
+                        test_rects.size() - 1);
+  for (size_t i = 0; i < len; ++i) {
+    PaintCanvas::SrcRectConstraint constraint =
+        i % 2 ? PaintCanvas::kStrict_SrcRectConstraint
+              : PaintCanvas::kFast_SrcRectConstraint;
+    buffer->push<DrawImageRectOp>(test_images[i], test_rects[i],
+                                  test_rects[i + 1], &test_flags[i],
+                                  constraint);
+  }
+
+  // Test optional flags.
+  buffer->push<DrawImageRectOp>(test_images[0], test_rects[0], test_rects[1],
+                                nullptr,
+                                PaintCanvas::kStrict_SrcRectConstraint);
+}
+
+void PushDrawIRectOps(PaintOpBuffer* buffer) {
+  size_t len = std::min(test_irects.size(), test_flags.size());
+  for (size_t i = 0; i < len; ++i)
+    buffer->push<DrawIRectOp>(test_irects[i], test_flags[i]);
+}
+
+void PushDrawLineOps(PaintOpBuffer* buffer) {
+  size_t len = std::min(test_floats.size() - 3, test_flags.size());
+  for (size_t i = 0; i < len; ++i) {
+    buffer->push<DrawLineOp>(test_floats[i], test_floats[i + 1],
+                             test_floats[i + 2], test_floats[i + 3],
+                             test_flags[i]);
+  }
+}
+
+void PushDrawOvalOps(PaintOpBuffer* buffer) {
+  size_t len = std::min(test_paths.size(), test_flags.size());
+  for (size_t i = 0; i < len; ++i)
+    buffer->push<DrawOvalOp>(test_rects[i], test_flags[i]);
+}
+
+void PushDrawPathOps(PaintOpBuffer* buffer) {
+  size_t len = std::min(test_paths.size(), test_flags.size());
+  for (size_t i = 0; i < len; ++i)
+    buffer->push<DrawPathOp>(test_paths[i], test_flags[i]);
+}
+
+void PushDrawPosTextOps(PaintOpBuffer* buffer) {
+  size_t len = std::min(std::min(test_flags.size(), test_strings.size()),
+                        test_point_arrays.size());
+  for (size_t i = 0; i < len; ++i) {
+    // Make sure empty array works fine.
+    SkPoint* array =
+        test_point_arrays[i].size() > 0 ? &test_point_arrays[i][0] : nullptr;
+    buffer->push_with_array<DrawPosTextOp>(
+        test_strings[i].c_str(), test_strings[i].size() + 1, array,
+        test_point_arrays[i].size(), test_flags[i]);
+  }
+}
+
+void PushDrawRectOps(PaintOpBuffer* buffer) {
+  size_t len = std::min(test_rects.size(), test_flags.size());
+  for (size_t i = 0; i < len; ++i)
+    buffer->push<DrawRectOp>(test_rects[i], test_flags[i]);
+}
+
+void PushDrawRRectOps(PaintOpBuffer* buffer) {
+  size_t len = std::min(test_rrects.size(), test_flags.size());
+  for (size_t i = 0; i < len; ++i)
+    buffer->push<DrawRRectOp>(test_rrects[i], test_flags[i]);
+}
+
+void PushDrawTextOps(PaintOpBuffer* buffer) {
+  size_t len = std::min(std::min(test_strings.size(), test_flags.size()),
+                        test_floats.size() - 1);
+  for (size_t i = 0; i < len; ++i) {
+    buffer->push_with_data<DrawTextOp>(
+        test_strings[i].c_str(), test_strings[i].size() + 1, test_floats[i],
+        test_floats[i + 1], test_flags[i]);
+  }
+}
+
+void PushDrawTextBlobOps(PaintOpBuffer* buffer) {
+  size_t len = std::min(std::min(test_blobs.size(), test_flags.size()),
+                        test_floats.size() - 1);
+  for (size_t i = 0; i < len; ++i) {
+    buffer->push<DrawTextBlobOp>(test_blobs[i], test_floats[i],
+                                 test_floats[i + 1], test_flags[i]);
+  }
+}
+
+void PushNoopOps(PaintOpBuffer* buffer) {
+  buffer->push<NoopOp>();
+  buffer->push<NoopOp>();
+  buffer->push<NoopOp>();
+  buffer->push<NoopOp>();
+}
+
+void PushRestoreOps(PaintOpBuffer* buffer) {
+  buffer->push<RestoreOp>();
+  buffer->push<RestoreOp>();
+  buffer->push<RestoreOp>();
+  buffer->push<RestoreOp>();
+}
+
+void PushRotateOps(PaintOpBuffer* buffer) {
+  for (size_t i = 0; i < test_floats.size(); ++i)
+    buffer->push<RotateOp>(test_floats[i]);
+}
+
+void PushSaveOps(PaintOpBuffer* buffer) {
+  buffer->push<SaveOp>();
+  buffer->push<SaveOp>();
+  buffer->push<SaveOp>();
+  buffer->push<SaveOp>();
+}
+
+void PushSaveLayerOps(PaintOpBuffer* buffer) {
+  size_t len = std::min(test_flags.size(), test_rects.size());
+  for (size_t i = 0; i < len; ++i)
+    buffer->push<SaveLayerOp>(&test_rects[i], &test_flags[i]);
+
+  // Test combinations of optional args.
+  buffer->push<SaveLayerOp>(nullptr, &test_flags[0]);
+  buffer->push<SaveLayerOp>(&test_rects[0], nullptr);
+  buffer->push<SaveLayerOp>(nullptr, nullptr);
+}
+
+void PushSaveLayerAlphaOps(PaintOpBuffer* buffer) {
+  size_t len = std::min(test_uint8s.size(), test_rects.size());
+  for (size_t i = 0; i < len; ++i)
+    buffer->push<SaveLayerAlphaOp>(&test_rects[i], test_uint8s[i], !!(i % 2));
+
+  // Test optional args.
+  buffer->push<SaveLayerAlphaOp>(nullptr, test_uint8s[0], false);
+}
+
+void PushScaleOps(PaintOpBuffer* buffer) {
+  for (size_t i = 0; i < test_floats.size(); i += 2)
+    buffer->push<ScaleOp>(test_floats[i], test_floats[i + 1]);
+}
+
+void PushSetMatrixOps(PaintOpBuffer* buffer) {
+  for (size_t i = 0; i < test_matrices.size(); ++i)
+    buffer->push<SetMatrixOp>(test_matrices[i]);
+}
+
+void PushTranslateOps(PaintOpBuffer* buffer) {
+  for (size_t i = 0; i < test_floats.size(); i += 2)
+    buffer->push<TranslateOp>(test_floats[i], test_floats[i + 1]);
+}
+
+void CompareFlags(const PaintFlags& original, const PaintFlags& written) {}
+
+void CompareImages(const PaintImage& original, const PaintImage& written) {}
+
+void CompareAnnotateOp(const AnnotateOp* original, const AnnotateOp* written) {
+  EXPECT_EQ(original->annotation_type, written->annotation_type);
+  EXPECT_EQ(original->rect, written->rect);
+  EXPECT_EQ(!!original->data, !!written->data);
+  if (original->data) {
+    EXPECT_EQ(original->data->size(), written->data->size());
+    EXPECT_EQ(0, memcmp(original->data->data(), written->data->data(),
+                        written->data->size()));
+  }
+}
+
+void CompareClipPathOp(const ClipPathOp* original, const ClipPathOp* written) {
+  EXPECT_TRUE(original->path == written->path);
+  EXPECT_EQ(original->op, written->op);
+  EXPECT_EQ(original->antialias, written->antialias);
+}
+
+void CompareClipRectOp(const ClipRectOp* original, const ClipRectOp* written) {
+  EXPECT_EQ(original->rect, written->rect);
+  EXPECT_EQ(original->op, written->op);
+  EXPECT_EQ(original->antialias, written->antialias);
+}
+
+void CompareClipRRectOp(const ClipRRectOp* original,
+                        const ClipRRectOp* written) {
+  EXPECT_EQ(original->rrect, written->rrect);
+  EXPECT_EQ(original->op, written->op);
+  EXPECT_EQ(original->antialias, written->antialias);
+}
+
+void CompareConcatOp(const ConcatOp* original, const ConcatOp* written) {
+  EXPECT_EQ(original->matrix, written->matrix);
+  EXPECT_EQ(original->matrix.getType(), written->matrix.getType());
+}
+
+void CompareDrawArcOp(const DrawArcOp* original, const DrawArcOp* written) {
+  CompareFlags(original->flags, written->flags);
+  EXPECT_EQ(original->oval, written->oval);
+  EXPECT_EQ(original->start_angle, written->start_angle);
+  EXPECT_EQ(original->sweep_angle, written->sweep_angle);
+  EXPECT_EQ(original->use_center, written->use_center);
+}
+
+void CompareDrawCircleOp(const DrawCircleOp* original,
+                         const DrawCircleOp* written) {
+  CompareFlags(original->flags, written->flags);
+  EXPECT_EQ(original->cx, written->cx);
+  EXPECT_EQ(original->cy, written->cy);
+  EXPECT_EQ(original->radius, written->radius);
+}
+
+void CompareDrawColorOp(const DrawColorOp* original,
+                        const DrawColorOp* written) {
+  EXPECT_EQ(original->color, written->color);
+}
+
+void CompareDrawDRRectOp(const DrawDRRectOp* original,
+                         const DrawDRRectOp* written) {
+  CompareFlags(original->flags, written->flags);
+  EXPECT_EQ(original->outer, written->outer);
+  EXPECT_EQ(original->inner, written->inner);
+}
+
+void CompareDrawImageOp(const DrawImageOp* original,
+                        const DrawImageOp* written) {
+  CompareFlags(original->flags, written->flags);
+  CompareImages(original->image, written->image);
+  EXPECT_EQ(original->left, written->left);
+  EXPECT_EQ(original->top, written->top);
+}
+
+void CompareDrawImageRectOp(const DrawImageRectOp* original,
+                            const DrawImageRectOp* written) {
+  CompareFlags(original->flags, written->flags);
+  CompareImages(original->image, written->image);
+  EXPECT_EQ(original->src, written->src);
+  EXPECT_EQ(original->dst, written->dst);
+}
+
+void CompareDrawIRectOp(const DrawIRectOp* original,
+                        const DrawIRectOp* written) {
+  CompareFlags(original->flags, written->flags);
+  EXPECT_EQ(original->rect, written->rect);
+}
+
+void CompareDrawLineOp(const DrawLineOp* original, const DrawLineOp* written) {
+  CompareFlags(original->flags, written->flags);
+  EXPECT_EQ(original->x0, written->x0);
+  EXPECT_EQ(original->y0, written->y0);
+  EXPECT_EQ(original->x1, written->x1);
+  EXPECT_EQ(original->y1, written->y1);
+}
+
+void CompareDrawOvalOp(const DrawOvalOp* original, const DrawOvalOp* written) {
+  CompareFlags(original->flags, written->flags);
+  EXPECT_EQ(original->oval, written->oval);
+}
+
+void CompareDrawPathOp(const DrawPathOp* original, const DrawPathOp* written) {
+  CompareFlags(original->flags, written->flags);
+  EXPECT_TRUE(original->path == written->path);
+}
+
+void CompareDrawPosTextOp(const DrawPosTextOp* original,
+                          const DrawPosTextOp* written) {
+  CompareFlags(original->flags, written->flags);
+  ASSERT_EQ(original->bytes, written->bytes);
+  EXPECT_EQ(std::string(static_cast<const char*>(original->GetData())),
+            std::string(static_cast<const char*>(written->GetData())));
+  ASSERT_EQ(original->count, written->count);
+  for (size_t i = 0; i < original->count; ++i)
+    EXPECT_EQ(original->GetArray()[i], written->GetArray()[i]);
+}
+
+void CompareDrawRectOp(const DrawRectOp* original, const DrawRectOp* written) {
+  CompareFlags(original->flags, written->flags);
+  EXPECT_EQ(original->rect, written->rect);
+}
+
+void CompareDrawRRectOp(const DrawRRectOp* original,
+                        const DrawRRectOp* written) {
+  CompareFlags(original->flags, written->flags);
+  EXPECT_EQ(original->rrect, written->rrect);
+}
+
+void CompareDrawTextOp(const DrawTextOp* original, const DrawTextOp* written) {
+  CompareFlags(original->flags, written->flags);
+  EXPECT_EQ(original->x, written->x);
+  EXPECT_EQ(original->y, written->y);
+  ASSERT_EQ(original->bytes, written->bytes);
+  EXPECT_EQ(std::string(static_cast<const char*>(original->GetData())),
+            std::string(static_cast<const char*>(written->GetData())));
+}
+
+void CompareDrawTextBlobOp(const DrawTextBlobOp* original,
+                           const DrawTextBlobOp* written) {
+  CompareFlags(original->flags, written->flags);
+  EXPECT_EQ(original->x, written->x);
+  EXPECT_EQ(original->y, written->y);
+
+  // TODO(enne): implement SkTextBlob serialization: http://crbug.com/737629
+  if (!original->blob || !written->blob)
+    return;
+
+  ASSERT_TRUE(original->blob);
+  ASSERT_TRUE(written->blob);
+
+  // No text blob operator==, so flatten them both and compare.
+  size_t max_size = original->skip;
+
+  std::vector<char> original_mem;
+  original_mem.resize(max_size);
+  SkBinaryWriteBuffer original_flattened(&original_mem[0], max_size);
+  original->blob->flatten(original_flattened);
+  original_mem.resize(original_flattened.bytesWritten());
+
+  std::vector<char> written_mem;
+  written_mem.resize(max_size);
+  SkBinaryWriteBuffer written_flattened(&written_mem[0], max_size);
+  written->blob->flatten(written_flattened);
+  written_mem.resize(written_flattened.bytesWritten());
+
+  ASSERT_EQ(original_mem.size(), written_mem.size());
+  EXPECT_EQ(original_mem, written_mem);
+}
+
+void CompareNoopOp(const NoopOp* original, const NoopOp* written) {
+  // Nothing to compare.
+}
+
+void CompareRestoreOp(const RestoreOp* original, const RestoreOp* written) {
+  // Nothing to compare.
+}
+
+void CompareRotateOp(const RotateOp* original, const RotateOp* written) {
+  EXPECT_EQ(original->degrees, written->degrees);
+}
+
+void CompareSaveOp(const SaveOp* original, const SaveOp* written) {
+  // Nothing to compare.
+}
+
+void CompareSaveLayerOp(const SaveLayerOp* original,
+                        const SaveLayerOp* written) {
+  CompareFlags(original->flags, written->flags);
+  EXPECT_EQ(original->bounds, written->bounds);
+}
+
+void CompareSaveLayerAlphaOp(const SaveLayerAlphaOp* original,
+                             const SaveLayerAlphaOp* written) {
+  EXPECT_EQ(original->bounds, written->bounds);
+  EXPECT_EQ(original->alpha, written->alpha);
+  EXPECT_EQ(original->preserve_lcd_text_requests,
+            written->preserve_lcd_text_requests);
+}
+
+void CompareScaleOp(const ScaleOp* original, const ScaleOp* written) {
+  EXPECT_EQ(original->sx, written->sx);
+  EXPECT_EQ(original->sy, written->sy);
+}
+
+void CompareSetMatrixOp(const SetMatrixOp* original,
+                        const SetMatrixOp* written) {
+  EXPECT_EQ(original->matrix, written->matrix);
+}
+
+void CompareTranslateOp(const TranslateOp* original,
+                        const TranslateOp* written) {
+  EXPECT_EQ(original->dx, written->dx);
+  EXPECT_EQ(original->dy, written->dy);
+}
+
+class PaintOpSerializationTest : public ::testing::TestWithParam<uint8_t> {
+ public:
+  PaintOpType GetParamType() const {
+    return static_cast<PaintOpType>(GetParam());
+  }
+
+  void PushTestOps(PaintOpType type) {
+    switch (type) {
+      case PaintOpType::Annotate:
+        PushAnnotateOps(&buffer_);
+        break;
+      case PaintOpType::ClipPath:
+        PushClipPathOps(&buffer_);
+        break;
+      case PaintOpType::ClipRect:
+        PushClipRectOps(&buffer_);
+        break;
+      case PaintOpType::ClipRRect:
+        PushClipRRectOps(&buffer_);
+        break;
+      case PaintOpType::Concat:
+        PushConcatOps(&buffer_);
+        break;
+      case PaintOpType::DrawArc:
+        PushDrawArcOps(&buffer_);
+        break;
+      case PaintOpType::DrawCircle:
+        PushDrawCircleOps(&buffer_);
+        break;
+      case PaintOpType::DrawColor:
+        PushDrawColorOps(&buffer_);
+        break;
+      case PaintOpType::DrawDRRect:
+        PushDrawDRRectOps(&buffer_);
+        break;
+      case PaintOpType::DrawImage:
+        PushDrawImageOps(&buffer_);
+        break;
+      case PaintOpType::DrawImageRect:
+        PushDrawImageRectOps(&buffer_);
+        break;
+      case PaintOpType::DrawIRect:
+        PushDrawIRectOps(&buffer_);
+        break;
+      case PaintOpType::DrawLine:
+        PushDrawLineOps(&buffer_);
+        break;
+      case PaintOpType::DrawOval:
+        PushDrawOvalOps(&buffer_);
+        break;
+      case PaintOpType::DrawPath:
+        PushDrawPathOps(&buffer_);
+        break;
+      case PaintOpType::DrawPosText:
+        PushDrawPosTextOps(&buffer_);
+        break;
+      case PaintOpType::DrawRecord:
+        // Not supported.
+        break;
+      case PaintOpType::DrawRect:
+        PushDrawRectOps(&buffer_);
+        break;
+      case PaintOpType::DrawRRect:
+        PushDrawRRectOps(&buffer_);
+        break;
+      case PaintOpType::DrawText:
+        PushDrawTextOps(&buffer_);
+        break;
+      case PaintOpType::DrawTextBlob:
+        PushDrawTextBlobOps(&buffer_);
+        break;
+      case PaintOpType::Noop:
+        PushNoopOps(&buffer_);
+        break;
+      case PaintOpType::Restore:
+        PushRestoreOps(&buffer_);
+        break;
+      case PaintOpType::Rotate:
+        PushRotateOps(&buffer_);
+        break;
+      case PaintOpType::Save:
+        PushSaveOps(&buffer_);
+        break;
+      case PaintOpType::SaveLayer:
+        PushSaveLayerOps(&buffer_);
+        break;
+      case PaintOpType::SaveLayerAlpha:
+        PushSaveLayerAlphaOps(&buffer_);
+        break;
+      case PaintOpType::Scale:
+        PushScaleOps(&buffer_);
+        break;
+      case PaintOpType::SetMatrix:
+        PushSetMatrixOps(&buffer_);
+        break;
+      case PaintOpType::Translate:
+        PushTranslateOps(&buffer_);
+        break;
+    }
+  }
+
+  static void ExpectOpsEqual(const PaintOp* original, const PaintOp* written) {
+    ASSERT_TRUE(original);
+    ASSERT_TRUE(written);
+    ASSERT_EQ(original->GetType(), written->GetType());
+    EXPECT_EQ(original->skip, written->skip);
+
+    switch (original->GetType()) {
+      case PaintOpType::Annotate:
+        CompareAnnotateOp(static_cast<const AnnotateOp*>(original),
+                          static_cast<const AnnotateOp*>(written));
+        break;
+      case PaintOpType::ClipPath:
+        CompareClipPathOp(static_cast<const ClipPathOp*>(original),
+                          static_cast<const ClipPathOp*>(written));
+        break;
+      case PaintOpType::ClipRect:
+        CompareClipRectOp(static_cast<const ClipRectOp*>(original),
+                          static_cast<const ClipRectOp*>(written));
+        break;
+      case PaintOpType::ClipRRect:
+        CompareClipRRectOp(static_cast<const ClipRRectOp*>(original),
+                           static_cast<const ClipRRectOp*>(written));
+        break;
+      case PaintOpType::Concat:
+        CompareConcatOp(static_cast<const ConcatOp*>(original),
+                        static_cast<const ConcatOp*>(written));
+        break;
+      case PaintOpType::DrawArc:
+        CompareDrawArcOp(static_cast<const DrawArcOp*>(original),
+                         static_cast<const DrawArcOp*>(written));
+        break;
+      case PaintOpType::DrawCircle:
+        CompareDrawCircleOp(static_cast<const DrawCircleOp*>(original),
+                            static_cast<const DrawCircleOp*>(written));
+        break;
+      case PaintOpType::DrawColor:
+        CompareDrawColorOp(static_cast<const DrawColorOp*>(original),
+                           static_cast<const DrawColorOp*>(written));
+        break;
+      case PaintOpType::DrawDRRect:
+        CompareDrawDRRectOp(static_cast<const DrawDRRectOp*>(original),
+                            static_cast<const DrawDRRectOp*>(written));
+        break;
+      case PaintOpType::DrawImage:
+        CompareDrawImageOp(static_cast<const DrawImageOp*>(original),
+                           static_cast<const DrawImageOp*>(written));
+        break;
+      case PaintOpType::DrawImageRect:
+        CompareDrawImageRectOp(static_cast<const DrawImageRectOp*>(original),
+                               static_cast<const DrawImageRectOp*>(written));
+        break;
+      case PaintOpType::DrawIRect:
+        CompareDrawIRectOp(static_cast<const DrawIRectOp*>(original),
+                           static_cast<const DrawIRectOp*>(written));
+        break;
+      case PaintOpType::DrawLine:
+        CompareDrawLineOp(static_cast<const DrawLineOp*>(original),
+                          static_cast<const DrawLineOp*>(written));
+        break;
+      case PaintOpType::DrawOval:
+        CompareDrawOvalOp(static_cast<const DrawOvalOp*>(original),
+                          static_cast<const DrawOvalOp*>(written));
+        break;
+      case PaintOpType::DrawPath:
+        CompareDrawPathOp(static_cast<const DrawPathOp*>(original),
+                          static_cast<const DrawPathOp*>(written));
+        break;
+      case PaintOpType::DrawPosText:
+        CompareDrawPosTextOp(static_cast<const DrawPosTextOp*>(original),
+                             static_cast<const DrawPosTextOp*>(written));
+        break;
+      case PaintOpType::DrawRecord:
+        // Not supported.
+        break;
+      case PaintOpType::DrawRect:
+        CompareDrawRectOp(static_cast<const DrawRectOp*>(original),
+                          static_cast<const DrawRectOp*>(written));
+        break;
+      case PaintOpType::DrawRRect:
+        CompareDrawRRectOp(static_cast<const DrawRRectOp*>(original),
+                           static_cast<const DrawRRectOp*>(written));
+        break;
+      case PaintOpType::DrawText:
+        CompareDrawTextOp(static_cast<const DrawTextOp*>(original),
+                          static_cast<const DrawTextOp*>(written));
+        break;
+      case PaintOpType::DrawTextBlob:
+        CompareDrawTextBlobOp(static_cast<const DrawTextBlobOp*>(original),
+                              static_cast<const DrawTextBlobOp*>(written));
+        break;
+      case PaintOpType::Noop:
+        CompareNoopOp(static_cast<const NoopOp*>(original),
+                      static_cast<const NoopOp*>(written));
+        break;
+      case PaintOpType::Restore:
+        CompareRestoreOp(static_cast<const RestoreOp*>(original),
+                         static_cast<const RestoreOp*>(written));
+        break;
+      case PaintOpType::Rotate:
+        CompareRotateOp(static_cast<const RotateOp*>(original),
+                        static_cast<const RotateOp*>(written));
+        break;
+      case PaintOpType::Save:
+        CompareSaveOp(static_cast<const SaveOp*>(original),
+                      static_cast<const SaveOp*>(written));
+        break;
+      case PaintOpType::SaveLayer:
+        CompareSaveLayerOp(static_cast<const SaveLayerOp*>(original),
+                           static_cast<const SaveLayerOp*>(written));
+        break;
+      case PaintOpType::SaveLayerAlpha:
+        CompareSaveLayerAlphaOp(static_cast<const SaveLayerAlphaOp*>(original),
+                                static_cast<const SaveLayerAlphaOp*>(written));
+        break;
+      case PaintOpType::Scale:
+        CompareScaleOp(static_cast<const ScaleOp*>(original),
+                       static_cast<const ScaleOp*>(written));
+        break;
+      case PaintOpType::SetMatrix:
+        CompareSetMatrixOp(static_cast<const SetMatrixOp*>(original),
+                           static_cast<const SetMatrixOp*>(written));
+        break;
+      case PaintOpType::Translate:
+        CompareTranslateOp(static_cast<const TranslateOp*>(original),
+                           static_cast<const TranslateOp*>(written));
+        break;
+    }
+  }
+
+  void ResizeOutputBuffer() {
+    // An arbitrary deserialization buffer size that should fit all the ops
+    // in the buffer_.
+    output_size_ = (100 + sizeof(LargestPaintOp)) * buffer_.size();
+    output_.reset(static_cast<char*>(
+        base::AlignedAlloc(output_size_, PaintOpBuffer::PaintOpAlign)));
+  }
+
+  bool IsTypeSupported() {
+    // DrawRecordOps must be flattened and are not currently serialized.
+    // All other types must push non-zero amounts of ops in PushTestOps.
+    return GetParamType() != PaintOpType::DrawRecord;
+  }
+
+ protected:
+  std::unique_ptr<char, base::AlignedFreeDeleter> output_;
+  size_t output_size_ = 0u;
+  PaintOpBuffer buffer_;
+};
+
+INSTANTIATE_TEST_CASE_P(
+    P,
+    PaintOpSerializationTest,
+    ::testing::Range(static_cast<uint8_t>(0),
+                     static_cast<uint8_t>(PaintOpType::LastPaintOpType)));
+
+// Test serializing and then deserializing all test ops.  They should all
+// write successfully and be identical to the original ops in the buffer.
+TEST_P(PaintOpSerializationTest, SmokeTest) {
+  if (!IsTypeSupported())
+    return;
+
+  PushTestOps(GetParamType());
+
+  ResizeOutputBuffer();
+
+  SimpleSerializer serializer(output_.get(), output_size_);
+  serializer.Serialize(buffer_);
+
+  // Expect all ops to write more than 0 bytes.
+  for (size_t i = 0; i < buffer_.size(); ++i) {
+    SCOPED_TRACE(base::StringPrintf(
+        "%s #%zd", PaintOpTypeToString(GetParamType()).c_str(), i));
+    EXPECT_GT(serializer.bytes_written()[i], 0u);
+  }
+
+  PaintOpBuffer::Iterator iter(&buffer_);
+  for (auto* base_written :
+       DeserializerIterator(output_.get(), serializer.TotalBytesWritten())) {
+    SCOPED_TRACE(base::StringPrintf(
+        "%s #%zd", PaintOpTypeToString(GetParamType()).c_str(), iter.op_idx()));
+    ExpectOpsEqual(*iter, base_written);
+    ++iter;
+  }
+
+  EXPECT_EQ(buffer_.size(), iter.op_idx());
+}
+
+// Verify for all test ops that serializing into a smaller size aborts
+// correctly and doesn't write anything.
+TEST_P(PaintOpSerializationTest, SerializationFailures) {
+  if (!IsTypeSupported())
+    return;
+
+  PushTestOps(GetParamType());
+
+  ResizeOutputBuffer();
+
+  SimpleSerializer serializer(output_.get(), output_size_);
+  serializer.Serialize(buffer_);
+  std::vector<size_t> bytes_written = serializer.bytes_written();
+
+  PaintOp::SerializeOptions options;
+
+  for (PaintOpBuffer::Iterator iter(&buffer_); iter; ++iter) {
+    SCOPED_TRACE(base::StringPrintf(
+        "%s #%zd", PaintOpTypeToString(GetParamType()).c_str(), iter.op_idx()));
+    size_t expected_bytes = bytes_written[iter.op_idx()];
+    EXPECT_GT(expected_bytes, 0u);
+
+    // Attempt to write op into a buffer of size |i|, and only expect
+    // it to succeed if the buffer is large enough.
+    for (size_t i = 0; i < bytes_written[iter.op_idx()] + 2; ++i) {
+      size_t written_bytes = iter->Serialize(output_.get(), i, options);
+      if (i >= expected_bytes) {
+        EXPECT_EQ(expected_bytes, written_bytes) << "i: " << i;
+      } else {
+        EXPECT_EQ(0u, written_bytes) << "i: " << i;
+      }
+    }
+  }
+}
+
+// Verify that deserializing test ops from too small buffers aborts
+// correctly, in case the deserialized data is lying about how big it is.
+TEST_P(PaintOpSerializationTest, DeserializationFailures) {
+  if (!IsTypeSupported())
+    return;
+
+  PushTestOps(GetParamType());
+
+  ResizeOutputBuffer();
+
+  SimpleSerializer serializer(output_.get(), output_size_);
+  serializer.Serialize(buffer_);
+
+  char* current = static_cast<char*>(output_.get());
+
+  static constexpr size_t kAlign = PaintOpBuffer::PaintOpAlign;
+  static constexpr size_t kOutputOpSize = sizeof(LargestPaintOp) + 100;
+  std::unique_ptr<char, base::AlignedFreeDeleter> deserialize_buffer_(
+      static_cast<char*>(base::AlignedAlloc(kOutputOpSize, kAlign)));
+
+  for (PaintOpBuffer::Iterator iter(&buffer_); iter; ++iter) {
+    PaintOp* serialized = reinterpret_cast<PaintOp*>(current);
+    uint32_t skip = serialized->skip;
+
+    // Read from buffers of various sizes to make sure that having a serialized
+    // op size that is larger than the input buffer provided causes a
+    // deserialization failure to return nullptr.  Also test a few valid sizes
+    // larger than read size.
+    for (size_t read_size = 0; read_size < skip + kAlign * 2 + 2; ++read_size) {
+      SCOPED_TRACE(
+          base::StringPrintf("%s #%zd, read_size: %zd",
+                             PaintOpTypeToString(GetParamType()).c_str(),
+                             iter.op_idx(), read_size));
+      // Because PaintOp::Deserialize early outs when the input size is < skip
+      // deliberately lie about the skip.  This op tooooootally fits.
+      // This will verify that individual op deserializing code behaves
+      // properly when presented with invalid offsets.
+      serialized->skip = read_size;
+      PaintOp* written = PaintOp::Deserialize(
+          current, read_size, deserialize_buffer_.get(), kOutputOpSize);
+
+      // Skips are only valid if they are aligned.
+      if (read_size >= skip && read_size % kAlign == 0) {
+        ASSERT_NE(nullptr, written);
+        ASSERT_LE(written->skip, kOutputOpSize);
+        EXPECT_EQ(GetParamType(), written->GetType());
+      } else {
+        EXPECT_EQ(nullptr, written);
+      }
+
+      if (written)
+        written->DestroyThis();
+    }
+
+    current += skip;
+  }
+}
+
+// Test generic PaintOp deserializing failure cases.
+TEST(PaintOpBufferTest, PaintOpDeserialize) {
+  static constexpr size_t kSize = sizeof(LargestPaintOp) + 100;
+  static constexpr size_t kAlign = PaintOpBuffer::PaintOpAlign;
+  std::unique_ptr<char, base::AlignedFreeDeleter> input_(
+      static_cast<char*>(base::AlignedAlloc(kSize, kAlign)));
+  std::unique_ptr<char, base::AlignedFreeDeleter> output_(
+      static_cast<char*>(base::AlignedAlloc(kSize, kAlign)));
+
+  PaintOpBuffer buffer;
+  buffer.push<DrawColorOp>(SK_ColorMAGENTA, SkBlendMode::kSrc);
+
+  PaintOpBuffer::Iterator iter(&buffer);
+  PaintOp* op = *iter;
+  ASSERT_TRUE(op);
+
+  PaintOp::SerializeOptions options;
+  size_t bytes_written = op->Serialize(input_.get(), kSize, options);
+  ASSERT_GT(bytes_written, 0u);
+
+  // can deserialize from exactly the right size
+  PaintOp* success =
+      PaintOp::Deserialize(input_.get(), bytes_written, output_.get(), kSize);
+  ASSERT_TRUE(success);
+  success->DestroyThis();
+
+  // fail to deserialize if skip goes past input size
+  // (the DeserializationFailures test above tests if the skip is lying)
+  for (size_t i = 0; i < bytes_written - 1; ++i)
+    EXPECT_FALSE(PaintOp::Deserialize(input_.get(), i, output_.get(), kSize));
+
+  // unaligned skips fail to deserialize
+  PaintOp* serialized = reinterpret_cast<PaintOp*>(input_.get());
+  EXPECT_EQ(0u, serialized->skip % kAlign);
+  serialized->skip -= 1;
+  EXPECT_FALSE(
+      PaintOp::Deserialize(input_.get(), bytes_written, output_.get(), kSize));
+  serialized->skip += 1;
+
+  // bogus types fail to deserialize
+  serialized->type = static_cast<uint8_t>(PaintOpType::LastPaintOpType) + 1;
+  EXPECT_FALSE(
+      PaintOp::Deserialize(input_.get(), bytes_written, output_.get(), kSize));
+}
+
 }  // namespace cc
diff --git a/cc/paint/paint_op_reader.cc b/cc/paint/paint_op_reader.cc
new file mode 100644
index 0000000..6428d4c7
--- /dev/null
+++ b/cc/paint/paint_op_reader.cc
@@ -0,0 +1,128 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/paint/paint_op_reader.h"
+
+#include <stddef.h>
+
+#include "cc/paint/paint_flags.h"
+#include "third_party/skia/include/core/SkPath.h"
+#include "third_party/skia/include/core/SkRRect.h"
+#include "third_party/skia/include/core/SkTextBlob.h"
+
+namespace cc {
+
+template <typename T>
+void PaintOpReader::ReadSimple(T* val) {
+  if (remaining_bytes_ < sizeof(T))
+    valid_ = false;
+  if (!valid_)
+    return;
+
+  *val = reinterpret_cast<const T*>(memory_)[0];
+
+  memory_ += sizeof(T);
+  remaining_bytes_ -= sizeof(T);
+}
+
+void PaintOpReader::ReadData(size_t bytes, void* data) {
+  if (remaining_bytes_ < bytes)
+    valid_ = false;
+  if (!valid_)
+    return;
+
+  memcpy(data, memory_, bytes);
+  memory_ += bytes;
+  remaining_bytes_ -= bytes;
+}
+
+void PaintOpReader::ReadArray(size_t count, SkPoint* array) {
+  size_t bytes = count * sizeof(SkPoint);
+  if (remaining_bytes_ < bytes)
+    valid_ = false;
+  // Overflow?
+  if (count > static_cast<size_t>(~0) / sizeof(SkPoint))
+    valid_ = false;
+  if (!valid_)
+    return;
+
+  memcpy(array, memory_, bytes);
+  memory_ += bytes;
+  remaining_bytes_ -= bytes;
+}
+
+void PaintOpReader::Read(SkScalar* data) {
+  ReadSimple(data);
+}
+
+void PaintOpReader::Read(size_t* data) {
+  ReadSimple(data);
+}
+
+void PaintOpReader::Read(uint8_t* data) {
+  ReadSimple(data);
+}
+
+void PaintOpReader::Read(SkRect* rect) {
+  ReadSimple(rect);
+}
+
+void PaintOpReader::Read(SkIRect* rect) {
+  ReadSimple(rect);
+}
+
+void PaintOpReader::Read(SkRRect* rect) {
+  ReadSimple(rect);
+}
+
+void PaintOpReader::Read(SkPath* path) {
+  if (!valid_)
+    return;
+
+  // TODO(enne): Should the writer write how many bytes it expects as well?
+  size_t read_bytes = path->readFromMemory(memory_, remaining_bytes_);
+  if (!read_bytes)
+    valid_ = false;
+
+  memory_ += read_bytes;
+  remaining_bytes_ -= read_bytes;
+}
+
+void PaintOpReader::Read(PaintFlags* flags) {
+  // TODO(enne): implement PaintFlags serialization: http://crbug.com/737629
+}
+
+void PaintOpReader::Read(PaintImage* image) {
+  // TODO(enne): implement PaintImage serialization: http://crbug.com/737629
+}
+
+void PaintOpReader::Read(sk_sp<SkData>* data) {
+  // Clobber any garbage.
+
+  size_t bytes;
+  ReadSimple(&bytes);
+  if (remaining_bytes_ < bytes)
+    valid_ = false;
+  if (!valid_)
+    return;
+
+  // Separate out empty vs not valid cases.
+  if (bytes == 0) {
+    bool has_data = false;
+    Read(&has_data);
+    if (has_data)
+      *data = SkData::MakeEmpty();
+    return;
+  }
+  *data = SkData::MakeWithCopy(memory_, bytes);
+
+  memory_ += bytes;
+  remaining_bytes_ -= bytes;
+}
+
+void PaintOpReader::Read(sk_sp<SkTextBlob>* blob) {
+  // TODO(enne): implement SkTextBlob serialization: http://crbug.com/737629
+}
+
+}  // namespace cc
diff --git a/cc/paint/paint_op_reader.h b/cc/paint/paint_op_reader.h
new file mode 100644
index 0000000..0252178
--- /dev/null
+++ b/cc/paint/paint_op_reader.h
@@ -0,0 +1,76 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_PAINT_PAINT_OP_READER_H_
+#define CC_PAINT_PAINT_OP_READER_H_
+
+#include <vector>
+
+#include "cc/paint/paint_op_writer.h"
+
+namespace cc {
+
+// PaintOpReader takes garbage |memory| and clobbers it with successive
+// read functions.
+class PaintOpReader {
+ public:
+  PaintOpReader(const void* memory, size_t size)
+      : memory_(static_cast<const char*>(memory) +
+                PaintOpWriter::HeaderBytes()),
+        remaining_bytes_(size - PaintOpWriter::HeaderBytes()) {
+    if (size < PaintOpWriter::HeaderBytes())
+      valid_ = false;
+  }
+
+  bool valid() const { return valid_; }
+
+  void ReadData(size_t bytes, void* data);
+  void ReadArray(size_t count, SkPoint* array);
+
+  void Read(SkScalar* data);
+  void Read(size_t* data);
+  void Read(uint8_t* data);
+  void Read(SkRect* rect);
+  void Read(SkIRect* rect);
+  void Read(SkRRect* rect);
+
+  void Read(SkPath* path);
+  void Read(PaintFlags* flags);
+  void Read(PaintImage* image);
+  void Read(sk_sp<SkData>* data);
+  void Read(sk_sp<SkTextBlob>* blob);
+
+  void Read(SkClipOp* op) {
+    uint8_t value = 0u;
+    Read(&value);
+    *op = static_cast<SkClipOp>(value);
+  }
+  void Read(PaintCanvas::AnnotationType* type) {
+    uint8_t value = 0u;
+    Read(&value);
+    *type = static_cast<PaintCanvas::AnnotationType>(value);
+  }
+  void Read(PaintCanvas::SrcRectConstraint* constraint) {
+    uint8_t value = 0u;
+    Read(&value);
+    *constraint = static_cast<PaintCanvas::SrcRectConstraint>(value);
+  }
+  void Read(bool* data) {
+    uint8_t value = 0u;
+    Read(&value);
+    *data = !!value;
+  }
+
+ private:
+  template <typename T>
+  void ReadSimple(T* val);
+
+  const char* memory_ = nullptr;
+  size_t remaining_bytes_ = 0u;
+  bool valid_ = true;
+};
+
+}  // namespace cc
+
+#endif  // CC_PAINT_PAINT_OP_READER_H_
diff --git a/cc/paint/paint_op_writer.cc b/cc/paint/paint_op_writer.cc
new file mode 100644
index 0000000..1248e17
--- /dev/null
+++ b/cc/paint/paint_op_writer.cc
@@ -0,0 +1,104 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/paint/paint_op_writer.h"
+
+#include "cc/paint/paint_flags.h"
+#include "third_party/skia/include/core/SkTextBlob.h"
+
+namespace cc {
+
+template <typename T>
+void PaintOpWriter::WriteSimple(const T& val) {
+  static_assert(base::is_trivially_copyable<T>::value, "");
+  if (sizeof(T) > remaining_bytes_)
+    valid_ = false;
+  if (!valid_)
+    return;
+
+  reinterpret_cast<T*>(memory_)[0] = val;
+
+  memory_ += sizeof(T);
+  remaining_bytes_ -= sizeof(T);
+}
+
+void PaintOpWriter::Write(size_t data) {
+  WriteSimple(data);
+}
+
+void PaintOpWriter::Write(SkScalar data) {
+  WriteSimple(data);
+}
+
+void PaintOpWriter::Write(uint8_t data) {
+  WriteSimple(data);
+}
+
+void PaintOpWriter::Write(const SkRect& rect) {
+  WriteSimple(rect);
+}
+
+void PaintOpWriter::Write(const SkIRect& rect) {
+  WriteSimple(rect);
+}
+
+void PaintOpWriter::Write(const SkRRect& rect) {
+  WriteSimple(rect);
+}
+
+void PaintOpWriter::Write(const SkPath& path) {
+  size_t bytes = path.writeToMemory(nullptr);
+  if (bytes > remaining_bytes_)
+    valid_ = false;
+  if (!valid_)
+    return;
+
+  path.writeToMemory(memory_);
+  memory_ += bytes;
+  remaining_bytes_ -= bytes;
+}
+
+void PaintOpWriter::Write(const PaintFlags& flags) {
+  // TODO(enne): implement PaintFlags serialization: http://crbug.com/737629
+}
+
+void PaintOpWriter::Write(const PaintImage& image, ImageDecodeCache* cache) {
+  // TODO(enne): implement PaintImage serialization: http://crbug.com/737629
+}
+
+void PaintOpWriter::Write(const sk_sp<SkData>& data) {
+  if (data.get() && data->size()) {
+    Write(data->size());
+    WriteData(data->size(), data->data());
+  } else {
+    // Differentiate between nullptr and valid but zero size.  It's not clear
+    // that this happens in practice, but seems better to be consistent.
+    Write(static_cast<size_t>(0));
+    Write(!!data.get());
+  }
+}
+
+void PaintOpWriter::Write(const sk_sp<SkTextBlob>& blob) {
+  // TODO(enne): implement SkTextBlob serialization: http://crbug.com/737629
+}
+
+void PaintOpWriter::WriteData(size_t bytes, const void* input) {
+  if (bytes > remaining_bytes_)
+    valid_ = false;
+  if (!valid_)
+    return;
+  if (bytes == 0)
+    return;
+
+  memcpy(memory_, input, bytes);
+  memory_ += bytes;
+  remaining_bytes_ -= bytes;
+}
+
+void PaintOpWriter::WriteArray(size_t count, const SkPoint* input) {
+  size_t bytes = sizeof(SkPoint) * count;
+  WriteData(bytes, input);
+}
+
+}  // namespace cc
diff --git a/cc/paint/paint_op_writer.h b/cc/paint/paint_op_writer.h
new file mode 100644
index 0000000..0163067f
--- /dev/null
+++ b/cc/paint/paint_op_writer.h
@@ -0,0 +1,71 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_PAINT_PAINT_OP_WRITER_H_
+#define CC_PAINT_PAINT_OP_WRITER_H_
+
+#include "cc/paint/paint_canvas.h"
+
+struct SkRect;
+struct SkIRect;
+class SkRRect;
+
+namespace cc {
+
+class ImageDecodeCache;
+
+class PaintOpWriter {
+ public:
+  PaintOpWriter(void* memory, size_t size)
+      : memory_(static_cast<char*>(memory) + HeaderBytes()),
+        size_(size),
+        remaining_bytes_(size - HeaderBytes()) {
+    // Leave space for header of type/skip.
+    DCHECK_GE(size, HeaderBytes());
+  }
+
+  static size_t constexpr HeaderBytes() { return 4u; }
+
+  // Write a sequence of arbitrary bytes.
+  void WriteData(size_t bytes, const void* input);
+
+  void WriteArray(size_t count, const SkPoint* input);
+
+  size_t size() const { return valid_ ? size_ - remaining_bytes_ : 0u; }
+
+  void Write(SkScalar data);
+  void Write(size_t data);
+  void Write(uint8_t data);
+  void Write(const SkRect& rect);
+  void Write(const SkIRect& rect);
+  void Write(const SkRRect& rect);
+
+  void Write(const SkPath& path);
+  void Write(const PaintFlags& flags);
+  void Write(const PaintImage& image, ImageDecodeCache* cache);
+  void Write(const sk_sp<SkData>& data);
+  void Write(const sk_sp<SkTextBlob>& blob);
+
+  void Write(SkClipOp op) { Write(static_cast<uint8_t>(op)); }
+  void Write(PaintCanvas::AnnotationType type) {
+    Write(static_cast<uint8_t>(type));
+  }
+  void Write(PaintCanvas::SrcRectConstraint constraint) {
+    Write(static_cast<uint8_t>(constraint));
+  }
+  void Write(bool data) { Write(static_cast<uint8_t>(data)); }
+
+ private:
+  template <typename T>
+  void WriteSimple(const T& val);
+
+  char* memory_ = nullptr;
+  size_t size_ = 0u;
+  size_t remaining_bytes_ = 0u;
+  bool valid_ = true;
+};
+
+}  // namespace cc
+
+#endif  // CC_PAINT_PAINT_OP_WRITER_H_
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/WebVrTabTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/WebVrTabTest.java
index 29c0e760..1c5bce9 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/WebVrTabTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/WebVrTabTest.java
@@ -14,7 +14,6 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.base.test.util.FlakyTest;
 import org.chromium.base.test.util.MinAndroidSdkLevel;
 import org.chromium.chrome.browser.ChromeSwitches;
 import org.chromium.chrome.test.ChromeActivityTestRule;
@@ -36,7 +35,6 @@
      */
     @Test
     @MediumTest
-    @FlakyTest(message = "crbug.com/738148")
     public void testPoseDataUnfocusedTab() throws InterruptedException {
         mVrTestRule.loadUrlAndAwaitInitialization(
                 VrTestRule.getHtmlTestFile("test_pose_data_unfocused_tab"), PAGE_LOAD_TIMEOUT_S);
diff --git a/chrome/browser/offline_pages/prefetch/prefetch_service_factory.cc b/chrome/browser/offline_pages/prefetch/prefetch_service_factory.cc
index e16338c..76c3cf59 100644
--- a/chrome/browser/offline_pages/prefetch/prefetch_service_factory.cc
+++ b/chrome/browser/offline_pages/prefetch/prefetch_service_factory.cc
@@ -7,13 +7,17 @@
 #include <memory>
 #include <utility>
 
+#include "base/files/file_path.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/singleton.h"
+#include "base/sequenced_task_runner.h"
+#include "base/task_scheduler/post_task.h"
 #include "chrome/browser/download/download_service_factory.h"
 #include "chrome/browser/offline_pages/prefetch/offline_metrics_collector_impl.h"
 #include "chrome/browser/offline_pages/prefetch/prefetch_instance_id_proxy.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/channel_info.h"
+#include "chrome/common/chrome_constants.h"
 #include "chrome/common/chrome_content_client.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/offline_pages/core/prefetch/prefetch_dispatcher_impl.h"
@@ -21,6 +25,7 @@
 #include "components/offline_pages/core/prefetch/prefetch_gcm_app_handler.h"
 #include "components/offline_pages/core/prefetch/prefetch_network_request_factory_impl.h"
 #include "components/offline_pages/core/prefetch/prefetch_service_impl.h"
+#include "components/offline_pages/core/prefetch/store/prefetch_store.h"
 #include "components/offline_pages/core/prefetch/suggested_articles_observer.h"
 #include "content/public/browser/browser_context.h"
 
@@ -49,15 +54,27 @@
     content::BrowserContext* context) const {
   Profile* profile = Profile::FromBrowserContext(context);
   DCHECK(profile);
+
+  auto offline_metrics_collector =
+      base::MakeUnique<OfflineMetricsCollectorImpl>(profile->GetPrefs());
+
   auto prefetch_dispatcher = base::MakeUnique<PrefetchDispatcherImpl>();
+
   auto prefetch_gcm_app_handler = base::MakeUnique<PrefetchGCMAppHandler>(
       base::MakeUnique<PrefetchInstanceIDProxy>(kPrefetchingOfflinePagesAppId,
                                                 context));
+
   auto prefetch_network_request_factory =
       base::MakeUnique<PrefetchNetworkRequestFactoryImpl>(
           profile->GetRequestContext(), chrome::GetChannel(), GetUserAgent());
-  auto offline_metrics_collector =
-      base::MakeUnique<OfflineMetricsCollectorImpl>(profile->GetPrefs());
+
+  scoped_refptr<base::SequencedTaskRunner> background_task_runner =
+      base::CreateSequencedTaskRunnerWithTraits({base::MayBlock()});
+  base::FilePath store_path =
+      profile->GetPath().Append(chrome::kOfflinePagePrefetchStoreDirname);
+  auto prefetch_store =
+      base::MakeUnique<PrefetchStore>(background_task_runner, store_path);
+
   auto suggested_articles_observer =
       base::MakeUnique<SuggestedArticlesObserver>();
 
@@ -68,7 +85,7 @@
   return new PrefetchServiceImpl(
       std::move(offline_metrics_collector), std::move(prefetch_dispatcher),
       std::move(prefetch_gcm_app_handler),
-      std::move(prefetch_network_request_factory),
+      std::move(prefetch_network_request_factory), std::move(prefetch_store),
       std::move(suggested_articles_observer), std::move(prefetch_downloader));
 }
 
diff --git a/chrome/browser/pdf/pdf_extension_test.cc b/chrome/browser/pdf/pdf_extension_test.cc
index 2a3cda54b..7b796c30 100644
--- a/chrome/browser/pdf/pdf_extension_test.cc
+++ b/chrome/browser/pdf/pdf_extension_test.cc
@@ -776,168 +776,6 @@
 }
 #endif
 
-IN_PROC_BROWSER_TEST_F(PDFExtensionTest, LinkCtrlLeftClick) {
-  GURL test_pdf_url(embedded_test_server()->GetURL("/pdf/test-link.pdf"));
-  WebContents* guest_contents = LoadPdfGetGuestContents(test_pdf_url);
-  ASSERT_TRUE(guest_contents);
-
-  // The link position of the test-link.pdf in page coordinates is (110, 110).
-  // Convert the link position from page coordinates to screen coordinates.
-  gfx::Point link_position(110, 110);
-  ConvertPageCoordToScreenCoord(guest_contents, &link_position);
-
-  WebContents* web_contents = GetActiveWebContents();
-
-  content::WindowedNotificationObserver observer(
-      chrome::NOTIFICATION_TAB_ADDED,
-      content::NotificationService::AllSources());
-  content::SimulateMouseClickAt(web_contents, kDefaultKeyModifier,
-                                blink::WebMouseEvent::Button::kLeft,
-                                link_position);
-  observer.Wait();
-
-  int tab_count = browser()->tab_strip_model()->count();
-  ASSERT_EQ(2, tab_count);
-
-  WebContents* active_web_contents = GetActiveWebContents();
-  ASSERT_EQ(web_contents, active_web_contents);
-
-  WebContents* new_web_contents =
-      browser()->tab_strip_model()->GetWebContentsAt(1);
-  ASSERT_TRUE(new_web_contents);
-  ASSERT_NE(web_contents, new_web_contents);
-
-  const GURL& url = new_web_contents->GetURL();
-  ASSERT_EQ(std::string("http://www.example.com/"), url.spec());
-}
-
-IN_PROC_BROWSER_TEST_F(PDFExtensionTest, LinkMiddleClick) {
-  GURL test_pdf_url(embedded_test_server()->GetURL("/pdf/test-link.pdf"));
-  WebContents* guest_contents = LoadPdfGetGuestContents(test_pdf_url);
-  ASSERT_TRUE(guest_contents);
-
-  // The link position of the test-link.pdf in page coordinates is (110, 110).
-  // Convert the link position from page coordinates to screen coordinates.
-  gfx::Point link_position(110, 110);
-  ConvertPageCoordToScreenCoord(guest_contents, &link_position);
-
-  WebContents* web_contents = GetActiveWebContents();
-
-  content::WindowedNotificationObserver observer(
-      chrome::NOTIFICATION_TAB_ADDED,
-      content::NotificationService::AllSources());
-  content::SimulateMouseClickAt(
-      web_contents, 0, blink::WebMouseEvent::Button::kMiddle, link_position);
-  observer.Wait();
-
-  int tab_count = browser()->tab_strip_model()->count();
-  ASSERT_EQ(2, tab_count);
-
-  WebContents* active_web_contents = GetActiveWebContents();
-  ASSERT_EQ(web_contents, active_web_contents);
-
-  WebContents* new_web_contents =
-      browser()->tab_strip_model()->GetWebContentsAt(1);
-  ASSERT_TRUE(new_web_contents);
-  ASSERT_NE(web_contents, new_web_contents);
-
-  const GURL& url = new_web_contents->GetURL();
-  ASSERT_EQ(std::string("http://www.example.com/"), url.spec());
-}
-
-IN_PROC_BROWSER_TEST_F(PDFExtensionTest, LinkCtrlShiftLeftClick) {
-  GURL test_pdf_url(embedded_test_server()->GetURL("/pdf/test-link.pdf"));
-  WebContents* guest_contents = LoadPdfGetGuestContents(test_pdf_url);
-  ASSERT_TRUE(guest_contents);
-
-  // The link position of the test-link.pdf in page coordinates is (110, 110).
-  // Convert the link position from page coordinates to screen coordinates.
-  gfx::Point link_position(110, 110);
-  ConvertPageCoordToScreenCoord(guest_contents, &link_position);
-
-  WebContents* web_contents = GetActiveWebContents();
-
-  int modifiers = blink::WebInputEvent::kShiftKey | kDefaultKeyModifier;
-
-  content::WindowedNotificationObserver observer(
-      chrome::NOTIFICATION_TAB_ADDED,
-      content::NotificationService::AllSources());
-  content::SimulateMouseClickAt(web_contents, modifiers,
-                                blink::WebMouseEvent::Button::kLeft,
-                                link_position);
-  observer.Wait();
-
-  int tab_count = browser()->tab_strip_model()->count();
-  ASSERT_EQ(2, tab_count);
-
-  WebContents* active_web_contents = GetActiveWebContents();
-  ASSERT_NE(web_contents, active_web_contents);
-
-  const GURL& url = active_web_contents->GetURL();
-  ASSERT_EQ(std::string("http://www.example.com/"), url.spec());
-}
-
-IN_PROC_BROWSER_TEST_F(PDFExtensionTest, LinkShiftMiddleClick) {
-  GURL test_pdf_url(embedded_test_server()->GetURL("/pdf/test-link.pdf"));
-  WebContents* guest_contents = LoadPdfGetGuestContents(test_pdf_url);
-  ASSERT_TRUE(guest_contents);
-
-  // The link position of the test-link.pdf in page coordinates is (110, 110).
-  // Convert the link position from page coordinates to screen coordinates.
-  gfx::Point link_position(110, 110);
-  ConvertPageCoordToScreenCoord(guest_contents, &link_position);
-
-  WebContents* web_contents = GetActiveWebContents();
-
-  content::WindowedNotificationObserver observer(
-      chrome::NOTIFICATION_TAB_ADDED,
-      content::NotificationService::AllSources());
-  content::SimulateMouseClickAt(web_contents, blink::WebInputEvent::kShiftKey,
-                                blink::WebMouseEvent::Button::kMiddle,
-                                link_position);
-  observer.Wait();
-
-  int tab_count = browser()->tab_strip_model()->count();
-  ASSERT_EQ(2, tab_count);
-
-  WebContents* active_web_contents = GetActiveWebContents();
-  ASSERT_NE(web_contents, active_web_contents);
-
-  const GURL& url = active_web_contents->GetURL();
-  ASSERT_EQ(std::string("http://www.example.com/"), url.spec());
-}
-
-IN_PROC_BROWSER_TEST_F(PDFExtensionTest, LinkShiftLeftClick) {
-  GURL test_pdf_url(embedded_test_server()->GetURL("/pdf/test-link.pdf"));
-  WebContents* guest_contents = LoadPdfGetGuestContents(test_pdf_url);
-  ASSERT_TRUE(guest_contents);
-  ASSERT_EQ(1U, chrome::GetTotalBrowserCount());
-
-  // The link position of the test-link.pdf in page coordinates is (110, 110).
-  // Convert the link position from page coordinates to screen coordinates.
-  gfx::Point link_position(110, 110);
-  ConvertPageCoordToScreenCoord(guest_contents, &link_position);
-
-  WebContents* web_contents = GetActiveWebContents();
-
-  content::WindowedNotificationObserver observer(
-      chrome::NOTIFICATION_BROWSER_WINDOW_READY,
-      content::NotificationService::AllSources());
-  content::SimulateMouseClickAt(web_contents, blink::WebInputEvent::kShiftKey,
-                                blink::WebMouseEvent::Button::kLeft,
-                                link_position);
-  observer.Wait();
-
-  ASSERT_EQ(2U, chrome::GetTotalBrowserCount());
-
-  WebContents* active_web_contents =
-      chrome::FindLastActive()->tab_strip_model()->GetActiveWebContents();
-  ASSERT_NE(web_contents, active_web_contents);
-
-  const GURL& url = active_web_contents->GetURL();
-  ASSERT_EQ(std::string("http://www.example.com/"), url.spec());
-}
-
 // Test that if the plugin tries to load a URL that redirects then it will fail
 // to load. This is to avoid the source origin of the document changing during
 // the redirect, which can have security implications. https://crbug.com/653749.
@@ -954,8 +792,7 @@
   WebContents* web_contents = GetActiveWebContents();
 
   ui_test_utils::NavigateToURLWithDisposition(
-      browser(), GURL("about:blank"),
-      WindowOpenDisposition::NEW_FOREGROUND_TAB,
+      browser(), GURL("about:blank"), WindowOpenDisposition::NEW_FOREGROUND_TAB,
       ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB |
           ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
   WebContents* active_web_contents = GetActiveWebContents();
@@ -970,11 +807,187 @@
   EXPECT_FALSE(active_web_contents->GetController().GetPendingEntry());
 }
 
+IN_PROC_BROWSER_TEST_F(PDFExtensionTest, OpenFromFTP) {
+  net::SpawnedTestServer ftp_server(
+      net::SpawnedTestServer::TYPE_FTP, net::SpawnedTestServer::kLocalhost,
+      base::FilePath(FILE_PATH_LITERAL("chrome/test/data/pdf")));
+  ASSERT_TRUE(ftp_server.Start());
+
+  GURL url(ftp_server.GetURL("/test.pdf"));
+  ASSERT_TRUE(LoadPdf(url));
+  EXPECT_EQ(base::ASCIIToUTF16("test.pdf"), GetActiveWebContents()->GetTitle());
+}
+
+class PDFExtensionLinkClickTest : public PDFExtensionTest {
+ public:
+  PDFExtensionLinkClickTest() : guest_contents_(nullptr) {}
+  ~PDFExtensionLinkClickTest() override {}
+
+ protected:
+  void LoadTestLinkPdfGetGuestContents() {
+    GURL test_pdf_url(embedded_test_server()->GetURL("/pdf/test-link.pdf"));
+    guest_contents_ = LoadPdfGetGuestContents(test_pdf_url);
+    ASSERT_TRUE(guest_contents_);
+  }
+
+  // The rectangle of the link in test-link.pdf is [72 706 164 719] in PDF user
+  // space. To calculate a position inside this rectangle, several
+  // transformations have to be applied:
+  // [a] (110, 110) in Blink page coordinates ->
+  // [b] (219, 169) in Blink screen coordinates ->
+  // [c] (115, 169) in PDF Device space coordinates ->
+  // [d] (82.5, 709.5) in PDF user space coordinates.
+  // This performs the [a] to [b] transformation, since that is the coordinate
+  // space content::SimulateMouseClickAt() needs.
+  gfx::Point GetLinkPosition() {
+    gfx::Point link_position(110, 110);
+    ConvertPageCoordToScreenCoord(guest_contents_, &link_position);
+    return link_position;
+  }
+
+  void SetGuestContents(WebContents* guest_contents) {
+    ASSERT_TRUE(guest_contents);
+    guest_contents_ = guest_contents;
+  }
+
+ private:
+  WebContents* guest_contents_;
+};
+
+IN_PROC_BROWSER_TEST_F(PDFExtensionLinkClickTest, CtrlLeft) {
+  LoadTestLinkPdfGetGuestContents();
+
+  WebContents* web_contents = GetActiveWebContents();
+
+  content::WindowedNotificationObserver observer(
+      chrome::NOTIFICATION_TAB_ADDED,
+      content::NotificationService::AllSources());
+  content::SimulateMouseClickAt(web_contents, kDefaultKeyModifier,
+                                blink::WebMouseEvent::Button::kLeft,
+                                GetLinkPosition());
+  observer.Wait();
+
+  int tab_count = browser()->tab_strip_model()->count();
+  ASSERT_EQ(2, tab_count);
+
+  WebContents* active_web_contents = GetActiveWebContents();
+  ASSERT_EQ(web_contents, active_web_contents);
+
+  WebContents* new_web_contents =
+      browser()->tab_strip_model()->GetWebContentsAt(1);
+  ASSERT_TRUE(new_web_contents);
+  ASSERT_NE(web_contents, new_web_contents);
+
+  const GURL& url = new_web_contents->GetURL();
+  EXPECT_EQ("http://www.example.com/", url.spec());
+}
+
+IN_PROC_BROWSER_TEST_F(PDFExtensionLinkClickTest, Middle) {
+  LoadTestLinkPdfGetGuestContents();
+
+  WebContents* web_contents = GetActiveWebContents();
+
+  content::WindowedNotificationObserver observer(
+      chrome::NOTIFICATION_TAB_ADDED,
+      content::NotificationService::AllSources());
+  content::SimulateMouseClickAt(web_contents, 0,
+                                blink::WebMouseEvent::Button::kMiddle,
+                                GetLinkPosition());
+  observer.Wait();
+
+  int tab_count = browser()->tab_strip_model()->count();
+  ASSERT_EQ(2, tab_count);
+
+  WebContents* active_web_contents = GetActiveWebContents();
+  ASSERT_EQ(web_contents, active_web_contents);
+
+  WebContents* new_web_contents =
+      browser()->tab_strip_model()->GetWebContentsAt(1);
+  ASSERT_TRUE(new_web_contents);
+  ASSERT_NE(web_contents, new_web_contents);
+
+  const GURL& url = new_web_contents->GetURL();
+  EXPECT_EQ("http://www.example.com/", url.spec());
+}
+
+IN_PROC_BROWSER_TEST_F(PDFExtensionLinkClickTest, CtrlShiftLeft) {
+  LoadTestLinkPdfGetGuestContents();
+
+  WebContents* web_contents = GetActiveWebContents();
+
+  const int modifiers = blink::WebInputEvent::kShiftKey | kDefaultKeyModifier;
+
+  content::WindowedNotificationObserver observer(
+      chrome::NOTIFICATION_TAB_ADDED,
+      content::NotificationService::AllSources());
+  content::SimulateMouseClickAt(web_contents, modifiers,
+                                blink::WebMouseEvent::Button::kLeft,
+                                GetLinkPosition());
+  observer.Wait();
+
+  int tab_count = browser()->tab_strip_model()->count();
+  ASSERT_EQ(2, tab_count);
+
+  WebContents* active_web_contents = GetActiveWebContents();
+  ASSERT_NE(web_contents, active_web_contents);
+
+  const GURL& url = active_web_contents->GetURL();
+  EXPECT_EQ("http://www.example.com/", url.spec());
+}
+
+IN_PROC_BROWSER_TEST_F(PDFExtensionLinkClickTest, ShiftMiddle) {
+  LoadTestLinkPdfGetGuestContents();
+
+  WebContents* web_contents = GetActiveWebContents();
+
+  content::WindowedNotificationObserver observer(
+      chrome::NOTIFICATION_TAB_ADDED,
+      content::NotificationService::AllSources());
+  content::SimulateMouseClickAt(web_contents, blink::WebInputEvent::kShiftKey,
+                                blink::WebMouseEvent::Button::kMiddle,
+                                GetLinkPosition());
+  observer.Wait();
+
+  int tab_count = browser()->tab_strip_model()->count();
+  ASSERT_EQ(2, tab_count);
+
+  WebContents* active_web_contents = GetActiveWebContents();
+  ASSERT_NE(web_contents, active_web_contents);
+
+  const GURL& url = active_web_contents->GetURL();
+  EXPECT_EQ("http://www.example.com/", url.spec());
+}
+
+IN_PROC_BROWSER_TEST_F(PDFExtensionLinkClickTest, ShiftLeft) {
+  LoadTestLinkPdfGetGuestContents();
+
+  ASSERT_EQ(1U, chrome::GetTotalBrowserCount());
+
+  WebContents* web_contents = GetActiveWebContents();
+
+  content::WindowedNotificationObserver observer(
+      chrome::NOTIFICATION_BROWSER_WINDOW_READY,
+      content::NotificationService::AllSources());
+  content::SimulateMouseClickAt(web_contents, blink::WebInputEvent::kShiftKey,
+                                blink::WebMouseEvent::Button::kLeft,
+                                GetLinkPosition());
+  observer.Wait();
+
+  ASSERT_EQ(2U, chrome::GetTotalBrowserCount());
+
+  WebContents* active_web_contents =
+      chrome::FindLastActive()->tab_strip_model()->GetActiveWebContents();
+  ASSERT_NE(web_contents, active_web_contents);
+
+  const GURL& url = active_web_contents->GetURL();
+  EXPECT_EQ("http://www.example.com/", url.spec());
+}
+
 // This test opens a PDF by clicking a link via javascript and verifies that
 // the PDF is loaded and functional by clicking a link in the PDF. The link
 // click in the PDF opens a new tab. The main page handles the pageShow event
 // and updates the history state.
-IN_PROC_BROWSER_TEST_F(PDFExtensionTest, OpenPDFOnLinkClickWithReplaceState) {
+IN_PROC_BROWSER_TEST_F(PDFExtensionLinkClickTest, OpenPDFWithReplaceState) {
   // Navigate to the main page.
   GURL test_url(
       embedded_test_server()->GetURL("/pdf/pdf_href_replace_state.html"));
@@ -997,19 +1010,14 @@
   // tab.
   content::BrowserPluginGuestManager* guest_manager =
       web_contents->GetBrowserContext()->GetGuestManager();
-  WebContents* guest_contents = guest_manager->GetFullPageGuest(web_contents);
-  ASSERT_TRUE(guest_contents);
-  // The link position of the test-link.pdf in page coordinates is (110, 110).
-  // Convert the link position from page coordinates to screen coordinates.
-  gfx::Point link_position(110, 110);
-  ConvertPageCoordToScreenCoord(guest_contents, &link_position);
+  SetGuestContents(guest_manager->GetFullPageGuest(web_contents));
 
   content::WindowedNotificationObserver observer(
       chrome::NOTIFICATION_TAB_ADDED,
       content::NotificationService::AllSources());
   content::SimulateMouseClickAt(web_contents, kDefaultKeyModifier,
                                 blink::WebMouseEvent::Button::kLeft,
-                                link_position);
+                                GetLinkPosition());
   observer.Wait();
 
   // We should have two tabs now. One with the PDF and the second for
@@ -1026,16 +1034,5 @@
   ASSERT_NE(web_contents, new_web_contents);
 
   const GURL& url = new_web_contents->GetURL();
-  ASSERT_EQ(GURL("http://www.example.com"), url);
-}
-
-IN_PROC_BROWSER_TEST_F(PDFExtensionTest, OpenFromFTP) {
-  net::SpawnedTestServer ftp_server(
-      net::SpawnedTestServer::TYPE_FTP, net::SpawnedTestServer::kLocalhost,
-      base::FilePath(FILE_PATH_LITERAL("chrome/test/data/pdf")));
-  ASSERT_TRUE(ftp_server.Start());
-
-  GURL url(ftp_server.GetURL("/test.pdf"));
-  ASSERT_TRUE(LoadPdf(url));
-  EXPECT_EQ(base::ASCIIToUTF16("test.pdf"), GetActiveWebContents()->GetTitle());
+  EXPECT_EQ("http://www.example.com/", url.spec());
 }
diff --git a/chrome/browser/resources/md_extensions/animation_helper.js b/chrome/browser/resources/md_extensions/animation_helper.js
index a429f184..00483b37 100644
--- a/chrome/browser/resources/md_extensions/animation_helper.js
+++ b/chrome/browser/resources/md_extensions/animation_helper.js
@@ -22,24 +22,25 @@
    * A helper object for setting entry/exit animations. Polymer's support of
    * this is pretty limited, since it doesn't allow for things like specifying
    * hero properties or nodes.
-   * @param {!HTMLElement} element The parent element to set the animations on.
-   *     This will be used as the page in to/fromPage.
-   * @param {?HTMLElement} node The node to use for scaling and fading
-   *     animations.
-   * @constructor
    */
-  function AnimationHelper(element, node) {
-    this.element_ = element;
-    this.node_ = node;
-    element.animationConfig = {};
-  }
+  class AnimationHelper {
+    /**
+     * @param {!HTMLElement} element The parent element to set the animations
+     *     on. This will be used as the page in to/fromPage.
+     * @param {?HTMLElement} node The node to use for scaling and fading
+     *     animations.
+     */
+    constructor(element, node) {
+      this.element_ = element;
+      this.node_ = node;
+      element.animationConfig = {};
+    }
 
-  AnimationHelper.prototype = {
     /**
      * Set the entry animation for the element.
      * @param {!Array<extensions.Animation>} animations
      */
-    setEntryAnimations: function(animations) {
+    setEntryAnimations(animations) {
       var configs = [];
       for (let animation of animations) {
         switch (animation) {
@@ -56,13 +57,13 @@
         }
       }
       this.element_.animationConfig.entry = configs;
-    },
+    }
 
     /**
      * Set the exit animation for the element.
      * @param {!Array<extensions.Animation>} animations
      */
-    setExitAnimations: function(animations) {
+    setExitAnimations(animations) {
       var configs = [];
       for (let animation of animations) {
         switch (animation) {
@@ -88,8 +89,8 @@
         }
       }
       this.element_.animationConfig.exit = configs;
-    },
-  };
+    }
+  }
 
   return {AnimationHelper: AnimationHelper};
 });
diff --git a/chrome/browser/resources/md_extensions/manager.js b/chrome/browser/resources/md_extensions/manager.js
index f4070d3..4bac6d8 100644
--- a/chrome/browser/resources/md_extensions/manager.js
+++ b/chrome/browser/resources/md_extensions/manager.js
@@ -422,18 +422,15 @@
     }
   });
 
-  /**
-   * @param {extensions.Manager} manager
-   * @constructor
-   * @implements {extensions.SidebarListDelegate}
-   */
-  function ListHelper(manager) {
-    this.manager_ = manager;
-  }
+  /** @implements {extensions.SidebarListDelegate} */
+  class ListHelper {
+    /** @param {extensions.Manager} manager */
+    constructor(manager) {
+      this.manager_ = manager;
+    }
 
-  ListHelper.prototype = {
     /** @override */
-    showType: function(type) {
+    showType(type) {
       var items;
       switch (type) {
         case extensions.ShowingType.EXTENSIONS:
@@ -446,13 +443,13 @@
 
       this.manager_.$ /* hack */['items-list'].set('items', assert(items));
       this.manager_.changePage({page: Page.LIST});
-    },
+    }
 
     /** @override */
-    showKeyboardShortcuts: function() {
+    showKeyboardShortcuts() {
       this.manager_.changePage({page: Page.SHORTCUTS});
-    },
-  };
+    }
+  }
 
   return {Manager: Manager};
 });
diff --git a/chrome/browser/resources/md_extensions/navigation_helper.js b/chrome/browser/resources/md_extensions/navigation_helper.js
index 43e2382..d15ca5996 100644
--- a/chrome/browser/resources/md_extensions/navigation_helper.js
+++ b/chrome/browser/resources/md_extensions/navigation_helper.js
@@ -31,26 +31,28 @@
    * A helper object to manage in-page navigations. Since the extensions page
    * needs to support different urls for different subpages (like the details
    * page), we use this object to manage the history and url conversions.
-   * @param {!function(!PageState):void} onHistoryChange A function to call when
-   *     the page has changed as a result of the user going back or forward in
-   *     history; called with the new active page.
-   * @constructor */
-  function NavigationHelper(onHistoryChange) {
-    this.onHistoryChange_ = onHistoryChange;
-    window.addEventListener('popstate', this.onPopState_.bind(this));
-  }
+   */
+  class NavigationHelper {
+    /**
+     * @param {!function(!PageState):void} onHistoryChange A function to call
+     *     when the page has changed as a result of the user going back or
+     *     forward in history; called with the new active page.
+     */
+    constructor(onHistoryChange) {
+      this.onHistoryChange_ = onHistoryChange;
+      window.addEventListener('popstate', this.onPopState_.bind(this));
+    }
 
-  NavigationHelper.prototype = {
     /** @private */
-    onPopState_: function() {
+    onPopState_() {
       this.onHistoryChange_(this.getCurrentPage());
-    },
+    }
 
     /**
-     * Returns the page that should be displayed for the current URL.
-     * @return {!PageState}
+     * @return {!PageState} The page that should be displayed for the current
+     *     URL.
      */
-    getCurrentPage: function() {
+    getCurrentPage() {
       var search = new URLSearchParams(location.search);
       var id = search.get('id');
       if (id)
@@ -66,13 +68,13 @@
         return {page: Page.SHORTCUTS};
 
       return {page: Page.LIST};
-    },
+    }
 
     /**
      * Called when a page changes, and pushes state to history to reflect it.
      * @param {!PageState} entry
      */
-    updateHistory: function(entry) {
+    updateHistory(entry) {
       var path;
       switch (entry.page) {
         case Page.LIST:
@@ -106,8 +108,8 @@
         history.replaceState(state, '', path);
       else
         history.pushState(state, '', path);
-    },
-  };
+    }
+  }
 
   return {NavigationHelper: NavigationHelper};
 });
diff --git a/chrome/browser/resources/md_extensions/service.js b/chrome/browser/resources/md_extensions/service.js
index c65380f..9694a3a 100644
--- a/chrome/browser/resources/md_extensions/service.js
+++ b/chrome/browser/resources/md_extensions/service.js
@@ -6,22 +6,26 @@
   'use strict';
 
   /**
-   * @constructor
    * @implements {extensions.ErrorPageDelegate}
    * @implements {extensions.ItemDelegate}
    * @implements {extensions.LoadErrorDelegate}
    * @implements {extensions.PackDialogDelegate}
    * @implements {extensions.ToolbarDelegate}
    */
-  function Service() {}
+  class Service {
+    constructor() {
+      /** @private {boolean} */
+      this.isDeleting_ = false;
 
-  Service.prototype = {
-    /** @private {boolean} */
-    isDeleting_: false,
+      /** @private {extensions.Manager} */
+      this.manager_;
+
+      /** @private {Array<chrome.developerPrivate.ExtensionInfo>} */
+      this.extensions_;
+    }
 
     /** @param {extensions.Manager} manager */
-    managerReady: function(manager) {
-      /** @private {extensions.Manager} */
+    managerReady(manager) {
       this.manager_ = manager;
       this.manager_.toolbar.setDelegate(this);
       this.manager_.set('itemDelegate', this);
@@ -44,7 +48,6 @@
       chrome.developerPrivate.getExtensionsInfo(
           {includeDisabled: true, includeTerminated: true},
           function(extensions) {
-            /** @private {Array<chrome.developerPrivate.ExtensionInfo>} */
             this.extensions_ = extensions;
             for (let extension of extensions)
               this.manager_.addItem(extension);
@@ -53,21 +56,21 @@
           }.bind(this));
       chrome.developerPrivate.getProfileConfiguration(
           this.onProfileStateChanged_.bind(this));
-    },
+    }
 
     /**
      * @param {chrome.developerPrivate.ProfileInfo} profileInfo
      * @private
      */
-    onProfileStateChanged_: function(profileInfo) {
+    onProfileStateChanged_(profileInfo) {
       this.manager_.set('inDevMode', profileInfo.inDeveloperMode);
-    },
+    }
 
     /**
      * @param {chrome.developerPrivate.EventData} eventData
      * @private
      */
-    onItemStateChanged_: function(eventData) {
+    onItemStateChanged_(eventData) {
       var currentIndex = this.extensions_.findIndex(function(extension) {
         return extension.id == eventData.item_id;
       });
@@ -103,7 +106,7 @@
         default:
           assertNotReached();
       }
-    },
+    }
 
     /**
      * Opens a file browser dialog for the user to select a file (or directory).
@@ -112,7 +115,7 @@
      * @return {Promise<string>} The promise to be resolved with the selected
      *     path.
      */
-    chooseFilePath_: function(selectType, fileType) {
+    chooseFilePath_(selectType, fileType) {
       return new Promise(function(resolve, reject) {
         chrome.developerPrivate.choosePath(
             selectType, fileType, function(path) {
@@ -124,20 +127,20 @@
               }
             });
       });
-    },
+    }
 
     /**
      * Updates an extension command.
      * @param {!CustomEvent} e
      * @private
      */
-    onExtensionCommandUpdated_: function(e) {
+    onExtensionCommandUpdated_(e) {
       chrome.developerPrivate.updateExtensionCommand({
         extensionId: e.detail.item,
         commandName: e.detail.commandName,
         keybinding: e.detail.keybinding,
       });
-    },
+    }
 
     /**
      * Called when shortcut capturing changes in order to suspend or re-enable
@@ -150,9 +153,9 @@
      * @param {!CustomEvent} e
      * @private
      */
-    onShortcutCaptureChanged_: function(isCapturing, e) {
+    onShortcutCaptureChanged_(isCapturing, e) {
       chrome.developerPrivate.setShortcutHandlingSuspended(isCapturing);
-    },
+    }
 
     /**
      * Attempts to load an unpacked extension, optionally as another attempt at
@@ -160,7 +163,7 @@
      * @param {string=} opt_retryGuid
      * @private
      */
-    loadUnpackedHelper_: function(opt_retryGuid) {
+    loadUnpackedHelper_(opt_retryGuid) {
       chrome.developerPrivate.loadUnpacked(
           {failQuietly: true, populateError: true, retryGuid: opt_retryGuid},
           (loadError) => {
@@ -174,10 +177,10 @@
               this.manager_.loadError.show();
             }
           });
-    },
+    }
 
     /** @override */
-    deleteItem: function(id) {
+    deleteItem(id) {
       if (this.isDeleting_)
         return;
       this.isDeleting_ = true;
@@ -188,67 +191,67 @@
         chrome.runtime.lastError;
         this.isDeleting_ = false;
       }.bind(this));
-    },
+    }
 
     /** @override */
-    setItemEnabled: function(id, isEnabled) {
+    setItemEnabled(id, isEnabled) {
       chrome.management.setEnabled(id, isEnabled);
-    },
+    }
 
     /** @override */
-    setItemAllowedIncognito: function(id, isAllowedIncognito) {
+    setItemAllowedIncognito(id, isAllowedIncognito) {
       chrome.developerPrivate.updateExtensionConfiguration({
         extensionId: id,
         incognitoAccess: isAllowedIncognito,
       });
-    },
+    }
 
     /** @override */
-    setItemAllowedOnFileUrls: function(id, isAllowedOnFileUrls) {
+    setItemAllowedOnFileUrls(id, isAllowedOnFileUrls) {
       chrome.developerPrivate.updateExtensionConfiguration({
         extensionId: id,
         fileAccess: isAllowedOnFileUrls,
       });
-    },
+    }
 
     /** @override */
-    setItemAllowedOnAllSites: function(id, isAllowedOnAllSites) {
+    setItemAllowedOnAllSites(id, isAllowedOnAllSites) {
       chrome.developerPrivate.updateExtensionConfiguration({
         extensionId: id,
         runOnAllUrls: isAllowedOnAllSites,
       });
-    },
+    }
 
     /** @override */
-    setItemCollectsErrors: function(id, collectsErrors) {
+    setItemCollectsErrors(id, collectsErrors) {
       chrome.developerPrivate.updateExtensionConfiguration({
         extensionId: id,
         errorCollection: collectsErrors,
       });
-    },
+    }
 
     /** @override */
-    inspectItemView: function(id, view) {
+    inspectItemView(id, view) {
       chrome.developerPrivate.openDevTools({
         extensionId: id,
         renderProcessId: view.renderProcessId,
         renderViewId: view.renderViewId,
         incognito: view.incognito,
       });
-    },
+    }
 
     /** @override */
-    reloadItem: function(id) {
+    reloadItem(id) {
       chrome.developerPrivate.reload(id, {failQuietly: false});
-    },
+    }
 
     /** @override */
-    repairItem: function(id) {
+    repairItem(id) {
       chrome.developerPrivate.repairExtension(id);
-    },
+    }
 
     /** @override */
-    showItemOptionsPage: function(id) {
+    showItemOptionsPage(id) {
       var extension = this.extensions_.find(function(e) {
         return e.id == id;
       });
@@ -259,66 +262,66 @@
         this.manager_.changePage(
             {page: Page.DETAILS, subpage: Dialog.OPTIONS, extensionId: id});
       }
-    },
+    }
 
     /** @override */
-    setProfileInDevMode: function(inDevMode) {
+    setProfileInDevMode(inDevMode) {
       chrome.developerPrivate.updateProfileConfiguration(
           {inDeveloperMode: inDevMode});
-    },
+    }
 
     /** @override */
-    loadUnpacked: function() {
+    loadUnpacked() {
       this.loadUnpackedHelper_();
-    },
+    }
 
     /** @override */
-    retryLoadUnpacked: function(retryGuid) {
+    retryLoadUnpacked(retryGuid) {
       this.loadUnpackedHelper_(retryGuid);
-    },
+    }
 
     /** @override */
-    choosePackRootDirectory: function() {
+    choosePackRootDirectory() {
       return this.chooseFilePath_(
           chrome.developerPrivate.SelectType.FOLDER,
           chrome.developerPrivate.FileType.LOAD);
-    },
+    }
 
     /** @override */
-    choosePrivateKeyPath: function() {
+    choosePrivateKeyPath() {
       return this.chooseFilePath_(
           chrome.developerPrivate.SelectType.FILE,
           chrome.developerPrivate.FileType.PEM);
-    },
+    }
 
     /** @override */
-    packExtension: function(rootPath, keyPath) {
+    packExtension(rootPath, keyPath) {
       chrome.developerPrivate.packDirectory(rootPath, keyPath);
-    },
+    }
 
     /** @override */
-    updateAllExtensions: function() {
+    updateAllExtensions() {
       chrome.developerPrivate.autoUpdate();
-    },
+    }
 
     /** @override */
-    deleteErrors: function(extensionId, errorIds, type) {
+    deleteErrors(extensionId, errorIds, type) {
       chrome.developerPrivate.deleteExtensionErrors({
         extensionId: extensionId,
         errorIds: errorIds,
         type: type,
       });
-    },
+    }
 
     /** @override */
-    requestFileSource: function(args) {
+    requestFileSource(args) {
       return new Promise(function(resolve, reject) {
         chrome.developerPrivate.requestFileSource(args, function(code) {
           resolve(code);
         });
       });
-    },
-  };
+    }
+  }
 
   cr.addSingletonGetter(Service);
 
diff --git a/chrome/browser/ui/app_list/app_list_view_delegate.cc b/chrome/browser/ui/app_list/app_list_view_delegate.cc
index aae349ce..d4c4c64 100644
--- a/chrome/browser/ui/app_list/app_list_view_delegate.cc
+++ b/chrome/browser/ui/app_list/app_list_view_delegate.cc
@@ -8,6 +8,7 @@
 
 #include <vector>
 
+#include "ash/public/interfaces/constants.mojom.h"
 #include "base/command_line.h"
 #include "base/metrics/user_metrics.h"
 #include "base/profiler/scoped_tracker.h"
@@ -38,11 +39,13 @@
 #include "content/public/browser/render_widget_host_view.h"
 #include "content/public/browser/speech_recognition_session_preamble.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/common/service_manager_connection.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/extension_set.h"
 #include "extensions/common/manifest_constants.h"
 #include "extensions/common/manifest_handlers/launcher_page_info.h"
+#include "services/service_manager/public/cpp/connector.h"
 #include "ui/app_list/app_list_switches.h"
 #include "ui/app_list/search_box_model.h"
 #include "ui/app_list/search_controller.h"
@@ -105,9 +108,11 @@
 
 AppListViewDelegate::AppListViewDelegate(AppListControllerDelegate* controller)
     : controller_(controller),
-      profile_(NULL),
-      model_(NULL),
-      template_url_service_observer_(this) {
+      profile_(nullptr),
+      model_(nullptr),
+      template_url_service_observer_(this),
+      observer_binding_(this),
+      weak_ptr_factory_(this) {
   CHECK(controller_);
   speech_ui_.reset(new app_list::SpeechUIModel);
 
@@ -127,6 +132,13 @@
   registrar_.Add(this,
                  chrome::NOTIFICATION_APP_TERMINATING,
                  content::NotificationService::AllSources());
+
+  content::ServiceManagerConnection::GetForProcess()
+      ->GetConnector()
+      ->BindInterface(ash::mojom::kServiceName, &wallpaper_controller_ptr_);
+  ash::mojom::WallpaperObserverAssociatedPtrInfo ptr_info;
+  observer_binding_.Bind(mojo::MakeRequest(&ptr_info));
+  wallpaper_controller_ptr_->AddObserver(std::move(ptr_info));
 }
 
 AppListViewDelegate::~AppListViewDelegate() {
@@ -183,6 +195,12 @@
     model_ = app_list::AppListSyncableServiceFactory::GetForProfile(profile_)
                  ->GetModel();
 
+    // After |model_| is initialized, make a GetWallpaperColors mojo call to set
+    // wallpaper colors for |model_|.
+    wallpaper_controller_ptr_->GetWallpaperColors(
+        base::Bind(&AppListViewDelegate::OnGetWallpaperColorsCallback,
+                   weak_ptr_factory_.GetWeakPtr()));
+
     app_sync_ui_state_watcher_.reset(
         new AppSyncUIStateWatcher(profile_, model_));
 
@@ -195,6 +213,11 @@
   model_->search_box()->Update(base::string16(), false);
 }
 
+void AppListViewDelegate::OnGetWallpaperColorsCallback(
+    const std::vector<SkColor>& colors) {
+  OnWallpaperColorsChanged(colors);
+}
+
 void AppListViewDelegate::SetUpSearchUI() {
   app_list::StartPageService* start_page_service =
       app_list::StartPageService::Get(profile_);
@@ -241,6 +264,14 @@
                                                 first_launcher_page_app_id));
 }
 
+void AppListViewDelegate::OnWallpaperColorsChanged(
+    const std::vector<SkColor>& prominent_colors) {
+  if (!model_)
+    return;
+
+  model_->search_box()->SetWallpaperProminentColors(prominent_colors);
+}
+
 void AppListViewDelegate::OnHotwordStateChanged(bool started) {
   if (started) {
     if (speech_ui_->state() == app_list::SPEECH_RECOGNITION_READY) {
diff --git a/chrome/browser/ui/app_list/app_list_view_delegate.h b/chrome/browser/ui/app_list/app_list_view_delegate.h
index 0a5d7540..67e59f1 100644
--- a/chrome/browser/ui/app_list/app_list_view_delegate.h
+++ b/chrome/browser/ui/app_list/app_list_view_delegate.h
@@ -11,9 +11,11 @@
 #include <string>
 #include <vector>
 
+#include "ash/public/interfaces/wallpaper.mojom.h"
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
 #include "base/scoped_observer.h"
 #include "chrome/browser/search/hotword_client.h"
 #include "chrome/browser/ui/app_list/start_page_observer.h"
@@ -21,6 +23,8 @@
 #include "components/search_engines/template_url_service_observer.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
+#include "mojo/public/cpp/bindings/associated_binding.h"
+#include "mojo/public/cpp/bindings/binding.h"
 #include "ui/app_list/app_list_view_delegate.h"
 
 namespace app_list {
@@ -41,6 +45,7 @@
 
 class AppListViewDelegate : public app_list::AppListViewDelegate,
                             public app_list::StartPageObserver,
+                            public ash::mojom::WallpaperObserver,
                             public HotwordClient,
                             public content::NotificationObserver,
                             public TemplateURLServiceObserver {
@@ -87,6 +92,9 @@
   void OnTemplateURLServiceChanged() override;
 
  private:
+  // Callback for ash::mojom::GetWallpaperColors.
+  void OnGetWallpaperColorsCallback(const std::vector<SkColor>& colors);
+
   // Updates the speech webview and start page for the current |profile_|.
   void SetUpSearchUI();
 
@@ -99,6 +107,10 @@
   void OnSpeechRecognitionStateChanged(
       app_list::SpeechRecognitionState new_state) override;
 
+  // Overridden from ash::mojom::WallpaperObserver:
+  void OnWallpaperColorsChanged(
+      const std::vector<SkColor>& prominent_colors) override;
+
   // Overridden from HotwordClient:
   void OnHotwordStateChanged(bool started) override;
   void OnHotwordRecognized(
@@ -142,6 +154,14 @@
   // Registers for NOTIFICATION_APP_TERMINATING to unload custom launcher pages.
   content::NotificationRegistrar registrar_;
 
+  // The binding this instance uses to implement mojom::WallpaperObserver.
+  mojo::AssociatedBinding<ash::mojom::WallpaperObserver> observer_binding_;
+
+  // Ash's mojom::WallpaperController.
+  ash::mojom::WallpaperControllerPtr wallpaper_controller_ptr_;
+
+  base::WeakPtrFactory<AppListViewDelegate> weak_ptr_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(AppListViewDelegate);
 };
 
diff --git a/chrome/browser/ui/views/payments/payment_request_journey_logger_browsertest.cc b/chrome/browser/ui/views/payments/payment_request_journey_logger_browsertest.cc
index b017fc6..ff960d9f 100644
--- a/chrome/browser/ui/views/payments/payment_request_journey_logger_browsertest.cc
+++ b/chrome/browser/ui/views/payments/payment_request_journey_logger_browsertest.cc
@@ -188,4 +188,273 @@
       JourneyLogger::NOT_SHOWN_REASON_CONCURRENT_REQUESTS, 1);
 }
 
+class PaymentRequestJourneyLoggerAllSectionStatsTest
+    : public PaymentRequestBrowserTestBase {
+ protected:
+  PaymentRequestJourneyLoggerAllSectionStatsTest()
+      : PaymentRequestBrowserTestBase(
+            "/payment_request_contact_details_and_free_shipping_test.html") {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(PaymentRequestJourneyLoggerAllSectionStatsTest);
+};
+
+// Tests that the correct number of suggestions shown for each section is logged
+// when a Payment Request is completed.
+IN_PROC_BROWSER_TEST_F(PaymentRequestJourneyLoggerAllSectionStatsTest,
+                       NumberOfSuggestionsShown_Completed) {
+  base::HistogramTester histogram_tester;
+
+  // Setup a credit card with an associated billing address.
+  autofill::AutofillProfile billing_address = autofill::test::GetFullProfile();
+  AddAutofillProfile(billing_address);
+  autofill::CreditCard card = autofill::test::GetCreditCard();
+  card.set_billing_address_id(billing_address.guid());
+  AddCreditCard(card);  // Visa.
+
+  // Add another address.
+  AddAutofillProfile(autofill::test::GetFullProfile2());
+
+  // Complete the Payment Request.
+  InvokePaymentRequestUI();
+  ResetEventObserver(DialogEvent::DIALOG_CLOSED);
+  PayWithCreditCardAndWait(base::ASCIIToUTF16("123"));
+
+  // Expect the appropriate number of suggestions shown to be logged.
+  histogram_tester.ExpectUniqueSample(
+      "PaymentRequest.NumberOfSuggestionsShown.CreditCards.Completed", 1, 1);
+  histogram_tester.ExpectUniqueSample(
+      "PaymentRequest.NumberOfSuggestionsShown.ShippingAddress.Completed", 2,
+      1);
+  histogram_tester.ExpectUniqueSample(
+      "PaymentRequest.NumberOfSuggestionsShown.ContactInfo.Completed", 2, 1);
+}
+
+// Tests that the correct number of suggestions shown for each section is logged
+// when a Payment Request is aborted by the user.
+IN_PROC_BROWSER_TEST_F(PaymentRequestJourneyLoggerAllSectionStatsTest,
+                       NumberOfSuggestionsShown_UserAborted) {
+  base::HistogramTester histogram_tester;
+
+  // Setup a credit card with an associated billing address.
+  autofill::AutofillProfile billing_address = autofill::test::GetFullProfile();
+  AddAutofillProfile(billing_address);
+  autofill::CreditCard card = autofill::test::GetCreditCard();
+  card.set_billing_address_id(billing_address.guid());
+  AddCreditCard(card);  // Visa.
+
+  // Add another address.
+  AddAutofillProfile(autofill::test::GetFullProfile2());
+
+  // The user aborts the Payment Request.
+  InvokePaymentRequestUI();
+  ClickOnCancel();
+
+  // Expect the appropriate number of suggestions shown to be logged.
+  histogram_tester.ExpectUniqueSample(
+      "PaymentRequest.NumberOfSuggestionsShown.CreditCards.UserAborted", 1, 1);
+  histogram_tester.ExpectUniqueSample(
+      "PaymentRequest.NumberOfSuggestionsShown.ShippingAddress.UserAborted", 2,
+      1);
+  histogram_tester.ExpectUniqueSample(
+      "PaymentRequest.NumberOfSuggestionsShown.ContactInfo.UserAborted", 2, 1);
+}
+
+class PaymentRequestJourneyLoggerNoShippingSectionStatsTest
+    : public PaymentRequestBrowserTestBase {
+ protected:
+  PaymentRequestJourneyLoggerNoShippingSectionStatsTest()
+      : PaymentRequestBrowserTestBase(
+            "/payment_request_contact_details_test.html") {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(
+      PaymentRequestJourneyLoggerNoShippingSectionStatsTest);
+};
+
+// Tests that the correct number of suggestions shown for each section is logged
+// when a Payment Request is completed.
+IN_PROC_BROWSER_TEST_F(PaymentRequestJourneyLoggerNoShippingSectionStatsTest,
+                       NumberOfSuggestionsShown_Completed) {
+  base::HistogramTester histogram_tester;
+
+  // Setup a credit card with an associated billing address.
+  autofill::AutofillProfile billing_address = autofill::test::GetFullProfile();
+  AddAutofillProfile(billing_address);
+  autofill::CreditCard card = autofill::test::GetCreditCard();
+  card.set_billing_address_id(billing_address.guid());
+  AddCreditCard(card);  // Visa.
+
+  // Add another address.
+  AddAutofillProfile(autofill::test::GetFullProfile2());
+
+  // Complete the Payment Request.
+  InvokePaymentRequestUI();
+  ResetEventObserver(DialogEvent::DIALOG_CLOSED);
+  PayWithCreditCardAndWait(base::ASCIIToUTF16("123"));
+
+  // Expect the appropriate number of suggestions shown to be logged.
+  histogram_tester.ExpectUniqueSample(
+      "PaymentRequest.NumberOfSuggestionsShown.CreditCards.Completed", 1, 1);
+  histogram_tester.ExpectUniqueSample(
+      "PaymentRequest.NumberOfSuggestionsShown.ContactInfo.Completed", 2, 1);
+
+  // There should be no log for shipping address since it was not requested.
+  histogram_tester.ExpectTotalCount(
+      "PaymentRequest.NumberOfSuggestionsShown.ShippingAddress.Completed", 0);
+}
+
+// Tests that the correct number of suggestions shown for each section is logged
+// when a Payment Request is aborted by the user.
+IN_PROC_BROWSER_TEST_F(PaymentRequestJourneyLoggerNoShippingSectionStatsTest,
+                       NumberOfSuggestionsShown_UserAborted) {
+  base::HistogramTester histogram_tester;
+
+  // Setup a credit card with an associated billing address.
+  autofill::AutofillProfile billing_address = autofill::test::GetFullProfile();
+  AddAutofillProfile(billing_address);
+  autofill::CreditCard card = autofill::test::GetCreditCard();
+  card.set_billing_address_id(billing_address.guid());
+  AddCreditCard(card);  // Visa.
+
+  // Add another address.
+  AddAutofillProfile(autofill::test::GetFullProfile2());
+
+  // The user aborts the Payment Request.
+  InvokePaymentRequestUI();
+  ClickOnCancel();
+
+  // Expect the appropriate number of suggestions shown to be logged.
+  histogram_tester.ExpectUniqueSample(
+      "PaymentRequest.NumberOfSuggestionsShown.CreditCards.UserAborted", 1, 1);
+  histogram_tester.ExpectUniqueSample(
+      "PaymentRequest.NumberOfSuggestionsShown.ContactInfo.UserAborted", 2, 1);
+
+  // There should be no log for shipping address since it was not requested.
+  histogram_tester.ExpectTotalCount(
+      "PaymentRequest.NumberOfSuggestionsShown.ShippingAddress.UserAborted", 0);
+}
+
+class PaymentRequestJourneyLoggerNoContactDetailSectionStatsTest
+    : public PaymentRequestBrowserTestBase {
+ protected:
+  PaymentRequestJourneyLoggerNoContactDetailSectionStatsTest()
+      : PaymentRequestBrowserTestBase(
+            "/payment_request_free_shipping_test.html") {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(
+      PaymentRequestJourneyLoggerNoContactDetailSectionStatsTest);
+};
+
+// Tests that the correct number of suggestions shown for each section is logged
+// when a Payment Request is completed.
+IN_PROC_BROWSER_TEST_F(
+    PaymentRequestJourneyLoggerNoContactDetailSectionStatsTest,
+    NumberOfSuggestionsShown_Completed) {
+  base::HistogramTester histogram_tester;
+
+  // Setup a credit card with an associated billing address.
+  autofill::AutofillProfile billing_address = autofill::test::GetFullProfile();
+  AddAutofillProfile(billing_address);
+  autofill::CreditCard card = autofill::test::GetCreditCard();
+  card.set_billing_address_id(billing_address.guid());
+  AddCreditCard(card);  // Visa.
+
+  // Add another address.
+  AddAutofillProfile(autofill::test::GetFullProfile2());
+
+  // Complete the Payment Request.
+  InvokePaymentRequestUI();
+  ResetEventObserver(DialogEvent::DIALOG_CLOSED);
+  PayWithCreditCardAndWait(base::ASCIIToUTF16("123"));
+
+  // Expect the appropriate number of suggestions shown to be logged.
+  histogram_tester.ExpectUniqueSample(
+      "PaymentRequest.NumberOfSuggestionsShown.CreditCards.Completed", 1, 1);
+  histogram_tester.ExpectUniqueSample(
+      "PaymentRequest.NumberOfSuggestionsShown.ShippingAddress.Completed", 2,
+      1);
+
+  // There should be no log for contact info since it was not requested.
+  histogram_tester.ExpectTotalCount(
+      "PaymentRequest.NumberOfSuggestionsShown.ContactInfo.Completed", 0);
+}
+
+// Tests that the correct number of suggestions shown for each section is logged
+// when a Payment Request is aborted by the user.
+IN_PROC_BROWSER_TEST_F(
+    PaymentRequestJourneyLoggerNoContactDetailSectionStatsTest,
+    NumberOfSuggestionsShown_UserAborted) {
+  base::HistogramTester histogram_tester;
+
+  // Setup a credit card with an associated billing address.
+  autofill::AutofillProfile billing_address = autofill::test::GetFullProfile();
+  AddAutofillProfile(billing_address);
+  autofill::CreditCard card = autofill::test::GetCreditCard();
+  card.set_billing_address_id(billing_address.guid());
+  AddCreditCard(card);  // Visa.
+
+  // Add another address.
+  AddAutofillProfile(autofill::test::GetFullProfile2());
+
+  // The user aborts the Payment Request.
+  InvokePaymentRequestUI();
+  ClickOnCancel();
+
+  // Expect the appropriate number of suggestions shown to be logged.
+  histogram_tester.ExpectUniqueSample(
+      "PaymentRequest.NumberOfSuggestionsShown.CreditCards.UserAborted", 1, 1);
+  histogram_tester.ExpectUniqueSample(
+      "PaymentRequest.NumberOfSuggestionsShown.ShippingAddress.UserAborted", 2,
+      1);
+
+  // There should be no log for contact info since it was not requested.
+  histogram_tester.ExpectTotalCount(
+      "PaymentRequest.NumberOfSuggestionsShown.ContactInfo.UserAborted", 0);
+}
+
+class PaymentRequestNotShownTest : public PaymentRequestBrowserTestBase {
+ protected:
+  PaymentRequestNotShownTest()
+      : PaymentRequestBrowserTestBase(
+            "/payment_request_can_make_payment_metrics_test.html") {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(PaymentRequestNotShownTest);
+};
+
+IN_PROC_BROWSER_TEST_F(PaymentRequestNotShownTest, OnlyNotShownMetricsLogged) {
+  base::HistogramTester histogram_tester;
+
+  // Initiate a Payment Request without showing it.
+  ASSERT_TRUE(content::ExecuteScript(GetActiveWebContents(), "queryNoShow();"));
+
+  // Navigate away to abort the Payment Request and trigger the logs.
+  NavigateTo("/payment_request_email_test.html");
+
+  // Initiated should be logged.
+  histogram_tester.ExpectBucketCount("PaymentRequest.CheckoutFunnel.Initiated",
+                                     1, 1);
+  // Show should not be logged.
+  histogram_tester.ExpectTotalCount("PaymentRequest.CheckoutFunnel.Shown", 0);
+  // Abort should not be logged.
+  histogram_tester.ExpectTotalCount("PaymentRequest.CheckoutFunnel.Aborted", 0);
+
+  // Make sure that the metrics that required the Payment Request to be shown
+  // are not logged.
+  histogram_tester.ExpectTotalCount("PaymentRequest.SelectedPaymentMethod", 0);
+  histogram_tester.ExpectTotalCount("PaymentRequest.RequestedInformation", 0);
+  histogram_tester.ExpectTotalCount("PaymentRequest.NumberOfSelectionAdds", 0);
+  histogram_tester.ExpectTotalCount("PaymentRequest.NumberOfSelectionChanges",
+                                    0);
+  histogram_tester.ExpectTotalCount("PaymentRequest.NumberOfSelectionEdits", 0);
+  histogram_tester.ExpectTotalCount("PaymentRequest.NumberOfSuggestionsShown",
+                                    0);
+  histogram_tester.ExpectTotalCount(
+      "PaymentRequest.UserHadSuggestionsForEverything", 0);
+  histogram_tester.ExpectTotalCount(
+      "PaymentRequest.UserDidNotHaveSuggestionsForEverything", 0);
+}
+
 }  // namespace payments
diff --git a/chrome/browser/win/jumplist.cc b/chrome/browser/win/jumplist.cc
index 7ebb5ea..0a22244 100644
--- a/chrome/browser/win/jumplist.cc
+++ b/chrome/browser/win/jumplist.cc
@@ -309,6 +309,10 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   if (timer_.IsRunning()) {
+    // TODO(chengx): Remove the UMA histogram below after fixing crbug/733034.
+    UMA_HISTOGRAM_COUNTS_10000(
+        "WinJumplist.NotificationTimeInterval",
+        (timer_.desired_run_time() - base::TimeTicks::Now()).InMilliseconds());
     timer_.Reset();
   } else {
     // base::Unretained is safe since |this| is guaranteed to outlive timer_.
diff --git a/chrome/common/chrome_constants.cc b/chrome/common/chrome_constants.cc
index 94cdc2a..e4ce7d5 100644
--- a/chrome/common/chrome_constants.cc
+++ b/chrome/common/chrome_constants.cc
@@ -157,6 +157,8 @@
     FPL("Offline Pages/archives");
 const base::FilePath::CharType kOfflinePageMetadataDirname[] =
     FPL("Offline Pages/metadata");
+const base::FilePath::CharType kOfflinePagePrefetchStoreDirname[] =
+    FPL("Offline Pages/prefech_store");
 const base::FilePath::CharType kOfflinePageRequestQueueDirname[] =
     FPL("Offline Pages/request_queue");
 const base::FilePath::CharType kPreferencesFilename[] = FPL("Preferences");
diff --git a/chrome/common/chrome_constants.h b/chrome/common/chrome_constants.h
index 8321b76..a170109 100644
--- a/chrome/common/chrome_constants.h
+++ b/chrome/common/chrome_constants.h
@@ -59,6 +59,7 @@
 extern const base::FilePath::CharType kMediaCacheDirname[];
 extern const base::FilePath::CharType kOfflinePageArchivesDirname[];
 extern const base::FilePath::CharType kOfflinePageMetadataDirname[];
+extern const base::FilePath::CharType kOfflinePagePrefetchStoreDirname[];
 extern const base::FilePath::CharType kOfflinePageRequestQueueDirname[];
 extern const base::FilePath::CharType kPreferencesFilename[];
 extern const base::FilePath::CharType kPreviewsOptOutDBFilename[];
diff --git a/chrome/test/data/payments/contact_details.js b/chrome/test/data/payments/contact_details.js
index e4e4df9..67eca44 100644
--- a/chrome/test/data/payments/contact_details.js
+++ b/chrome/test/data/payments/contact_details.js
@@ -12,7 +12,7 @@
 function buy() {  // eslint-disable-line no-unused-vars
   try {
     new PaymentRequest(
-        [{supportedMethods: ['https://bobpay.com', 'amex']}],
+        [{supportedMethods: ['https://bobpay.com', 'amex', 'visa']}],
         {total: {label: 'Total', amount: {currency: 'USD', value: '5.00'}}},
         {requestPayerName: true, requestPayerEmail: true,
          requestPayerPhone: true})
diff --git a/chromeos/components/tether/BUILD.gn b/chromeos/components/tether/BUILD.gn
index 1f0c4c1..fd5cc912 100644
--- a/chromeos/components/tether/BUILD.gn
+++ b/chromeos/components/tether/BUILD.gn
@@ -28,6 +28,8 @@
     "device_status_util.h",
     "disconnect_tethering_operation.cc",
     "disconnect_tethering_operation.h",
+    "host_connection_metrics_logger.cc",
+    "host_connection_metrics_logger.h",
     "host_scan_cache.cc",
     "host_scan_cache.h",
     "host_scan_device_prioritizer.h",
@@ -143,6 +145,7 @@
     "connect_tethering_operation_unittest.cc",
     "device_status_util_unittest.cc",
     "disconnect_tethering_operation_unittest.cc",
+    "host_connection_metrics_logger_unittest.cc",
     "host_scan_cache_unittest.cc",
     "host_scan_device_prioritizer_impl_unittest.cc",
     "host_scan_scheduler_unittest.cc",
diff --git a/chromeos/components/tether/host_connection_metrics_logger.cc b/chromeos/components/tether/host_connection_metrics_logger.cc
new file mode 100644
index 0000000..4159020
--- /dev/null
+++ b/chromeos/components/tether/host_connection_metrics_logger.cc
@@ -0,0 +1,116 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/components/tether/host_connection_metrics_logger.h"
+
+#include "base/metrics/histogram_macros.h"
+
+namespace chromeos {
+
+namespace tether {
+
+HostConnectionMetricsLogger::HostConnectionMetricsLogger() {}
+
+HostConnectionMetricsLogger::~HostConnectionMetricsLogger() {}
+
+void HostConnectionMetricsLogger::RecordConnectionToHostResult(
+    ConnectionToHostResult result) {
+  switch (result) {
+    case ConnectionToHostResult::CONNECTION_RESULT_PROVISIONING_FAILED:
+      RecordConnectionResultProvisioningFailure(
+          ConnectionToHostResult_ProvisioningFailureEventType::
+              PROVISIONING_FAILED);
+      break;
+    case ConnectionToHostResult::CONNECTION_RESULT_SUCCESS:
+      RecordConnectionResultSuccess(
+          ConnectionToHostResult_SuccessEventType::SUCCESS);
+      break;
+    case ConnectionToHostResult::CONNECTION_RESULT_FAILURE_UNKNOWN_ERROR:
+      RecordConnectionResultFailure(
+          ConnectionToHostResult_FailureEventType::UNKNOWN_ERROR);
+      break;
+    case ConnectionToHostResult::
+        CONNECTION_RESULT_FAILURE_CLIENT_CONNECTION_TIMEOUT:
+      RecordConnectionResultFailureClientConnection(
+          ConnectionToHostResult_FailureClientConnectionEventType::TIMEOUT);
+      break;
+    case ConnectionToHostResult::
+        CONNECTION_RESULT_FAILURE_CLIENT_CONNECTION_CANCELED_BY_NEW_ATTEMPT:
+      RecordConnectionResultFailureClientConnection(
+          ConnectionToHostResult_FailureClientConnectionEventType::
+              CANCELED_BY_NEW_ATTEMPT);
+      break;
+    case ConnectionToHostResult::
+        CONNECTION_RESULT_FAILURE_TETHERING_TIMED_OUT_FIRST_TIME_SETUP_WAS_REQUIRED:
+      RecordConnectionResultFailureTetheringTimeout(
+          ConnectionToHostResult_FailureTetheringTimeoutEventType::
+              FIRST_TIME_SETUP_WAS_REQUIRED);
+      break;
+    case ConnectionToHostResult::
+        CONNECTION_RESULT_FAILURE_TETHERING_TIMED_OUT_FIRST_TIME_SETUP_WAS_NOT_REQUIRED:
+      RecordConnectionResultFailureTetheringTimeout(
+          ConnectionToHostResult_FailureTetheringTimeoutEventType::
+              FIRST_TIME_SETUP_WAS_NOT_REQUIRED);
+      break;
+    default:
+      NOTREACHED();
+  };
+}
+
+void HostConnectionMetricsLogger::RecordConnectionResultProvisioningFailure(
+    ConnectionToHostResult_ProvisioningFailureEventType event_type) {
+  UMA_HISTOGRAM_ENUMERATION(
+      "InstantTethering.ConnectionToHostResult.ProvisioningFailureRate",
+      event_type,
+      ConnectionToHostResult_ProvisioningFailureEventType::
+          PROVISIONING_FAILURE_MAX);
+}
+
+void HostConnectionMetricsLogger::RecordConnectionResultSuccess(
+    ConnectionToHostResult_SuccessEventType event_type) {
+  UMA_HISTOGRAM_ENUMERATION(
+      "InstantTethering.ConnectionToHostResult.SuccessRate", event_type,
+      ConnectionToHostResult_SuccessEventType::SUCCESS_MAX);
+
+  RecordConnectionResultProvisioningFailure(
+      ConnectionToHostResult_ProvisioningFailureEventType::OTHER);
+}
+
+void HostConnectionMetricsLogger::RecordConnectionResultFailure(
+    ConnectionToHostResult_FailureEventType event_type) {
+  UMA_HISTOGRAM_ENUMERATION(
+      "InstantTethering.ConnectionToHostResult.Failure", event_type,
+      ConnectionToHostResult_FailureEventType::FAILURE_MAX);
+
+  RecordConnectionResultSuccess(
+      ConnectionToHostResult_SuccessEventType::FAILURE);
+}
+
+void HostConnectionMetricsLogger::RecordConnectionResultFailureClientConnection(
+    ConnectionToHostResult_FailureClientConnectionEventType event_type) {
+  UMA_HISTOGRAM_ENUMERATION(
+      "InstantTethering.ConnectionToHostResult.Failure.ClientConnection",
+      event_type,
+      ConnectionToHostResult_FailureClientConnectionEventType::
+          FAILURE_CLIENT_CONNECTION_MAX);
+
+  RecordConnectionResultFailure(
+      ConnectionToHostResult_FailureEventType::CLIENT_CONNECTION_ERROR);
+}
+
+void HostConnectionMetricsLogger::RecordConnectionResultFailureTetheringTimeout(
+    ConnectionToHostResult_FailureTetheringTimeoutEventType event_type) {
+  UMA_HISTOGRAM_ENUMERATION(
+      "InstantTethering.ConnectionToHostResult.Failure.TetheringTimeout",
+      event_type,
+      ConnectionToHostResult_FailureTetheringTimeoutEventType::
+          FAILURE_TETHERING_TIMEOUT_MAX);
+
+  RecordConnectionResultFailure(
+      ConnectionToHostResult_FailureEventType::TETHERING_TIMED_OUT);
+}
+
+}  // namespace tether
+
+}  // namespace chromeos
diff --git a/chromeos/components/tether/host_connection_metrics_logger.h b/chromeos/components/tether/host_connection_metrics_logger.h
new file mode 100644
index 0000000..f5a22ef
--- /dev/null
+++ b/chromeos/components/tether/host_connection_metrics_logger.h
@@ -0,0 +1,141 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_COMPONENTS_TETHER_HOST_CONNECTION_METRICS_LOGGER_H_
+#define CHROMEOS_COMPONENTS_TETHER_HOST_CONNECTION_METRICS_LOGGER_H_
+
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+
+namespace chromeos {
+
+namespace tether {
+
+// Wrapper around metrics reporting for host connection results. Clients are
+// expected to report the result of a host connection attempt once it has
+// concluded.
+class HostConnectionMetricsLogger {
+ public:
+  enum ConnectionToHostResult {
+    CONNECTION_RESULT_PROVISIONING_FAILED,
+    CONNECTION_RESULT_SUCCESS,
+    CONNECTION_RESULT_FAILURE_UNKNOWN_ERROR,
+    CONNECTION_RESULT_FAILURE_CLIENT_CONNECTION_TIMEOUT,
+    CONNECTION_RESULT_FAILURE_CLIENT_CONNECTION_CANCELED_BY_NEW_ATTEMPT,
+    CONNECTION_RESULT_FAILURE_TETHERING_TIMED_OUT_FIRST_TIME_SETUP_WAS_REQUIRED,
+    CONNECTION_RESULT_FAILURE_TETHERING_TIMED_OUT_FIRST_TIME_SETUP_WAS_NOT_REQUIRED
+  };
+
+  // Record the result of an attempted host connection.
+  virtual void RecordConnectionToHostResult(ConnectionToHostResult result);
+
+  HostConnectionMetricsLogger();
+  virtual ~HostConnectionMetricsLogger();
+
+ private:
+  friend class HostConnectionMetricsLoggerTest;
+  FRIEND_TEST_ALL_PREFIXES(HostConnectionMetricsLoggerTest,
+                           RecordConnectionResultProvisioningFailure);
+  FRIEND_TEST_ALL_PREFIXES(HostConnectionMetricsLoggerTest,
+                           RecordConnectionResultSuccess);
+  FRIEND_TEST_ALL_PREFIXES(HostConnectionMetricsLoggerTest,
+                           RecordConnectionResultFailure);
+  FRIEND_TEST_ALL_PREFIXES(
+      HostConnectionMetricsLoggerTest,
+      RecordConnectionResultFailureClientConnection_Timeout);
+  FRIEND_TEST_ALL_PREFIXES(
+      HostConnectionMetricsLoggerTest,
+      RecordConnectionResultFailureClientConnection_CanceledByNewAttempt);
+  FRIEND_TEST_ALL_PREFIXES(
+      HostConnectionMetricsLoggerTest,
+      RecordConnectionResultFailureTetheringTimeout_SetupRequired);
+  FRIEND_TEST_ALL_PREFIXES(
+      HostConnectionMetricsLoggerTest,
+      RecordConnectionResultFailureTetheringTimeout_SetupNotRequired);
+
+  // An Instant Tethering connection can fail for several different reasons.
+  // Though traditionally success and each failure case would be logged to a
+  // single enum, we have chosen to start at a top-level of view of simply
+  // "success vs failure", and then iteratively breaking down the failure count
+  // into its types (and possibly sub-types). Because of this cascading nature,
+  // when a failure sub-type occurs, the code path in question must report that
+  // sub-type as well as all the super-types above it. This would be cumbersome,
+  // and thus HostConnectionMetricsLogger exists to provide a simple API which
+  // handles all the other metrics that need to be reported.
+  //
+  // The most top-level metric is
+  // InstantTethering.ConnectionToHostResult.ProvisioningFailureRate. Its
+  // breakdown, and the breakdown of its subsquent metrics, is described in
+  // tools/metrics/histograms/histograms.xml.
+  enum ConnectionToHostResult_ProvisioningFailureEventType {
+    PROVISIONING_FAILED = 0,
+    OTHER = 1,
+    PROVISIONING_FAILURE_MAX
+  };
+
+  enum ConnectionToHostResult_SuccessEventType {
+    SUCCESS = 0,
+    FAILURE = 1,
+    SUCCESS_MAX
+  };
+
+  enum ConnectionToHostResult_FailureEventType {
+    UNKNOWN_ERROR = 0,
+    TETHERING_TIMED_OUT = 1,
+    CLIENT_CONNECTION_ERROR = 2,
+    FAILURE_MAX
+  };
+
+  enum ConnectionToHostResult_FailureClientConnectionEventType {
+    TIMEOUT = 0,
+    CANCELED_BY_NEW_ATTEMPT = 1,
+    FAILURE_CLIENT_CONNECTION_MAX
+  };
+
+  enum ConnectionToHostResult_FailureTetheringTimeoutEventType {
+    FIRST_TIME_SETUP_WAS_REQUIRED = 0,
+    FIRST_TIME_SETUP_WAS_NOT_REQUIRED = 1,
+    FAILURE_TETHERING_TIMEOUT_MAX
+  };
+
+  // Record if a host connection attempt never went through due to provisioning
+  // failure, or otherwise continued. Should only be publicly called with
+  // an argument of ConnectionToHostResult_ProvisioningFailureEventType::OTHER.
+  void RecordConnectionResultProvisioningFailure(
+      ConnectionToHostResult_ProvisioningFailureEventType event_type);
+
+  // Record if a host connection attempt succeeded or failed. Should only be
+  // publicly called with
+  // an argument of ConnectionToHostResult_SuccessEventType::SUCCESS. Failure is
+  // covered by the RecordConnectionResultFailure() method.
+  void RecordConnectionResultSuccess(
+      ConnectionToHostResult_SuccessEventType event_type);
+
+  // Record how a host connection attempt failed. Should only be
+  // publicly called with an argument of
+  // ConnectionToHostResult_FailureEventType::UNKNOWN_ERROR. Failure due to
+  // client error or tethering
+  // timeout is covered
+  // by theRecordConnectionResultFailureClientConnection() or
+  // RecordConnectionResultFailureTetheringTimeout() methods, respectively.
+  void RecordConnectionResultFailure(
+      ConnectionToHostResult_FailureEventType event_type);
+
+  // Record how a host connection attempt failed due to a client error.
+  void RecordConnectionResultFailureClientConnection(
+      ConnectionToHostResult_FailureClientConnectionEventType event_type);
+
+  // Record the conditions of when host connection attempt failed due to
+  // the host timing out during tethering.
+  void RecordConnectionResultFailureTetheringTimeout(
+      ConnectionToHostResult_FailureTetheringTimeoutEventType event_type);
+
+  DISALLOW_COPY_AND_ASSIGN(HostConnectionMetricsLogger);
+};
+
+}  // namespace tether
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_COMPONENTS_TETHER_HOST_CONNECTION_METRICS_LOGGER_H_
diff --git a/chromeos/components/tether/host_connection_metrics_logger_unittest.cc b/chromeos/components/tether/host_connection_metrics_logger_unittest.cc
new file mode 100644
index 0000000..296ecc8
--- /dev/null
+++ b/chromeos/components/tether/host_connection_metrics_logger_unittest.cc
@@ -0,0 +1,189 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/components/tether/host_connection_metrics_logger.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/test/histogram_tester.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromeos {
+
+namespace tether {
+
+class HostConnectionMetricsLoggerTest : public testing::Test {
+ protected:
+  HostConnectionMetricsLoggerTest() {}
+
+  void SetUp() override {
+    metrics_logger_ = base::MakeUnique<HostConnectionMetricsLogger>();
+  }
+
+  void VerifyProvisioningFailure(
+      HostConnectionMetricsLogger::
+          ConnectionToHostResult_ProvisioningFailureEventType event_type) {
+    histogram_tester_.ExpectUniqueSample(
+        "InstantTethering.ConnectionToHostResult.ProvisioningFailureRate",
+        event_type, 1);
+  }
+
+  void VerifySuccess(
+      HostConnectionMetricsLogger::ConnectionToHostResult_SuccessEventType
+          event_type) {
+    histogram_tester_.ExpectUniqueSample(
+        "InstantTethering.ConnectionToHostResult.SuccessRate", event_type, 1);
+  }
+
+  void VerifyFailure(
+      HostConnectionMetricsLogger::ConnectionToHostResult_FailureEventType
+          event_type) {
+    histogram_tester_.ExpectUniqueSample(
+        "InstantTethering.ConnectionToHostResult.Failure", event_type, 1);
+  }
+
+  void VerifyFailure_ClientConnection(
+      HostConnectionMetricsLogger::
+          ConnectionToHostResult_FailureClientConnectionEventType event_type) {
+    histogram_tester_.ExpectUniqueSample(
+        "InstantTethering.ConnectionToHostResult.Failure.ClientConnection",
+        event_type, 1);
+  }
+
+  void VerifyFailure_TetheringTimeout(
+      HostConnectionMetricsLogger::
+          ConnectionToHostResult_FailureTetheringTimeoutEventType event_type) {
+    histogram_tester_.ExpectUniqueSample(
+        "InstantTethering.ConnectionToHostResult.Failure.TetheringTimeout",
+        event_type, 1);
+  }
+
+  std::unique_ptr<HostConnectionMetricsLogger> metrics_logger_;
+
+  base::HistogramTester histogram_tester_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HostConnectionMetricsLoggerTest);
+};
+
+TEST_F(HostConnectionMetricsLoggerTest,
+       RecordConnectionResultProvisioningFailure) {
+  metrics_logger_->RecordConnectionToHostResult(
+      HostConnectionMetricsLogger::ConnectionToHostResult::
+          CONNECTION_RESULT_PROVISIONING_FAILED);
+
+  VerifyProvisioningFailure(
+      HostConnectionMetricsLogger::
+          ConnectionToHostResult_ProvisioningFailureEventType::
+              PROVISIONING_FAILED);
+}
+
+TEST_F(HostConnectionMetricsLoggerTest, RecordConnectionResultSuccess) {
+  metrics_logger_->RecordConnectionToHostResult(
+      HostConnectionMetricsLogger::ConnectionToHostResult::
+          CONNECTION_RESULT_SUCCESS);
+
+  VerifySuccess(HostConnectionMetricsLogger::
+                    ConnectionToHostResult_SuccessEventType::SUCCESS);
+  VerifyProvisioningFailure(
+      HostConnectionMetricsLogger::
+          ConnectionToHostResult_ProvisioningFailureEventType::OTHER);
+}
+
+TEST_F(HostConnectionMetricsLoggerTest, RecordConnectionResultFailure) {
+  metrics_logger_->RecordConnectionToHostResult(
+      HostConnectionMetricsLogger::ConnectionToHostResult::
+          CONNECTION_RESULT_FAILURE_UNKNOWN_ERROR);
+
+  VerifyFailure(HostConnectionMetricsLogger::
+                    ConnectionToHostResult_FailureEventType::UNKNOWN_ERROR);
+
+  VerifySuccess(HostConnectionMetricsLogger::
+                    ConnectionToHostResult_SuccessEventType::FAILURE);
+  VerifyProvisioningFailure(
+      HostConnectionMetricsLogger::
+          ConnectionToHostResult_ProvisioningFailureEventType::OTHER);
+}
+
+TEST_F(HostConnectionMetricsLoggerTest,
+       RecordConnectionResultFailureClientConnection_Timeout) {
+  metrics_logger_->RecordConnectionToHostResult(
+      HostConnectionMetricsLogger::ConnectionToHostResult::
+          CONNECTION_RESULT_FAILURE_CLIENT_CONNECTION_TIMEOUT);
+
+  VerifyFailure_ClientConnection(
+      HostConnectionMetricsLogger::
+          ConnectionToHostResult_FailureClientConnectionEventType::TIMEOUT);
+  VerifyFailure(
+      HostConnectionMetricsLogger::ConnectionToHostResult_FailureEventType::
+          CLIENT_CONNECTION_ERROR);
+  VerifySuccess(HostConnectionMetricsLogger::
+                    ConnectionToHostResult_SuccessEventType::FAILURE);
+  VerifyProvisioningFailure(
+      HostConnectionMetricsLogger::
+          ConnectionToHostResult_ProvisioningFailureEventType::OTHER);
+}
+
+TEST_F(HostConnectionMetricsLoggerTest,
+       RecordConnectionResultFailureClientConnection_CanceledByNewAttempt) {
+  metrics_logger_->RecordConnectionToHostResult(
+      HostConnectionMetricsLogger::ConnectionToHostResult::
+          CONNECTION_RESULT_FAILURE_CLIENT_CONNECTION_CANCELED_BY_NEW_ATTEMPT);
+
+  VerifyFailure_ClientConnection(
+      HostConnectionMetricsLogger::
+          ConnectionToHostResult_FailureClientConnectionEventType::
+              CANCELED_BY_NEW_ATTEMPT);
+  VerifyFailure(
+      HostConnectionMetricsLogger::ConnectionToHostResult_FailureEventType::
+          CLIENT_CONNECTION_ERROR);
+  VerifySuccess(HostConnectionMetricsLogger::
+                    ConnectionToHostResult_SuccessEventType::FAILURE);
+  VerifyProvisioningFailure(
+      HostConnectionMetricsLogger::
+          ConnectionToHostResult_ProvisioningFailureEventType::OTHER);
+}
+
+TEST_F(HostConnectionMetricsLoggerTest,
+       RecordConnectionResultFailureTetheringTimeout_SetupRequired) {
+  metrics_logger_->RecordConnectionToHostResult(
+      HostConnectionMetricsLogger::ConnectionToHostResult::
+          CONNECTION_RESULT_FAILURE_TETHERING_TIMED_OUT_FIRST_TIME_SETUP_WAS_REQUIRED);
+
+  VerifyFailure_TetheringTimeout(
+      HostConnectionMetricsLogger::
+          ConnectionToHostResult_FailureTetheringTimeoutEventType::
+              FIRST_TIME_SETUP_WAS_REQUIRED);
+  VerifyFailure(
+      HostConnectionMetricsLogger::ConnectionToHostResult_FailureEventType::
+          TETHERING_TIMED_OUT);
+  VerifySuccess(HostConnectionMetricsLogger::
+                    ConnectionToHostResult_SuccessEventType::FAILURE);
+  VerifyProvisioningFailure(
+      HostConnectionMetricsLogger::
+          ConnectionToHostResult_ProvisioningFailureEventType::OTHER);
+}
+
+TEST_F(HostConnectionMetricsLoggerTest,
+       RecordConnectionResultFailureTetheringTimeout_SetupNotRequired) {
+  metrics_logger_->RecordConnectionToHostResult(
+      HostConnectionMetricsLogger::ConnectionToHostResult::
+          CONNECTION_RESULT_FAILURE_TETHERING_TIMED_OUT_FIRST_TIME_SETUP_WAS_NOT_REQUIRED);
+
+  VerifyFailure_TetheringTimeout(
+      HostConnectionMetricsLogger::
+          ConnectionToHostResult_FailureTetheringTimeoutEventType::
+              FIRST_TIME_SETUP_WAS_NOT_REQUIRED);
+  VerifyFailure(
+      HostConnectionMetricsLogger::ConnectionToHostResult_FailureEventType::
+          TETHERING_TIMED_OUT);
+  VerifySuccess(HostConnectionMetricsLogger::
+                    ConnectionToHostResult_SuccessEventType::FAILURE);
+  VerifyProvisioningFailure(
+      HostConnectionMetricsLogger::
+          ConnectionToHostResult_ProvisioningFailureEventType::OTHER);
+}
+
+}  // namespace tether
+
+}  // namespace chromeos
diff --git a/components/BUILD.gn b/components/BUILD.gn
index 91a0c0e1..8024ba4 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -203,6 +203,7 @@
       "//components/offline_pages/core:unit_tests",
       "//components/offline_pages/core/background:unit_tests",
       "//components/offline_pages/core/downloads:unit_tests",
+      "//components/offline_pages/core/prefetch:unit_tests",
       "//components/offline_pages/core/request_header:unit_tests",
       "//components/password_manager/content/browser:unit_tests",
       "//components/payments/content:unit_tests",
diff --git a/components/offline_pages/core/prefetch/BUILD.gn b/components/offline_pages/core/prefetch/BUILD.gn
index ec22924..de42722 100644
--- a/components/offline_pages/core/prefetch/BUILD.gn
+++ b/components/offline_pages/core/prefetch/BUILD.gn
@@ -44,6 +44,10 @@
     "prefetch_service_impl.h",
     "prefetch_types.cc",
     "prefetch_types.h",
+    "store/prefetch_store.cc",
+    "store/prefetch_store.h",
+    "store/prefetch_store_utils.cc",
+    "store/prefetch_store_utils.h",
     "suggested_articles_observer.cc",
     "suggested_articles_observer.h",
   ]
@@ -65,6 +69,7 @@
     "//components/version_info",
     "//google_apis",
     "//net:net",
+    "//sql:sql",
     "//url",
   ]
 }
@@ -76,6 +81,8 @@
     "prefetch_request_test_base.h",
     "prefetch_service_test_taco.cc",
     "prefetch_service_test_taco.h",
+    "store/prefetch_store_test_util.cc",
+    "store/prefetch_store_test_util.h",
     "task_test_base.cc",
     "task_test_base.h",
     "test_offline_metrics_collector.h",
@@ -96,6 +103,8 @@
     "//components/prefs:test_support",
     "//components/version_info:channel",
     "//net:test_support",
+    "//sql:sql",
+    "//url",
   ]
 }
 
@@ -111,6 +120,7 @@
 source_set("unit_tests") {
   testonly = true
   sources = [
+    "add_unique_urls_task_unittest.cc",
     "generate_page_bundle_request_unittest.cc",
     "generate_page_bundle_task_unittest.cc",
     "get_operation_request_unittest.cc",
@@ -123,12 +133,14 @@
     "prefetch_request_fetcher_unittest.cc",
     "prefetch_request_operation_response_unittest.cc",
     "prefetch_server_urls_unittest.cc",
+    "store/prefetch_store_unittest.cc",
     "suggested_articles_observer_unittest.cc",
   ]
 
   deps = [
     ":prefetch",
     ":test_support",
+    "//base",
     "//components/download/public",
     "//components/gcm_driver/instance_id",
     "//components/offline_pages/core",
@@ -137,6 +149,7 @@
     "//components/variations:test_support",
     "//components/version_info:channel",
     "//net:test_support",
+    "//sql:sql",
     "//testing/gmock",
     "//testing/gtest",
     "//url",
diff --git a/components/offline_pages/core/prefetch/DEPS b/components/offline_pages/core/prefetch/DEPS
index 2bebfd7..98d9dd2 100644
--- a/components/offline_pages/core/prefetch/DEPS
+++ b/components/offline_pages/core/prefetch/DEPS
@@ -6,4 +6,5 @@
   "+components/ntp_snippets",
   "+components/version_info",
   "+net",
+  "+sql",
 ]
diff --git a/components/offline_pages/core/prefetch/add_unique_urls_task.cc b/components/offline_pages/core/prefetch/add_unique_urls_task.cc
index e25cc54..9307342 100644
--- a/components/offline_pages/core/prefetch/add_unique_urls_task.cc
+++ b/components/offline_pages/core/prefetch/add_unique_urls_task.cc
@@ -4,53 +4,135 @@
 
 #include "components/offline_pages/core/prefetch/add_unique_urls_task.h"
 
+#include <map>
 #include <memory>
+#include <set>
 #include <utility>
 
 #include "base/bind.h"
 #include "base/callback.h"
+#include "base/time/time.h"
+#include "components/offline_pages/core/prefetch/prefetch_types.h"
+#include "components/offline_pages/core/prefetch/store/prefetch_store.h"
+#include "components/offline_pages/core/prefetch/store/prefetch_store_utils.h"
+#include "sql/connection.h"
+#include "sql/statement.h"
+#include "sql/transaction.h"
 #include "url/gurl.h"
 
 namespace offline_pages {
 
 namespace {
 
-// Adds new prefetch item entries to the store using the URLs and client IDs
-// from |prefetch_urls| and the client's |name_space|. Also cleans up entries in
-// the Zombie state from the client's |name_space| except for the ones
-// whose URL is contained in |prefetch_urls|.
-// Returns the number of added prefecth items.
-static int AddUrlsAndCleanupZombies(
-    const std::string& name_space,
-    const std::vector<PrefetchURL>& prefetch_urls) {
-  NOTIMPLEMENTED();
-  return 1;
+std::map<std::string, std::pair<int64_t, PrefetchItemState>>
+FindExistingPrefetchItemsInNamespaceSync(sql::Connection* db,
+                                         const std::string& name_space) {
+  static const char kSql[] =
+      "SELECT offline_id, state, requested_url FROM prefetch_items"
+      " WHERE client_namespace = ?";
+  sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
+  statement.BindString(0, name_space);
+
+  std::map<std::string, std::pair<int64_t, PrefetchItemState>> result;
+  while (statement.Step()) {
+    result.insert(std::make_pair(
+        statement.ColumnString(2),
+        std::make_pair(statement.ColumnInt64(0), static_cast<PrefetchItemState>(
+                                                     statement.ColumnInt(1)))));
+  }
+
+  return result;
 }
 
-// TODO(fgorski): replace this with the SQL executor.
-static void Execute(base::RepeatingCallback<int()> command_callback,
-                    base::OnceCallback<void(int)> result_callback) {
-  std::move(result_callback).Run(command_callback.Run());
+bool CreatePrefetchItemSync(sql::Connection* db,
+                            const std::string& name_space,
+                            const PrefetchURL& prefetch_url) {
+  static const char kSql[] =
+      "INSERT INTO prefetch_items"
+      " (offline_id, requested_url, client_namespace, client_id, creation_time,"
+      " freshness_time)"
+      " VALUES"
+      " (?, ?, ?, ?, ?, ?)";
+
+  int64_t now_internal = base::Time::Now().ToInternalValue();
+  sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
+  statement.BindInt64(0, GenerateOfflineId());
+  statement.BindString(1, prefetch_url.url.spec());
+  statement.BindString(2, name_space);
+  statement.BindString(3, prefetch_url.id);
+  statement.BindInt64(4, now_internal);
+  statement.BindInt64(5, now_internal);
+
+  return statement.Run();
+}
+
+// Adds new prefetch item entries to the store using the URLs and client IDs
+// from |candidate_prefetch_urls| and the client's |name_space|. Also cleans up
+// entries in the Zombie state from the client's |name_space| except for the
+// ones whose URL is contained in |candidate_prefetch_urls|.
+// Returns the number of added prefecth items.
+AddUniqueUrlsTask::Result AddUrlsAndCleanupZombiesSync(
+    const std::string& name_space,
+    const std::vector<PrefetchURL>& candidate_prefetch_urls,
+    sql::Connection* db) {
+  if (!db)
+    return AddUniqueUrlsTask::Result::STORE_ERROR;
+
+  sql::Transaction transaction(db);
+  if (!transaction.Begin())
+    return AddUniqueUrlsTask::Result::STORE_ERROR;
+
+  std::map<std::string, std::pair<int64_t, PrefetchItemState>> existing_items =
+      FindExistingPrefetchItemsInNamespaceSync(db, name_space);
+
+  AddUniqueUrlsTask::Result result(AddUniqueUrlsTask::Result::NOTHING_ADDED);
+  for (const auto& prefetch_url : candidate_prefetch_urls) {
+    auto iter = existing_items.find(prefetch_url.url.spec());
+    if (iter == existing_items.end()) {
+      if (!CreatePrefetchItemSync(db, name_space, prefetch_url))
+        return AddUniqueUrlsTask::Result::STORE_ERROR;  // Transaction rollback.
+      result = AddUniqueUrlsTask::Result::URLS_ADDED;
+    } else {
+      // Removing from the list of existing items if it was requested again, to
+      // prevent it from being removed in the next step.
+      existing_items.erase(iter);
+    }
+  }
+
+  // Purge remaining zombie IDs.
+  for (const auto& existing_item : existing_items) {
+    if (existing_item.second.second != PrefetchItemState::ZOMBIE)
+      continue;
+    if (!DeletePrefetchItemByOfflineIdSync(db, existing_item.second.first))
+      return AddUniqueUrlsTask::Result::STORE_ERROR;  // Transaction rollback.
+  }
+
+  if (!transaction.Commit())
+    return AddUniqueUrlsTask::Result::STORE_ERROR;  // Transaction rollback.
+
+  return result;
 }
 }
 
 AddUniqueUrlsTask::AddUniqueUrlsTask(
+    PrefetchStore* prefetch_store,
     const std::string& name_space,
     const std::vector<PrefetchURL>& prefetch_urls)
-    : name_space_(name_space),
+    : prefetch_store_(prefetch_store),
+      name_space_(name_space),
       prefetch_urls_(prefetch_urls),
       weak_ptr_factory_(this) {}
 
 AddUniqueUrlsTask::~AddUniqueUrlsTask() {}
 
 void AddUniqueUrlsTask::Run() {
-  Execute(base::BindRepeating(&AddUrlsAndCleanupZombies, name_space_,
-                              prefetch_urls_),
-          base::BindOnce(&AddUniqueUrlsTask::OnUrlsAdded,
-                         weak_ptr_factory_.GetWeakPtr()));
+  prefetch_store_->Execute(base::BindOnce(&AddUrlsAndCleanupZombiesSync,
+                                          name_space_, prefetch_urls_),
+                           base::BindOnce(&AddUniqueUrlsTask::OnUrlsAdded,
+                                          weak_ptr_factory_.GetWeakPtr()));
 }
 
-void AddUniqueUrlsTask::OnUrlsAdded(int added_entry_count) {
+void AddUniqueUrlsTask::OnUrlsAdded(Result result) {
   // TODO(carlosk): schedule NWake here if at least one new entry was added to
   // the store.
   TaskComplete();
diff --git a/components/offline_pages/core/prefetch/add_unique_urls_task.h b/components/offline_pages/core/prefetch/add_unique_urls_task.h
index c265cd3b..b400915 100644
--- a/components/offline_pages/core/prefetch/add_unique_urls_task.h
+++ b/components/offline_pages/core/prefetch/add_unique_urls_task.h
@@ -8,11 +8,13 @@
 #include <string>
 #include <vector>
 
+#include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "components/offline_pages/core/prefetch/prefetch_types.h"
 #include "components/offline_pages/core/task.h"
 
 namespace offline_pages {
+class PrefetchStore;
+struct PrefetchURL;
 
 // Task that adds new URL suggestions to the pipeline. URLs are matched against
 // existing ones from any stage of the process so that only new, unique ones are
@@ -25,15 +27,25 @@
 // from the store.
 class AddUniqueUrlsTask : public Task {
  public:
-  AddUniqueUrlsTask(const std::string& name_space,
+  // Result of executing the command in the store.
+  enum class Result {
+    NOTHING_ADDED,
+    URLS_ADDED,
+    STORE_ERROR,
+  };
+
+  AddUniqueUrlsTask(PrefetchStore* prefetch_store,
+                    const std::string& name_space,
                     const std::vector<PrefetchURL>& prefetch_urls);
   ~AddUniqueUrlsTask() override;
 
   void Run() override;
 
  private:
-  void OnUrlsAdded(int added_entry_count);
+  void OnUrlsAdded(Result result);
 
+  // Prefetch store to execute against. Not owned.
+  PrefetchStore* prefetch_store_;
   const std::string& name_space_;
   std::vector<PrefetchURL> prefetch_urls_;
 
diff --git a/components/offline_pages/core/prefetch/add_unique_urls_task_unittest.cc b/components/offline_pages/core/prefetch/add_unique_urls_task_unittest.cc
new file mode 100644
index 0000000..4a9c043
--- /dev/null
+++ b/components/offline_pages/core/prefetch/add_unique_urls_task_unittest.cc
@@ -0,0 +1,129 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/prefetch/add_unique_urls_task.h"
+
+#include <string>
+#include <vector>
+
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/offline_pages/core/prefetch/prefetch_types.h"
+#include "components/offline_pages/core/prefetch/store/prefetch_store.h"
+#include "components/offline_pages/core/prefetch/store/prefetch_store_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace offline_pages {
+namespace {
+void VerifyItemCount(int expected_count, int actual_count) {
+  EXPECT_EQ(expected_count, actual_count);
+}
+}  // namespace
+
+class AddUniqueUrlsTaskTest : public testing::Test {
+ public:
+  AddUniqueUrlsTaskTest();
+  ~AddUniqueUrlsTaskTest() override = default;
+
+  void SetUp() override;
+  void TearDown() override;
+
+  PrefetchStore* store() { return store_test_util_.store(); }
+
+  PrefetchStoreTestUtil* store_util() { return &store_test_util_; }
+
+  void PumpLoop();
+
+ private:
+  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+  base::ThreadTaskRunnerHandle task_runner_handle_;
+  PrefetchStoreTestUtil store_test_util_;
+};
+
+AddUniqueUrlsTaskTest::AddUniqueUrlsTaskTest()
+    : task_runner_(new base::TestSimpleTaskRunner),
+      task_runner_handle_(task_runner_) {}
+
+void AddUniqueUrlsTaskTest::SetUp() {
+  store_test_util_.BuildStoreInMemory();
+}
+
+void AddUniqueUrlsTaskTest::TearDown() {
+  store_test_util_.DeleteStore();
+  PumpLoop();
+}
+
+void AddUniqueUrlsTaskTest::PumpLoop() {
+  task_runner_->RunUntilIdle();
+}
+
+TEST_F(AddUniqueUrlsTaskTest, AddTaskInEmptyStore) {
+  std::string name_space("test");
+  std::vector<PrefetchURL> urls;
+  urls.push_back(PrefetchURL{"ID-1", GURL("https://www.google.com/")});
+  urls.push_back(PrefetchURL{"ID-2", GURL("http://www.example.com/")});
+  AddUniqueUrlsTask task(store(), name_space, urls);
+  task.Run();
+  PumpLoop();
+
+  store_util()->CountPrefetchItems(base::BindOnce(&VerifyItemCount, 2));
+  PumpLoop();
+}
+
+TEST_F(AddUniqueUrlsTaskTest, DontAddURLIfItExists) {
+  std::string name_space("test");
+  std::vector<PrefetchURL> urls;
+  urls.push_back(PrefetchURL{"ID-1", GURL("https://www.google.com/")});
+  urls.push_back(PrefetchURL{"ID-2", GURL("http://www.example.com/")});
+  AddUniqueUrlsTask task1(store(), name_space, urls);
+  task1.Run();
+  PumpLoop();
+
+  urls.clear();
+  urls.push_back(PrefetchURL{"ID-1", GURL("https://www.google.com/")});
+  urls.push_back(PrefetchURL{"ID-3", GURL("https://news.google.com/")});
+  AddUniqueUrlsTask task2(store(), name_space, urls);
+  task2.Run();
+  PumpLoop();
+
+  // Do the count here.
+  store_util()->CountPrefetchItems(base::BindOnce(&VerifyItemCount, 3));
+  PumpLoop();
+}
+
+TEST_F(AddUniqueUrlsTaskTest, HandleZombiePrefetchItems) {
+  std::string name_space("test");
+  std::vector<PrefetchURL> urls;
+  urls.push_back(PrefetchURL{"ID-1", GURL("https://www.google.com/")});
+  urls.push_back(PrefetchURL{"ID-2", GURL("http://www.example.com/")});
+  urls.push_back(PrefetchURL{"ID-3", GURL("https://news.google.com/")});
+  AddUniqueUrlsTask task1(store(), name_space, urls);
+  task1.Run();
+  PumpLoop();
+
+  store_util()->ZombifyPrefetchItem(name_space, urls[0].url,
+                                    base::BindOnce(&VerifyItemCount, 1));
+  PumpLoop();
+  store_util()->ZombifyPrefetchItem(name_space, urls[1].url,
+                                    base::BindOnce(&VerifyItemCount, 1));
+  PumpLoop();
+
+  urls.clear();
+  urls.push_back(PrefetchURL{"ID-1", GURL("https://www.google.com/")});
+  urls.push_back(PrefetchURL{"ID-3", GURL("https://news.google.com/")});
+  urls.push_back(PrefetchURL{"ID-4", GURL("https://chrome.google.com/")});
+  // ID-1 is expected to stay in zombie state.
+  // ID-2 is expected to be removed, because it is in zombie state.
+  // ID-3 is still requested, so it is ignored.
+  // ID-4 is added.
+  AddUniqueUrlsTask task2(store(), name_space, urls);
+  task2.Run();
+  PumpLoop();
+
+  // Do the count here.
+  store_util()->CountPrefetchItems(base::BindOnce(&VerifyItemCount, 3));
+  PumpLoop();
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/prefetch/prefetch_dispatcher_impl.cc b/components/offline_pages/core/prefetch/prefetch_dispatcher_impl.cc
index ce4b1fd8..04b82549 100644
--- a/components/offline_pages/core/prefetch/prefetch_dispatcher_impl.cc
+++ b/components/offline_pages/core/prefetch/prefetch_dispatcher_impl.cc
@@ -49,8 +49,9 @@
   if (!IsPrefetchingOfflinePagesEnabled())
     return;
 
-  std::unique_ptr<Task> add_task =
-      base::MakeUnique<AddUniqueUrlsTask>(name_space, prefetch_urls);
+  PrefetchStore* prefetch_store = service_->GetPrefetchStore();
+  std::unique_ptr<Task> add_task = base::MakeUnique<AddUniqueUrlsTask>(
+      prefetch_store, name_space, prefetch_urls);
   task_queue_.AddTask(std::move(add_task));
 
   // TODO(dewittj): Remove when we have proper scheduling.
diff --git a/components/offline_pages/core/prefetch/prefetch_dispatcher_impl_unittest.cc b/components/offline_pages/core/prefetch/prefetch_dispatcher_impl_unittest.cc
index d324c5d..7944ff1 100644
--- a/components/offline_pages/core/prefetch/prefetch_dispatcher_impl_unittest.cc
+++ b/components/offline_pages/core/prefetch/prefetch_dispatcher_impl_unittest.cc
@@ -17,6 +17,8 @@
 #include "components/offline_pages/core/prefetch/prefetch_network_request_factory.h"
 #include "components/offline_pages/core/prefetch/prefetch_service.h"
 #include "components/offline_pages/core/prefetch/prefetch_service_test_taco.h"
+#include "components/offline_pages/core/prefetch/store/prefetch_store.h"
+#include "components/offline_pages/core/prefetch/store/prefetch_store_test_util.h"
 #include "components/offline_pages/core/prefetch/suggested_articles_observer.h"
 #include "components/offline_pages/core/prefetch/test_prefetch_network_request_factory.h"
 #include "components/version_info/channel.h"
@@ -25,9 +27,10 @@
 #include "url/gurl.h"
 
 namespace offline_pages {
-
 namespace {
 
+const std::string kTestNamespace = "TestPrefetchClientNamespace";
+
 class TestScopedBackgroundTask
     : public PrefetchDispatcher::ScopedBackgroundTask {
  public:
@@ -45,8 +48,6 @@
 
 class PrefetchDispatcherTest : public testing::Test {
  public:
-  const std::string TEST_NAMESPACE = "TestPrefetchClientNamespace";
-
   PrefetchDispatcherTest();
 
   // Test implementation.
@@ -54,6 +55,7 @@
   void TearDown() override;
 
   void PumpLoop();
+
   PrefetchDispatcher::ScopedBackgroundTask* GetBackgroundTask() {
     return dispatcher_->background_task_.get();
   }
@@ -67,29 +69,27 @@
   std::vector<PrefetchURL> test_urls_;
 
  private:
-  scoped_refptr<base::TestSimpleTaskRunner> task_runner_ =
-      new base::TestSimpleTaskRunner;
+  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
   base::ThreadTaskRunnerHandle task_runner_handle_;
-  OfflineEventLogger logger_;
+  std::unique_ptr<PrefetchServiceTestTaco> taco_;
+
   base::test::ScopedFeatureList feature_list_;
 
-  PrefetchServiceTestTaco taco_;
   // Owned by |taco_|.
   PrefetchDispatcherImpl* dispatcher_;
 };
 
 PrefetchDispatcherTest::PrefetchDispatcherTest()
-    : task_runner_handle_(task_runner_) {
+    : task_runner_(new base::TestSimpleTaskRunner),
+      task_runner_handle_(task_runner_) {
   feature_list_.InitAndEnableFeature(kPrefetchingOfflinePagesFeature);
-
-  dispatcher_ = new PrefetchDispatcherImpl();
-  taco_.SetPrefetchDispatcher(base::WrapUnique(dispatcher_));
-
-  taco_.CreatePrefetchService();
 }
 
 void PrefetchDispatcherTest::SetUp() {
-  ASSERT_FALSE(task_runner()->HasPendingTask());
+  dispatcher_ = new PrefetchDispatcherImpl();
+  taco_.reset(new PrefetchServiceTestTaco);
+  taco_->SetPrefetchDispatcher(base::WrapUnique(dispatcher_));
+  taco_->CreatePrefetchService();
 
   ASSERT_TRUE(test_urls_.empty());
   test_urls_.push_back({"1", GURL("http://testurl.com/foo")});
@@ -97,7 +97,9 @@
 }
 
 void PrefetchDispatcherTest::TearDown() {
-  task_runner()->ClearPendingTasks();
+  // Ensures that the store is properly disposed off.
+  taco_.reset();
+  PumpLoop();
 }
 
 void PrefetchDispatcherTest::PumpLoop() {
@@ -109,7 +111,7 @@
   // with the state of adding tasks, and that the end state is we have tests
   // that verify the proper tasks were added in the proper order at each wakeup
   // signal of the dispatcher.
-  prefetch_dispatcher()->AddCandidatePrefetchURLs(TEST_NAMESPACE, test_urls_);
+  prefetch_dispatcher()->AddCandidatePrefetchURLs(kTestNamespace, test_urls_);
   prefetch_dispatcher()->RemoveAllUnprocessedPrefetchURLs(
       kSuggestedArticlesNamespace);
   prefetch_dispatcher()->RemovePrefetchURLsByClientId(
@@ -117,9 +119,8 @@
 }
 
 TEST_F(PrefetchDispatcherTest, AddCandidatePrefetchURLsTask) {
-  prefetch_dispatcher()->AddCandidatePrefetchURLs(TEST_NAMESPACE, test_urls_);
+  prefetch_dispatcher()->AddCandidatePrefetchURLs(kTestNamespace, test_urls_);
   EXPECT_TRUE(dispatcher_task_queue()->HasPendingTasks());
-  EXPECT_TRUE(dispatcher_task_queue()->HasRunningTask());
   PumpLoop();
   EXPECT_FALSE(dispatcher_task_queue()->HasPendingTasks());
   EXPECT_FALSE(dispatcher_task_queue()->HasRunningTask());
@@ -132,7 +133,7 @@
   // Don't add a task for new prefetch URLs.
   PrefetchURL prefetch_url("id", GURL("https://www.chromium.org"));
   prefetch_dispatcher()->AddCandidatePrefetchURLs(
-      TEST_NAMESPACE, std::vector<PrefetchURL>(1, prefetch_url));
+      kTestNamespace, std::vector<PrefetchURL>(1, prefetch_url));
   EXPECT_FALSE(dispatcher_task_queue()->HasRunningTask());
 
   // Do nothing with a new background task.
diff --git a/components/offline_pages/core/prefetch/prefetch_downloader_unittest.cc b/components/offline_pages/core/prefetch/prefetch_downloader_unittest.cc
index 9823617..1befed2 100644
--- a/components/offline_pages/core/prefetch/prefetch_downloader_unittest.cc
+++ b/components/offline_pages/core/prefetch/prefetch_downloader_unittest.cc
@@ -141,16 +141,23 @@
         task_runner_handle_(task_runner_) {}
 
   void SetUp() override {
+    prefetch_service_taco_.reset(new PrefetchServiceTestTaco);
+
     auto downloader =
         base::MakeUnique<PrefetchDownloader>(&download_service_, kTestChannel);
     download_service_.set_prefetch_downloader(downloader.get());
-    prefetch_service_taco_.SetPrefetchDownloader(std::move(downloader));
+    prefetch_service_taco_->SetPrefetchDownloader(std::move(downloader));
 
-    prefetch_service_taco_.CreatePrefetchService();
+    prefetch_service_taco_->CreatePrefetchService();
     GetPrefetchDownloader()->SetCompletedCallback(base::Bind(
         &PrefetchDownloaderTest::OnDownloadCompleted, base::Unretained(this)));
   }
 
+  void TearDown() override {
+    prefetch_service_taco_.reset();
+    PumpLoop();
+  }
+
   void SetDownloadServiceReady(bool ready) {
     download_service_.set_ready(ready);
     if (ready)
@@ -184,13 +191,13 @@
   }
 
   PrefetchDownloader* GetPrefetchDownloader() const {
-    return prefetch_service_taco_.prefetch_service()->GetPrefetchDownloader();
+    return prefetch_service_taco_->prefetch_service()->GetPrefetchDownloader();
   }
 
   scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
   base::ThreadTaskRunnerHandle task_runner_handle_;
   download::TestDownloadService download_service_;
-  PrefetchServiceTestTaco prefetch_service_taco_;
+  std::unique_ptr<PrefetchServiceTestTaco> prefetch_service_taco_;
   std::vector<PrefetchDownloadResult> completed_downloads_;
 };
 
@@ -209,6 +216,7 @@
   std::string alt_value;
   EXPECT_TRUE(net::GetValueForKeyInQuery(download_url, "alt", &alt_value));
   EXPECT_EQ("media", alt_value);
+  PumpLoop();
 }
 
 TEST_F(PrefetchDownloaderTest, StartDownloadBeforeServiceReady) {
diff --git a/components/offline_pages/core/prefetch/prefetch_gcm_app_handler_unittest.cc b/components/offline_pages/core/prefetch/prefetch_gcm_app_handler_unittest.cc
index 04f19b9a..fe969a8 100644
--- a/components/offline_pages/core/prefetch/prefetch_gcm_app_handler_unittest.cc
+++ b/components/offline_pages/core/prefetch/prefetch_gcm_app_handler_unittest.cc
@@ -38,21 +38,29 @@
  public:
   PrefetchGCMAppHandlerTest()
       : task_runner_(new base::TestSimpleTaskRunner),
-        task_runner_handle_(task_runner_) {
+        task_runner_handle_(task_runner_) {}
+
+  void SetUp() override {
     auto dispatcher = base::MakeUnique<TestPrefetchDispatcher>();
+    test_dispatcher_ = dispatcher.get();
 
     auto token_factory = base::MakeUnique<TestTokenFactory>();
     token_factory_ = token_factory.get();
 
     auto gcm_app_handler =
         base::MakeUnique<PrefetchGCMAppHandler>(std::move(token_factory));
-
-    test_dispatcher_ = dispatcher.get();
     handler_ = gcm_app_handler.get();
 
-    prefetch_service_taco_.SetPrefetchGCMHandler(std::move(gcm_app_handler));
-    prefetch_service_taco_.SetPrefetchDispatcher(std::move(dispatcher));
-    prefetch_service_taco_.CreatePrefetchService();
+    prefetch_service_taco_.reset(new PrefetchServiceTestTaco);
+    prefetch_service_taco_->SetPrefetchGCMHandler(std::move(gcm_app_handler));
+    prefetch_service_taco_->SetPrefetchDispatcher(std::move(dispatcher));
+    prefetch_service_taco_->CreatePrefetchService();
+  }
+
+  void TearDown() override {
+    // Ensures that the store is properly disposed off.
+    prefetch_service_taco_.reset();
+    task_runner_->RunUntilIdle();
   }
 
   TestPrefetchDispatcher* dispatcher() { return test_dispatcher_; }
@@ -62,7 +70,8 @@
  private:
   scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
   base::ThreadTaskRunnerHandle task_runner_handle_;
-  PrefetchServiceTestTaco prefetch_service_taco_;
+  std::unique_ptr<PrefetchServiceTestTaco> prefetch_service_taco_;
+
   // Owned by the taco.
   TestPrefetchDispatcher* test_dispatcher_;
   // Owned by the taco.
diff --git a/components/offline_pages/core/prefetch/prefetch_item.cc b/components/offline_pages/core/prefetch/prefetch_item.cc
index 3c435bb2..375bf09 100644
--- a/components/offline_pages/core/prefetch/prefetch_item.cc
+++ b/components/offline_pages/core/prefetch/prefetch_item.cc
@@ -13,9 +13,9 @@
 PrefetchItem::~PrefetchItem(){};
 
 bool PrefetchItem::operator==(const PrefetchItem& other) const {
-  return guid == other.guid && client_id == other.client_id &&
-         state == other.state && url == other.url &&
-         final_archived_url == other.final_archived_url &&
+  return offline_id == other.offline_id && guid == other.guid &&
+         client_id == other.client_id && state == other.state &&
+         url == other.url && final_archived_url == other.final_archived_url &&
          request_archive_attempt_count == other.request_archive_attempt_count &&
          operation_name == other.operation_name &&
          archive_body_name == other.archive_body_name &&
diff --git a/components/offline_pages/core/prefetch/prefetch_item.h b/components/offline_pages/core/prefetch/prefetch_item.h
index b6b001a..ad22a92 100644
--- a/components/offline_pages/core/prefetch/prefetch_item.h
+++ b/components/offline_pages/core/prefetch/prefetch_item.h
@@ -28,6 +28,10 @@
   bool operator==(const PrefetchItem& other) const;
   bool operator!=(const PrefetchItem& other) const;
 
+  // Primary key that stays consistent between prefetch item, request and
+  // offline page.
+  int64_t offline_id = 0;
+
   // Primary key/ID for this prefetch item (See |base::GenerateGUID()|). This
   // value will be reused when communicating with other systems accepting GUID
   // identifiers for operations linked to this item.
diff --git a/components/offline_pages/core/prefetch/prefetch_item_unittest.cc b/components/offline_pages/core/prefetch/prefetch_item_unittest.cc
index e5710ea..3778281 100644
--- a/components/offline_pages/core/prefetch/prefetch_item_unittest.cc
+++ b/components/offline_pages/core/prefetch/prefetch_item_unittest.cc
@@ -16,6 +16,11 @@
   EXPECT_EQ(item1, PrefetchItem());
   EXPECT_EQ(item1, PrefetchItem(item1));
 
+  item1.offline_id = 77L;
+  EXPECT_NE(item1, PrefetchItem());
+  EXPECT_EQ(item1, PrefetchItem(item1));
+  item1 = PrefetchItem();
+
   item1.guid = "A";
   EXPECT_NE(item1, PrefetchItem());
   EXPECT_EQ(item1, PrefetchItem(item1));
diff --git a/components/offline_pages/core/prefetch/prefetch_service.h b/components/offline_pages/core/prefetch/prefetch_service.h
index 10e7105..eb196b4 100644
--- a/components/offline_pages/core/prefetch/prefetch_service.h
+++ b/components/offline_pages/core/prefetch/prefetch_service.h
@@ -14,6 +14,7 @@
 class PrefetchDownloader;
 class PrefetchGCMHandler;
 class PrefetchNetworkRequestFactory;
+class PrefetchStore;
 class SuggestedArticlesObserver;
 
 // Main class and entry point for the Offline Pages Prefetching feature, that
@@ -33,6 +34,7 @@
   virtual PrefetchGCMHandler* GetPrefetchGCMHandler() = 0;
   virtual PrefetchNetworkRequestFactory* GetPrefetchNetworkRequestFactory() = 0;
   virtual PrefetchDownloader* GetPrefetchDownloader() = 0;
+  virtual PrefetchStore* GetPrefetchStore() = 0;
 
   // May be |nullptr| in tests.  The PrefetchService does not depend on the
   // SuggestedArticlesObserver, it merely owns it for lifetime purposes.
diff --git a/components/offline_pages/core/prefetch/prefetch_service_impl.cc b/components/offline_pages/core/prefetch/prefetch_service_impl.cc
index 478d4109..b255451 100644
--- a/components/offline_pages/core/prefetch/prefetch_service_impl.cc
+++ b/components/offline_pages/core/prefetch/prefetch_service_impl.cc
@@ -13,6 +13,7 @@
 #include "components/offline_pages/core/prefetch/prefetch_downloader.h"
 #include "components/offline_pages/core/prefetch/prefetch_gcm_handler.h"
 #include "components/offline_pages/core/prefetch/prefetch_network_request_factory.h"
+#include "components/offline_pages/core/prefetch/store/prefetch_store.h"
 #include "components/offline_pages/core/prefetch/suggested_articles_observer.h"
 
 namespace offline_pages {
@@ -22,12 +23,14 @@
     std::unique_ptr<PrefetchDispatcher> dispatcher,
     std::unique_ptr<PrefetchGCMHandler> gcm_handler,
     std::unique_ptr<PrefetchNetworkRequestFactory> network_request_factory,
+    std::unique_ptr<PrefetchStore> prefetch_store,
     std::unique_ptr<SuggestedArticlesObserver> suggested_articles_observer,
     std::unique_ptr<PrefetchDownloader> prefetch_downloader)
     : offline_metrics_collector_(std::move(offline_metrics_collector)),
       prefetch_dispatcher_(std::move(dispatcher)),
       prefetch_gcm_handler_(std::move(gcm_handler)),
       network_request_factory_(std::move(network_request_factory)),
+      prefetch_store_(std::move(prefetch_store)),
       suggested_articles_observer_(std::move(suggested_articles_observer)),
       prefetch_downloader_(std::move(prefetch_downloader)) {
   prefetch_dispatcher_->SetService(this);
@@ -56,6 +59,10 @@
   return network_request_factory_.get();
 }
 
+PrefetchStore* PrefetchServiceImpl::GetPrefetchStore() {
+  return prefetch_store_.get();
+}
+
 SuggestedArticlesObserver* PrefetchServiceImpl::GetSuggestedArticlesObserver() {
   return suggested_articles_observer_.get();
 }
diff --git a/components/offline_pages/core/prefetch/prefetch_service_impl.h b/components/offline_pages/core/prefetch/prefetch_service_impl.h
index 98e5d61..df8a54b 100644
--- a/components/offline_pages/core/prefetch/prefetch_service_impl.h
+++ b/components/offline_pages/core/prefetch/prefetch_service_impl.h
@@ -13,12 +13,6 @@
 #include "components/offline_pages/core/prefetch/prefetch_types.h"
 
 namespace offline_pages {
-class OfflineMetricsCollector;
-class PrefetchDispatcher;
-class PrefetchDownloader;
-class PrefetchGCMHandler;
-class PrefetchNetworkRequestFactory;
-class SuggestedArticlesObserver;
 
 class PrefetchServiceImpl : public PrefetchService {
  public:
@@ -27,6 +21,7 @@
       std::unique_ptr<PrefetchDispatcher> dispatcher,
       std::unique_ptr<PrefetchGCMHandler> gcm_handler,
       std::unique_ptr<PrefetchNetworkRequestFactory> network_request_factory,
+      std::unique_ptr<PrefetchStore> prefetch_store,
       std::unique_ptr<SuggestedArticlesObserver> suggested_articles_observer,
       std::unique_ptr<PrefetchDownloader> prefetch_downloader);
   ~PrefetchServiceImpl() override;
@@ -36,6 +31,7 @@
   PrefetchDispatcher* GetPrefetchDispatcher() override;
   PrefetchGCMHandler* GetPrefetchGCMHandler() override;
   PrefetchNetworkRequestFactory* GetPrefetchNetworkRequestFactory() override;
+  PrefetchStore* GetPrefetchStore() override;
   SuggestedArticlesObserver* GetSuggestedArticlesObserver() override;
   OfflineEventLogger* GetLogger() override;
   PrefetchDownloader* GetPrefetchDownloader() override;
@@ -53,6 +49,7 @@
   std::unique_ptr<PrefetchDispatcher> prefetch_dispatcher_;
   std::unique_ptr<PrefetchGCMHandler> prefetch_gcm_handler_;
   std::unique_ptr<PrefetchNetworkRequestFactory> network_request_factory_;
+  std::unique_ptr<PrefetchStore> prefetch_store_;
   std::unique_ptr<SuggestedArticlesObserver> suggested_articles_observer_;
   std::unique_ptr<PrefetchDownloader> prefetch_downloader_;
 
diff --git a/components/offline_pages/core/prefetch/prefetch_service_test_taco.cc b/components/offline_pages/core/prefetch/prefetch_service_test_taco.cc
index e74c98a..6296a2e 100644
--- a/components/offline_pages/core/prefetch/prefetch_service_test_taco.cc
+++ b/components/offline_pages/core/prefetch/prefetch_service_test_taco.cc
@@ -13,6 +13,8 @@
 #include "components/offline_pages/core/prefetch/prefetch_downloader.h"
 #include "components/offline_pages/core/prefetch/prefetch_gcm_handler.h"
 #include "components/offline_pages/core/prefetch/prefetch_service_impl.h"
+#include "components/offline_pages/core/prefetch/store/prefetch_store.h"
+#include "components/offline_pages/core/prefetch/store/prefetch_store_test_util.h"
 #include "components/offline_pages/core/prefetch/suggested_articles_observer.h"
 #include "components/offline_pages/core/prefetch/test_offline_metrics_collector.h"
 #include "components/offline_pages/core/prefetch/test_prefetch_dispatcher.h"
@@ -32,6 +34,10 @@
   network_request_factory_ =
       base::MakeUnique<TestPrefetchNetworkRequestFactory>();
 
+  PrefetchStoreTestUtil store_test_util;
+  store_test_util.BuildStoreInMemory();
+  prefetch_store_sql_ = store_test_util.ReleaseStore();
+
   suggested_articles_observer_ = base::MakeUnique<SuggestedArticlesObserver>();
   prefetch_downloader_ = base::WrapUnique(new PrefetchDownloader(kTestChannel));
   // This sets up the testing articles as an empty vector, we can ignore the
@@ -65,6 +71,12 @@
   network_request_factory_ = std::move(network_request_factory);
 }
 
+void PrefetchServiceTestTaco::SetPrefetchStoreSql(
+    std::unique_ptr<PrefetchStore> prefetch_store_sql) {
+  CHECK(!prefetch_service_);
+  prefetch_store_sql_ = std::move(prefetch_store_sql);
+}
+
 void PrefetchServiceTestTaco::SetSuggestedArticlesObserver(
     std::unique_ptr<SuggestedArticlesObserver> suggested_articles_observer) {
   CHECK(!prefetch_service_);
@@ -79,12 +91,14 @@
 
 void PrefetchServiceTestTaco::CreatePrefetchService() {
   CHECK(metrics_collector_ && dispatcher_ && gcm_handler_ &&
-        suggested_articles_observer_ && network_request_factory_ &&
-        prefetch_downloader_);
+        network_request_factory_ && prefetch_store_sql_ &&
+        suggested_articles_observer_ && prefetch_downloader_);
+
   prefetch_service_ = base::MakeUnique<PrefetchServiceImpl>(
       std::move(metrics_collector_), std::move(dispatcher_),
       std::move(gcm_handler_), std::move(network_request_factory_),
-      std::move(suggested_articles_observer_), std::move(prefetch_downloader_));
+      std::move(prefetch_store_sql_), std::move(suggested_articles_observer_),
+      std::move(prefetch_downloader_));
 }
 
 std::unique_ptr<PrefetchService>
diff --git a/components/offline_pages/core/prefetch/prefetch_service_test_taco.h b/components/offline_pages/core/prefetch/prefetch_service_test_taco.h
index aa56544..d4db9a1 100644
--- a/components/offline_pages/core/prefetch/prefetch_service_test_taco.h
+++ b/components/offline_pages/core/prefetch/prefetch_service_test_taco.h
@@ -18,6 +18,7 @@
 class PrefetchGCMHandler;
 class PrefetchService;
 class PrefetchNetworkRequestFactory;
+class PrefetchStore;
 class SuggestedArticlesObserver;
 
 // The taco class acts as a wrapper around the prefetch service making
@@ -43,6 +44,7 @@
   // Default type: TestNetworkRequestFactory.
   void SetPrefetchNetworkRequestFactory(
       std::unique_ptr<PrefetchNetworkRequestFactory> network_request_factory);
+  void SetPrefetchStoreSql(std::unique_ptr<PrefetchStore> prefetch_store_sql);
   // Defaults to SuggestedArticlesObserver.  Initializes the testing suggestions
   // by default, so no ContentSuggestionsService is required..
   void SetSuggestedArticlesObserver(
@@ -70,6 +72,7 @@
   std::unique_ptr<PrefetchDispatcher> dispatcher_;
   std::unique_ptr<PrefetchGCMHandler> gcm_handler_;
   std::unique_ptr<PrefetchNetworkRequestFactory> network_request_factory_;
+  std::unique_ptr<PrefetchStore> prefetch_store_sql_;
   std::unique_ptr<SuggestedArticlesObserver> suggested_articles_observer_;
   std::unique_ptr<PrefetchDownloader> prefetch_downloader_;
 
diff --git a/components/offline_pages/core/prefetch/store/prefetch_store.cc b/components/offline_pages/core/prefetch/store/prefetch_store.cc
new file mode 100644
index 0000000..f6cecd57
--- /dev/null
+++ b/components/offline_pages/core/prefetch/store/prefetch_store.cc
@@ -0,0 +1,173 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/prefetch/store/prefetch_store.h"
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/sequenced_task_runner.h"
+#include "base/task_runner_util.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/offline_pages/core/offline_store_types.h"
+#include "components/offline_pages/core/prefetch/prefetch_item.h"
+#include "components/offline_pages/core/prefetch/store/prefetch_store_utils.h"
+#include "sql/connection.h"
+#include "sql/meta_table.h"
+#include "sql/statement.h"
+#include "sql/transaction.h"
+
+namespace offline_pages {
+namespace {
+
+// Name of the table with prefetch items.
+const char kPrefetchItemsTableName[] = "prefetch_items";
+const char kPrefetchStoreFileName[] = "PrefetchStore.db";
+
+const int kCurrentVersion = 1;
+const int kCompatibleVersion = 1;
+
+using InitializeCallback =
+    base::Callback<void(InitializationStatus,
+                        std::unique_ptr<sql::Connection>)>;
+
+bool CreatePrefetchItemsTable(sql::Connection* db) {
+  static const char kSql[] =
+      "CREATE TABLE prefetch_items"
+      "(offline_id INTEGER PRIMARY KEY NOT NULL,"
+      " state INTEGER NOT NULL DEFAULT 0,"
+      " request_archive_attempt_count INTEGER NOT NULL DEFAULT 0,"
+      " archive_body_length INTEGER_NOT_NULL DEFAULT -1,"
+      " creation_time INTEGER NOT NULL,"
+      " freshness_time INTEGER NOT NULL,"
+      " error_code INTEGER NOT NULL DEFAULT 0,"
+      " guid VARCHAR NOT NULL DEFAULT '',"
+      " client_namespace VARCHAR NOT NULL DEFAULT '',"
+      " client_id VARCHAR NOT NULL DEFAULT '',"
+      " requested_url VARCHAR NOT NULL DEFAULT '',"
+      " final_archived_url VARCHAR NOT NULL DEFAULT '',"
+      " operation_name VARCHAR NOT NULL DEFAULT '',"
+      " archive_body_name VARCHAR NOT NULL DEFAULT ''"
+      ")";
+  return db->Execute(kSql);
+}
+
+bool CreateSchema(sql::Connection* db) {
+  // If you create a transaction but don't Commit() it is automatically
+  // rolled back by its destructor when it falls out of scope.
+  sql::Transaction transaction(db);
+  if (!transaction.Begin())
+    return false;
+
+  if (!db->DoesTableExist(kPrefetchItemsTableName)) {
+    if (!CreatePrefetchItemsTable(db))
+      return false;
+  }
+
+  sql::MetaTable meta_table;
+  meta_table.Init(db, kCurrentVersion, kCompatibleVersion);
+
+  // This would be a great place to add indices when we need them.
+  return transaction.Commit();
+}
+
+bool PrepareDirectory(const base::FilePath& path) {
+  base::File::Error error = base::File::FILE_OK;
+  if (!base::DirectoryExists(path.DirName())) {
+    if (!base::CreateDirectoryAndGetError(path.DirName(), &error)) {
+      LOG(ERROR) << "Failed to create prefetch db directory: "
+                 << base::File::ErrorToString(error);
+      return false;
+    }
+  }
+  return true;
+}
+
+// TODO(fgorski): This function and this part of the system in general could
+// benefit from a better status code reportable through UMA to better capture
+// the reason for failure, aiding the process of repeated attempts to
+// open/initialize the database.
+bool InitializeSync(sql::Connection* db,
+                    const base::FilePath& path,
+                    bool in_memory) {
+  // These values are default.
+  db->set_page_size(4096);
+  db->set_cache_size(500);
+  db->set_histogram_tag("PrefetchStore");
+  db->set_exclusive_locking();
+
+  if (!in_memory && !PrepareDirectory(path))
+    return false;
+
+  bool open_db_result = false;
+  if (in_memory)
+    open_db_result = db->OpenInMemory();
+  else
+    open_db_result = db->Open(path);
+
+  if (!open_db_result) {
+    LOG(ERROR) << "Failed to open database, in memory: " << in_memory;
+    return false;
+  }
+  db->Preload();
+
+  return CreateSchema(db);
+}
+
+}  // namespace
+
+PrefetchStore::PrefetchStore(
+    scoped_refptr<base::SequencedTaskRunner> blocking_task_runner)
+    : blocking_task_runner_(std::move(blocking_task_runner)),
+      in_memory_(true),
+      db_(new sql::Connection,
+          base::OnTaskRunnerDeleter(blocking_task_runner_)),
+      initialization_status_(InitializationStatus::NOT_INITIALIZED),
+      weak_ptr_factory_(this) {}
+
+PrefetchStore::PrefetchStore(
+    scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,
+    const base::FilePath& path)
+    : blocking_task_runner_(std::move(blocking_task_runner)),
+      db_file_path_(path.AppendASCII(kPrefetchStoreFileName)),
+      in_memory_(false),
+      db_(new sql::Connection,
+          base::OnTaskRunnerDeleter(blocking_task_runner_)),
+      initialization_status_(InitializationStatus::NOT_INITIALIZED),
+      weak_ptr_factory_(this) {}
+
+PrefetchStore::~PrefetchStore() {}
+
+void PrefetchStore::Initialize(base::OnceClosure pending_command) {
+  DCHECK_EQ(initialization_status_, InitializationStatus::NOT_INITIALIZED);
+
+  initialization_status_ = InitializationStatus::INITIALIZING;
+  base::PostTaskAndReplyWithResult(
+      blocking_task_runner_.get(), FROM_HERE,
+      base::BindOnce(&InitializeSync, db_.get(), db_file_path_, in_memory_),
+      base::BindOnce(&PrefetchStore::OnInitializeDone,
+                     weak_ptr_factory_.GetWeakPtr(),
+                     std::move(pending_command)));
+}
+
+void PrefetchStore::OnInitializeDone(base::OnceClosure pending_command,
+                                     bool success) {
+  DCHECK_EQ(initialization_status_, InitializationStatus::INITIALIZING);
+  initialization_status_ =
+      success ? InitializationStatus::SUCCESS : InitializationStatus::FAILURE;
+
+  CHECK(!pending_command.is_null());
+  std::move(pending_command).Run();
+
+  // Once pending commands are empty, we get back to NOT_INITIALIZED state, to
+  // make it possible to retry initialization next time a DB operation is
+  // attempted.
+  if (initialization_status_ == InitializationStatus::FAILURE)
+    initialization_status_ = InitializationStatus::NOT_INITIALIZED;
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/prefetch/store/prefetch_store.h b/components/offline_pages/core/prefetch/store/prefetch_store.h
new file mode 100644
index 0000000..15929a6
--- /dev/null
+++ b/components/offline_pages/core/prefetch/store/prefetch_store.h
@@ -0,0 +1,122 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_STORE_PREFETCH_STORE_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_STORE_PREFETCH_STORE_H_
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/location.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequenced_task_runner.h"
+#include "base/task_runner_util.h"
+
+namespace sql {
+class Connection;
+}
+
+namespace offline_pages {
+
+enum class InitializationStatus {
+  NOT_INITIALIZED,
+  INITIALIZING,
+  SUCCESS,
+  FAILURE,
+};
+
+// PrefetchStore is a front end to SQLite store hosting prefetch related
+// items.
+//
+// The store controls the pointer to the SQLite database and only makes it
+// available to the |RunCallback| of the |Execute| method on the blocking
+// thread.
+//
+// Store has a set of auxiliary functions meant to be used on the blocking
+// thread. They can be found in prefetch_store_sql_utils file.
+class PrefetchStore {
+ public:
+  // Definition of the callback that is going to run the core of the command in
+  // the |Execute| method.
+  template <typename T>
+  using RunCallback = base::OnceCallback<T(sql::Connection*)>;
+
+  // Definition of the callback used to pass the result back to the caller of
+  // |Execute| method.
+  template <typename T>
+  using ResultCallback = base::OnceCallback<void(T)>;
+
+  explicit PrefetchStore(
+      scoped_refptr<base::SequencedTaskRunner> blocking_task_runner);
+  PrefetchStore(scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,
+                const base::FilePath& database_dir);
+  ~PrefetchStore();
+
+  // Executes a |run_callback| on SQL store on the blocking thread, and posts
+  // its result back to calling thread through |result_callback|. The work will
+  // be postponed if the store is in NOT_INITIALIZED or INITIALIZING, in which
+  // case the work will have to wait until initialization is completed. Calling
+  // |Execute| when store is NOT_INITIALIZED will cause the store initialization
+  // to start.
+  // Store initialization status needs to be SUCCESS for test task to run, or
+  // FAILURE, in which case the |db| pointer passed to |run_callback| will be
+  // null and such case should be gracefully handled.
+  template <typename T>
+  void Execute(RunCallback<T> run_callback, ResultCallback<T> result_callback) {
+    CHECK_NE(initialization_status_, InitializationStatus::INITIALIZING);
+
+    if (initialization_status_ == InitializationStatus::NOT_INITIALIZED) {
+      Initialize(base::BindOnce(
+          &PrefetchStore::Execute<T>, weak_ptr_factory_.GetWeakPtr(),
+          std::move(run_callback), std::move(result_callback)));
+      return;
+    }
+
+    sql::Connection* db =
+        initialization_status_ == InitializationStatus::SUCCESS ? db_.get()
+                                                                : nullptr;
+    base::PostTaskAndReplyWithResult(
+        blocking_task_runner_.get(), FROM_HERE,
+        base::BindOnce(std::move(run_callback), db),
+        std::move(result_callback));
+  }
+
+  // Gets the initialization status of the store.
+  InitializationStatus initialization_status() const {
+    return initialization_status_;
+  }
+
+ private:
+  // Used internally to initialize connection.
+  void Initialize(base::OnceClosure pending_command);
+
+  // Used to conclude opening/resetting DB connection.
+  void OnInitializeDone(base::OnceClosure pending_command, bool success);
+
+  // Background thread where all SQL access should be run.
+  scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
+
+  // Path to the database on disk.
+  base::FilePath db_file_path_;
+
+  // Only open the store in memory. Used for testing.
+  bool in_memory_;
+
+  // Database connection.
+  std::unique_ptr<sql::Connection, base::OnTaskRunnerDeleter> db_;
+
+  // Initialization status of the store.
+  InitializationStatus initialization_status_;
+
+  // Weak pointer to control the callback.
+  base::WeakPtrFactory<PrefetchStore> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(PrefetchStore);
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_STORE_PREFETCH_STORE_H_
diff --git a/components/offline_pages/core/prefetch/store/prefetch_store_test_util.cc b/components/offline_pages/core/prefetch/store/prefetch_store_test_util.cc
new file mode 100644
index 0000000..b3fb7a4
--- /dev/null
+++ b/components/offline_pages/core/prefetch/store/prefetch_store_test_util.cc
@@ -0,0 +1,90 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/prefetch/store/prefetch_store_test_util.h"
+
+#include "base/bind.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/offline_pages/core/prefetch/prefetch_types.h"
+#include "sql/connection.h"
+#include "sql/statement.h"
+#include "url/gurl.h"
+
+namespace offline_pages {
+namespace {
+
+int CountPrefetchItemsSync(sql::Connection* db) {
+  // Not starting transaction as this is a single read.
+  static const char kSql[] = "SELECT COUNT(offline_id) FROM prefetch_items";
+  sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
+  if (statement.Step())
+    return statement.ColumnInt(0);
+
+  return kStoreCommandFailed;
+}
+
+int UpdateItemsStateSync(const std::string& name_space,
+                         const std::string& url,
+                         PrefetchItemState state,
+                         sql::Connection* db) {
+  static const char kSql[] =
+      "UPDATE prefetch_items"
+      " SET state = ?"
+      " WHERE client_namespace = ? AND requested_url = ?";
+
+  sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
+  statement.BindInt(0, static_cast<int>(state));
+  statement.BindString(1, name_space);
+  statement.BindString(2, url);
+  if (statement.Run())
+    return db->GetLastChangeCount();
+
+  return kStoreCommandFailed;
+}
+
+}  // namespace
+
+PrefetchStoreTestUtil::PrefetchStoreTestUtil() {}
+
+PrefetchStoreTestUtil::~PrefetchStoreTestUtil() = default;
+
+void PrefetchStoreTestUtil::BuildStore() {
+  if (!temp_directory_.CreateUniqueTempDir())
+    DVLOG(1) << "temp_directory_ not created";
+
+  store_.reset(new PrefetchStore(base::ThreadTaskRunnerHandle::Get(),
+                                 temp_directory_.GetPath()));
+}
+
+void PrefetchStoreTestUtil::BuildStoreInMemory() {
+  store_.reset(new PrefetchStore(base::ThreadTaskRunnerHandle::Get()));
+}
+
+std::unique_ptr<PrefetchStore> PrefetchStoreTestUtil::ReleaseStore() {
+  return std::move(store_);
+}
+
+void PrefetchStoreTestUtil::DeleteStore() {
+  store_.reset();
+  if (temp_directory_.IsValid()) {
+    if (!temp_directory_.Delete())
+      DVLOG(1) << "temp_directory_ not created";
+  }
+}
+
+void PrefetchStoreTestUtil::CountPrefetchItems(
+    PrefetchStore::ResultCallback<int> result_callback) {
+  store_->Execute(base::BindOnce(&CountPrefetchItemsSync),
+                  std::move(result_callback));
+}
+
+void PrefetchStoreTestUtil::ZombifyPrefetchItem(
+    const std::string& name_space,
+    const GURL& url,
+    PrefetchStore::ResultCallback<int> result_callback) {
+  store_->Execute(base::BindOnce(&UpdateItemsStateSync, name_space, url.spec(),
+                                 PrefetchItemState::ZOMBIE),
+                  std::move(result_callback));
+}
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/prefetch/store/prefetch_store_test_util.h b/components/offline_pages/core/prefetch/store/prefetch_store_test_util.h
new file mode 100644
index 0000000..c330308a
--- /dev/null
+++ b/components/offline_pages/core/prefetch/store/prefetch_store_test_util.h
@@ -0,0 +1,54 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_STORE_PREFETCH_STORE_TEST_UTIL_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_STORE_PREFETCH_STORE_TEST_UTIL_H_
+
+#include <memory>
+
+#include "base/callback_forward.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/macros.h"
+#include "components/offline_pages/core/prefetch/store/prefetch_store.h"
+
+class GURL;
+
+namespace base {
+class ScopedTempDir;
+}  // namespace base
+
+namespace offline_pages {
+const int kStoreCommandFailed = -1;
+
+class PrefetchStoreTestUtil {
+ public:
+  PrefetchStoreTestUtil();
+  ~PrefetchStoreTestUtil();
+
+  void BuildStore();
+  void BuildStoreInMemory();
+
+  void CountPrefetchItems(PrefetchStore::ResultCallback<int> result_callback);
+
+  void ZombifyPrefetchItem(const std::string& name_space,
+                           const GURL& url,
+                           PrefetchStore::ResultCallback<int> reuslt_callback);
+
+  // Releases the ownership of currently controlled store.
+  std::unique_ptr<PrefetchStore> ReleaseStore();
+
+  void DeleteStore();
+
+  PrefetchStore* store() { return store_.get(); }
+
+ private:
+  base::ScopedTempDir temp_directory_;
+  std::unique_ptr<PrefetchStore> store_;
+
+  DISALLOW_COPY_AND_ASSIGN(PrefetchStoreTestUtil);
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_STORE_PREFETCH_STORE_TEST_UTIL_H_
diff --git a/components/offline_pages/core/prefetch/store/prefetch_store_unittest.cc b/components/offline_pages/core/prefetch/store/prefetch_store_unittest.cc
new file mode 100644
index 0000000..a463fce
--- /dev/null
+++ b/components/offline_pages/core/prefetch/store/prefetch_store_unittest.cc
@@ -0,0 +1,66 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/prefetch/store/prefetch_store.h"
+
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/offline_pages/core/prefetch/store/prefetch_store_test_util.h"
+#include "sql/connection.h"
+#include "sql/statement.h"
+#include "sql/transaction.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace offline_pages {
+namespace {
+
+int CountPrefetchItems(sql::Connection* db) {
+  // Not starting transaction as this is a single read.
+  static const char kSql[] = "SELECT COUNT(offline_id) FROM prefetch_items";
+  sql::Statement statement(db->GetUniqueStatement(kSql));
+  if (statement.Step())
+    return statement.ColumnInt(0);
+
+  return -1;
+}
+
+void CountPrefetchItemsResult(int expected_count, int actual_count) {
+  EXPECT_EQ(expected_count, actual_count);
+}
+
+}  // namespace
+
+class PrefetchStoreTest : public testing::Test {
+ public:
+  PrefetchStoreTest();
+  ~PrefetchStoreTest() override = default;
+
+  void SetUp() override { store_test_util_.BuildStoreInMemory(); }
+
+  void TearDown() override {
+    store_test_util_.DeleteStore();
+    PumpLoop();
+  }
+
+  PrefetchStore* store() { return store_test_util_.store(); }
+
+  void PumpLoop() { task_runner_->RunUntilIdle(); }
+
+ private:
+  PrefetchStoreTestUtil store_test_util_;
+  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+  base::ThreadTaskRunnerHandle task_runner_handle_;
+};
+
+PrefetchStoreTest::PrefetchStoreTest()
+    : task_runner_(new base::TestSimpleTaskRunner),
+      task_runner_handle_(task_runner_) {}
+
+TEST_F(PrefetchStoreTest, InitializeStore) {
+  store()->Execute<int>(base::BindOnce(&CountPrefetchItems),
+                        base::BindOnce(&CountPrefetchItemsResult, 0));
+  PumpLoop();
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/prefetch/store/prefetch_store_utils.cc b/components/offline_pages/core/prefetch/store/prefetch_store_utils.cc
new file mode 100644
index 0000000..e5c28d90
--- /dev/null
+++ b/components/offline_pages/core/prefetch/store/prefetch_store_utils.cc
@@ -0,0 +1,29 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/prefetch/store/prefetch_store_utils.h"
+
+#include <limits>
+
+#include "base/rand_util.h"
+#include "components/offline_pages/core/prefetch/prefetch_item.h"
+#include "sql/connection.h"
+#include "sql/statement.h"
+
+namespace offline_pages {
+
+int64_t GenerateOfflineId() {
+  return base::RandGenerator(std::numeric_limits<int64_t>::max()) + 1;
+}
+
+bool DeletePrefetchItemByOfflineIdSync(sql::Connection* db,
+                                       int64_t offline_id) {
+  DCHECK(db);
+  static const char kSql[] = "DELETE FROM prefetch_items WHERE offline_id=?";
+  sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
+  statement.BindInt64(0, offline_id);
+  return statement.Run();
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/prefetch/store/prefetch_store_utils.h b/components/offline_pages/core/prefetch/store/prefetch_store_utils.h
new file mode 100644
index 0000000..d47696b
--- /dev/null
+++ b/components/offline_pages/core/prefetch/store/prefetch_store_utils.h
@@ -0,0 +1,25 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_STORE_PREFETCH_STORE_UTILS_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_STORE_PREFETCH_STORE_UTILS_H_
+
+#include <stdint.h>
+
+namespace sql {
+class Connection;
+}  // namespace sql
+
+namespace offline_pages {
+
+// Creates an offline ID for the prefetch item.
+int64_t GenerateOfflineId();
+
+// Deletes a prefetch item by its offline ID. Returns whether it was the item
+// was successfully deleted.
+bool DeletePrefetchItemByOfflineIdSync(sql::Connection* db, int64_t offline_id);
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_STORE_PREFETCH_STORE_UTILS_H_
diff --git a/components/offline_pages/core/prefetch/suggested_articles_observer_unittest.cc b/components/offline_pages/core/prefetch/suggested_articles_observer_unittest.cc
index 5abea6c..a243c2e 100644
--- a/components/offline_pages/core/prefetch/suggested_articles_observer_unittest.cc
+++ b/components/offline_pages/core/prefetch/suggested_articles_observer_unittest.cc
@@ -36,17 +36,26 @@
  public:
   OfflinePageSuggestedArticlesObserverTest()
       : task_runner_(new base::TestSimpleTaskRunner),
-        task_runner_handle_(task_runner_) {
+        task_runner_handle_(task_runner_) {}
+
+  void SetUp() override {
+    prefetch_service_test_taco_ = base::MakeUnique<PrefetchServiceTestTaco>();
     test_prefetch_dispatcher_ = new TestPrefetchDispatcher();
-    prefetch_service_test_taco_.SetPrefetchDispatcher(
+    prefetch_service_test_taco_->SetPrefetchDispatcher(
         base::WrapUnique(test_prefetch_dispatcher_));
-    prefetch_service_test_taco_.SetSuggestedArticlesObserver(
+    prefetch_service_test_taco_->SetSuggestedArticlesObserver(
         base::MakeUnique<SuggestedArticlesObserver>());
-    prefetch_service_test_taco_.CreatePrefetchService();
+    prefetch_service_test_taco_->CreatePrefetchService();
+  }
+
+  void TearDown() override {
+    // Ensure the store can be properly disposed off.
+    prefetch_service_test_taco_.reset();
+    task_runner_->RunUntilIdle();
   }
 
   SuggestedArticlesObserver* observer() {
-    return prefetch_service_test_taco_.prefetch_service()
+    return prefetch_service_test_taco_->prefetch_service()
         ->GetSuggestedArticlesObserver();
   }
 
@@ -61,7 +70,7 @@
  private:
   scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
   base::ThreadTaskRunnerHandle task_runner_handle_;
-  PrefetchServiceTestTaco prefetch_service_test_taco_;
+  std::unique_ptr<PrefetchServiceTestTaco> prefetch_service_test_taco_;
 
   // Owned by the PrefetchServiceTestTaco.
   TestPrefetchDispatcher* test_prefetch_dispatcher_;
diff --git a/components/payments/content/payment_request.cc b/components/payments/content/payment_request.cc
index a4717e2..cf0c0718 100644
--- a/components/payments/content/payment_request.cc
+++ b/components/payments/content/payment_request.cc
@@ -85,7 +85,7 @@
         delegate_->GetApplicationLocale());
     state_ = base::MakeUnique<PaymentRequestState>(
         spec_.get(), this, delegate_->GetApplicationLocale(),
-        delegate_->GetPersonalDataManager(), delegate_.get());
+        delegate_->GetPersonalDataManager(), delegate_.get(), &journey_logger_);
     return;
   }
 
@@ -107,7 +107,7 @@
       delegate_->GetApplicationLocale());
   state_ = base::MakeUnique<PaymentRequestState>(
       spec_.get(), this, delegate_->GetApplicationLocale(),
-      delegate_->GetPersonalDataManager(), delegate_.get());
+      delegate_->GetPersonalDataManager(), delegate_.get(), &journey_logger_);
 }
 
 void PaymentRequest::Show() {
diff --git a/components/payments/content/payment_request_state.cc b/components/payments/content/payment_request_state.cc
index 740a4d6..1fefb16 100644
--- a/components/payments/content/payment_request_state.cc
+++ b/components/payments/content/payment_request_state.cc
@@ -16,6 +16,7 @@
 #include "components/autofill/core/browser/personal_data_manager.h"
 #include "components/payments/content/payment_response_helper.h"
 #include "components/payments/core/autofill_payment_instrument.h"
+#include "components/payments/core/journey_logger.h"
 #include "components/payments/core/payment_instrument.h"
 #include "components/payments/core/payment_request_data_util.h"
 #include "components/payments/core/payment_request_delegate.h"
@@ -27,13 +28,15 @@
     Delegate* delegate,
     const std::string& app_locale,
     autofill::PersonalDataManager* personal_data_manager,
-    PaymentRequestDelegate* payment_request_delegate)
+    PaymentRequestDelegate* payment_request_delegate,
+    JourneyLogger* journey_logger)
     : is_ready_to_pay_(false),
       is_waiting_for_merchant_validation_(false),
       app_locale_(app_locale),
       spec_(spec),
       delegate_(delegate),
       personal_data_manager_(personal_data_manager),
+      journey_logger_(journey_logger),
       selected_shipping_profile_(nullptr),
       selected_shipping_option_error_profile_(nullptr),
       selected_contact_profile_(nullptr),
@@ -272,12 +275,29 @@
   shipping_profiles_ = profile_comparator()->FilterProfilesForShipping(
       raw_profiles_for_filtering);
 
+  // Set the number of suggestions shown for the sections requested by the
+  // merchant.
+  if (spec_->request_payer_name() || spec_->request_payer_phone() ||
+      spec_->request_payer_email()) {
+    journey_logger_->SetNumberOfSuggestionsShown(
+        JourneyLogger::Section::SECTION_CONTACT_INFO, contact_profiles_.size());
+  }
+  if (spec_->request_shipping()) {
+    journey_logger_->SetNumberOfSuggestionsShown(
+        JourneyLogger::Section::SECTION_SHIPPING_ADDRESS,
+        shipping_profiles_.size());
+  }
+
   // Create the list of available instruments. A copy of each card will be made
   // by their respective AutofillPaymentInstrument.
   const std::vector<autofill::CreditCard*>& cards =
       personal_data_manager_->GetCreditCardsToSuggest();
   for (autofill::CreditCard* card : cards)
     AddAutofillPaymentInstrument(/*selected=*/false, *card);
+
+  journey_logger_->SetNumberOfSuggestionsShown(
+      JourneyLogger::Section::SECTION_CREDIT_CARDS,
+      available_instruments().size());
 }
 
 void PaymentRequestState::SetDefaultProfileSelections() {
diff --git a/components/payments/content/payment_request_state.h b/components/payments/content/payment_request_state.h
index 19fcb35..cf25e01 100644
--- a/components/payments/content/payment_request_state.h
+++ b/components/payments/content/payment_request_state.h
@@ -26,6 +26,7 @@
 
 namespace payments {
 
+class JourneyLogger;
 class PaymentInstrument;
 class PaymentRequestDelegate;
 
@@ -70,7 +71,8 @@
                       Delegate* delegate,
                       const std::string& app_locale,
                       autofill::PersonalDataManager* personal_data_manager,
-                      PaymentRequestDelegate* payment_request_delegate);
+                      PaymentRequestDelegate* payment_request_delegate,
+                      JourneyLogger* journey_logger);
   ~PaymentRequestState() override;
 
   // PaymentResponseHelper::Delegate
@@ -215,10 +217,11 @@
 
   const std::string app_locale_;
 
-  // Not owned. Never null. Both outlive this object.
+  // Not owned. Never null. Will outlive this object.
   PaymentRequestSpec* spec_;
   Delegate* delegate_;
   autofill::PersonalDataManager* personal_data_manager_;
+  JourneyLogger* journey_logger_;
 
   autofill::AutofillProfile* selected_shipping_profile_;
   autofill::AutofillProfile* selected_shipping_option_error_profile_;
diff --git a/components/payments/content/payment_request_state_unittest.cc b/components/payments/content/payment_request_state_unittest.cc
index bb6c602..0e0a014 100644
--- a/components/payments/content/payment_request_state_unittest.cc
+++ b/components/payments/content/payment_request_state_unittest.cc
@@ -13,6 +13,7 @@
 #include "components/autofill/core/browser/credit_card.h"
 #include "components/autofill/core/browser/test_personal_data_manager.h"
 #include "components/payments/content/payment_request_spec.h"
+#include "components/payments/core/journey_logger.h"
 #include "components/payments/core/test_payment_request_delegate.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/WebKit/public/platform/modules/payments/payment_request.mojom.h"
@@ -26,6 +27,9 @@
   PaymentRequestStateTest()
       : num_on_selected_information_changed_called_(0),
         test_payment_request_delegate_(/*personal_data_manager=*/nullptr),
+        journey_logger_(test_payment_request_delegate_.IsIncognito(),
+                        GURL("http://www.test.com"),
+                        test_payment_request_delegate_.GetUkmRecorder()),
         address_(autofill::test::GetFullProfile()),
         credit_card_visa_(autofill::test::GetCreditCard()) {
     test_personal_data_manager_.AddTestingProfile(&address_);
@@ -59,7 +63,7 @@
         /*observer=*/nullptr, "en-US");
     state_ = base::MakeUnique<PaymentRequestState>(
         spec_.get(), this, "en-US", &test_personal_data_manager_,
-        &test_payment_request_delegate_);
+        &test_payment_request_delegate_, &journey_logger_);
     state_->AddObserver(this);
   }
 
@@ -115,6 +119,7 @@
   mojom::PaymentAddressPtr selected_shipping_address_;
   autofill::TestPersonalDataManager test_personal_data_manager_;
   TestPaymentRequestDelegate test_payment_request_delegate_;
+  JourneyLogger journey_logger_;
 
   // Test data.
   autofill::AutofillProfile address_;
diff --git a/components/payments/core/journey_logger.cc b/components/payments/core/journey_logger.cc
index c2170ac..bb98e356 100644
--- a/components/payments/core/journey_logger.cc
+++ b/components/payments/core/journey_logger.cc
@@ -164,18 +164,15 @@
   has_recorded_ = true;
 
   RecordCheckoutFlowMetrics();
-
-  RecordPaymentMethodMetric();
-
-  RecordRequestedInformationMetrics();
-
-  RecordSectionSpecificStats(completion_status);
-
-  // Record the CanMakePayment metrics based on whether the transaction was
-  // completed or aborted by the user (UserAborted) or otherwise (OtherAborted).
   RecordCanMakePaymentStats(completion_status);
-
   RecordUrlKeyedMetrics(completion_status);
+
+  // These following metrics only make sense if the UI was shown to the user.
+  if (was_show_called_) {
+    RecordPaymentMethodMetric();
+    RecordRequestedInformationMetrics();
+    RecordSectionSpecificStats(completion_status);
+  }
 }
 
 void JourneyLogger::RecordCheckoutFlowMetrics() {
@@ -201,10 +198,6 @@
 }
 
 void JourneyLogger::RecordRequestedInformationMetrics() {
-  if (!was_show_called_) {
-    return;
-  }
-
   DCHECK(requested_information_ != REQUESTED_INFORMATION_MAX);
   UMA_HISTOGRAM_ENUMERATION("PaymentRequest.RequestedInformation",
                             requested_information_, REQUESTED_INFORMATION_MAX);
diff --git a/components/payments/core/journey_logger_unittest.cc b/components/payments/core/journey_logger_unittest.cc
index 26211be..2fd288e 100644
--- a/components/payments/core/journey_logger_unittest.cc
+++ b/components/payments/core/journey_logger_unittest.cc
@@ -439,9 +439,17 @@
   JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
                        /*ukm_recorder=*/nullptr);
 
+  // The merchant only requests payment information.
+  logger.SetRequestedInformation(
+      /*requested_shipping=*/false, /*requested_email=*/false,
+      /*requested_phone=*/false, /*requested_name=*/false);
+
   // Simulate that the user had suggestions for all the requested sections.
   logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_CREDIT_CARDS, 1);
 
+  // Simulate that the Payment Request was shown to the user.
+  logger.SetShowCalled();
+
   // Simulate that the user completes the checkout.
   logger.SetCompleted();
 
@@ -463,9 +471,17 @@
   JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
                        /*ukm_recorder=*/nullptr);
 
+  // The merchant only requests payment information.
+  logger.SetRequestedInformation(
+      /*requested_shipping=*/false, /*requested_email=*/false,
+      /*requested_phone=*/false, /*requested_name=*/false);
+
   // Simulate that the user had suggestions for all the requested sections.
   logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_CREDIT_CARDS, 1);
 
+  // Simulate that the Payment Request was shown to the user.
+  logger.SetShowCalled();
+
   // Simulate that the user aborts the checkout.
   logger.SetAborted(JourneyLogger::ABORT_REASON_ABORTED_BY_USER);
 
@@ -487,9 +503,17 @@
   JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
                        /*ukm_recorder=*/nullptr);
 
+  // The merchant only requests payment information.
+  logger.SetRequestedInformation(
+      /*requested_shipping=*/false, /*requested_email=*/false,
+      /*requested_phone=*/false, /*requested_name=*/false);
+
   // Simulate that the user had suggestions for all the requested sections.
   logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_CREDIT_CARDS, 1);
 
+  // Simulate that the Payment Request was shown to the user.
+  logger.SetShowCalled();
+
   // Simulate that the checkout is aborted.
   logger.SetAborted(JourneyLogger::ABORT_REASON_OTHER);
 
@@ -512,9 +536,17 @@
   JourneyLogger logger(/*is_incognito=*/true, /*url=*/GURL(""),
                        /*ukm_recorder=*/nullptr);
 
+  // The merchant only requests payment information.
+  logger.SetRequestedInformation(
+      /*requested_shipping=*/false, /*requested_email=*/false,
+      /*requested_phone=*/false, /*requested_name=*/false);
+
   // Simulate that the user had suggestions for all the requested sections.
   logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_CREDIT_CARDS, 1);
 
+  // Simulate that the Payment Request was shown to the user.
+  logger.SetShowCalled();
+
   // Simulate that the user completes the checkout.
   logger.SetCompleted();
 
@@ -536,9 +568,17 @@
   JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
                        /*ukm_recorder=*/nullptr);
 
-  // Simulate that the user had suggestions for all the requested sections.
+  // The merchant only requests payment information.
+  logger.SetRequestedInformation(
+      /*requested_shipping=*/false, /*requested_email=*/false,
+      /*requested_phone=*/false, /*requested_name=*/false);
+
+  // Simulate that the user had suggestions for none of the requested sections.
   logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_CREDIT_CARDS, 0);
 
+  // Simulate that the Payment Request was shown to the user.
+  logger.SetShowCalled();
+
   // Simulate that the user completes the checkout.
   logger.SetCompleted();
 
@@ -561,9 +601,17 @@
   JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
                        /*ukm_recorder=*/nullptr);
 
-  // Simulate that the user had suggestions for all the requested sections.
+  // The merchant only requests payment information.
+  logger.SetRequestedInformation(
+      /*requested_shipping=*/false, /*requested_email=*/false,
+      /*requested_phone=*/false, /*requested_name=*/false);
+
+  // Simulate that the user had suggestions for none of the requested sections.
   logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_CREDIT_CARDS, 0);
 
+  // Simulate that the Payment Request was shown to the user.
+  logger.SetShowCalled();
+
   // Simulate that the user aborts the checkout.
   logger.SetAborted(JourneyLogger::ABORT_REASON_ABORTED_BY_USER);
 
@@ -586,9 +634,17 @@
   JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
                        /*ukm_recorder=*/nullptr);
 
-  // Simulate that the user had suggestions for all the requested sections.
+  // The merchant only requests payment information.
+  logger.SetRequestedInformation(
+      /*requested_shipping=*/false, /*requested_email=*/false,
+      /*requested_phone=*/false, /*requested_name=*/false);
+
+  // Simulate that the user had suggestions for none of the requested sections.
   logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_CREDIT_CARDS, 0);
 
+  // Simulate that the Payment Request was shown to the user.
+  logger.SetShowCalled();
+
   // Simulate that the the checkout is aborted.
   logger.SetAborted(JourneyLogger::ABORT_REASON_OTHER);
 
@@ -612,9 +668,17 @@
   JourneyLogger logger(/*is_incognito=*/true, /*url=*/GURL(""),
                        /*ukm_recorder=*/nullptr);
 
-  // Simulate that the user had suggestions for all the requested sections.
+  // The merchant only requests payment information.
+  logger.SetRequestedInformation(
+      /*requested_shipping=*/false, /*requested_email=*/false,
+      /*requested_phone=*/false, /*requested_name=*/false);
+
+  // Simulate that the user had suggestions for none of the requested sections.
   logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_CREDIT_CARDS, 0);
 
+  // Simulate that the Payment Request was shown to the user.
+  logger.SetShowCalled();
+
   // Simulate that the user aborts the checkout.
   logger.SetAborted(JourneyLogger::ABORT_REASON_ABORTED_BY_USER);
 
@@ -640,9 +704,13 @@
 
   // Make the two loggers have different data.
   logger1.SetShowCalled();
-  logger1.SetRequestedInformation(true, true, false, false);
+  logger1.SetRequestedInformation(
+      /*requested_shipping=*/true, /*requested_email=*/true,
+      /*requested_phone=*/false, /*requested_name=*/false);
   logger2.SetShowCalled();
-  logger2.SetRequestedInformation(true, false, false, false);
+  logger2.SetRequestedInformation(
+      /*requested_shipping=*/true, /*requested_email=*/false,
+      /*requested_phone=*/false, /*requested_name=*/false);
 
   logger1.SetCanMakePaymentValue(true);
 
diff --git a/components/subresource_filter/core/common/indexed_ruleset.cc b/components/subresource_filter/core/common/indexed_ruleset.cc
index d24fde53..009258e7 100644
--- a/components/subresource_filter/core/common/indexed_ruleset.cc
+++ b/components/subresource_filter/core/common/indexed_ruleset.cc
@@ -16,7 +16,7 @@
 // RulesetIndexer --------------------------------------------------------------
 
 // static
-const int RulesetIndexer::kIndexedFormatVersion = 17;
+const int RulesetIndexer::kIndexedFormatVersion = 18;
 
 RulesetIndexer::RulesetIndexer()
     : blacklist_(&builder_), whitelist_(&builder_), deactivation_(&builder_) {}
diff --git a/components/url_pattern_index/flat/url_pattern_index.fbs b/components/url_pattern_index/flat/url_pattern_index.fbs
index 3a3cc7d..cb5d305a 100644
--- a/components/url_pattern_index/flat/url_pattern_index.fbs
+++ b/components/url_pattern_index/flat/url_pattern_index.fbs
@@ -28,19 +28,46 @@
   IS_MATCH_CASE,
 }
 
+// The options controlling whether or not to activate filtering for subresources
+// of documents that match the URL pattern of the rule.
+// Corresponds to url_pattern_index::proto::ActivationType.
+enum ActivationType : ubyte (bit_flags) {
+  DOCUMENT,       // Disable all rules on the page.
+  GENERIC_BLOCK,  // Disable generic URL rules on the page.
+}
+
+// The types of subresource requests that a URL rule should be applied to.
+// Corresponds to url_pattern_index::proto::ElementType.
+enum ElementType : ushort (bit_flags) {
+  OTHER,
+  SCRIPT,
+  IMAGE,
+  STYLESHEET,
+  OBJECT,
+  XMLHTTPREQUEST,
+  // TODO(crbug.com/713774): Remove OBJECT_SUBREQUEST type once
+  // url_pattern_index no longer has a dependency on proto::UrlRule.
+  OBJECT_SUBREQUEST,
+  SUBDOCUMENT,
+  PING,
+  MEDIA,
+  FONT,
+  WEBSOCKET,
+  // Note: Update the default value for |element_types| field in UrlRule, on
+  // adding/removing values from this enum.
+}
+
 // The flat representation of a single URL rule. For more details regarding the
 // fields please see the comments to url_pattern_index::proto::UrlRule.
 table UrlRule {
   // Rule matching options, a bitmask consisting of OptionFlags.
   options : ubyte;
 
-  // A bitmask of element types, same as proto::UrlRule::element_types. Enables
-  // all element types except POPUP by default.
-  // Note: Keep it equal to ELEMENT_TYPE_ALL-ELEMENT_TYPE_POPUP for compactness.
-  element_types : ushort = 6143;
+  // A bitmask of ElementType. Equals ElementType_ANY by default for
+  // compactness.
+  element_types : ushort = 4095;
 
-  // A bitmask of activation types, same as proto::UrlRule::activation_types.
-  // Disables all activation types by default.
+  // A bitmask of ActivationType. Disables all activation types by default.
   activation_types : ubyte = 0;
 
   // Use SUBSTRING as default, since it's the most used pattern type. Same as
@@ -52,6 +79,8 @@
   anchor_right : AnchorType = NONE;
 
   // The list of domains to be included/excluded from the filter's affected set.
+  // Kept sorted for fast matching. Should either be null or have at least a
+  // single element.
   domains_included : [string];
   domains_excluded : [string];
 
diff --git a/components/url_pattern_index/url_pattern_index.cc b/components/url_pattern_index/url_pattern_index.cc
index c18091a..f0586d0 100644
--- a/components/url_pattern_index/url_pattern_index.cc
+++ b/components/url_pattern_index/url_pattern_index.cc
@@ -8,6 +8,7 @@
 #include <limits>
 #include <string>
 
+#include "base/containers/flat_map.h"
 #include "base/logging.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/strings/string_piece.h"
@@ -25,6 +26,55 @@
 using FlatDomains = flatbuffers::Vector<FlatStringOffset>;
 using FlatDomainsOffset = flatbuffers::Offset<FlatDomains>;
 
+// Maps proto::ActivationType to flat::ActivationType.
+const base::flat_map<proto::ActivationType, flat::ActivationType>
+    kActivationTypeMap(
+        {
+            {proto::ACTIVATION_TYPE_UNSPECIFIED, flat::ActivationType_NONE},
+            {proto::ACTIVATION_TYPE_DOCUMENT, flat::ActivationType_DOCUMENT},
+            // ELEMHIDE is not supported.
+            {proto::ACTIVATION_TYPE_ELEMHIDE, flat::ActivationType_NONE},
+            // GENERICHIDE is not supported.
+            {proto::ACTIVATION_TYPE_GENERICHIDE, flat::ActivationType_NONE},
+            {proto::ACTIVATION_TYPE_GENERICBLOCK,
+             flat::ActivationType_GENERIC_BLOCK},
+        },
+        base::KEEP_FIRST_OF_DUPES);
+
+// Maps proto::ElementType to flat::ElementType.
+const base::flat_map<proto::ElementType, flat::ElementType> kElementTypeMap(
+    {
+        {proto::ELEMENT_TYPE_UNSPECIFIED, flat::ElementType_NONE},
+        {proto::ELEMENT_TYPE_OTHER, flat::ElementType_OTHER},
+        {proto::ELEMENT_TYPE_SCRIPT, flat::ElementType_SCRIPT},
+        {proto::ELEMENT_TYPE_IMAGE, flat::ElementType_IMAGE},
+        {proto::ELEMENT_TYPE_STYLESHEET, flat::ElementType_STYLESHEET},
+        {proto::ELEMENT_TYPE_OBJECT, flat::ElementType_OBJECT},
+        {proto::ELEMENT_TYPE_XMLHTTPREQUEST, flat::ElementType_XMLHTTPREQUEST},
+        {proto::ELEMENT_TYPE_OBJECT_SUBREQUEST,
+         flat::ElementType_OBJECT_SUBREQUEST},
+        {proto::ELEMENT_TYPE_SUBDOCUMENT, flat::ElementType_SUBDOCUMENT},
+        {proto::ELEMENT_TYPE_PING, flat::ElementType_PING},
+        {proto::ELEMENT_TYPE_MEDIA, flat::ElementType_MEDIA},
+        {proto::ELEMENT_TYPE_FONT, flat::ElementType_FONT},
+        // Filtering popups is not supported.
+        {proto::ELEMENT_TYPE_POPUP, flat::ElementType_NONE},
+        {proto::ELEMENT_TYPE_WEBSOCKET, flat::ElementType_WEBSOCKET},
+    },
+    base::KEEP_FIRST_OF_DUPES);
+
+flat::ActivationType ProtoToFlatActivationType(proto::ActivationType type) {
+  const auto it = kActivationTypeMap.find(type);
+  DCHECK(it != kActivationTypeMap.end());
+  return it->second;
+}
+
+flat::ElementType ProtoToFlatElementType(proto::ElementType type) {
+  const auto it = kElementTypeMap.find(type);
+  DCHECK(it != kElementTypeMap.end());
+  return it->second;
+}
+
 base::StringPiece ToStringPiece(const flatbuffers::String* string) {
   DCHECK(string);
   return base::StringPiece(string->c_str(), string->size());
@@ -43,6 +93,15 @@
                       [](char c) { return base::IsAsciiUpper(c); });
 }
 
+// Returns a bitmask of all the keys of the |map| passed.
+template <typename T>
+int GetKeysMask(const T& map) {
+  int mask = 0;
+  for (const auto& pair : map)
+    mask |= pair.first;
+  return mask;
+}
+
 // Checks whether a URL |rule| can be converted to its FlatBuffers equivalent,
 // and performs the actual conversion.
 class UrlRuleFlatBufferConverter {
@@ -142,6 +201,9 @@
   }
 
   bool InitializeOptions() {
+    static_assert(flat::OptionFlag_ANY <= std::numeric_limits<uint8_t>::max(),
+                  "Option flags can not be stored in uint8_t.");
+
     if (rule_.semantics() == proto::RULE_SEMANTICS_WHITELIST) {
       options_ |= flat::OptionFlag_IS_WHITELIST;
     } else if (rule_.semantics() != proto::RULE_SEMANTICS_BLACKLIST) {
@@ -170,33 +232,41 @@
   }
 
   bool InitializeElementTypes() {
-    static_assert(
-        proto::ELEMENT_TYPE_ALL <= std::numeric_limits<uint16_t>::max(),
-        "Element types can not be stored in uint16_t.");
-    element_types_ = static_cast<uint16_t>(rule_.element_types());
+    static_assert(flat::ElementType_ANY <= std::numeric_limits<uint16_t>::max(),
+                  "Element types can not be stored in uint16_t.");
 
-    // Note: Normally we can not distinguish between the main plugin resource
-    // and any other loads it makes. We treat them both as OBJECT requests.
-    if (element_types_ & proto::ELEMENT_TYPE_OBJECT_SUBREQUEST)
-      element_types_ |= proto::ELEMENT_TYPE_OBJECT;
+    // Ensure all proto::ElementType(s) are mapped in |kElementTypeMap|.
+    DCHECK_EQ(proto::ELEMENT_TYPE_ALL, GetKeysMask(kElementTypeMap));
 
-    // Ignore unknown element types.
-    element_types_ &= proto::ELEMENT_TYPE_ALL;
-    // Filtering popups is not supported.
-    element_types_ &= ~proto::ELEMENT_TYPE_POPUP;
+    element_types_ = flat::ElementType_NONE;
+
+    for (const auto& pair : kElementTypeMap)
+      if (rule_.element_types() & pair.first)
+        element_types_ |= pair.second;
+
+    // Normally we can not distinguish between the main plugin resource and any
+    // other loads it makes. We treat them both as OBJECT requests. Hence an
+    // OBJECT request would also match OBJECT_SUBREQUEST rules, but not the
+    // the other way round.
+    if (element_types_ & flat::ElementType_OBJECT_SUBREQUEST)
+      element_types_ |= flat::ElementType_OBJECT;
 
     return true;
   }
 
   bool InitializeActivationTypes() {
     static_assert(
-        proto::ACTIVATION_TYPE_ALL <= std::numeric_limits<uint8_t>::max(),
+        flat::ActivationType_ANY <= std::numeric_limits<uint8_t>::max(),
         "Activation types can not be stored in uint8_t.");
-    activation_types_ = static_cast<uint8_t>(rule_.activation_types());
 
-    // Only the following activation types are supported, ignore the others.
-    activation_types_ &=
-        proto::ACTIVATION_TYPE_DOCUMENT | proto::ACTIVATION_TYPE_GENERICBLOCK;
+    // Ensure all proto::ActivationType(s) are mapped in |kActivationTypeMap|.
+    DCHECK_EQ(proto::ACTIVATION_TYPE_ALL, GetKeysMask(kActivationTypeMap));
+
+    activation_types_ = flat::ActivationType_NONE;
+
+    for (const auto& pair : kActivationTypeMap)
+      if (rule_.activation_types() & pair.first)
+        activation_types_ |= pair.second;
 
     return true;
   }
@@ -430,21 +500,21 @@
 
 // Returns whether the request matches flags of the specified URL |rule|. Takes
 // into account:
-//  - |element_type| of the requested resource, if not *_UNSPECIFIED.
-//  - |activation_type| for a subdocument request, if not *_UNSPECIFIED.
+//  - |element_type| of the requested resource, if not *_NONE.
+//  - |activation_type| for a subdocument request, if not *_NONE.
 //  - Whether the resource |is_third_party| w.r.t. its embedding document.
 bool DoesRuleFlagsMatch(const flat::UrlRule& rule,
-                        proto::ElementType element_type,
-                        proto::ActivationType activation_type,
+                        flat::ElementType element_type,
+                        flat::ActivationType activation_type,
                         bool is_third_party) {
-  DCHECK((element_type == proto::ELEMENT_TYPE_UNSPECIFIED) !=
-         (activation_type == proto::ACTIVATION_TYPE_UNSPECIFIED));
+  DCHECK((element_type == flat::ElementType_NONE) !=
+         (activation_type == flat::ActivationType_NONE));
 
-  if (element_type != proto::ELEMENT_TYPE_UNSPECIFIED &&
+  if (element_type != flat::ElementType_NONE &&
       !(rule.element_types() & element_type)) {
     return false;
   }
-  if (activation_type != proto::ACTIVATION_TYPE_UNSPECIFIED &&
+  if (activation_type != flat::ActivationType_NONE &&
       !(rule.activation_types() & activation_type)) {
     return false;
   }
@@ -465,8 +535,8 @@
     const FlatUrlRuleList* candidates,
     const GURL& url,
     const url::Origin& document_origin,
-    proto::ElementType element_type,
-    proto::ActivationType activation_type,
+    flat::ElementType element_type,
+    flat::ActivationType activation_type,
     bool is_third_party,
     bool disable_generic_rules) {
   if (!candidates)
@@ -497,8 +567,8 @@
     const flat::UrlPatternIndex& index,
     const GURL& url,
     const url::Origin& document_origin,
-    proto::ElementType element_type,
-    proto::ActivationType activation_type,
+    flat::ElementType element_type,
+    flat::ActivationType activation_type,
     bool is_third_party,
     bool disable_generic_rules) {
   const FlatNGramIndex* hash_table = index.ngram_index();
@@ -552,10 +622,23 @@
     proto::ActivationType activation_type,
     bool is_third_party,
     bool disable_generic_rules) const {
+  return FindMatch(url, first_party_origin,
+                   ProtoToFlatElementType(element_type),
+                   ProtoToFlatActivationType(activation_type), is_third_party,
+                   disable_generic_rules);
+}
+
+const flat::UrlRule* UrlPatternIndexMatcher::FindMatch(
+    const GURL& url,
+    const url::Origin& first_party_origin,
+    flat::ElementType element_type,
+    flat::ActivationType activation_type,
+    bool is_third_party,
+    bool disable_generic_rules) const {
   if (!flat_index_ || !url.is_valid())
     return nullptr;
-  if ((element_type == proto::ELEMENT_TYPE_UNSPECIFIED) ==
-      (activation_type == proto::ACTIVATION_TYPE_UNSPECIFIED)) {
+  if ((element_type == flat::ElementType_NONE) ==
+      (activation_type == flat::ActivationType_NONE)) {
     return nullptr;
   }
 
diff --git a/components/url_pattern_index/url_pattern_index.h b/components/url_pattern_index/url_pattern_index.h
index 22e84b1..8f76188 100644
--- a/components/url_pattern_index/url_pattern_index.h
+++ b/components/url_pattern_index/url_pattern_index.h
@@ -124,6 +124,16 @@
                                  bool disable_generic_rules) const;
 
  private:
+  // Helper function to work with flat::*Type(s). If the index contains one or
+  // more UrlRules that match the request, returns one of them (it is undefined
+  // which one). Otherwise, returns nullptr.
+  const flat::UrlRule* FindMatch(const GURL& url,
+                                 const url::Origin& first_party_origin,
+                                 flat::ElementType element_type,
+                                 flat::ActivationType activation_type,
+                                 bool is_third_party,
+                                 bool disable_generic_rules) const;
+
   // Must outlive this instance.
   const flat::UrlPatternIndex* flat_index_;
 
diff --git a/components/wallpaper/BUILD.gn b/components/wallpaper/BUILD.gn
index 15ae1dfa..5bd8026d 100644
--- a/components/wallpaper/BUILD.gn
+++ b/components/wallpaper/BUILD.gn
@@ -8,6 +8,7 @@
     "wallpaper_color_calculator.h",
     "wallpaper_color_calculator_observer.h",
     "wallpaper_color_extraction_result.h",
+    "wallpaper_color_profile.h",
     "wallpaper_files_id.cc",
     "wallpaper_files_id.h",
     "wallpaper_layout.h",
diff --git a/components/wallpaper/wallpaper_color_profile.h b/components/wallpaper/wallpaper_color_profile.h
new file mode 100644
index 0000000..3112eec
--- /dev/null
+++ b/components/wallpaper/wallpaper_color_profile.h
@@ -0,0 +1,25 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_WALLPAPER_WALLPAPER_COLOR_PROFILE_H_
+#define COMPONENTS_WALLPAPER_WALLPAPER_COLOR_PROFILE_H_
+
+namespace wallpaper {
+
+// The color profile type, ordered as the color profiles applied in
+// ash::WallpaperController.
+enum class ColorProfileType {
+  DARK_VIBRANT = 0,
+  NORMAL_VIBRANT,
+  LIGHT_VIBRANT,
+  DARK_MUTED,
+  NORMAL_MUTED,
+  LIGHT_MUTED,
+
+  NUM_OF_COLOR_PROFILES,
+};
+
+}  // namespace wallpaper
+
+#endif  // COMPONENTS_WALLPAPER_WALLPAPER_COLOR_PROFILE_H_
diff --git a/content/browser/appcache/appcache_subresource_url_factory.cc b/content/browser/appcache/appcache_subresource_url_factory.cc
index 70f002df..e971ba6 100644
--- a/content/browser/appcache/appcache_subresource_url_factory.cc
+++ b/content/browser/appcache/appcache_subresource_url_factory.cc
@@ -10,9 +10,9 @@
 #include "content/browser/appcache/appcache_url_loader_job.h"
 #include "content/browser/appcache/appcache_url_loader_request.h"
 #include "content/browser/url_loader_factory_getter.h"
-#include "content/common/url_loader_factory.mojom.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/common/resource_request.h"
+#include "content/public/common/url_loader_factory.mojom.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "mojo/public/cpp/bindings/interface_ptr.h"
diff --git a/content/browser/appcache/appcache_subresource_url_factory.h b/content/browser/appcache/appcache_subresource_url_factory.h
index f55afc47..a5620cf 100644
--- a/content/browser/appcache/appcache_subresource_url_factory.h
+++ b/content/browser/appcache/appcache_subresource_url_factory.h
@@ -6,7 +6,7 @@
 #define CONTENT_BROWSER_APPCACHE_APPCACHE_SUBRESOURCE_URL_FACTORY_H_
 
 #include "base/memory/ref_counted.h"
-#include "content/common/url_loader_factory.mojom.h"
+#include "content/public/common/url_loader_factory.mojom.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "url/gurl.h"
diff --git a/content/browser/appcache/appcache_url_loader_job.h b/content/browser/appcache/appcache_url_loader_job.h
index f3470cb5d..42615a68 100644
--- a/content/browser/appcache/appcache_url_loader_job.h
+++ b/content/browser/appcache/appcache_url_loader_job.h
@@ -16,8 +16,8 @@
 #include "content/browser/appcache/appcache_storage.h"
 #include "content/browser/loader/url_loader_request_handler.h"
 #include "content/common/content_export.h"
-#include "content/common/url_loader.mojom.h"
 #include "content/public/common/resource_request.h"
+#include "content/public/common/url_loader.mojom.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "mojo/public/cpp/system/data_pipe.h"
 
diff --git a/content/browser/blob_storage/blob_internals_url_loader.h b/content/browser/blob_storage/blob_internals_url_loader.h
index 4dc9ef2..858584d9 100644
--- a/content/browser/blob_storage/blob_internals_url_loader.h
+++ b/content/browser/blob_storage/blob_internals_url_loader.h
@@ -5,7 +5,7 @@
 #ifndef CONTENT_BROWSER_BLOB_STORAGE_BLOB_INTERNALS_URL_LOADER_H_
 #define CONTENT_BROWSER_BLOB_STORAGE_BLOB_INTERNALS_URL_LOADER_H_
 
-#include "content/common/url_loader.mojom.h"
+#include "content/public/common/url_loader.mojom.h"
 
 namespace content {
 class ChromeBlobStorageContext;
diff --git a/content/browser/blob_storage/blob_url_loader_factory.cc b/content/browser/blob_storage/blob_url_loader_factory.cc
index dbb2883..f9c2da3 100644
--- a/content/browser/blob_storage/blob_url_loader_factory.cc
+++ b/content/browser/blob_storage/blob_url_loader_factory.cc
@@ -13,8 +13,8 @@
 #include "content/browser/blob_storage/chrome_blob_storage_context.h"
 #include "content/browser/storage_partition_impl.h"
 #include "content/common/net_adapters.h"
-#include "content/common/url_loader.mojom.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/common/url_loader.mojom.h"
 #include "mojo/public/cpp/system/simple_watcher.h"
 #include "net/base/io_buffer.h"
 #include "net/http/http_byte_range.h"
diff --git a/content/browser/blob_storage/blob_url_loader_factory.h b/content/browser/blob_storage/blob_url_loader_factory.h
index 9f01143..a58effb3 100644
--- a/content/browser/blob_storage/blob_url_loader_factory.h
+++ b/content/browser/blob_storage/blob_url_loader_factory.h
@@ -9,8 +9,8 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "content/common/content_export.h"
-#include "content/common/url_loader_factory.mojom.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/common/url_loader_factory.mojom.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
 
 namespace storage {
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index 20f9660d..cfda90f4 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -926,6 +926,11 @@
 #endif
 }
 
+gpu::GpuChannelEstablishFactory*
+BrowserMainLoop::gpu_channel_establish_factory() const {
+  return BrowserGpuChannelHostFactory::instance();
+}
+
 #if defined(OS_ANDROID)
 void BrowserMainLoop::SynchronouslyFlushStartupTasks() {
   startup_task_runner_->RunAllTasksNow();
diff --git a/content/browser/browser_main_loop.h b/content/browser/browser_main_loop.h
index f771cf9..7b72970 100644
--- a/content/browser/browser_main_loop.h
+++ b/content/browser/browser_main_loop.h
@@ -43,6 +43,10 @@
 class DiscardableSharedMemoryManager;
 }
 
+namespace gpu {
+class GpuChannelEstablishFactory;
+}
+
 namespace media {
 class AudioManager;
 class AudioSystem;
@@ -170,6 +174,8 @@
     return startup_trace_file_;
   }
 
+  gpu::GpuChannelEstablishFactory* gpu_channel_establish_factory() const;
+
 #if defined(OS_ANDROID)
   void SynchronouslyFlushStartupTasks();
 #endif  // OS_ANDROID
diff --git a/content/browser/devtools/protocol/network_handler.cc b/content/browser/devtools/protocol/network_handler.cc
index 4e735e2..e5a831c 100644
--- a/content/browser/devtools/protocol/network_handler.cc
+++ b/content/browser/devtools/protocol/network_handler.cc
@@ -21,7 +21,6 @@
 #include "content/browser/frame_host/frame_tree_node.h"
 #include "content/browser/frame_host/render_frame_host_impl.h"
 #include "content/common/navigation_params.h"
-#include "content/common/resource_request_completion_status.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/browsing_data_remover.h"
@@ -35,6 +34,7 @@
 #include "content/public/common/content_switches.h"
 #include "content/public/common/resource_devtools_info.h"
 #include "content/public/common/resource_request.h"
+#include "content/public/common/resource_request_completion_status.h"
 #include "content/public/common/resource_response.h"
 #include "net/base/net_errors.h"
 #include "net/base/upload_bytes_element_reader.h"
diff --git a/content/browser/frame_host/navigator_impl.cc b/content/browser/frame_host/navigator_impl.cc
index e7f78f4..dd1dc184 100644
--- a/content/browser/frame_host/navigator_impl.cc
+++ b/content/browser/frame_host/navigator_impl.cc
@@ -29,7 +29,6 @@
 #include "content/common/navigation_params.h"
 #include "content/common/page_messages.h"
 #include "content/common/site_isolation_policy.h"
-#include "content/common/url_loader_factory.mojom.h"
 #include "content/common/view_messages.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/content_browser_client.h"
@@ -45,6 +44,7 @@
 #include "content/public/common/content_client.h"
 #include "content/public/common/content_constants.h"
 #include "content/public/common/resource_response.h"
+#include "content/public/common/url_loader_factory.mojom.h"
 #include "net/base/net_errors.h"
 #include "url/gurl.h"
 #include "url/url_util.h"
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h
index 9a1a5ac..b326ec43 100644
--- a/content/browser/frame_host/render_frame_host_impl.h
+++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -41,10 +41,10 @@
 #include "content/common/frame_replication_state.h"
 #include "content/common/image_downloader/image_downloader.mojom.h"
 #include "content/common/navigation_params.h"
-#include "content/common/url_loader_factory.mojom.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/common/javascript_dialog_type.h"
 #include "content/public/common/previews_state.h"
+#include "content/public/common/url_loader_factory.mojom.h"
 #include "device/wake_lock/public/interfaces/wake_lock_context.mojom.h"
 #include "media/mojo/interfaces/interface_factory.mojom.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
diff --git a/content/browser/gpu/browser_gpu_channel_host_factory.cc b/content/browser/gpu/browser_gpu_channel_host_factory.cc
index 62d1ae1..9c77f8a 100644
--- a/content/browser/gpu/browser_gpu_channel_host_factory.cc
+++ b/content/browser/gpu/browser_gpu_channel_host_factory.cc
@@ -182,10 +182,6 @@
   }
 }
 
-bool BrowserGpuChannelHostFactory::CanUseForTesting() {
-  return GpuDataManager::GetInstance()->GpuAccessAllowed(NULL);
-}
-
 void BrowserGpuChannelHostFactory::Initialize(bool establish_gpu_channel) {
   DCHECK(!instance_);
   instance_ = new BrowserGpuChannelHostFactory();
diff --git a/content/browser/gpu/browser_gpu_channel_host_factory.h b/content/browser/gpu/browser_gpu_channel_host_factory.h
index ed50f12..a2ba4d1 100644
--- a/content/browser/gpu/browser_gpu_channel_host_factory.h
+++ b/content/browser/gpu/browser_gpu_channel_host_factory.h
@@ -44,9 +44,6 @@
   // thread stops.
   void CloseChannel();
 
-  // Used to skip GpuChannelHost tests when there can be no GPU process.
-  static bool CanUseForTesting();
-
   // Overridden from gpu::GpuChannelEstablishFactory:
   // The factory will return a null GpuChannelHost in the callback during
   // shutdown.
diff --git a/content/browser/gpu/gpu_ipc_browsertests.cc b/content/browser/gpu/gpu_ipc_browsertests.cc
index de30097..6073e19 100644
--- a/content/browser/gpu/gpu_ipc_browsertests.cc
+++ b/content/browser/gpu/gpu_ipc_browsertests.cc
@@ -6,14 +6,15 @@
 #include "base/memory/ptr_util.h"
 #include "base/run_loop.h"
 #include "build/build_config.h"
+#include "content/browser/browser_main_loop.h"
 #include "content/browser/compositor/image_transport_factory.h"
-#include "content/browser/gpu/browser_gpu_channel_host_factory.h"
 #include "content/browser/gpu/gpu_process_host.h"
 #include "content/common/gpu_stream_constants.h"
 #include "content/public/browser/gpu_data_manager.h"
 #include "content/public/browser/gpu_utils.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/test/content_browser_test.h"
+#include "gpu/ipc/client/gpu_channel_host.h"
 #include "services/ui/gpu/interfaces/gpu_service.mojom.h"
 #include "services/ui/public/cpp/gpu/context_provider_command_buffer.h"
 #include "third_party/skia/include/core/SkCanvas.h"
@@ -59,11 +60,9 @@
   ~EstablishGpuChannelHelper() {}
 
   scoped_refptr<gpu::GpuChannelHost> EstablishGpuChannelSyncRunLoop() {
-    if (!content::BrowserGpuChannelHostFactory::instance())
-      content::BrowserGpuChannelHostFactory::Initialize(true);
-
-    content::BrowserGpuChannelHostFactory* factory =
-        content::BrowserGpuChannelHostFactory::instance();
+    gpu::GpuChannelEstablishFactory* factory =
+        content::BrowserMainLoop::GetInstance()
+            ->gpu_channel_establish_factory();
     CHECK(factory);
     base::RunLoop run_loop;
     factory->EstablishGpuChannel(base::Bind(
@@ -82,7 +81,7 @@
   void SetUpOnMainThread() override {
     // This may leave the provider_ null in some cases, so tests need to early
     // out.
-    if (!content::BrowserGpuChannelHostFactory::CanUseForTesting())
+    if (!content::GpuDataManager::GetInstance()->GpuAccessAllowed(nullptr))
       return;
 
     EstablishGpuChannelHelper helper;
@@ -124,15 +123,9 @@
 class BrowserGpuChannelHostFactoryTest : public ContentBrowserTest {
  public:
   void SetUpOnMainThread() override {
-    if (!BrowserGpuChannelHostFactory::CanUseForTesting())
+    if (!GpuDataManager::GetInstance()->GpuAccessAllowed(nullptr))
       return;
-
-    // Start all tests without a gpu channel so that the tests exercise a
-    // consistent codepath.
-    if (!BrowserGpuChannelHostFactory::instance())
-      BrowserGpuChannelHostFactory::Initialize(false);
     CHECK(GetFactory());
-
     ContentBrowserTest::SetUpOnMainThread();
   }
 
@@ -155,8 +148,8 @@
   }
 
  protected:
-  BrowserGpuChannelHostFactory* GetFactory() {
-    return BrowserGpuChannelHostFactory::instance();
+  gpu::GpuChannelEstablishFactory* GetFactory() {
+    return BrowserMainLoop::GetInstance()->gpu_channel_establish_factory();
   }
 
   bool IsChannelEstablished() {
diff --git a/content/browser/histogram_internals_url_loader.h b/content/browser/histogram_internals_url_loader.h
index a867809..15cd781 100644
--- a/content/browser/histogram_internals_url_loader.h
+++ b/content/browser/histogram_internals_url_loader.h
@@ -5,7 +5,7 @@
 #ifndef CONTENT_BROWSER_HISTOGRAM_INTERNALS_URL_LOADER_H_
 #define CONTENT_BROWSER_HISTOGRAM_INTERNALS_URL_LOADER_H_
 
-#include "content/common/url_loader.mojom.h"
+#include "content/public/common/url_loader.mojom.h"
 
 namespace content {
 
diff --git a/content/browser/loader/DEPS b/content/browser/loader/DEPS
index 3622dab2..33ce038 100644
--- a/content/browser/loader/DEPS
+++ b/content/browser/loader/DEPS
@@ -29,15 +29,15 @@
 
     # TODO: To be replaced by mojo.
     "+content/common/resource_messages.h",
-    "+content/common/resource_request_completion_status.h",
     "+content/common/view_messages.h",
+    "+content/public/common/resource_request_completion_status.h",
   ],
   "downloaded_temp_file_impl\.(cc|h)": [
     "-content",
     "+content/browser/loader/downloaded_temp_file_impl.h",
     "+content/browser/loader/resource_dispatcher_host_impl.h",
     "+content/common/content_export.h",
-    "+content/common/url_loader_factory.mojom.h"
+    "+content/public/common/url_loader_factory.mojom.h"
   ],
   "resource_buffer.*\.(cc|h)": [
     "-content",
@@ -67,11 +67,11 @@
     "+content/browser/loader/resource_scheduler.h",
     "+content/browser/loader/upload_progress_tracker.h",
     "+content/common/content_export.h",
-    "+content/common/resource_request_completion_status.h",
-    "+content/common/url_loader.mojom.h",
     "+content/public/browser/global_request_id.h",
+    "+content/public/common/resource_request_completion_status.h",
     "+content/public/common/resource_response.h",
     "+content/public/common/resource_type.h",
+    "+content/public/common/url_loader.mojom.h",
   ],
   "mojo_async_resource_handler_unittest\.cc": [
     "-content",
@@ -82,9 +82,6 @@
     "+content/browser/loader/resource_dispatcher_host_impl.h",
     "+content/browser/loader/resource_request_info_impl.h",
     "+content/browser/loader/resource_scheduler.h",
-    "+content/common/resource_request_completion_status.h",
-    "+content/common/url_loader.mojom.h",
-    "+content/common/url_loader_factory.mojom.h",
     "+content/public/browser/appcache_service.h",
     "+content/public/browser/navigation_data.h",
     "+content/public/browser/resource_context.h",
@@ -93,8 +90,11 @@
     "+content/public/browser/stream_info.h",
     "+content/public/common/previews_state.h",
     "+content/public/common/resource_request.h",
+    "+content/public/common/resource_request_completion_status.h",
     "+content/public/common/resource_response.h",
     "+content/public/common/resource_type.h",
+    "+content/public/common/url_loader.mojom.h",
+    "+content/public/common/url_loader_factory.mojom.h",
     "+content/public/test/test_browser_context.h",
     "+content/public/test/test_browser_thread_bundle.h",
   ],
@@ -127,7 +127,6 @@
     "+content/browser/loader/throttling_resource_handler.h",
     "+content/browser/loader/wake_lock_resource_throttle.h",
     "+content/common/resource_request_body.h",
-    "+content/common/url_loader.mojom.h",
     "+content/public/browser/global_request_id.h",
     "+content/public/browser/resource_dispatcher_host.h",
     "+content/public/browser/resource_dispatcher_host_delegate.h",
@@ -135,6 +134,7 @@
     "+content/public/browser/resource_throttle.h",
     "+content/public/common/previews_state.h",
     "+content/public/common/resource_request_body.h",
+    "+content/public/common/url_loader.mojom.h",
 
     # TODO: These all have to be removed.
     "+content/browser/appcache/appcache_interceptor.h",
@@ -180,9 +180,9 @@
 
     # TODO: To be replaced by mojo.
     "+content/common/resource_messages.h",
-    "+content/common/resource_request_completion_status.h",
     "+content/common/view_messages.h",
     "+content/public/common/resource_request.h",
+    "+content/public/common/resource_request_completion_status.h",
   ],
   "resource_handler\.(cc|h)": [
     "-content",
@@ -230,13 +230,13 @@
     "+content/browser/loader/resource_request_info_impl.h",
     "+content/browser/loader/resource_requester_info.h",
     "+content/common/content_export.h",
-    "+content/common/url_loader.mojom.h",
     "+content/public/browser/global_request_id.h",
     "+content/public/browser/resource_request_info.h",
     "+content/public/common/previews_state.h",
     "+content/public/common/referrer.h",
     "+content/public/common/resource_request_body.h",
     "+content/public/common/resource_type.h",
+    "+content/public/common/url_loader.mojom.h",
 
     # TODO: these all have to be removed.
     "+content/browser/frame_host/frame_tree_node.h",
@@ -304,10 +304,10 @@
   "test_url_loader_client\.(cc|h)": [
     "-content",
     "+content/browser/loader/test_url_loader_client.h",
-    "+content/common/resource_request_completion_status.h",
-    "+content/common/url_loader.mojom.h",
-    "+content/common/url_loader_factory.mojom.h",
+    "+content/public/common/resource_request_completion_status.h",
     "+content/public/common/resource_response.h",
+    "+content/public/common/url_loader.mojom.h",
+    "+content/public/common/url_loader_factory.mojom.h",
   ],
   "upload_progress_tracker\.(cc|h)": [
     "-content",
@@ -320,9 +320,9 @@
     "+content/browser/loader/resource_requester_info.h",
     "+content/browser/loader/url_loader_factory_impl.h",
     "+content/common/content_export.h",
-    "+content/common/url_loader.mojom.h",
-    "+content/common/url_loader_factory.mojom.h",
     "+content/public/common/resource_request.h",
+    "+content/public/common/url_loader.mojom.h",
+    "+content/public/common/url_loader_factory.mojom.h",
   ],
   "url_loader_factory_impl_unittest\.cc": [
     "-content",
@@ -334,13 +334,13 @@
     "+content/browser/loader/resource_request_info_impl.h",
     "+content/browser/loader/url_loader_factory_impl.h",
     "+content/browser/loader_delegate_impl.h",
-    "+content/common/resource_request_completion_status.h",
-    "+content/common/url_loader.mojom.h",
-    "+content/common/url_loader_factory.mojom.h",
     "+content/public/browser/resource_context.h",
     "+content/public/browser/resource_dispatcher_host_delegate.h",
     "+content/public/common/content_paths.h",
     "+content/public/common/resource_request.h",
+    "+content/public/common/resource_request_completion_status.h",
+    "+content/public/common/url_loader.mojom.h",
+    "+content/public/common/url_loader_factory.mojom.h",
     "+content/public/test/test_browser_context.h",
     "+content/public/test/test_browser_thread_bundle.h",
 
diff --git a/content/browser/loader/async_resource_handler.cc b/content/browser/loader/async_resource_handler.cc
index a9b2e0e..c8c8dd0 100644
--- a/content/browser/loader/async_resource_handler.cc
+++ b/content/browser/loader/async_resource_handler.cc
@@ -25,8 +25,8 @@
 #include "content/browser/loader/resource_request_info_impl.h"
 #include "content/browser/loader/upload_progress_tracker.h"
 #include "content/common/resource_messages.h"
-#include "content/common/resource_request_completion_status.h"
 #include "content/common/view_messages.h"
+#include "content/public/common/resource_request_completion_status.h"
 #include "content/public/common/resource_response.h"
 #include "ipc/ipc_message_macros.h"
 #include "net/base/io_buffer.h"
diff --git a/content/browser/loader/downloaded_temp_file_impl.h b/content/browser/loader/downloaded_temp_file_impl.h
index 160511b..0ef0231 100644
--- a/content/browser/loader/downloaded_temp_file_impl.h
+++ b/content/browser/loader/downloaded_temp_file_impl.h
@@ -7,7 +7,7 @@
 
 #include "base/macros.h"
 #include "content/common/content_export.h"
-#include "content/common/url_loader_factory.mojom.h"
+#include "content/public/common/url_loader_factory.mojom.h"
 
 namespace content {
 
diff --git a/content/browser/loader/mojo_async_resource_handler.cc b/content/browser/loader/mojo_async_resource_handler.cc
index 7ec6e4a..1a84825 100644
--- a/content/browser/loader/mojo_async_resource_handler.cc
+++ b/content/browser/loader/mojo_async_resource_handler.cc
@@ -23,8 +23,8 @@
 #include "content/browser/loader/resource_request_info_impl.h"
 #include "content/browser/loader/resource_scheduler.h"
 #include "content/browser/loader/upload_progress_tracker.h"
-#include "content/common/resource_request_completion_status.h"
 #include "content/public/browser/global_request_id.h"
+#include "content/public/common/resource_request_completion_status.h"
 #include "content/public/common/resource_response.h"
 #include "mojo/public/c/system/data_pipe.h"
 #include "mojo/public/cpp/bindings/message.h"
diff --git a/content/browser/loader/mojo_async_resource_handler.h b/content/browser/loader/mojo_async_resource_handler.h
index bbe3370..e139f12 100644
--- a/content/browser/loader/mojo_async_resource_handler.h
+++ b/content/browser/loader/mojo_async_resource_handler.h
@@ -16,8 +16,8 @@
 #include "content/browser/loader/resource_handler.h"
 #include "content/browser/loader/upload_progress_tracker.h"
 #include "content/common/content_export.h"
-#include "content/common/url_loader.mojom.h"
 #include "content/public/common/resource_type.h"
+#include "content/public/common/url_loader.mojom.h"
 #include "mojo/public/cpp/bindings/associated_binding.h"
 #include "mojo/public/cpp/system/data_pipe.h"
 #include "mojo/public/cpp/system/simple_watcher.h"
diff --git a/content/browser/loader/mojo_async_resource_handler_unittest.cc b/content/browser/loader/mojo_async_resource_handler_unittest.cc
index 92fdb49..1fe6c2b 100644
--- a/content/browser/loader/mojo_async_resource_handler_unittest.cc
+++ b/content/browser/loader/mojo_async_resource_handler_unittest.cc
@@ -24,9 +24,6 @@
 #include "content/browser/loader/resource_request_info_impl.h"
 #include "content/browser/loader/resource_scheduler.h"
 #include "content/browser/loader/test_url_loader_client.h"
-#include "content/common/resource_request_completion_status.h"
-#include "content/common/url_loader.mojom.h"
-#include "content/common/url_loader_factory.mojom.h"
 #include "content/public/browser/appcache_service.h"
 #include "content/public/browser/navigation_data.h"
 #include "content/public/browser/resource_context.h"
@@ -34,8 +31,11 @@
 #include "content/public/browser/resource_throttle.h"
 #include "content/public/browser/stream_info.h"
 #include "content/public/common/previews_state.h"
+#include "content/public/common/resource_request_completion_status.h"
 #include "content/public/common/resource_response.h"
 #include "content/public/common/resource_type.h"
+#include "content/public/common/url_loader.mojom.h"
+#include "content/public/common/url_loader_factory.mojom.h"
 #include "content/public/test/test_browser_context.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "mojo/public/c/system/data_pipe.h"
diff --git a/content/browser/loader/navigation_url_loader_delegate.h b/content/browser/loader/navigation_url_loader_delegate.h
index c31a9e0..34723cf 100644
--- a/content/browser/loader/navigation_url_loader_delegate.h
+++ b/content/browser/loader/navigation_url_loader_delegate.h
@@ -10,7 +10,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "content/common/content_export.h"
-#include "content/common/url_loader_factory.mojom.h"
+#include "content/public/common/url_loader_factory.mojom.h"
 #include "mojo/public/cpp/system/data_pipe.h"
 
 namespace net {
diff --git a/content/browser/loader/navigation_url_loader_network_service.cc b/content/browser/loader/navigation_url_loader_network_service.cc
index 588f30c..d558945 100644
--- a/content/browser/loader/navigation_url_loader_network_service.cc
+++ b/content/browser/loader/navigation_url_loader_network_service.cc
@@ -27,7 +27,6 @@
 #include "content/browser/webui/url_data_manager_backend.h"
 #include "content/browser/webui/web_ui_url_loader_factory.h"
 #include "content/common/throttling_url_loader.h"
-#include "content/common/url_loader_factory.mojom.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/content_browser_client.h"
@@ -38,6 +37,7 @@
 #include "content/public/browser/stream_handle.h"
 #include "content/public/common/referrer.h"
 #include "content/public/common/url_constants.h"
+#include "content/public/common/url_loader_factory.mojom.h"
 #include "net/base/load_flags.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "net/url_request/url_request_context.h"
diff --git a/content/browser/loader/navigation_url_loader_network_service.h b/content/browser/loader/navigation_url_loader_network_service.h
index 6e57f8f..c15a4dad 100644
--- a/content/browser/loader/navigation_url_loader_network_service.h
+++ b/content/browser/loader/navigation_url_loader_network_service.h
@@ -8,8 +8,8 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "content/browser/loader/navigation_url_loader.h"
-#include "content/common/url_loader.mojom.h"
 #include "content/public/browser/ssl_status.h"
+#include "content/public/common/url_loader.mojom.h"
 
 namespace net {
 struct RedirectInfo;
diff --git a/content/browser/loader/resource_dispatcher_host_impl.cc b/content/browser/loader/resource_dispatcher_host_impl.cc
index 0ccbee7..88185f1 100644
--- a/content/browser/loader/resource_dispatcher_host_impl.cc
+++ b/content/browser/loader/resource_dispatcher_host_impl.cc
@@ -74,7 +74,6 @@
 #include "content/browser/streams/stream_registry.h"
 #include "content/common/net/url_request_service_worker_data.h"
 #include "content/common/resource_messages.h"
-#include "content/common/resource_request_completion_status.h"
 #include "content/common/site_isolation_policy.h"
 #include "content/common/view_messages.h"
 #include "content/public/browser/browser_thread.h"
@@ -91,6 +90,7 @@
 #include "content/public/common/content_switches.h"
 #include "content/public/common/resource_request.h"
 #include "content/public/common/resource_request_body.h"
+#include "content/public/common/resource_request_completion_status.h"
 #include "ipc/ipc_message_macros.h"
 #include "ipc/ipc_message_start.h"
 #include "net/base/auth.h"
diff --git a/content/browser/loader/resource_dispatcher_host_impl.h b/content/browser/loader/resource_dispatcher_host_impl.h
index 49673ff9..0b3d32c 100644
--- a/content/browser/loader/resource_dispatcher_host_impl.h
+++ b/content/browser/loader/resource_dispatcher_host_impl.h
@@ -30,7 +30,6 @@
 #include "content/browser/loader/global_routing_id.h"
 #include "content/browser/loader/resource_loader_delegate.h"
 #include "content/common/content_export.h"
-#include "content/common/url_loader.mojom.h"
 #include "content/public/browser/global_request_id.h"
 #include "content/public/browser/resource_dispatcher_host.h"
 #include "content/public/browser/resource_request_info.h"
@@ -38,6 +37,7 @@
 #include "content/public/common/previews_state.h"
 #include "content/public/common/request_context_type.h"
 #include "content/public/common/resource_type.h"
+#include "content/public/common/url_loader.mojom.h"
 #include "ipc/ipc_message.h"
 #include "net/base/load_states.h"
 #include "net/base/request_priority.h"
diff --git a/content/browser/loader/resource_message_filter.h b/content/browser/loader/resource_message_filter.h
index eead286..b318a552 100644
--- a/content/browser/loader/resource_message_filter.h
+++ b/content/browser/loader/resource_message_filter.h
@@ -14,10 +14,10 @@
 #include "base/sequenced_task_runner_helpers.h"
 #include "base/single_thread_task_runner.h"
 #include "content/common/content_export.h"
-#include "content/common/url_loader_factory.mojom.h"
 #include "content/public/browser/browser_associated_interface.h"
 #include "content/public/browser/browser_message_filter.h"
 #include "content/public/common/resource_type.h"
+#include "content/public/common/url_loader_factory.mojom.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 
 namespace storage {
diff --git a/content/browser/loader/resource_request_info_impl.h b/content/browser/loader/resource_request_info_impl.h
index e03a6876..a306acb 100644
--- a/content/browser/loader/resource_request_info_impl.h
+++ b/content/browser/loader/resource_request_info_impl.h
@@ -16,13 +16,13 @@
 #include "base/supports_user_data.h"
 #include "content/browser/blob_storage/chrome_blob_storage_context.h"
 #include "content/browser/loader/resource_requester_info.h"
-#include "content/common/url_loader.mojom.h"
 #include "content/public/browser/navigation_ui_data.h"
 #include "content/public/browser/resource_request_info.h"
 #include "content/public/common/previews_state.h"
 #include "content/public/common/referrer.h"
 #include "content/public/common/resource_request_body.h"
 #include "content/public/common/resource_type.h"
+#include "content/public/common/url_loader.mojom.h"
 #include "net/base/load_states.h"
 
 namespace content {
diff --git a/content/browser/loader/test_url_loader_client.h b/content/browser/loader/test_url_loader_client.h
index 3e10b7a..bb5e249 100644
--- a/content/browser/loader/test_url_loader_client.h
+++ b/content/browser/loader/test_url_loader_client.h
@@ -10,10 +10,10 @@
 
 #include "base/callback.h"
 #include "base/macros.h"
-#include "content/common/resource_request_completion_status.h"
-#include "content/common/url_loader.mojom.h"
-#include "content/common/url_loader_factory.mojom.h"
+#include "content/public/common/resource_request_completion_status.h"
 #include "content/public/common/resource_response.h"
+#include "content/public/common/url_loader.mojom.h"
+#include "content/public/common/url_loader_factory.mojom.h"
 #include "mojo/public/c/system/data_pipe.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "net/url_request/redirect_info.h"
diff --git a/content/browser/loader/url_loader_factory_impl.cc b/content/browser/loader/url_loader_factory_impl.cc
index 2c82c87..2de2e1f 100644
--- a/content/browser/loader/url_loader_factory_impl.cc
+++ b/content/browser/loader/url_loader_factory_impl.cc
@@ -7,8 +7,8 @@
 #include "base/memory/ptr_util.h"
 #include "content/browser/loader/resource_dispatcher_host_impl.h"
 #include "content/browser/loader/resource_requester_info.h"
-#include "content/common/url_loader.mojom.h"
 #include "content/public/common/resource_request.h"
+#include "content/public/common/url_loader.mojom.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 
 namespace content {
diff --git a/content/browser/loader/url_loader_factory_impl.h b/content/browser/loader/url_loader_factory_impl.h
index 39404f7..6177975c 100644
--- a/content/browser/loader/url_loader_factory_impl.h
+++ b/content/browser/loader/url_loader_factory_impl.h
@@ -9,7 +9,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/single_thread_task_runner.h"
 #include "content/common/content_export.h"
-#include "content/common/url_loader_factory.mojom.h"
+#include "content/public/common/url_loader_factory.mojom.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 
 namespace content {
diff --git a/content/browser/loader/url_loader_factory_impl_unittest.cc b/content/browser/loader/url_loader_factory_impl_unittest.cc
index a15126d..ab9dd1fc 100644
--- a/content/browser/loader/url_loader_factory_impl_unittest.cc
+++ b/content/browser/loader/url_loader_factory_impl_unittest.cc
@@ -28,13 +28,13 @@
 #include "content/browser/loader/resource_request_info_impl.h"
 #include "content/browser/loader/test_url_loader_client.h"
 #include "content/browser/loader_delegate_impl.h"
-#include "content/common/resource_request_completion_status.h"
-#include "content/common/url_loader.mojom.h"
-#include "content/common/url_loader_factory.mojom.h"
 #include "content/public/browser/resource_context.h"
 #include "content/public/browser/resource_dispatcher_host_delegate.h"
 #include "content/public/common/content_paths.h"
 #include "content/public/common/resource_request.h"
+#include "content/public/common/resource_request_completion_status.h"
+#include "content/public/common/url_loader.mojom.h"
+#include "content/public/common/url_loader_factory.mojom.h"
 #include "content/public/test/test_browser_context.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "mojo/public/c/system/data_pipe.h"
diff --git a/content/browser/loader/url_loader_request_handler.h b/content/browser/loader/url_loader_request_handler.h
index f52974c0..fb0a1f4b 100644
--- a/content/browser/loader/url_loader_request_handler.h
+++ b/content/browser/loader/url_loader_request_handler.h
@@ -8,8 +8,8 @@
 #include <memory>
 #include "base/callback_forward.h"
 #include "base/macros.h"
-#include "content/common/url_loader.mojom.h"
-#include "content/common/url_loader_factory.mojom.h"
+#include "content/public/common/url_loader.mojom.h"
+#include "content/public/common/url_loader_factory.mojom.h"
 
 namespace content {
 
diff --git a/content/browser/renderer_host/compositor_impl_android.cc b/content/browser/renderer_host/compositor_impl_android.cc
index 6e363825..0cd9667 100644
--- a/content/browser/renderer_host/compositor_impl_android.cc
+++ b/content/browser/renderer_host/compositor_impl_android.cc
@@ -51,8 +51,8 @@
 #include "components/viz/service/display_embedder/compositor_overlay_candidate_validator_android.h"
 #include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
 #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
+#include "content/browser/browser_main_loop.h"
 #include "content/browser/compositor/surface_utils.h"
-#include "content/browser/gpu/browser_gpu_channel_host_factory.h"
 #include "content/browser/gpu/browser_gpu_memory_buffer_manager.h"
 #include "content/browser/gpu/compositor_util.h"
 #include "content/browser/renderer_host/render_widget_host_impl.h"
@@ -411,9 +411,11 @@
     gpu::SharedMemoryLimits shared_memory_limits,
     ContextProviderCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  BrowserGpuChannelHostFactory::instance()->EstablishGpuChannel(
-      base::Bind(&CreateContextProviderAfterGpuChannelEstablished, handle,
-                 attributes, shared_memory_limits, callback));
+  BrowserMainLoop::GetInstance()
+      ->gpu_channel_establish_factory()
+      ->EstablishGpuChannel(
+          base::Bind(&CreateContextProviderAfterGpuChannelEstablished, handle,
+                     attributes, shared_memory_limits, callback));
 }
 
 // static
@@ -687,8 +689,10 @@
       this, &CompositorImpl::OnGpuChannelTimeout);
 
   DCHECK(surface_handle_ != gpu::kNullSurfaceHandle);
-  BrowserGpuChannelHostFactory::instance()->EstablishGpuChannel(base::Bind(
-      &CompositorImpl::OnGpuChannelEstablished, weak_factory_.GetWeakPtr()));
+  BrowserMainLoop::GetInstance()
+      ->gpu_channel_establish_factory()
+      ->EstablishGpuChannel(base::Bind(&CompositorImpl::OnGpuChannelEstablished,
+                                       weak_factory_.GetWeakPtr()));
 }
 
 void CompositorImpl::OnGpuChannelTimeout() {
diff --git a/content/browser/renderer_host/media/video_capture_gpu_jpeg_decoder.cc b/content/browser/renderer_host/media/video_capture_gpu_jpeg_decoder.cc
index c94fe4c..697000895 100644
--- a/content/browser/renderer_host/media/video_capture_gpu_jpeg_decoder.cc
+++ b/content/browser/renderer_host/media/video_capture_gpu_jpeg_decoder.cc
@@ -15,10 +15,11 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
-#include "content/browser/gpu/browser_gpu_channel_host_factory.h"
+#include "content/browser/browser_main_loop.h"
 #include "content/browser/gpu/gpu_process_host.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/common/content_switches.h"
+#include "gpu/ipc/client/gpu_channel_host.h"
 #include "media/base/media_switches.h"
 #include "media/base/video_frame.h"
 #include "media/gpu/ipc/client/gpu_jpeg_decode_accelerator_host.h"
@@ -250,11 +251,12 @@
     const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
     base::WeakPtr<VideoCaptureGpuJpegDecoder> weak_this) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DCHECK(BrowserGpuChannelHostFactory::instance());
 
-  BrowserGpuChannelHostFactory::instance()->EstablishGpuChannel(
-      base::Bind(&VideoCaptureGpuJpegDecoder::GpuChannelEstablishedOnUIThread,
-                 task_runner, weak_this));
+  BrowserMainLoop::GetInstance()
+      ->gpu_channel_establish_factory()
+      ->EstablishGpuChannel(base::Bind(
+          &VideoCaptureGpuJpegDecoder::GpuChannelEstablishedOnUIThread,
+          task_runner, weak_this));
 }
 
 // static
@@ -279,7 +281,7 @@
   } else if (gpu_channel_host->gpu_info().jpeg_decode_accelerator_supported) {
     gpu_channel_host_ = std::move(gpu_channel_host);
     scoped_refptr<base::SingleThreadTaskRunner> io_task_runner =
-        BrowserGpuChannelHostFactory::instance()->GetIOThreadTaskRunner();
+        BrowserThread::GetTaskRunnerForThread(BrowserThread::IO);
 
     int32_t route_id = gpu_channel_host_->GenerateRouteID();
     std::unique_ptr<media::GpuJpegDecodeAcceleratorHost> decoder(
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index e799662..d14f4113 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -139,7 +139,6 @@
 #include "content/common/content_switches_internal.h"
 #include "content/common/frame_messages.h"
 #include "content/common/in_process_child_thread_params.h"
-#include "content/common/network_service.mojom.h"
 #include "content/common/render_process_messages.h"
 #include "content/common/resource_messages.h"
 #include "content/common/service_manager/child_connection.h"
@@ -164,6 +163,7 @@
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/mojo_channel_switches.h"
+#include "content/public/common/network_service.mojom.h"
 #include "content/public/common/process_type.h"
 #include "content/public/common/resource_type.h"
 #include "content/public/common/result_codes.h"
diff --git a/content/browser/renderer_host/render_process_host_impl.h b/content/browser/renderer_host/render_process_host_impl.h
index 96e664d..94a0d1f7 100644
--- a/content/browser/renderer_host/render_process_host_impl.h
+++ b/content/browser/renderer_host/render_process_host_impl.h
@@ -35,9 +35,9 @@
 #include "content/common/renderer.mojom.h"
 #include "content/common/renderer_host.mojom.h"
 #include "content/common/storage_partition_service.mojom.h"
-#include "content/common/url_loader_factory.mojom.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/common/service_manager_connection.h"
+#include "content/public/common/url_loader_factory.mojom.h"
 #include "ipc/ipc_channel_proxy.h"
 #include "ipc/ipc_platform_file.h"
 #include "media/media_features.h"
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc
index 364819d1..2c426e7 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -557,8 +557,7 @@
 void RenderWidgetHostViewAura::Hide() {
   window_->Hide();
 
-  // TODO(wjmaclean): can host_ ever be null?
-  if (host_ && !host_->is_hidden()) {
+  if (!host_->is_hidden()) {
     host_->WasHidden();
     if (delegated_frame_host_)
       delegated_frame_host_->WasHidden();
@@ -769,7 +768,7 @@
   ui::OnScreenKeyboardDisplayManager* osk_display_manager =
       ui::OnScreenKeyboardDisplayManager::GetInstance();
   DCHECK(osk_display_manager);
-  if (editable && host_ && host_->GetView() && host_->delegate()) {
+  if (editable && host_->GetView() && host_->delegate()) {
     keyboard_observer_.reset(new WinScreenKeyboardObserver(
         host_, location_dips_screen, device_scale_factor_, window_));
     virtual_keyboard_requested_ =
@@ -1229,9 +1228,8 @@
   }
 
   // Ignore character messages for VKEY_RETURN sent on CTRL+M. crbug.com/315547
-  // TODO(wjmaclean): can host_ ever be null?
-  if (host_ && (event_handler_->accept_return_character() ||
-                event.GetCharacter() != ui::VKEY_RETURN)) {
+  if (event_handler_->accept_return_character() ||
+      event.GetCharacter() != ui::VKEY_RETURN) {
     // Send a blink::WebInputEvent::Char event to |host_|.
     ForwardKeyboardEventWithLatencyInfo(
         NativeWebKeyboardEvent(event, event.GetCharacter()), *event.latency(),
@@ -1404,10 +1402,6 @@
 }
 
 void RenderWidgetHostViewAura::OnInputMethodChanged() {
-  // TODO(wjmaclean): can host_ ever be null?
-  if (!host_)
-    return;
-
   // TODO(suzhe): implement the newly added “locale” property of HTML DOM
   // TextEvent.
 }
diff --git a/content/browser/service_worker/service_worker_fetch_dispatcher.h b/content/browser/service_worker/service_worker_fetch_dispatcher.h
index 47bc334..fa2a126 100644
--- a/content/browser/service_worker/service_worker_fetch_dispatcher.h
+++ b/content/browser/service_worker/service_worker_fetch_dispatcher.h
@@ -18,9 +18,9 @@
 #include "content/common/service_worker/service_worker_event_dispatcher.mojom.h"
 #include "content/common/service_worker/service_worker_status_code.h"
 #include "content/common/service_worker/service_worker_types.h"
-#include "content/common/url_loader.mojom.h"
-#include "content/common/url_loader_factory.mojom.h"
 #include "content/public/common/resource_type.h"
+#include "content/public/common/url_loader.mojom.h"
+#include "content/public/common/url_loader_factory.mojom.h"
 #include "mojo/public/cpp/system/data_pipe.h"
 #include "net/log/net_log_with_source.h"
 
diff --git a/content/browser/service_worker/service_worker_url_loader_job.h b/content/browser/service_worker/service_worker_url_loader_job.h
index df84f4e2..86037a2 100644
--- a/content/browser/service_worker/service_worker_url_loader_job.h
+++ b/content/browser/service_worker/service_worker_url_loader_job.h
@@ -13,7 +13,7 @@
 #include "content/browser/service_worker/service_worker_url_job_wrapper.h"
 #include "content/common/service_worker/service_worker_status_code.h"
 #include "content/common/service_worker/service_worker_types.h"
-#include "content/common/url_loader.mojom.h"
+#include "content/public/common/url_loader.mojom.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "mojo/public/cpp/system/data_pipe.h"
 #include "third_party/WebKit/public/platform/modules/serviceworker/service_worker_stream_handle.mojom.h"
diff --git a/content/browser/storage_partition_impl.h b/content/browser/storage_partition_impl.h
index 9a50b97..c5798a2 100644
--- a/content/browser/storage_partition_impl.h
+++ b/content/browser/storage_partition_impl.h
@@ -30,9 +30,9 @@
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
 #include "content/browser/url_loader_factory_getter.h"
 #include "content/common/content_export.h"
-#include "content/common/network_service.mojom.h"
 #include "content/common/storage_partition_service.mojom.h"
 #include "content/public/browser/storage_partition.h"
+#include "content/public/common/network_service.mojom.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "net/cookies/cookie_store.h"
 #include "storage/browser/quota/special_storage_policy.h"
diff --git a/content/browser/url_loader_factory_getter.cc b/content/browser/url_loader_factory_getter.cc
index d5013516..67cd6bac 100644
--- a/content/browser/url_loader_factory_getter.cc
+++ b/content/browser/url_loader_factory_getter.cc
@@ -6,7 +6,7 @@
 
 #include "base/bind.h"
 #include "content/browser/storage_partition_impl.h"
-#include "content/common/network_service.mojom.h"
+#include "content/public/common/network_service.mojom.h"
 
 namespace content {
 
diff --git a/content/browser/url_loader_factory_getter.h b/content/browser/url_loader_factory_getter.h
index 1ea7281..12864590 100644
--- a/content/browser/url_loader_factory_getter.h
+++ b/content/browser/url_loader_factory_getter.h
@@ -8,8 +8,8 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "content/common/content_export.h"
-#include "content/common/url_loader_factory.mojom.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/common/url_loader_factory.mojom.h"
 
 namespace content {
 
diff --git a/content/browser/webui/network_error_url_loader.h b/content/browser/webui/network_error_url_loader.h
index 076a6682..b4e1532 100644
--- a/content/browser/webui/network_error_url_loader.h
+++ b/content/browser/webui/network_error_url_loader.h
@@ -5,7 +5,7 @@
 #ifndef CONTENT_BROWSER_WEBUI_NETWORK_ERROR_URL_LOADER_H_
 #define CONTENT_BROWSER_WEBUI_NETWORK_ERROR_URL_LOADER_H_
 
-#include "content/common/url_loader.mojom.h"
+#include "content/public/common/url_loader.mojom.h"
 
 namespace content {
 
diff --git a/content/browser/webui/web_ui_url_loader_factory.cc b/content/browser/webui/web_ui_url_loader_factory.cc
index 3e5ad0a..99bc1b30 100644
--- a/content/browser/webui/web_ui_url_loader_factory.cc
+++ b/content/browser/webui/web_ui_url_loader_factory.cc
@@ -22,11 +22,11 @@
 #include "content/browser/webui/network_error_url_loader.h"
 #include "content/browser/webui/url_data_manager_backend.h"
 #include "content/browser/webui/url_data_source_impl.h"
-#include "content/common/network_service.mojom.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/common/network_service.mojom.h"
 #include "content/public/common/url_constants.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "third_party/zlib/google/compression_utils.h"
diff --git a/content/browser/webui/web_ui_url_loader_factory.h b/content/browser/webui/web_ui_url_loader_factory.h
index b52d653..733e8e3 100644
--- a/content/browser/webui/web_ui_url_loader_factory.h
+++ b/content/browser/webui/web_ui_url_loader_factory.h
@@ -5,7 +5,7 @@
 #ifndef CONTENT_BROWSER_WEBUI_WEB_UI_URL_LOADER_FACTORY_H_
 #define CONTENT_BROWSER_WEBUI_WEB_UI_URL_LOADER_FACTORY_H_
 
-#include "content/common/url_loader_factory.mojom.h"
+#include "content/public/common/url_loader_factory.mojom.h"
 
 namespace content {
 class FrameTreeNode;
diff --git a/content/child/resource_dispatcher.cc b/content/child/resource_dispatcher.cc
index 790f652..4d724c1 100644
--- a/content/child/resource_dispatcher.cc
+++ b/content/child/resource_dispatcher.cc
@@ -31,12 +31,12 @@
 #include "content/common/inter_process_time_ticks_converter.h"
 #include "content/common/navigation_params.h"
 #include "content/common/resource_messages.h"
-#include "content/common/resource_request_completion_status.h"
 #include "content/common/throttling_url_loader.h"
 #include "content/public/child/fixed_received_data.h"
 #include "content/public/child/request_peer.h"
 #include "content/public/child/resource_dispatcher_delegate.h"
 #include "content/public/common/resource_request.h"
+#include "content/public/common/resource_request_completion_status.h"
 #include "content/public/common/resource_response.h"
 #include "content/public/common/resource_type.h"
 #include "net/base/net_errors.h"
diff --git a/content/child/resource_dispatcher.h b/content/child/resource_dispatcher.h
index 091bf39..fc7aa465 100644
--- a/content/child/resource_dispatcher.h
+++ b/content/child/resource_dispatcher.h
@@ -22,8 +22,8 @@
 #include "base/single_thread_task_runner.h"
 #include "base/time/time.h"
 #include "content/common/content_export.h"
-#include "content/common/url_loader.mojom.h"
 #include "content/public/common/resource_type.h"
+#include "content/public/common/url_loader.mojom.h"
 #include "content/public/common/url_loader_throttle.h"
 #include "ipc/ipc_listener.h"
 #include "ipc/ipc_sender.h"
diff --git a/content/child/resource_dispatcher_unittest.cc b/content/child/resource_dispatcher_unittest.cc
index f7a8c9e..e42b0e15 100644
--- a/content/child/resource_dispatcher_unittest.cc
+++ b/content/child/resource_dispatcher_unittest.cc
@@ -24,7 +24,6 @@
 #include "content/child/test_request_peer.h"
 #include "content/common/appcache_interfaces.h"
 #include "content/common/resource_messages.h"
-#include "content/common/resource_request_completion_status.h"
 #include "content/public/child/fixed_received_data.h"
 #include "content/public/child/request_peer.h"
 #include "content/public/child/resource_dispatcher_delegate.h"
@@ -32,6 +31,7 @@
 #include "content/public/common/request_context_frame_type.h"
 #include "content/public/common/resource_request.h"
 #include "content/public/common/resource_request_body.h"
+#include "content/public/common/resource_request_completion_status.h"
 #include "content/public/common/resource_response.h"
 #include "content/public/common/service_worker_modes.h"
 #include "net/base/net_errors.h"
diff --git a/content/child/url_loader_client_impl.h b/content/child/url_loader_client_impl.h
index 872c678..16af56c 100644
--- a/content/child/url_loader_client_impl.h
+++ b/content/child/url_loader_client_impl.h
@@ -11,7 +11,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "content/common/content_export.h"
-#include "content/common/url_loader.mojom.h"
+#include "content/public/common/url_loader.mojom.h"
 #include "ipc/ipc_message.h"
 #include "mojo/public/cpp/system/data_pipe.h"
 
diff --git a/content/child/url_loader_client_impl_unittest.cc b/content/child/url_loader_client_impl_unittest.cc
index 3ae14d5..f290cdb 100644
--- a/content/child/url_loader_client_impl_unittest.cc
+++ b/content/child/url_loader_client_impl_unittest.cc
@@ -10,7 +10,7 @@
 #include "base/run_loop.h"
 #include "content/child/resource_dispatcher.h"
 #include "content/child/test_request_peer.h"
-#include "content/common/url_loader_factory.mojom.h"
+#include "content/public/common/url_loader_factory.mojom.h"
 #include "ipc/ipc_sender.h"
 #include "mojo/public/cpp/bindings/associated_interface_ptr_info.h"
 #include "mojo/public/cpp/bindings/binding.h"
diff --git a/content/child/url_response_body_consumer.cc b/content/child/url_response_body_consumer.cc
index bce3d4e4..2766bf7 100644
--- a/content/child/url_response_body_consumer.cc
+++ b/content/child/url_response_body_consumer.cc
@@ -11,8 +11,8 @@
 #include "content/child/resource_dispatcher.h"
 #include "content/child/site_isolation_stats_gatherer.h"
 #include "content/common/resource_messages.h"
-#include "content/common/resource_request_completion_status.h"
 #include "content/public/child/request_peer.h"
+#include "content/public/common/resource_request_completion_status.h"
 
 namespace content {
 
diff --git a/content/child/url_response_body_consumer.h b/content/child/url_response_body_consumer.h
index efa55dc..ecbbf0db 100644
--- a/content/child/url_response_body_consumer.h
+++ b/content/child/url_response_body_consumer.h
@@ -16,7 +16,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/single_thread_task_runner.h"
 #include "content/common/content_export.h"
-#include "content/common/url_loader.mojom.h"
+#include "content/public/common/url_loader.mojom.h"
 #include "mojo/public/cpp/system/data_pipe.h"
 #include "mojo/public/cpp/system/simple_watcher.h"
 
diff --git a/content/child/url_response_body_consumer_unittest.cc b/content/child/url_response_body_consumer_unittest.cc
index be98a8e..f29fb2d3 100644
--- a/content/child/url_response_body_consumer_unittest.cc
+++ b/content/child/url_response_body_consumer_unittest.cc
@@ -14,10 +14,10 @@
 #include "content/child/request_extra_data.h"
 #include "content/child/resource_dispatcher.h"
 #include "content/common/resource_messages.h"
-#include "content/common/resource_request_completion_status.h"
 #include "content/public/child/request_peer.h"
 #include "content/public/common/request_context_frame_type.h"
 #include "content/public/common/resource_request.h"
+#include "content/public/common/resource_request_completion_status.h"
 #include "content/public/common/service_worker_modes.h"
 #include "net/base/request_priority.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/content/child/web_url_loader_impl.cc b/content/child/web_url_loader_impl.cc
index 0c6e896..0da42fd 100644
--- a/content/child/web_url_loader_impl.cc
+++ b/content/child/web_url_loader_impl.cc
@@ -36,7 +36,6 @@
 #include "content/child/weburlresponse_extradata_impl.h"
 #include "content/common/resource_messages.h"
 #include "content/common/service_worker/service_worker_types.h"
-#include "content/common/url_loader.mojom.h"
 #include "content/public/child/fixed_received_data.h"
 #include "content/public/child/request_peer.h"
 #include "content/public/common/browser_side_navigation_policy.h"
@@ -44,6 +43,7 @@
 #include "content/public/common/resource_request.h"
 #include "content/public/common/resource_request_body.h"
 #include "content/public/common/service_worker_modes.h"
+#include "content/public/common/url_loader.mojom.h"
 #include "net/base/data_url.h"
 #include "net/base/filename_util.h"
 #include "net/base/net_errors.h"
diff --git a/content/child/web_url_loader_impl.h b/content/child/web_url_loader_impl.h
index 4fc8700..3678fa8 100644
--- a/content/child/web_url_loader_impl.h
+++ b/content/child/web_url_loader_impl.h
@@ -9,8 +9,8 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "content/common/content_export.h"
-#include "content/common/url_loader_factory.mojom.h"
 #include "content/public/common/resource_response.h"
+#include "content/public/common/url_loader_factory.mojom.h"
 #include "mojo/public/cpp/system/data_pipe.h"
 #include "net/url_request/redirect_info.h"
 #include "third_party/WebKit/public/platform/WebURLLoader.h"
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index e9efee7..c26f875f 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -20,7 +20,10 @@
 buildflag_header("features") {
   header = "features.h"
 
-  flags = [ "USE_EXTERNAL_POPUP_MENU=$use_external_popup_menu" ]
+  flags = [
+    "USE_EXTERNAL_POPUP_MENU=$use_external_popup_menu",
+    "ALLOW_CRITICAL_MEMORY_PRESSURE_HANDLING_IN_FOREGROUND=$allow_critical_memory_pressure_handling_in_foreground",
+  ]
 }
 
 source_set("common") {
@@ -229,7 +232,6 @@
     "media/video_capture.h",
     "message_port.cc",
     "message_port.h",
-    "mutable_network_traffic_annotation_tag_struct_traits.h",
     "navigation_gesture.h",
     "navigation_params.cc",
     "navigation_params.h",
@@ -278,8 +280,6 @@
     "resize_params.h",
     "resource_messages.cc",
     "resource_messages.h",
-    "resource_request_completion_status.cc",
-    "resource_request_completion_status.h",
     "sandbox_init_mac.cc",
     "sandbox_init_mac.h",
     "sandbox_init_win.cc",
@@ -379,6 +379,7 @@
     "//components/tracing:startup_tracing",
     "//content:resources",
     "//content/app/resources",
+    "//content/public/common:interfaces",
     "//content/public/common:service_names",
     "//device/base/synchronization",
     "//device/bluetooth",
@@ -445,7 +446,10 @@
   libs = []
   ldflags = []
 
-  allow_circular_includes_from = [ ":mojo_bindings" ]
+  allow_circular_includes_from = [
+    ":mojo_bindings",
+    "//content/public/common:interfaces",
+  ]
 
   if (is_android && use_seccomp_bpf) {
     set_sources_assignment_filter([])
@@ -608,9 +612,7 @@
     "media/media_devices.mojom",
     "media/renderer_audio_output_stream_factory.mojom",
     "memory_coordinator.mojom",
-    "mutable_network_traffic_annotation_tag.mojom",
     "native_types.mojom",
-    "network_service.mojom",
     "push_messaging.mojom",
     "render_frame_message_filter.mojom",
     "render_message_filter.mojom",
@@ -624,8 +626,6 @@
     "service_worker/service_worker_provider_interfaces.mojom",
     "service_worker/service_worker_types.mojom",
     "storage_partition_service.mojom",
-    "url_loader.mojom",
-    "url_loader_factory.mojom",
     "video_capture.mojom",
     "worker_url_loader_factory_provider.mojom",
   ]
diff --git a/content/common/features.gni b/content/common/features.gni
index c43056a..b1bcb83 100644
--- a/content/common/features.gni
+++ b/content/common/features.gni
@@ -2,7 +2,13 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/config/chromecast_build.gni")
+
 declare_args() {
   # Whether or not to use external popup menu.
   use_external_popup_menu = is_android || is_mac
+
+  # Whether to perform critical memory pressure handling when in foreground (if
+  # false, critical memory pressure is treated like moderate pressure in foreground).
+  allow_critical_memory_pressure_handling_in_foreground = is_chromecast
 }
diff --git a/content/common/renderer_host.mojom b/content/common/renderer_host.mojom
index b2a271a..8269deae 100644
--- a/content/common/renderer_host.mojom
+++ b/content/common/renderer_host.mojom
@@ -4,7 +4,7 @@
 
 module content.mojom;
 
-import "content/common/url_loader_factory.mojom";
+import "content/public/common/url_loader_factory.mojom";
 
 // The primordial interface implemented by a render process host. This should be
 // used for implementing renderer-to-browser messages.
diff --git a/content/common/resource_messages.h b/content/common/resource_messages.h
index 31a5453b..0fe7c8438 100644
--- a/content/common/resource_messages.h
+++ b/content/common/resource_messages.h
@@ -14,10 +14,10 @@
 #include "base/process/process.h"
 #include "content/common/content_param_traits_macros.h"
 #include "content/common/navigation_params.h"
-#include "content/common/resource_request_completion_status.h"
 #include "content/public/common/common_param_traits.h"
 #include "content/public/common/resource_request.h"
 #include "content/public/common/resource_request_body.h"
+#include "content/public/common/resource_request_completion_status.h"
 #include "content/public/common/resource_response.h"
 #include "content/public/common/service_worker_modes.h"
 #include "ipc/ipc_message_macros.h"
diff --git a/content/common/service_worker/service_worker_event_dispatcher.mojom b/content/common/service_worker/service_worker_event_dispatcher.mojom
index 93832a5..34a174c 100644
--- a/content/common/service_worker/service_worker_event_dispatcher.mojom
+++ b/content/common/service_worker/service_worker_event_dispatcher.mojom
@@ -4,7 +4,7 @@
 
 module content.mojom;
 
-import "content/common/url_loader.mojom";
+import "content/public/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";
diff --git a/content/common/throttling_url_loader.h b/content/common/throttling_url_loader.h
index e9f85dc..bb15aefe 100644
--- a/content/common/throttling_url_loader.h
+++ b/content/common/throttling_url_loader.h
@@ -12,8 +12,8 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "content/common/content_export.h"
 #include "content/common/possibly_associated_interface_ptr.h"
-#include "content/common/url_loader.mojom.h"
-#include "content/common/url_loader_factory.mojom.h"
+#include "content/public/common/url_loader.mojom.h"
+#include "content/public/common/url_loader_factory.mojom.h"
 #include "content/public/common/url_loader_throttle.h"
 #include "mojo/public/cpp/bindings/binding.h"
 
diff --git a/content/common/throttling_url_loader_unittest.cc b/content/common/throttling_url_loader_unittest.cc
index 3068e9ae..ac3a1fc 100644
--- a/content/common/throttling_url_loader_unittest.cc
+++ b/content/common/throttling_url_loader_unittest.cc
@@ -7,8 +7,8 @@
 #include "base/macros.h"
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
-#include "content/common/url_loader.mojom.h"
-#include "content/common/url_loader_factory.mojom.h"
+#include "content/public/common/url_loader.mojom.h"
+#include "content/public/common/url_loader_factory.mojom.h"
 #include "content/public/common/url_loader_throttle.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/content/common/typemaps.gni b/content/common/typemaps.gni
index 95068be3..b65fe78 100644
--- a/content/common/typemaps.gni
+++ b/content/common/typemaps.gni
@@ -6,19 +6,12 @@
   "//content/common/background_fetch/background_fetch_types.typemap",
   "//content/common/native_types.typemap",
   "//content/common/media/media_devices.typemap",
-  "//content/common/mutable_network_traffic_annotation_tag.typemap",
   "//content/common/push_messaging.typemap",
   "//content/common/service_worker/embedded_worker.typemap",
   "//content/common/service_worker/service_worker_event_dispatcher.typemap",
   "//content/common/service_worker/service_worker_fetch_request.typemap",
   "//content/common/service_worker/service_worker_provider.typemap",
   "//content/common/service_worker/service_worker_types.typemap",
-  "//content/common/ssl_info.typemap",
-  "//content/common/url_loader_status.typemap",
-  "//content/common/url_request.typemap",
-  "//content/common/url_request_redirect_info.typemap",
-  "//content/common/url_response_head.typemap",
-  "//content/common/url_sync_load_result.typemap",
   "//content/common/web_preferences.typemap",
   "//content/common/media/media_session.typemap",
 ]
diff --git a/content/common/url_request_struct_traits.h b/content/common/url_request_struct_traits.h
index 3aecba8..0326632 100644
--- a/content/common/url_request_struct_traits.h
+++ b/content/common/url_request_struct_traits.h
@@ -5,7 +5,7 @@
 #ifndef CONTENT_COMMON_URL_REQUEST_STRUCT_TRAITS_H_
 #define CONTENT_COMMON_URL_REQUEST_STRUCT_TRAITS_H_
 
-#include "content/common/url_loader.mojom.h"
+#include "content/public/common/url_loader.mojom.h"
 #include "mojo/public/cpp/bindings/enum_traits.h"
 #include "net/base/request_priority.h"
 
diff --git a/content/common/worker_url_loader_factory_provider.mojom b/content/common/worker_url_loader_factory_provider.mojom
index 6590e4f..71a4956 100644
--- a/content/common/worker_url_loader_factory_provider.mojom
+++ b/content/common/worker_url_loader_factory_provider.mojom
@@ -4,7 +4,7 @@
 
 module content.mojom;
 
-import "url_loader_factory.mojom";
+import "content/public/common/url_loader_factory.mojom";
 
 // A renderer-side interface that is returned by CreateWorkerFetchContext for
 // the browser to notify the renderer process when there's a controller change.
diff --git a/content/network/DEPS b/content/network/DEPS
index 935a07d..ac2ce4d 100644
--- a/content/network/DEPS
+++ b/content/network/DEPS
@@ -4,16 +4,16 @@
   "-content",
   "+content/common/content_export.h",
   "+content/common/net_adapters.h",
-  "+content/common/network_service.mojom.h",
-  "+content/common/url_loader.mojom.h",
-  "+content/common/url_loader_factory.mojom.h",
   "+content/network",
   "+content/public/common/content_client.h",
   "+content/public/common/content_switches.h",
+  "+content/public/common/network_service.mojom.h",
   "+content/public/common/referrer.h",
   "+content/public/common/resource_request.h",
   "+content/public/common/resource_response.h",
   "+content/public/common/url_constants.h",
+  "+content/public/common/url_loader.mojom.h",
+  "+content/public/common/url_loader_factory.mojom.h",
   "+services/service_manager/public",
 ]
 
diff --git a/content/network/cache_url_loader.h b/content/network/cache_url_loader.h
index a4e1521..28d9d4b 100644
--- a/content/network/cache_url_loader.h
+++ b/content/network/cache_url_loader.h
@@ -5,7 +5,7 @@
 #ifndef CONTENT_NETWORK_CACHE_URL_LOADER_H_
 #define CONTENT_NETWORK_CACHE_URL_LOADER_H_
 
-#include "content/common/url_loader.mojom.h"
+#include "content/public/common/url_loader.mojom.h"
 
 namespace net {
 class URLRequestContext;
diff --git a/content/network/network_context.h b/content/network/network_context.h
index b6b5332f..278923f 100644
--- a/content/network/network_context.h
+++ b/content/network/network_context.h
@@ -12,8 +12,8 @@
 
 #include "base/macros.h"
 #include "content/common/content_export.h"
-#include "content/common/network_service.mojom.h"
-#include "content/common/url_loader_factory.mojom.h"
+#include "content/public/common/network_service.mojom.h"
+#include "content/public/common/url_loader_factory.mojom.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "mojo/public/cpp/bindings/strong_binding_set.h"
 
diff --git a/content/network/network_service.h b/content/network/network_service.h
index c945b9ad..703ffc2 100644
--- a/content/network/network_service.h
+++ b/content/network/network_service.h
@@ -9,7 +9,7 @@
 
 #include "base/macros.h"
 #include "content/common/content_export.h"
-#include "content/common/network_service.mojom.h"
+#include "content/public/common/network_service.mojom.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
 #include "services/service_manager/public/cpp/service.h"
diff --git a/content/network/network_service_unittest.cc b/content/network/network_service_unittest.cc
index 398808c6..29aa109 100644
--- a/content/network/network_service_unittest.cc
+++ b/content/network/network_service_unittest.cc
@@ -7,9 +7,9 @@
 #include "base/run_loop.h"
 #include "base/test/scoped_task_environment.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "content/common/network_service.mojom.h"
 #include "content/network/network_context.h"
 #include "content/network/network_service.h"
+#include "content/public/common/network_service.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace content {
diff --git a/content/network/network_service_url_loader_factory_impl.h b/content/network/network_service_url_loader_factory_impl.h
index 1c22d25b..8518717 100644
--- a/content/network/network_service_url_loader_factory_impl.h
+++ b/content/network/network_service_url_loader_factory_impl.h
@@ -6,7 +6,7 @@
 #define CONTENT_NETWORK_NETWORK_SERVICE_URL_LOADER_FACTORY_IMPL_H_
 
 #include "base/macros.h"
-#include "content/common/url_loader_factory.mojom.h"
+#include "content/public/common/url_loader_factory.mojom.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 
 namespace content {
diff --git a/content/network/url_loader_impl.cc b/content/network/url_loader_impl.cc
index 6ecf2b73..5b86d6e 100644
--- a/content/network/url_loader_impl.cc
+++ b/content/network/url_loader_impl.cc
@@ -8,10 +8,10 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
 #include "content/common/net_adapters.h"
-#include "content/common/url_loader_factory.mojom.h"
 #include "content/network/network_context.h"
 #include "content/public/common/referrer.h"
 #include "content/public/common/resource_response.h"
+#include "content/public/common/url_loader_factory.mojom.h"
 #include "net/base/elements_upload_data_stream.h"
 #include "net/base/load_flags.h"
 #include "net/base/upload_bytes_element_reader.h"
diff --git a/content/network/url_loader_impl.h b/content/network/url_loader_impl.h
index b23f2f6d..df0211b 100644
--- a/content/network/url_loader_impl.h
+++ b/content/network/url_loader_impl.h
@@ -11,7 +11,7 @@
 
 #include "base/memory/weak_ptr.h"
 #include "content/common/content_export.h"
-#include "content/common/url_loader.mojom.h"
+#include "content/public/common/url_loader.mojom.h"
 #include "mojo/public/cpp/bindings/associated_binding.h"
 #include "mojo/public/cpp/system/simple_watcher.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
diff --git a/content/public/common/BUILD.gn b/content/public/common/BUILD.gn
index 0e20d7f5..2ce68b81d 100644
--- a/content/public/common/BUILD.gn
+++ b/content/public/common/BUILD.gn
@@ -170,6 +170,7 @@
     "mhtml_generation_params.h",
     "mojo_channel_switches.cc",
     "mojo_channel_switches.h",
+    "mutable_network_traffic_annotation_tag_struct_traits.h",
     "notification_resources.cc",
     "notification_resources.h",
     "origin_trial_policy.cc",
@@ -208,6 +209,8 @@
     "resource_request.h",
     "resource_request_body.cc",
     "resource_request_body.h",
+    "resource_request_completion_status.cc",
+    "resource_request_completion_status.h",
     "resource_response.cc",
     "resource_response.h",
     "resource_response_info.cc",
@@ -259,9 +262,9 @@
   public_configs = [ "//v8:external_startup_data" ]
 
   public_deps = [
+    ":interfaces",
     ":service_names",
     "//content/common",
-    "//content/public/common:interfaces",
     "//ipc",
     "//mojo/edk/system",
     "//mojo/public/cpp/bindings",
@@ -304,6 +307,7 @@
 
   # //content/common needs to include public headers.
   allow_circular_includes_from = [
+    ":interfaces",
     "//content/common",
     "//content/common:mojo_bindings",
   ]
@@ -341,10 +345,31 @@
 }
 
 mojom("interfaces") {
+  # Must depend on //content/public/common/ instead, for component build.
+  visibility = [
+    ":common_sources",
+    "//content/common/*",
+  ]
+
   sources = [
+    "mutable_network_traffic_annotation_tag.mojom",
+    "network_service.mojom",
     "network_service_test.mojom",
+    "url_loader.mojom",
+    "url_loader_factory.mojom",
     "window_container_type.mojom",
   ]
+
+  public_deps = [
+    "//mojo/common:common_custom_types",
+    "//url/mojo:url_mojom_gurl",
+    "//url/mojo:url_mojom_origin",
+  ]
+
+  component_output_prefix = "content_public_common_mojo_bindings"
+  export_class_attribute = "CONTENT_EXPORT"
+  export_define = "CONTENT_IMPLEMENTATION=1"
+  export_header = "content/common/content_export.h"
 }
 
 mojom("service_names") {
diff --git a/content/common/mutable_network_traffic_annotation_tag.mojom b/content/public/common/mutable_network_traffic_annotation_tag.mojom
similarity index 100%
rename from content/common/mutable_network_traffic_annotation_tag.mojom
rename to content/public/common/mutable_network_traffic_annotation_tag.mojom
diff --git a/content/common/mutable_network_traffic_annotation_tag.typemap b/content/public/common/mutable_network_traffic_annotation_tag.typemap
similarity index 63%
rename from content/common/mutable_network_traffic_annotation_tag.typemap
rename to content/public/common/mutable_network_traffic_annotation_tag.typemap
index dd9917d..986566b1 100644
--- a/content/common/mutable_network_traffic_annotation_tag.typemap
+++ b/content/public/common/mutable_network_traffic_annotation_tag.typemap
@@ -2,8 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-mojom = "//content/common/mutable_network_traffic_annotation_tag.mojom"
-traits_headers = [ "//content/common/mutable_network_traffic_annotation_tag_struct_traits.h" ]
+mojom = "//content/public/common/mutable_network_traffic_annotation_tag.mojom"
+traits_headers = [ "//content/public/common/mutable_network_traffic_annotation_tag_struct_traits.h" ]
 deps = [
   "//net:traffic_annotation",
 ]
diff --git a/content/common/mutable_network_traffic_annotation_tag_struct_traits.h b/content/public/common/mutable_network_traffic_annotation_tag_struct_traits.h
similarity index 72%
rename from content/common/mutable_network_traffic_annotation_tag_struct_traits.h
rename to content/public/common/mutable_network_traffic_annotation_tag_struct_traits.h
index 55f9207..d17df8b 100644
--- a/content/common/mutable_network_traffic_annotation_tag_struct_traits.h
+++ b/content/public/common/mutable_network_traffic_annotation_tag_struct_traits.h
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_COMMON_MUTABLE_NETWORK_TRAFFIC_ANNOTATION_PARAM_TRAITS_H_
-#define CONTENT_COMMON_MUTABLE_NETWORK_TRAFFIC_ANNOTATION_PARAM_TRAITS_H_
+#ifndef CONTENT_PUBLIC_COMMON_MUTABLE_NETWORK_TRAFFIC_ANNOTATION_PARAM_TRAITS_H_
+#define CONTENT_PUBLIC_COMMON_MUTABLE_NETWORK_TRAFFIC_ANNOTATION_PARAM_TRAITS_H_
 
-#include "content/common/mutable_network_traffic_annotation_tag.mojom.h"
+#include "content/public/common/mutable_network_traffic_annotation_tag.mojom.h"
 #include "mojo/common/common_custom_types_struct_traits.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 
@@ -28,4 +28,4 @@
 
 }  // namespace mojo
 
-#endif  // CONTENT_COMMON_MUTABLE_NETWORK_TRAFFIC_ANNOTATION_PARAM_TRAITS_H_
+#endif  // CONTENT_PUBLIC_COMMON_MUTABLE_NETWORK_TRAFFIC_ANNOTATION_PARAM_TRAITS_H_
diff --git a/content/common/network_service.mojom b/content/public/common/network_service.mojom
similarity index 100%
rename from content/common/network_service.mojom
rename to content/public/common/network_service.mojom
diff --git a/content/common/resource_request_completion_status.cc b/content/public/common/resource_request_completion_status.cc
similarity index 91%
rename from content/common/resource_request_completion_status.cc
rename to content/public/common/resource_request_completion_status.cc
index 3720d24f..2056851 100644
--- a/content/common/resource_request_completion_status.cc
+++ b/content/public/common/resource_request_completion_status.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/common/resource_request_completion_status.h"
+#include "content/public/common/resource_request_completion_status.h"
 
 #include "net/base/net_errors.h"
 
diff --git a/content/common/resource_request_completion_status.h b/content/public/common/resource_request_completion_status.h
similarity index 86%
rename from content/common/resource_request_completion_status.h
rename to content/public/common/resource_request_completion_status.h
index 073704e2..2a50977 100644
--- a/content/common/resource_request_completion_status.h
+++ b/content/public/common/resource_request_completion_status.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_COMMON_RESOURCE_REQUEST_COMPLETION_STATUS_H_
-#define CONTENT_COMMON_RESOURCE_REQUEST_COMPLETION_STATUS_H_
+#ifndef CONTENT_PUBLIC_COMMON_RESOURCE_REQUEST_COMPLETION_STATUS_H_
+#define CONTENT_PUBLIC_COMMON_RESOURCE_REQUEST_COMPLETION_STATUS_H_
 
 #include <stdint.h>
 #include <string>
@@ -46,4 +46,4 @@
 
 }  // namespace content
 
-#endif  // CONTENT_COMMON_RESOURCE_REQUEST_COMPLETION_STATUS_H_
+#endif  // CONTENT_PUBLIC_COMMON_RESOURCE_REQUEST_COMPLETION_STATUS_H_
diff --git a/content/public/common/service_worker_modes.typemap b/content/public/common/service_worker_modes.typemap
new file mode 100644
index 0000000..bf6d5651
--- /dev/null
+++ b/content/public/common/service_worker_modes.typemap
@@ -0,0 +1,11 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+mojom = "//content/public/common/service_worker_modes.mojom"
+public_headers = [ "//content/public/common/service_worker_modes.h" ]
+type_mappings = [
+  "blink.mojom.FetchCredentialsMode=::content::FetchCredentialsMode",
+  "blink.mojom.FetchRedirectMode=::content::FetchRedirectMode",
+  "blink.mojom.FetchRequestMode=::content::FetchRequestMode",
+]
diff --git a/content/common/ssl_info.typemap b/content/public/common/ssl_info.typemap
similarity index 87%
rename from content/common/ssl_info.typemap
rename to content/public/common/ssl_info.typemap
index 6e4ee34..e598fc72 100644
--- a/content/common/ssl_info.typemap
+++ b/content/public/common/ssl_info.typemap
@@ -2,7 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-mojom = "//content/common/url_loader.mojom"
+mojom = "//content/public/common/url_loader.mojom"
 public_headers = [ "//net/ssl/ssl_info.h" ]
 traits_headers = [ "//content/common/resource_messages.h" ]
 deps = [
diff --git a/content/public/common/typemaps.gni b/content/public/common/typemaps.gni
index 2f87ff66..a9cff966 100644
--- a/content/public/common/typemaps.gni
+++ b/content/public/common/typemaps.gni
@@ -4,6 +4,13 @@
 
 typemaps = [
   "//content/public/common/manifest.typemap",
+  "//content/public/common/mutable_network_traffic_annotation_tag.typemap",
   "//content/public/common/referrer.typemap",
   "//content/public/common/resource_type.typemap",
+  "//content/public/common/ssl_info.typemap",
+  "//content/public/common/url_loader_status.typemap",
+  "//content/public/common/url_request.typemap",
+  "//content/public/common/url_request_redirect_info.typemap",
+  "//content/public/common/url_response_head.typemap",
+  "//content/public/common/url_sync_load_result.typemap",
 ]
diff --git a/content/common/url_loader.mojom b/content/public/common/url_loader.mojom
similarity index 100%
rename from content/common/url_loader.mojom
rename to content/public/common/url_loader.mojom
diff --git a/content/common/url_loader_factory.mojom b/content/public/common/url_loader_factory.mojom
similarity index 100%
rename from content/common/url_loader_factory.mojom
rename to content/public/common/url_loader_factory.mojom
diff --git a/content/common/url_loader_status.typemap b/content/public/common/url_loader_status.typemap
similarity index 74%
rename from content/common/url_loader_status.typemap
rename to content/public/common/url_loader_status.typemap
index 66e57b63..28c8bd7e 100644
--- a/content/common/url_loader_status.typemap
+++ b/content/public/common/url_loader_status.typemap
@@ -2,8 +2,9 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-mojom = "//content/common/url_loader.mojom"
-public_headers = [ "//content/common/resource_request_completion_status.h" ]
+mojom = "//content/public/common/url_loader.mojom"
+public_headers =
+    [ "//content/public/common/resource_request_completion_status.h" ]
 traits_headers = [ "//content/common/resource_messages.h" ]
 deps = [
   "//content:export",
diff --git a/content/common/url_request.typemap b/content/public/common/url_request.typemap
similarity index 91%
rename from content/common/url_request.typemap
rename to content/public/common/url_request.typemap
index 6a727e1..d910b2a 100644
--- a/content/common/url_request.typemap
+++ b/content/public/common/url_request.typemap
@@ -2,7 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-mojom = "//content/common/url_loader.mojom"
+mojom = "//content/public/common/url_loader.mojom"
 public_headers = [ "//content/public/common/resource_request.h" ]
 traits_headers = [
   "//content/common/resource_messages.h",
diff --git a/content/common/url_request_redirect_info.typemap b/content/public/common/url_request_redirect_info.typemap
similarity index 88%
rename from content/common/url_request_redirect_info.typemap
rename to content/public/common/url_request_redirect_info.typemap
index fcec85b..cf98da5b 100644
--- a/content/common/url_request_redirect_info.typemap
+++ b/content/public/common/url_request_redirect_info.typemap
@@ -2,7 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-mojom = "//content/common/url_loader.mojom"
+mojom = "//content/public/common/url_loader.mojom"
 public_headers = [ "//net/url_request/redirect_info.h" ]
 traits_headers = [ "//content/common/resource_messages.h" ]
 deps = [
diff --git a/content/common/url_response_head.typemap b/content/public/common/url_response_head.typemap
similarity index 89%
rename from content/common/url_response_head.typemap
rename to content/public/common/url_response_head.typemap
index 2918562..e67bc9c4 100644
--- a/content/common/url_response_head.typemap
+++ b/content/public/common/url_response_head.typemap
@@ -2,7 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-mojom = "//content/common/url_loader.mojom"
+mojom = "//content/public/common/url_loader.mojom"
 public_headers = [ "//content/public/common/resource_response.h" ]
 traits_headers = [ "//content/common/resource_messages.h" ]
 deps = [
diff --git a/content/common/url_sync_load_result.typemap b/content/public/common/url_sync_load_result.typemap
similarity index 85%
rename from content/common/url_sync_load_result.typemap
rename to content/public/common/url_sync_load_result.typemap
index a8d96175..4fd35f5 100644
--- a/content/common/url_sync_load_result.typemap
+++ b/content/public/common/url_sync_load_result.typemap
@@ -2,7 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-mojom = "//content/common/url_loader_factory.mojom"
+mojom = "//content/public/common/url_loader_factory.mojom"
 public_headers = [ "//content/public/common/resource_response.h" ]
 traits_headers = [ "//content/common/resource_messages.h" ]
 type_mappings = [ "content.mojom.URLSyncLoadResult=content::SyncLoadResult" ]
diff --git a/content/renderer/media/media_stream_video_capturer_source.cc b/content/renderer/media/media_stream_video_capturer_source.cc
index f4d0568..583eba3 100644
--- a/content/renderer/media/media_stream_video_capturer_source.cc
+++ b/content/renderer/media/media_stream_video_capturer_source.cc
@@ -197,21 +197,7 @@
       device_info().session_id, device_info().device.type, is_secure));
 }
 
-void MediaStreamVideoCapturerSource::GetCurrentSupportedFormats(
-    int max_requested_width,
-    int max_requested_height,
-    double max_requested_frame_rate,
-    const VideoCaptureDeviceFormatsCB& callback) {
-  source_->GetCurrentSupportedFormats(
-      max_requested_width,
-      max_requested_height,
-      max_requested_frame_rate,
-      callback);
-}
-
 void MediaStreamVideoCapturerSource::StartSourceImpl(
-    const media::VideoCaptureFormat& format,
-    const blink::WebMediaConstraints& constraints,
     const VideoCaptureDeliverFrameCB& frame_callback) {
   is_capture_starting_ = true;
   source_->StartCapture(
@@ -225,7 +211,7 @@
 }
 
 base::Optional<media::VideoCaptureFormat>
-MediaStreamVideoCapturerSource::GetCurrentFormatImpl() const {
+MediaStreamVideoCapturerSource::GetCurrentFormat() const {
   return base::Optional<media::VideoCaptureFormat>(
       capture_params_.requested_format);
 }
diff --git a/content/renderer/media/media_stream_video_capturer_source.h b/content/renderer/media/media_stream_video_capturer_source.h
index ee8abe2..f4fa6067 100644
--- a/content/renderer/media/media_stream_video_capturer_source.h
+++ b/content/renderer/media/media_stream_video_capturer_source.h
@@ -48,18 +48,10 @@
   void RequestRefreshFrame() override;
   void OnHasConsumers(bool has_consumers) override;
   void OnCapturingLinkSecured(bool is_secure) override;
-  void GetCurrentSupportedFormats(
-      int max_requested_width,
-      int max_requested_height,
-      double max_requested_frame_rate,
-      const VideoCaptureDeviceFormatsCB& callback) override;
   void StartSourceImpl(
-      const media::VideoCaptureFormat& format,
-      const blink::WebMediaConstraints& constraints,
       const VideoCaptureDeliverFrameCB& frame_callback) override;
   void StopSourceImpl() override;
-  base::Optional<media::VideoCaptureFormat> GetCurrentFormatImpl()
-      const override;
+  base::Optional<media::VideoCaptureFormat> GetCurrentFormat() const override;
 
   // RenderFrameObserver implementation.
   void OnDestruct() final {}
diff --git a/content/renderer/media/media_stream_video_capturer_source_unittest.cc b/content/renderer/media/media_stream_video_capturer_source_unittest.cc
index 11efe04..0735060 100644
--- a/content/renderer/media/media_stream_video_capturer_source_unittest.cc
+++ b/content/renderer/media/media_stream_video_capturer_source_unittest.cc
@@ -33,35 +33,15 @@
 
 class MockVideoCapturerSource : public media::VideoCapturerSource {
  public:
-  MockVideoCapturerSource() {
-    ON_CALL(*this, GetCurrentSupportedFormats(_, _, _, _))
-        .WillByDefault(WithArgs<3>(
-            Invoke(this, &MockVideoCapturerSource::EnumerateDeviceFormats)));
-  }
+  MockVideoCapturerSource() {}
 
   MOCK_METHOD0(RequestRefreshFrame, void());
-  MOCK_METHOD4(GetCurrentSupportedFormats,
-              void(int max_requested_width,
-                   int max_requested_height,
-                   double max_requested_frame_rate,
-                   const VideoCaptureDeviceFormatsCB& callback));
   MOCK_METHOD0(GetPreferredFormats, media::VideoCaptureFormats());
   MOCK_METHOD3(StartCapture,
                void(const media::VideoCaptureParams& params,
                     const VideoCaptureDeliverFrameCB& new_frame_callback,
                     const RunningCallback& running_callback));
   MOCK_METHOD0(StopCapture, void());
-
-  void EnumerateDeviceFormats(const VideoCaptureDeviceFormatsCB& callback) {
-    media::VideoCaptureFormat kFormatSmall(gfx::Size(640, 480), 30.0,
-                                           media::PIXEL_FORMAT_I420);
-    media::VideoCaptureFormat kFormatLarge(gfx::Size(1920, 1080), 30.0,
-                                           media::PIXEL_FORMAT_I420);
-    media::VideoCaptureFormats formats;
-    formats.push_back(kFormatSmall);
-    formats.push_back(kFormatLarge);
-    callback.Run(formats);
-  }
 };
 
 class FakeMediaStreamVideoSink : public MediaStreamVideoSink {
diff --git a/content/renderer/media/media_stream_video_renderer_sink_unittest.cc b/content/renderer/media/media_stream_video_renderer_sink_unittest.cc
index 6ced490..b65d93b 100644
--- a/content/renderer/media/media_stream_video_renderer_sink_unittest.cc
+++ b/content/renderer/media/media_stream_video_renderer_sink_unittest.cc
@@ -40,7 +40,7 @@
  public:
   MediaStreamVideoRendererSinkTest()
       : child_process_(new ChildProcess()),
-        mock_source_(new MockMediaStreamVideoSource(false)) {
+        mock_source_(new MockMediaStreamVideoSource()) {
     blink_source_.Initialize(blink::WebString::FromASCII("dummy_source_id"),
                              blink::WebMediaStreamSource::kTypeVideo,
                              blink::WebString::FromASCII("dummy_source_name"),
diff --git a/content/renderer/media/media_stream_video_source.cc b/content/renderer/media/media_stream_video_source.cc
index 54852968b..203d632 100644
--- a/content/renderer/media/media_stream_video_source.cc
+++ b/content/renderer/media/media_stream_video_source.cc
@@ -61,9 +61,7 @@
   switch (state_) {
     case NEW: {
       state_ = STARTING;
-      blink::WebMediaConstraints ignored_constraints;
       StartSourceImpl(
-          media::VideoCaptureFormat() /* ignored */, ignored_constraints,
           base::Bind(&VideoTrackAdapter::DeliverFrameOnIO, track_adapter_));
       break;
     }
@@ -132,11 +130,6 @@
 base::Optional<media::VideoCaptureFormat>
 MediaStreamVideoSource::GetCurrentFormat() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  return GetCurrentFormatImpl();
-}
-
-base::Optional<media::VideoCaptureFormat>
-MediaStreamVideoSource::GetCurrentFormatImpl() const {
   return base::Optional<media::VideoCaptureFormat>();
 }
 
diff --git a/content/renderer/media/media_stream_video_source.h b/content/renderer/media/media_stream_video_source.h
index c5f03c63..9fdc291 100644
--- a/content/renderer/media/media_stream_video_source.h
+++ b/content/renderer/media/media_stream_video_source.h
@@ -37,13 +37,6 @@
 // MediaStreaVideoSources such as local video capture, video sources received
 // on a PeerConnection or a source created in NaCl.
 // All methods calls will be done from the main render thread.
-//
-// When the first track is added to the source by calling AddTrack, the
-// MediaStreamVideoSource implementation calls GetCurrentSupportedFormats.
-// The source implementation must call OnSupportedFormats.
-// MediaStreamVideoSource then match the constraints provided in AddTrack with
-// the formats and call StartSourceImpl. The source implementation must call
-// OnStartDone when the underlying source has been started or failed to start.
 class CONTENT_EXPORT MediaStreamVideoSource : public MediaStreamSource {
  public:
   enum {
@@ -85,7 +78,8 @@
   // Returns the task runner where video frames will be delivered on.
   base::SingleThreadTaskRunner* io_task_runner() const;
 
-  base::Optional<media::VideoCaptureFormat> GetCurrentFormat() const;
+  // Implementations must return the capture format if available.
+  virtual base::Optional<media::VideoCaptureFormat> GetCurrentFormat() const;
 
   base::WeakPtr<MediaStreamVideoSource> GetWeakPtr() {
     return weak_factory_.GetWeakPtr();
@@ -100,35 +94,11 @@
   // Sets muted state and notifies it to all registered tracks.
   virtual void SetMutedState(bool state);
 
-  // An implementation must fetch the formats that can currently be used by
-  // the source and call OnSupportedFormats when done.
-  // |max_requested_height| and |max_requested_width| is the max height and
-  // width set as a mandatory constraint if set when calling
-  // MediaStreamVideoSource::AddTrack. If max height and max width is not set
-  // |max_requested_height| and |max_requested_width| are 0.
-  // TODO(guidou): Remove when the standard constraints code stabilizes.
-  // http://crbug.com/706408
-  virtual void GetCurrentSupportedFormats(
-      int max_requested_width,
-      int max_requested_height,
-      double max_requested_frame_rate,
-      const VideoCaptureDeviceFormatsCB& callback) = 0;
-
-  // TODO(guidou): Rename to GetCurrentFormat. http://crbug.com/706804
-  virtual base::Optional<media::VideoCaptureFormat> GetCurrentFormatImpl()
-      const;
-
-  // An implementation must start capturing frames using the requested
-  // |format|. The fulfilled |constraints| are provided as additional context,
-  // and may be used to modify the behavior of the source. When the source has
-  // started or the source failed to start OnStartDone must be called. An
-  // implementation must call |frame_callback| on the IO thread with the
+  // An implementation must start capturing frames after this method is called.
+  // When the source has started or failed to start OnStartDone must be called.
+  // An implementation must call |frame_callback| on the IO thread with the
   // captured frames.
-  // TODO(guidou): Remove |format| and |constraints| parameters.
-  // http://crbug.com/706408
   virtual void StartSourceImpl(
-      const media::VideoCaptureFormat& format,
-      const blink::WebMediaConstraints& constraints,
       const VideoCaptureDeliverFrameCB& frame_callback) = 0;
   void OnStartDone(MediaStreamRequestResult result);
 
diff --git a/content/renderer/media/mock_media_stream_registry.cc b/content/renderer/media/mock_media_stream_registry.cc
index 7f10526e..ff5ecdf 100644
--- a/content/renderer/media/mock_media_stream_registry.cc
+++ b/content/renderer/media/mock_media_stream_registry.cc
@@ -66,8 +66,7 @@
   blink_source.Initialize("mock video source id",
                           blink::WebMediaStreamSource::kTypeVideo,
                           "mock video source name", false /* remote */);
-  MockMediaStreamVideoSource* native_source =
-      new MockMediaStreamVideoSource(false /* manual get supported formats */);
+  MockMediaStreamVideoSource* native_source = new MockMediaStreamVideoSource();
   blink_source.SetExtraData(native_source);
   blink::WebMediaStreamTrack blink_track;
   blink_track.Initialize(blink::WebString::FromUTF8(track_id), blink_source);
diff --git a/content/renderer/media/mock_media_stream_video_source.cc b/content/renderer/media/mock_media_stream_video_source.cc
index 05070df..d134d46 100644
--- a/content/renderer/media/mock_media_stream_video_source.cc
+++ b/content/renderer/media/mock_media_stream_video_source.cc
@@ -10,30 +10,21 @@
 
 namespace content {
 
-MockMediaStreamVideoSource::MockMediaStreamVideoSource(
-    bool manual_get_supported_formats)
-    : MockMediaStreamVideoSource(manual_get_supported_formats, false) {}
+MockMediaStreamVideoSource::MockMediaStreamVideoSource()
+    : MockMediaStreamVideoSource(false) {}
 
 MockMediaStreamVideoSource::MockMediaStreamVideoSource(
-    bool manual_get_supported_formats,
     bool respond_to_request_refresh_frame)
-    : manual_get_supported_formats_(manual_get_supported_formats),
-      respond_to_request_refresh_frame_(respond_to_request_refresh_frame),
+    : respond_to_request_refresh_frame_(respond_to_request_refresh_frame),
       max_requested_height_(0),
       max_requested_width_(0),
       max_requested_frame_rate_(0.0),
-      attempted_to_start_(false) {
-  supported_formats_.push_back(media::VideoCaptureFormat(
-      gfx::Size(MediaStreamVideoSource::kDefaultWidth,
-                MediaStreamVideoSource::kDefaultHeight),
-      MediaStreamVideoSource::kDefaultFrameRate, media::PIXEL_FORMAT_I420));
-}
+      attempted_to_start_(false) {}
 
 MockMediaStreamVideoSource::MockMediaStreamVideoSource(
     const media::VideoCaptureFormat& format,
     bool respond_to_request_refresh_frame)
     : format_(format),
-      manual_get_supported_formats_(false),
       respond_to_request_refresh_frame_(respond_to_request_refresh_frame),
       max_requested_height_(format.frame_size.height()),
       max_requested_width_(format.frame_size.width()),
@@ -54,11 +45,6 @@
   OnStartDone(MEDIA_DEVICE_TRACK_START_FAILURE);
 }
 
-void MockMediaStreamVideoSource::CompleteGetSupportedFormats() {
-  DCHECK(!formats_callback_.is_null());
-  base::ResetAndReturn(&formats_callback_).Run(supported_formats_);
-}
-
 void MockMediaStreamVideoSource::RequestRefreshFrame() {
   DCHECK(!frame_callback_.is_null());
   if (respond_to_request_refresh_frame_) {
@@ -70,26 +56,7 @@
   }
 }
 
-void MockMediaStreamVideoSource::GetCurrentSupportedFormats(
-    int max_requested_height,
-    int max_requested_width,
-    double max_requested_frame_rate,
-    const VideoCaptureDeviceFormatsCB& callback) {
-  DCHECK(formats_callback_.is_null());
-  max_requested_height_ = max_requested_height;
-  max_requested_width_ = max_requested_width;
-  max_requested_frame_rate_ = max_requested_frame_rate;
-
-  if (manual_get_supported_formats_) {
-    formats_callback_ = callback;
-    return;
-  }
-  callback.Run(supported_formats_);
-}
-
 void MockMediaStreamVideoSource::StartSourceImpl(
-    const media::VideoCaptureFormat& format,
-    const blink::WebMediaConstraints& constraints,
     const VideoCaptureDeliverFrameCB& frame_callback) {
   DCHECK(frame_callback_.is_null());
   attempted_to_start_ = true;
@@ -100,7 +67,8 @@
 }
 
 base::Optional<media::VideoCaptureFormat>
-MockMediaStreamVideoSource::GetCurrentFormatImpl() const {
+MockMediaStreamVideoSource::GetCurrentFormat() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return base::Optional<media::VideoCaptureFormat>(format_);
 }
 
diff --git a/content/renderer/media/mock_media_stream_video_source.h b/content/renderer/media/mock_media_stream_video_source.h
index 7c4746e..0dac396 100644
--- a/content/renderer/media/mock_media_stream_video_source.h
+++ b/content/renderer/media/mock_media_stream_video_source.h
@@ -14,9 +14,8 @@
 
 class MockMediaStreamVideoSource : public MediaStreamVideoSource {
  public:
-  explicit MockMediaStreamVideoSource(bool manual_get_supported_formats);
-  MockMediaStreamVideoSource(bool manual_get_supported_formats,
-                             bool respond_to_request_refresh_frame);
+  MockMediaStreamVideoSource();
+  explicit MockMediaStreamVideoSource(bool respond_to_request_refresh_frame);
   MockMediaStreamVideoSource(const media::VideoCaptureFormat& format,
                              bool respond_to_request_refresh_frame);
   virtual ~MockMediaStreamVideoSource();
@@ -33,10 +32,6 @@
   // or FailToStartMockedSource has not been called.
   bool SourceHasAttemptedToStart() { return attempted_to_start_; }
 
-  void SetSupportedFormats(const media::VideoCaptureFormats& formats) {
-    supported_formats_ = formats;
-  }
-
   // Delivers |frame| to all registered tracks on the IO thread. Its up to the
   // call to make sure MockMediaStreamVideoSource is not destroyed before the
   // frame has been delivered.
@@ -59,29 +54,18 @@
 
  protected:
   // Implements MediaStreamVideoSource.
-  void GetCurrentSupportedFormats(
-      int max_requested_height,
-      int max_requested_width,
-      double max_requested_frame_rate,
-      const VideoCaptureDeviceFormatsCB& callback) override;
   void StartSourceImpl(
-      const media::VideoCaptureFormat& format,
-      const blink::WebMediaConstraints& constraints,
       const VideoCaptureDeliverFrameCB& frame_callback) override;
   void StopSourceImpl() override;
-  base::Optional<media::VideoCaptureFormat> GetCurrentFormatImpl()
-      const override;
+  base::Optional<media::VideoCaptureFormat> GetCurrentFormat() const override;
 
  private:
   media::VideoCaptureFormat format_;
-  media::VideoCaptureFormats supported_formats_;
-  bool manual_get_supported_formats_;
   bool respond_to_request_refresh_frame_;
   int max_requested_height_;
   int max_requested_width_;
   double max_requested_frame_rate_;
   bool attempted_to_start_;
-  VideoCaptureDeviceFormatsCB formats_callback_;
   VideoCaptureDeliverFrameCB frame_callback_;
 
   DISALLOW_COPY_AND_ASSIGN(MockMediaStreamVideoSource);
diff --git a/content/renderer/media/pepper_to_video_track_adapter.cc b/content/renderer/media/pepper_to_video_track_adapter.cc
index 104bd01..ca3b2db 100644
--- a/content/renderer/media/pepper_to_video_track_adapter.cc
+++ b/content/renderer/media/pepper_to_video_track_adapter.cc
@@ -46,14 +46,7 @@
 
  protected:
   // MediaStreamVideoSource implementation.
-  void GetCurrentSupportedFormats(
-      int max_requested_width,
-      int max_requested_height,
-      double max_requested_frame_rate,
-      const VideoCaptureDeviceFormatsCB& callback) override;
   void StartSourceImpl(
-      const media::VideoCaptureFormat& format,
-      const blink::WebMediaConstraints& constraints,
       const VideoCaptureDeliverFrameCB& frame_callback) override;
   void StopSourceImpl() override;
 
@@ -116,22 +109,7 @@
   DVLOG(3) << "PpFrameWriter dtor";
 }
 
-void PpFrameWriter::GetCurrentSupportedFormats(
-    int max_requested_width,
-    int max_requested_height,
-    double max_requested_frame_rate,
-    const VideoCaptureDeviceFormatsCB& callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DVLOG(3) << "PpFrameWriter::GetCurrentSupportedFormats()";
-  // Since the input is free to change the resolution at any point in time
-  // the supported formats are unknown.
-  media::VideoCaptureFormats formats;
-  callback.Run(formats);
-}
-
 void PpFrameWriter::StartSourceImpl(
-    const media::VideoCaptureFormat& format,
-    const blink::WebMediaConstraints& constraints,
     const VideoCaptureDeliverFrameCB& frame_callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!delegate_.get());
diff --git a/content/renderer/media/rtc_peer_connection_handler_unittest.cc b/content/renderer/media/rtc_peer_connection_handler_unittest.cc
index 66d6be9..91cb0f5 100644
--- a/content/renderer/media/rtc_peer_connection_handler_unittest.cc
+++ b/content/renderer/media/rtc_peer_connection_handler_unittest.cc
@@ -313,7 +313,7 @@
                             blink::WebString::FromUTF8("video_track"),
                             false /* remote */);
     MockMediaStreamVideoSource* native_video_source =
-        new MockMediaStreamVideoSource(false);
+        new MockMediaStreamVideoSource();
     video_source.SetExtraData(native_video_source);
 
     blink::WebVector<blink::WebMediaStreamTrack> audio_tracks(
diff --git a/content/renderer/media/user_media_client_impl_unittest.cc b/content/renderer/media/user_media_client_impl_unittest.cc
index b5505f0..0230fdb 100644
--- a/content/renderer/media/user_media_client_impl_unittest.cc
+++ b/content/renderer/media/user_media_client_impl_unittest.cc
@@ -98,11 +98,10 @@
 
 class MockMediaStreamVideoCapturerSource : public MockMediaStreamVideoSource {
  public:
-  MockMediaStreamVideoCapturerSource(
-      const StreamDeviceInfo& device,
-      const SourceStoppedCallback& stop_callback,
-      PeerConnectionDependencyFactory* factory)
-  : MockMediaStreamVideoSource(false) {
+  MockMediaStreamVideoCapturerSource(const StreamDeviceInfo& device,
+                                     const SourceStoppedCallback& stop_callback,
+                                     PeerConnectionDependencyFactory* factory)
+      : MockMediaStreamVideoSource() {
     SetDeviceInfo(device);
     SetStopCallback(stop_callback);
   }
diff --git a/content/renderer/media/webrtc/media_stream_remote_video_source.cc b/content/renderer/media/webrtc/media_stream_remote_video_source.cc
index bf34d04..bfadcb9f 100644
--- a/content/renderer/media/webrtc/media_stream_remote_video_source.cc
+++ b/content/renderer/media/webrtc/media_stream_remote_video_source.cc
@@ -186,21 +186,7 @@
   StopSourceImpl();
 }
 
-void MediaStreamRemoteVideoSource::GetCurrentSupportedFormats(
-    int max_requested_width,
-    int max_requested_height,
-    double max_requested_frame_rate,
-    const VideoCaptureDeviceFormatsCB& callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  media::VideoCaptureFormats formats;
-  // Since the remote end is free to change the resolution at any point in time
-  // the supported formats are unknown.
-  callback.Run(formats);
-}
-
 void MediaStreamRemoteVideoSource::StartSourceImpl(
-    const media::VideoCaptureFormat& format,
-    const blink::WebMediaConstraints& constraints,
     const VideoCaptureDeliverFrameCB& frame_callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!delegate_.get());
diff --git a/content/renderer/media/webrtc/media_stream_remote_video_source.h b/content/renderer/media/webrtc/media_stream_remote_video_source.h
index b3d50cd..0355ae8 100644
--- a/content/renderer/media/webrtc/media_stream_remote_video_source.h
+++ b/content/renderer/media/webrtc/media_stream_remote_video_source.h
@@ -5,6 +5,8 @@
 #ifndef CONTENT_RENDERER_MEDIA_WEBRTC_MEDIA_STREAM_REMOTE_VIDEO_SOURCE_H_
 #define CONTENT_RENDERER_MEDIA_WEBRTC_MEDIA_STREAM_REMOTE_VIDEO_SOURCE_H_
 
+#include <memory>
+
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/single_thread_task_runner.h"
@@ -24,7 +26,8 @@
 class CONTENT_EXPORT MediaStreamRemoteVideoSource
      : public MediaStreamVideoSource {
  public:
-  MediaStreamRemoteVideoSource(std::unique_ptr<TrackObserver> observer);
+  explicit MediaStreamRemoteVideoSource(
+      std::unique_ptr<TrackObserver> observer);
   ~MediaStreamRemoteVideoSource() override;
 
   // Should be called when the remote video track this source originates from is
@@ -34,15 +37,7 @@
 
  protected:
   // Implements MediaStreamVideoSource.
-  void GetCurrentSupportedFormats(
-      int max_requested_width,
-      int max_requested_height,
-      double max_requested_frame_rate,
-      const VideoCaptureDeviceFormatsCB& callback) override;
-
   void StartSourceImpl(
-      const media::VideoCaptureFormat& format,
-      const blink::WebMediaConstraints& constraints,
       const VideoCaptureDeliverFrameCB& frame_callback) override;
 
   void StopSourceImpl() override;
diff --git a/content/renderer/media/webrtc/webrtc_media_stream_adapter_map_unittest.cc b/content/renderer/media/webrtc/webrtc_media_stream_adapter_map_unittest.cc
index db43fcdd..4ecec10 100644
--- a/content/renderer/media/webrtc/webrtc_media_stream_adapter_map_unittest.cc
+++ b/content/renderer/media/webrtc/webrtc_media_stream_adapter_map_unittest.cc
@@ -5,6 +5,7 @@
 #include "content/renderer/media/webrtc/webrtc_media_stream_adapter_map.h"
 
 #include <memory>
+#include <string>
 
 #include "base/memory/ref_counted.h"
 #include "base/message_loop/message_loop.h"
@@ -50,8 +51,7 @@
     video_source.Initialize("video_source",
                             blink::WebMediaStreamSource::kTypeVideo,
                             "video_source", false /* remote */);
-    MediaStreamVideoSource* native_source =
-        new MockMediaStreamVideoSource(false);
+    MediaStreamVideoSource* native_source = new MockMediaStreamVideoSource();
     video_source.SetExtraData(native_source);
     web_video_tracks[0] = MediaStreamVideoTrack::CreateVideoTrack(
         native_source, MediaStreamVideoSource::ConstraintsCallback(), true);
diff --git a/content/renderer/media/webrtc/webrtc_media_stream_adapter_unittest.cc b/content/renderer/media/webrtc/webrtc_media_stream_adapter_unittest.cc
index 1d4204eb..6c26895c3 100644
--- a/content/renderer/media/webrtc/webrtc_media_stream_adapter_unittest.cc
+++ b/content/renderer/media/webrtc/webrtc_media_stream_adapter_unittest.cc
@@ -83,8 +83,7 @@
       blink::WebMediaStreamSource video_source;
       video_source.Initialize("video", blink::WebMediaStreamSource::kTypeVideo,
                               "video", false /* remote */);
-      MediaStreamVideoSource* native_source =
-          new MockMediaStreamVideoSource(false);
+      MediaStreamVideoSource* native_source = new MockMediaStreamVideoSource();
       video_source.SetExtraData(native_source);
       video_track_vector[0] = MediaStreamVideoTrack::CreateVideoTrack(
           native_source, MediaStreamVideoSource::ConstraintsCallback(), true);
diff --git a/content/renderer/media/webrtc/webrtc_media_stream_track_adapter_unittest.cc b/content/renderer/media/webrtc/webrtc_media_stream_track_adapter_unittest.cc
index 2a14cc4..247b3096 100644
--- a/content/renderer/media/webrtc/webrtc_media_stream_track_adapter_unittest.cc
+++ b/content/renderer/media/webrtc/webrtc_media_stream_track_adapter_unittest.cc
@@ -65,8 +65,7 @@
                           blink::WebMediaStreamSource::kTypeVideo,
                           blink::WebString::FromUTF8("local_video_track"),
                           false);
-    MockMediaStreamVideoSource* video_source =
-        new MockMediaStreamVideoSource(true);
+    MockMediaStreamVideoSource* video_source = new MockMediaStreamVideoSource();
     // Takes ownership of |video_source|.
     web_source.SetExtraData(video_source);
 
diff --git a/content/renderer/media_capture_from_element/canvas_capture_handler_unittest.cc b/content/renderer/media_capture_from_element/canvas_capture_handler_unittest.cc
index 76a214b..c3af903 100644
--- a/content/renderer/media_capture_from_element/canvas_capture_handler_unittest.cc
+++ b/content/renderer/media_capture_from_element/canvas_capture_handler_unittest.cc
@@ -74,12 +74,6 @@
     DoOnDeliverFrame(video_frame, estimated_capture_time);
   }
 
-  MOCK_METHOD1(DoOnVideoCaptureDeviceFormats,
-               void(const media::VideoCaptureFormats&));
-  void OnVideoCaptureDeviceFormats(const media::VideoCaptureFormats& formats) {
-    DoOnVideoCaptureDeviceFormats(formats);
-  }
-
   MOCK_METHOD1(DoOnRunning, void(bool));
   void OnRunning(bool state) { DoOnRunning(state); }
 
@@ -174,16 +168,7 @@
   media::VideoCapturerSource* source = GetVideoCapturerSource(ms_source);
   EXPECT_TRUE(source != nullptr);
 
-  media::VideoCaptureFormats formats;
-  EXPECT_CALL(*this, DoOnVideoCaptureDeviceFormats(_))
-      .Times(1)
-      .WillOnce(SaveArg<0>(&formats));
-  source->GetCurrentSupportedFormats(
-      media::limits::kMaxCanvas /* max_requesteed_width */,
-      media::limits::kMaxCanvas /* max_requesteed_height */,
-      media::limits::kMaxFramesPerSecond /* max_requested_frame_rate */,
-      base::Bind(&CanvasCaptureHandlerTest::OnVideoCaptureDeviceFormats,
-                 base::Unretained(this)));
+  media::VideoCaptureFormats formats = source->GetPreferredFormats();
   ASSERT_EQ(2u, formats.size());
   EXPECT_EQ(kTestCanvasCaptureWidth, formats[0].frame_size.width());
   EXPECT_EQ(kTestCanvasCaptureHeight, formats[0].frame_size.height());
diff --git a/content/renderer/media_capture_from_element/html_video_element_capturer_source_unittest.cc b/content/renderer/media_capture_from_element/html_video_element_capturer_source_unittest.cc
index 8f2ffa07..95e73fd 100644
--- a/content/renderer/media_capture_from_element/html_video_element_capturer_source_unittest.cc
+++ b/content/renderer/media_capture_from_element/html_video_element_capturer_source_unittest.cc
@@ -99,12 +99,6 @@
     DoOnDeliverFrame(video_frame, estimated_capture_time);
   }
 
-  MOCK_METHOD1(DoOnVideoCaptureDeviceFormats,
-               void(const media::VideoCaptureFormats&));
-  void OnVideoCaptureDeviceFormats(const media::VideoCaptureFormats& formats) {
-    DoOnVideoCaptureDeviceFormats(formats);
-  }
-
   MOCK_METHOD1(DoOnRunning, void(bool));
   void OnRunning(bool state) { DoOnRunning(state); }
 
@@ -126,18 +120,8 @@
 // frames.
 TEST_F(HTMLVideoElementCapturerSourceTest, GetFormatsAndStartAndStop) {
   InSequence s;
-  media::VideoCaptureFormats formats;
-  EXPECT_CALL(*this, DoOnVideoCaptureDeviceFormats(_))
-      .Times(1)
-      .WillOnce(SaveArg<0>(&formats));
-
-  html_video_capturer_->GetCurrentSupportedFormats(
-      media::limits::kMaxCanvas /* max_requesteed_width */,
-      media::limits::kMaxCanvas /* max_requesteed_height */,
-      media::limits::kMaxFramesPerSecond /* max_requested_frame_rate */,
-      base::Bind(
-          &HTMLVideoElementCapturerSourceTest::OnVideoCaptureDeviceFormats,
-          base::Unretained(this)));
+  media::VideoCaptureFormats formats =
+      html_video_capturer_->GetPreferredFormats();
   ASSERT_EQ(1u, formats.size());
   EXPECT_EQ(web_media_player_->NaturalSize().width,
             formats[0].frame_size.width());
diff --git a/content/renderer/media_recorder/video_track_recorder_unittest.cc b/content/renderer/media_recorder/video_track_recorder_unittest.cc
index 287127c..572c216 100644
--- a/content/renderer/media_recorder/video_track_recorder_unittest.cc
+++ b/content/renderer/media_recorder/video_track_recorder_unittest.cc
@@ -61,8 +61,7 @@
     : public TestWithParam<
           testing::tuple<VideoTrackRecorder::CodecId, gfx::Size, bool>> {
  public:
-  VideoTrackRecorderTest()
-      : mock_source_(new MockMediaStreamVideoSource(false)) {
+  VideoTrackRecorderTest() : mock_source_(new MockMediaStreamVideoSource()) {
     const blink::WebString webkit_track_id(
         blink::WebString::FromASCII("dummy"));
     blink_source_.Initialize(webkit_track_id,
diff --git a/content/renderer/pepper/pepper_media_stream_video_track_host.cc b/content/renderer/pepper/pepper_media_stream_video_track_host.cc
index 4924a98..b486a17 100644
--- a/content/renderer/pepper/pepper_media_stream_video_track_host.cc
+++ b/content/renderer/pepper/pepper_media_stream_video_track_host.cc
@@ -414,23 +414,7 @@
 
   ~VideoSource() final { StopSourceImpl(); }
 
-  void GetCurrentSupportedFormats(
-      int max_requested_width, int max_requested_height,
-      double max_requested_frame_rate,
-      const VideoCaptureDeviceFormatsCB& callback) final {
-    media::VideoCaptureFormats formats;
-    if (host_) {
-      formats.push_back(media::VideoCaptureFormat(
-          host_->plugin_frame_size_,
-          kDefaultOutputFrameRate,
-          ToPixelFormat(host_->plugin_frame_format_)));
-    }
-    callback.Run(formats);
-  }
-
   void StartSourceImpl(
-      const media::VideoCaptureFormat& format,
-      const blink::WebMediaConstraints& constraints,
       const VideoCaptureDeliverFrameCB& frame_callback) final {
     if (host_) {
       host_->frame_deliverer_ =
@@ -444,8 +428,7 @@
   }
 
  private:
-  base::Optional<media::VideoCaptureFormat> GetCurrentFormatImpl()
-      const override {
+  base::Optional<media::VideoCaptureFormat> GetCurrentFormat() const override {
     if (host_) {
       return base::Optional<media::VideoCaptureFormat>(
           media::VideoCaptureFormat(
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index 71fd95c..7683fb5 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -37,7 +37,6 @@
 #include "content/common/host_zoom.mojom.h"
 #include "content/common/renderer.mojom.h"
 #include "content/common/unique_name_helper.h"
-#include "content/common/url_loader_factory.mojom.h"
 #include "content/public/common/console_message_level.h"
 #include "content/public/common/javascript_dialog_type.h"
 #include "content/public/common/previews_state.h"
@@ -45,6 +44,7 @@
 #include "content/public/common/renderer_preferences.h"
 #include "content/public/common/request_context_type.h"
 #include "content/public/common/stop_find_action.h"
+#include "content/public/common/url_loader_factory.mojom.h"
 #include "content/public/renderer/render_frame.h"
 #include "content/renderer/frame_blame_context.h"
 #include "content/renderer/media/media_factory.h"
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index 8cf0aef8..6ae346c 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -2517,11 +2517,13 @@
   v8::MemoryPressureLevel v8_memory_pressure_level =
       static_cast<v8::MemoryPressureLevel>(memory_pressure_level);
 
+#if !BUILDFLAG(ALLOW_CRITICAL_MEMORY_PRESSURE_HANDLING_IN_FOREGROUND)
   // In order to reduce performance impact, translate critical level to
-  // moderate level for foregroud renderer.
+  // moderate level for foreground renderer.
   if (!RendererIsHidden() &&
       v8_memory_pressure_level == v8::MemoryPressureLevel::kCritical)
     v8_memory_pressure_level = v8::MemoryPressureLevel::kModerate;
+#endif  // !BUILDFLAG(ALLOW_CRITICAL_MEMORY_PRESSURE_HANDLING_IN_FOREGROUND)
 
   blink::MainThreadIsolate()->MemoryPressureNotification(
       v8_memory_pressure_level);
diff --git a/content/renderer/render_thread_impl.h b/content/renderer/render_thread_impl.h
index a05e2740..e653a59 100644
--- a/content/renderer/render_thread_impl.h
+++ b/content/renderer/render_thread_impl.h
@@ -42,7 +42,7 @@
 #include "content/common/renderer.mojom.h"
 #include "content/common/renderer_host.mojom.h"
 #include "content/common/storage_partition_service.mojom.h"
-#include "content/common/url_loader_factory.mojom.h"
+#include "content/public/common/url_loader_factory.mojom.h"
 #include "content/public/renderer/render_thread.h"
 #include "content/renderer/gpu/compositor_dependencies.h"
 #include "content/renderer/layout_test_dependencies.h"
diff --git a/content/renderer/renderer_blink_platform_impl.h b/content/renderer/renderer_blink_platform_impl.h
index 3ea9374..c5614a1 100644
--- a/content/renderer/renderer_blink_platform_impl.h
+++ b/content/renderer/renderer_blink_platform_impl.h
@@ -20,7 +20,7 @@
 #include "content/child/blink_platform_impl.h"
 #include "content/common/content_export.h"
 #include "content/common/possibly_associated_interface_ptr.h"
-#include "content/common/url_loader_factory.mojom.h"
+#include "content/public/common/url_loader_factory.mojom.h"
 #include "content/renderer/origin_trials/web_trial_token_validator_impl.h"
 #include "content/renderer/top_level_blame_context.h"
 #include "content/renderer/webpublicsuffixlist_impl.h"
diff --git a/content/shell/BUILD.gn b/content/shell/BUILD.gn
index 785bd7a..8003b96 100644
--- a/content/shell/BUILD.gn
+++ b/content/shell/BUILD.gn
@@ -231,7 +231,7 @@
     "//content/app/resources",
     "//content/app/strings",
     "//content/gpu",
-    "//content/public/common:interfaces",
+    "//content/public/common",
     "//content/public/common:service_names",
     "//content/shell/test_runner:test_runner",
     "//content/test:content_test_mojo_bindings",
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index c44a09e..0a63b0d 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -262,7 +262,7 @@
     "//content/gpu",
     "//content/public/browser",
     "//content/public/child",
-    "//content/public/common:interfaces",
+    "//content/public/common",
     "//content/public/common:service_names",
     "//content/public/renderer",
     "//content/public/utility",
@@ -350,9 +350,9 @@
     deps += [
       "//third_party/webrtc/api:libjingle_peerconnection",
       "//third_party/webrtc/api:rtc_stats_api",
-      "//third_party/webrtc/rtc_base:rtc_base_approved",
       "//third_party/webrtc/media:rtc_media_base",
       "//third_party/webrtc/modules/video_capture",
+      "//third_party/webrtc/rtc_base:rtc_base_approved",
       "//third_party/webrtc/stats:rtc_stats",
       "//third_party/webrtc_overrides:init_webrtc",
     ]
@@ -1699,10 +1699,10 @@
       "//third_party/webrtc/api:rtc_stats_api",
       "//third_party/webrtc/api:video_frame_api",
       "//third_party/webrtc/api/video_codecs:video_codecs_api",
-      "//third_party/webrtc/rtc_base:rtc_base",
       "//third_party/webrtc/media:rtc_media",
       "//third_party/webrtc/modules/desktop_capture:primitives",
       "//third_party/webrtc/modules/video_capture",
+      "//third_party/webrtc/rtc_base:rtc_base",
       "//third_party/webrtc/stats:rtc_stats_test_utils",
       "//third_party/webrtc_overrides:init_webrtc",
       "//ui/shell_dialogs:shell_dialogs",
diff --git a/content/test/test_navigation_url_loader.cc b/content/test/test_navigation_url_loader.cc
index 7ea49f2..6a7477d 100644
--- a/content/test/test_navigation_url_loader.cc
+++ b/content/test/test_navigation_url_loader.cc
@@ -7,7 +7,6 @@
 #include <utility>
 
 #include "content/browser/loader/navigation_url_loader_delegate.h"
-#include "content/common/url_loader_factory.mojom.h"
 #include "content/public/browser/global_request_id.h"
 #include "content/public/browser/navigation_data.h"
 #include "content/public/browser/render_process_host.h"
@@ -16,6 +15,7 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/browser_side_navigation_policy.h"
 #include "content/public/common/resource_response.h"
+#include "content/public/common/url_loader_factory.mojom.h"
 #include "net/url_request/redirect_info.h"
 
 namespace content {
diff --git a/docs/README.md b/docs/README.md
index b4601df..d9f77bd 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -294,6 +294,8 @@
     used to automatically set proxy settings.
 *   [Installing Chromium OS on VMWare](installation_at_vmware.md) - How to
     install Chromium OS on VMWare.
+*   [User Data Directory](user_data_dir.md) - How the user data and cache
+    directories are determined on all platforms.
 
 ### Probably Obsolete
 *   [Old ChromeOS build instructions](old_chromeos_build_instructions.md)
diff --git a/docs/user_data_dir.md b/docs/user_data_dir.md
new file mode 100644
index 0000000..64643d1
--- /dev/null
+++ b/docs/user_data_dir.md
@@ -0,0 +1,219 @@
+# User Data Directory
+
+[TOC]
+
+## Introduction
+
+The user data directory contains profile data such as history, bookmarks, and
+cookies, as well as other per-installation local state.
+
+Each [profile](https://support.google.com/chrome/answer/2364824) is a
+subdirectory (often `Default`) within the user data directory.
+
+## Current Location
+
+To determine the user data directory for a running Chrome instance:
+
+1. Navigate to `chrome://version`
+2. Look for the `Profile Path` field.  This gives the path to the profile
+   directory.
+3. The user data directory is the parent of the profile directory.
+
+Example (Windows):
+
+* [Profile Path] `C:\Users\Alice\AppData\Local\Google\Chrome\User Data\Default`
+* [User Data Dir] `C:\Users\Alice\AppData\Local\Google\Chrome\User Data`
+
+## Default Location
+
+The default location of the user data directory is computed by
+[`chrome::GetDefaultUserDataDirectory`](https://cs.chromium.org/chromium/src/chrome/common/chrome_paths_internal.h?q=GetDefaultUserDataDirectory).
+
+Generally it varies by
+
+* OS platform,
+* branding ([Chrome vs. Chromium](chromium_browser_vs_google_chrome.md), based
+  on `is_chrome_branded` in [GN
+  args](https://www.chromium.org/developers/gn-build-configuration)), and
+* [release channel](https://www.chromium.org/getting-involved/dev-channel)
+  (stable / beta / dev / canary).
+
+### Windows
+
+The default location is in the local app data folder:
+
+* [Chrome] `%LOCALAPPDATA%\Google\Chrome\User Data`
+* [Chrome Canary] `%LOCALAPPDATA%\Google\Chrome SxS\User Data`
+* [Chromium] `%LOCALAPPDATA%\Chromium\User Data`
+
+(The canary channel suffix is determined using
+[`InstallConstants::install_suffix`](https://cs.chromium.org/chromium/src/chrome/install_static/install_constants.h?q=install_suffix).)
+
+### Mac OS X
+
+The default location is in the `Application Support` folder:
+
+* [Chrome] `~/Library/Application Support/Google/Chrome`
+* [Chrome Canary] `~/Library/Application Support/Google/Chrome Canary`
+* [Chromium] `~/Library/Application Support/Chromium`
+
+(The canary channel suffix is determined using the `CrProductDirName` key in the
+browser app's `Info.plist`.)
+
+### Linux
+
+The default location is in `~/.config`:
+
+* [Chrome Stable] `~/.config/google-chrome`
+* [Chrome Beta] `~/.config/google-chrome-beta`
+* [Chrome Dev] `~/.config/google-chrome-unstable`
+* [Chromium] `~/.config/chromium`
+
+(The beta and dev channel suffixes are determined from `$CHROME_VERSION_EXTRA`,
+which is passed by the [launch wrapper script](https://cs.chromium.org/chromium/src/chrome/installer/linux/common/wrapper?q=CHROME_VERSION_EXTRA).)
+
+The `~/.config` portion of the default location can be overridden by
+`$CHROME_CONFIG_HOME` (since M61) or by `$XDG_CONFIG_HOME`.
+
+Note that `$XDG_CONFIG_HOME` affects all applications conforming to the
+[XDG Base Directory Spec](https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html),
+while `$CHROME_CONFIG_HOME` is specific to Chrome and Chromium.
+
+### Chrome OS
+
+The default location is: `/home/chronos`
+
+### Android
+
+The default location comes from
+[Context.getDir](https://developer.android.com/reference/android/content/Context.html#getDir(java.lang.String, int))
+and is specific to the app.
+
+Example: `/data/user/0/com.android.chrome/app_chrome`
+
+### iOS
+
+The default location is inside the application support directory in the app
+sandbox.
+
+* [Chrome] `Library/Application Support/Google/Chrome`
+* [Chromium] `Library/Application Support/Chromium`
+
+## Overriding the User Data Directory
+
+### Command Line
+
+On all platforms, the user data directory can be overridden by passing the
+`--user-data-dir` command-line flag to the Chrome binary.
+
+Example:
+
+* [Windows] `chrome.exe --user-data-dir=c:\foo`
+* [Linux] `google-chrome --user-data-dir=/path/to/foo`
+
+### Environment (Linux)
+
+On Linux, the user data directory can also be overridden with the
+`$CHROME_USER_DATA_DIR` environment variable.
+
+The `--user-data-dir` flag takes precedence if both are present.
+
+### Chrome Remote Desktop sessions (Linux)
+
+A single Chrome instance cannot show windows on multiple X displays, and two
+running Chrome instances cannot share the same user data directory.
+Therefore, it's desirable for Chrome to have a separate user data directory
+when running inside a [Chrome Remote
+Desktop](https://support.google.com/chrome/answer/1649523) (CRD) virtual session
+on a Linux host.
+
+By default, CRD achieves this by setting `$CHROME_USER_DATA_DIR` in the session.
+Unfortunately this means that inside the session we don't get separate defaults
+for different channels (Stable, Beta, Dev) or for Chrome vs. Chromium.  This can
+lead to profile version errors ("Your profile can not be used because it is from
+a newer version of Google Chrome").
+
+Since M61, this can be solved by setting `$CHROME_CONFIG_HOME` instead of
+`$CHROME_USER_DATA_DIR`. Specifically, put the following in
+`~/.chrome-remote-desktop-session`:
+
+```
+export CHROME_CONFIG_HOME="$HOME/.config/chrome-remote-desktop/chrome-config"
+unset CHROME_USER_DATA_DIR
+. /etc/chrome-remote-desktop-session
+```
+
+Then restart the host by running: `/etc/init.d/chrome-remote-desktop restart`
+
+### Writing an AppleScript wrapper (Mac OS X)
+
+On Mac OS X, you can create an application that runs Chrome with a custom
+`--user-data-dir`:
+
+1. Open Applications > Utilities > Script Editor.
+
+2. Enter:
+
+```
+set chrome to "\"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome\""
+set userdatadir to "\"$HOME/Library/Application Support/Google/Chrome Alt\""
+do shell script chrome & " --user-data-dir=" & userdatadir & " > /dev/null 2>&1 &"
+```
+
+3. Modify as needed for your installation path, Chrome versus Chromium, and
+   desired user data directory.
+
+4. Save the script in your Applications directory with the file format
+   "Application".
+
+5. Close the Script Editor, find your newly created application, and run it.
+   This opens a Chrome instance pointing to your new profile.
+
+If you want, you can give this application the same icon as Chrome:
+
+1. Select the Google Chrome application and choose File > Get Info.
+2. Select the icon at the top left of the info dialog.  You will see a blue
+   highlight around the icon.
+3. Press &#8984;C to copy the icon.
+4. Open the info dialog for the new application and select the icon in the
+   top left.
+5. Press &#8984;V to paste the copied icon.
+
+## User Cache Directory
+
+On Windows and ChromeOS, the user cache dir is the same as the profile dir.
+(The profile dir is inside the user data dir.)
+
+On Mac OS X and iOS, the user cache dir is derived from the profile dir as
+follows:
+
+1. If `Library/Application Support` is an ancestor of the profile dir, the user
+   cache dir is `Library/Caches` plus the relative path from `Application
+   Support` to the profile dir.
+2. Otherwise, the user cache dir is the same as the profile dir.
+
+Example (Mac OS X):
+
+* [user data dir] `~/Library/Application Support/Google/Chrome`
+* [profile dir] `~/Library/Application Support/Google/Chrome/Default`
+* [user cache dir] `~/Library/Caches/Google/Chrome/Default`
+
+On Linux, the user cache dir is derived from the profile dir as follows:
+
+1. Determine the system config dir.  This is `~/.config`, unless overridden by
+   `$XDG_CONFIG_HOME`.  (This step ignores `$CHROME_CONFIG_HOME`.)
+2. Determine the system cache dir.  This is `~/.cache`, unless overridden by
+   `$XDG_CACHE_HOME`.
+3. If the system config dir is an ancestor of the profile dir, the user cache
+   dir is the system cache dir plus the relative path from the system config
+   dir to the profile dir.
+4. Otherwise, the user cache dir is the same as the profile dir.
+
+Example (Linux):
+
+* [user data dir] `~/.config/google-chrome`
+* [profile dir] `~/.config/google-chrome/Default`
+* [user cache dir] `~/.cache/google-chrome/Default`
+
+On Android, the user cache directory comes from
+[Context.getCacheDir](https://developer.android.com/reference/android/content/Context.html#getCacheDir()).
diff --git a/ios/build/bots/tests/common_tests.json b/ios/build/bots/tests/common_tests.json
index d720305..f0e18794 100644
--- a/ios/build/bots/tests/common_tests.json
+++ b/ios/build/bots/tests/common_tests.json
@@ -16,9 +16,6 @@
       "app": "ios_net_unittests"
     },
     {
-      "app": "ios_web_view_inttests"
-    },
-    {
       "app": "net_unittests"
     },
     {
diff --git a/ios/chrome/browser/payments/payment_request.h b/ios/chrome/browser/payments/payment_request.h
index 52a58db..1abcb8d 100644
--- a/ios/chrome/browser/payments/payment_request.h
+++ b/ios/chrome/browser/payments/payment_request.h
@@ -12,6 +12,7 @@
 #include <vector>
 
 #include "base/macros.h"
+#include "base/memory/weak_ptr.h"
 #include "components/autofill/core/browser/credit_card.h"
 #include "components/payments/core/payment_options_provider.h"
 #include "components/payments/core/payment_request_base_delegate.h"
@@ -28,6 +29,8 @@
 class AddressNormalizer;
 class AddressNormalizerImpl;
 class CurrencyFormatter;
+class PaymentInstrument;
+class AutofillPaymentInstrument;
 }  // namespace payments
 
 namespace ios {
@@ -39,7 +42,11 @@
 // initiating UI to request full card details for payment.
 @protocol PaymentRequestUIDelegate<NSObject>
 
-- (void)openFullCardRequestUI;
+- (void)
+requestFullCreditCard:(const autofill::CreditCard&)creditCard
+       resultDelegate:
+           (base::WeakPtr<autofill::payments::FullCardRequest::ResultDelegate>)
+               resultDelegate;
 
 @end
 
@@ -151,10 +158,6 @@
     return supported_card_networks_;
   }
 
-  const std::set<std::string>& basic_card_specified_networks() const {
-    return basic_card_specified_networks_;
-  }
-
   const std::map<std::string, std::set<std::string>>& stringified_method_data()
       const {
     return stringified_method_data_;
@@ -165,27 +168,27 @@
     return supported_card_types_set_;
   }
 
-  // Adds |credit_card| to the list of cached credit cards, updates the list of
-  // available credit cards, and returns a reference to the cached copy of
+  // Creates and adds an AutofillPaymentInstrument, which makes a copy of
   // |credit_card|.
-  virtual autofill::CreditCard* AddCreditCard(
+  virtual payments::AutofillPaymentInstrument* AddAutofillPaymentInstrument(
       const autofill::CreditCard& credit_card);
 
-  // Returns the available autofill credit cards for this user that match a
-  // supported type specified in |web_payment_request_|.
-  const std::vector<autofill::CreditCard*>& credit_cards() const {
-    return credit_cards_;
+  // Returns the available payment methods for this user that match a supported
+  // type specified in |web_payment_request_|.
+  const std::vector<payments::PaymentInstrument*>& payment_methods() const {
+    return payment_methods_;
   }
 
-  // Returns the currently selected credit card for this PaymentRequest flow if
-  // there is one. Returns nullptr if there is no selected credit card.
-  autofill::CreditCard* selected_credit_card() const {
-    return selected_credit_card_;
+  // Returns the currently selected payment method for this PaymentRequest flow
+  // if there is one. Returns nullptr if there is no selected payment method.
+  payments::PaymentInstrument* selected_payment_method() const {
+    return selected_payment_method_;
   }
 
-  // Sets the currently selected credit card for this PaymentRequest flow.
-  void set_selected_credit_card(autofill::CreditCard* credit_card) {
-    selected_credit_card_ = credit_card;
+  // Sets the currently selected payment method for this PaymentRequest flow.
+  void set_selected_payment_method(
+      payments::PaymentInstrument* payment_method) {
+    selected_payment_method_ = payment_method;
   }
 
   // Returns the available shipping options from |web_payment_request_|.
@@ -216,14 +219,14 @@
   // cached profiles ordered by completeness.
   void PopulateAvailableProfiles();
 
-  // Fetches the autofill credit cards for this user from the
-  // PersonalDataManager that match a supported type specified in
-  // |web_payment_request_| and stores copies of them, owned by this
-  // PaymentRequest, in credit_card_cache_.
-  void PopulateCreditCardCache();
+  // Fetches the payment methods for this user that match a supported type
+  // specified in |web_payment_request_| and stores copies of them, owned
+  // by this PaymentRequest, in payment_method_cache_.
+  void PopulatePaymentMethodCache();
 
-  // Sets the available credit cards as references to the cached credit cards.
-  void PopulateAvailableCreditCards();
+  // Sets the available payment methods as references to the cached payment
+  // methods.
+  void PopulateAvailablePaymentMethods();
 
   // Sets the available shipping options as references to the shipping options
   // in |web_payment_request_|.
@@ -264,14 +267,16 @@
   std::vector<autofill::AutofillProfile*> contact_profiles_;
   autofill::AutofillProfile* selected_contact_profile_;
 
-  // Credit cards returnd by the Data Manager may change due to (e.g.)
-  // sync events, meaning PaymentRequest may outlive them. Therefore, credit
-  // cards are fetched once and their copies are cached here. Whenever credit
-  // cards are requested a vector of pointers to these copies are returned.
-  std::vector<std::unique_ptr<autofill::CreditCard>> credit_card_cache_;
+  // Some payment methods, such as credit cards returned by the Data Manager,
+  // may change due to (e.g.) sync events, meaning PaymentRequest may outlive
+  // them. Therefore, payment methods are fetched once and their copies are
+  // cached here. Whenever payment methods are requested a vector of pointers to
+  // these copies are returned.
+  std::vector<std::unique_ptr<payments::PaymentInstrument>>
+      payment_method_cache_;
 
-  std::vector<autofill::CreditCard*> credit_cards_;
-  autofill::CreditCard* selected_credit_card_;
+  std::vector<payments::PaymentInstrument*> payment_methods_;
+  payments::PaymentInstrument* selected_payment_method_;
 
   // A vector of supported basic card networks. This encompasses everything that
   // the merchant supports and should be used for support checks.
diff --git a/ios/chrome/browser/payments/payment_request.mm b/ios/chrome/browser/payments/payment_request.mm
index 091c41c..b61fe86 100644
--- a/ios/chrome/browser/payments/payment_request.mm
+++ b/ios/chrome/browser/payments/payment_request.mm
@@ -16,7 +16,9 @@
 #include "components/autofill/core/browser/region_data_loader_impl.h"
 #include "components/autofill/core/browser/validation.h"
 #include "components/payments/core/address_normalizer_impl.h"
+#include "components/payments/core/autofill_payment_instrument.h"
 #include "components/payments/core/currency_formatter.h"
+#include "components/payments/core/payment_instrument.h"
 #include "components/payments/core/payment_request_data_util.h"
 #include "components/prefs/pref_service.h"
 #include "components/signin/core/browser/signin_manager.h"
@@ -64,15 +66,15 @@
           GetAddressInputStorage())),
       selected_shipping_profile_(nullptr),
       selected_contact_profile_(nullptr),
-      selected_credit_card_(nullptr),
+      selected_payment_method_(nullptr),
       selected_shipping_option_(nullptr),
       profile_comparator_(GetApplicationContext()->GetApplicationLocale(),
                           *this) {
   PopulateAvailableShippingOptions();
   PopulateProfileCache();
   PopulateAvailableProfiles();
-  PopulateCreditCardCache();
-  PopulateAvailableCreditCards();
+  PopulatePaymentMethodCache();
+  PopulateAvailablePaymentMethods();
 
   SetSelectedShippingOption();
 
@@ -94,17 +96,15 @@
     }
   }
 
-  // TODO(crbug.com/702063): Change this code to prioritize credit cards by use
-  // count and other means.
-  auto first_complete_credit_card = std::find_if(
-      credit_cards_.begin(), credit_cards_.end(),
-      [this](const autofill::CreditCard* credit_card) {
-        DCHECK(credit_card);
-        return payment_request_util::IsCreditCardCompleteForPayment(
-            *credit_card, billing_profiles());
-      });
-  if (first_complete_credit_card != credit_cards_.end())
-    selected_credit_card_ = *first_complete_credit_card;
+  // TODO(crbug.com/702063): Change this code to prioritize payment methods by
+  // use count and other means.
+  auto first_complete_payment_method =
+      std::find_if(payment_methods_.begin(), payment_methods_.end(),
+                   [this](payments::PaymentInstrument* payment_method) {
+                     return payment_method->IsCompleteForPayment();
+                   });
+  if (first_complete_payment_method != payment_methods_.end())
+    selected_payment_method_ = *first_complete_payment_method;
 }
 
 PaymentRequest::~PaymentRequest() {}
@@ -135,10 +135,8 @@
     const autofill::CreditCard& credit_card,
     base::WeakPtr<autofill::payments::FullCardRequest::ResultDelegate>
         result_delegate) {
-  // TODO: In the follow-up CL openFullCardRequestUI will take in arguments,
-  // specifically the |result_delegate| to be used in the
-  // |payment_request_ui_delegate_| object.
-  [payment_request_ui_delegate_ openFullCardRequestUI];
+  [payment_request_ui_delegate_ requestFullCreditCard:credit_card
+                                       resultDelegate:result_delegate];
 }
 
 payments::AddressNormalizer* PaymentRequest::GetAddressNormalizer() {
@@ -253,14 +251,49 @@
       profile_comparator_.FilterProfilesForShipping(raw_profiles_for_filtering);
 }
 
-autofill::CreditCard* PaymentRequest::AddCreditCard(
+payments::AutofillPaymentInstrument*
+PaymentRequest::AddAutofillPaymentInstrument(
     const autofill::CreditCard& credit_card) {
-  credit_card_cache_.push_back(
-      base::MakeUnique<autofill::CreditCard>(credit_card));
+  std::string basic_card_issuer_network =
+      autofill::data_util::GetPaymentRequestData(credit_card.network())
+          .basic_card_issuer_network;
 
-  PopulateAvailableCreditCards();
+  if (!base::ContainsValue(supported_card_networks_,
+                           basic_card_issuer_network) ||
+      !supported_card_types_set_.count(credit_card.card_type())) {
+    return nullptr;
+  }
 
-  return credit_card_cache_.back().get();
+  // If the merchant specified the card network as part of the "basic-card"
+  // payment method, use "basic-card" as the method_name. Otherwise, use
+  // the name of the network directly.
+  std::string method_name = basic_card_issuer_network;
+  if (basic_card_specified_networks_.count(basic_card_issuer_network)) {
+    method_name = "basic_card";
+  }
+
+  // The total number of card types: credit, debit, prepaid, unknown.
+  constexpr size_t kTotalNumberOfCardTypes = 4U;
+
+  // Whether the card type (credit, debit, prepaid) matches the type that the
+  // merchant has requested exactly. This should be false for unknown card
+  // types, if the merchant cannot accept some card types.
+  bool matches_merchant_card_type_exactly =
+      credit_card.card_type() != autofill::CreditCard::CARD_TYPE_UNKNOWN ||
+      supported_card_types_set_.size() == kTotalNumberOfCardTypes;
+
+  // AutofillPaymentInstrument makes a copy of |credit_card| so it is
+  // effectively owned by this object.
+  payment_method_cache_.push_back(
+      base::MakeUnique<payments::AutofillPaymentInstrument>(
+          method_name, credit_card, matches_merchant_card_type_exactly,
+          billing_profiles(), GetApplicationContext()->GetApplicationLocale(),
+          this));
+
+  PopulateAvailablePaymentMethods();
+
+  return static_cast<payments::AutofillPaymentInstrument*>(
+      payment_method_cache_.back().get());
 }
 
 payments::PaymentsProfileComparator* PaymentRequest::profile_comparator() {
@@ -268,17 +301,10 @@
 }
 
 bool PaymentRequest::CanMakePayment() const {
-  for (const autofill::CreditCard* credit_card : credit_cards_) {
-    DCHECK(credit_card);
-    autofill::CreditCardCompletionStatus status =
-        autofill::GetCompletionStatusForCard(
-            *credit_card, GetApplicationContext()->GetApplicationLocale(),
-            billing_profiles());
-    // A card only has to have a cardholder name and a number for the purposes
-    // of CanMakePayment. An expired card or one without a billing address is
-    // valid for this purpose.
-    return !(status & autofill::CREDIT_CARD_NO_CARDHOLDER ||
-             status & autofill::CREDIT_CARD_NO_NUMBER);
+  for (payments::PaymentInstrument* payment_method : payment_methods_) {
+    if (payment_method->IsValidForCanMakePayment()) {
+      return true;
+    }
   }
   return false;
 }
@@ -299,11 +325,10 @@
     }
   }
 
-  DCHECK(selected_credit_card_);
-  personal_data_manager_->RecordUseOf(*selected_credit_card_);
+  selected_payment_method_->RecordUse();
 }
 
-void PaymentRequest::PopulateCreditCardCache() {
+void PaymentRequest::PopulatePaymentMethodCache() {
   for (const payments::PaymentMethodData& method_data_entry :
        web_payment_request_.method_data) {
     for (const std::string& method : method_data_entry.supported_methods) {
@@ -325,30 +350,25 @@
   if (credit_cards_to_suggest.empty())
     return;
 
-  credit_card_cache_.reserve(credit_cards_to_suggest.size());
+  // TODO(crbug.com/602666): Determine number of possible payments so
+  // that we can appropriate reserve space in the following vector.
 
-  for (const auto* credit_card : credit_cards_to_suggest) {
-    std::string spec_issuer_network =
-        autofill::data_util::GetPaymentRequestData(credit_card->network())
-            .basic_card_issuer_network;
-    if (base::ContainsValue(supported_card_networks_, spec_issuer_network)) {
-      credit_card_cache_.push_back(
-          base::MakeUnique<autofill::CreditCard>(*credit_card));
-    }
-  }
+  payment_method_cache_.reserve(credit_cards_to_suggest.size());
+
+  for (const auto* credit_card : credit_cards_to_suggest)
+    AddAutofillPaymentInstrument(*credit_card);
 }
 
-void PaymentRequest::PopulateAvailableCreditCards() {
-  if (credit_card_cache_.empty())
+void PaymentRequest::PopulateAvailablePaymentMethods() {
+  if (payment_method_cache_.empty())
     return;
 
-  credit_cards_.clear();
-  credit_cards_.reserve(credit_card_cache_.size());
+  payment_methods_.clear();
+  payment_methods_.reserve(payment_method_cache_.size());
 
-  // TODO(crbug.com/602666): Implement prioritization rules for credit cards.
-  for (auto const& credit_card : credit_card_cache_) {
-    credit_cards_.push_back(credit_card.get());
-  }
+  // TODO(crbug.com/602666): Implement prioritization rules for payment methods.
+  for (auto const& payment_method : payment_method_cache_)
+    payment_methods_.push_back(payment_method.get());
 }
 
 void PaymentRequest::PopulateAvailableShippingOptions() {
diff --git a/ios/chrome/browser/payments/payment_request_unittest.mm b/ios/chrome/browser/payments/payment_request_unittest.mm
index b6383c6..b01fe09c 100644
--- a/ios/chrome/browser/payments/payment_request_unittest.mm
+++ b/ios/chrome/browser/payments/payment_request_unittest.mm
@@ -10,6 +10,7 @@
 #include "components/autofill/core/browser/autofill_type.h"
 #include "components/autofill/core/browser/field_types.h"
 #include "components/autofill/core/browser/test_personal_data_manager.h"
+#include "components/payments/core/autofill_payment_instrument.h"
 #include "components/payments/core/currency_formatter.h"
 #include "components/payments/core/payment_method_data.h"
 #include "ios/chrome/browser/application_context.h"
@@ -265,8 +266,9 @@
   EXPECT_EQ("unionpay", payment_request.supported_card_networks()[1]);
 }
 
-// Tests that a credit card can be added to the list of available credit cards.
-TEST_F(PaymentRequestTest, AddCreditCard) {
+// Tests that an autofill payment instrumnt e.g., credit cards can be added
+// to the list of available payment methods.
+TEST_F(PaymentRequestTest, AddAutofillPaymentInstrument) {
   web::PaymentRequest web_payment_request;
   payments::PaymentMethodData method_datum;
   method_datum.supported_methods.push_back("basic-card");
@@ -281,14 +283,14 @@
 
   TestPaymentRequest payment_request(web_payment_request,
                                      &personal_data_manager);
-  EXPECT_EQ(1U, payment_request.credit_cards().size());
+  EXPECT_EQ(1U, payment_request.payment_methods().size());
 
   autofill::CreditCard credit_card_2 = autofill::test::GetCreditCard2();
-  autofill::CreditCard* added_credit_card =
-      payment_request.AddCreditCard(credit_card_2);
+  payments::AutofillPaymentInstrument* added_credit_card =
+      payment_request.AddAutofillPaymentInstrument(credit_card_2);
 
-  EXPECT_EQ(2U, payment_request.credit_cards().size());
-  EXPECT_EQ(credit_card_2, *added_credit_card);
+  EXPECT_EQ(2U, payment_request.payment_methods().size());
+  EXPECT_EQ(credit_card_2, *added_credit_card->credit_card());
 }
 
 // Tests that a profile can be added to the list of available profiles.
@@ -491,7 +493,7 @@
   // No payment methods are selected because none are available!
   TestPaymentRequest payment_request(web_payment_request,
                                      &personal_data_manager);
-  EXPECT_EQ(nullptr, payment_request.selected_credit_card());
+  EXPECT_EQ(nullptr, payment_request.selected_payment_method());
 }
 
 // Test that loading expired credit cards works as expected.
@@ -510,7 +512,12 @@
   // credit_card is selected because expired cards are valid for payment.
   TestPaymentRequest payment_request(web_payment_request,
                                      &personal_data_manager);
-  EXPECT_EQ(credit_card.guid(), payment_request.selected_credit_card()->guid());
+  EXPECT_EQ(payment_request.selected_payment_method()->type(),
+            payments::PaymentInstrument::Type::AUTOFILL);
+  payments::AutofillPaymentInstrument* payment_instrument =
+      static_cast<payments::AutofillPaymentInstrument*>(
+          payment_request.selected_payment_method());
+  EXPECT_EQ(credit_card.guid(), payment_instrument->credit_card()->guid());
 }
 
 // Test that loading complete payment methods works as expected.
@@ -534,8 +541,10 @@
   // model).
   TestPaymentRequest payment_request(web_payment_request,
                                      &personal_data_manager);
-  EXPECT_EQ(credit_card2.guid(),
-            payment_request.selected_credit_card()->guid());
+  payments::AutofillPaymentInstrument* payment_instrument =
+      static_cast<payments::AutofillPaymentInstrument*>(
+          payment_request.selected_payment_method());
+  EXPECT_EQ(credit_card2.guid(), payment_instrument->credit_card()->guid());
 }
 
 // Test that loading incomplete payment methods works as expected.
@@ -558,7 +567,10 @@
   // because it is complete.
   TestPaymentRequest payment_request(web_payment_request,
                                      &personal_data_manager);
-  EXPECT_EQ(credit_card.guid(), payment_request.selected_credit_card()->guid());
+  payments::AutofillPaymentInstrument* payment_instrument =
+      static_cast<payments::AutofillPaymentInstrument*>(
+          payment_request.selected_payment_method());
+  EXPECT_EQ(credit_card.guid(), payment_instrument->credit_card()->guid());
 }
 
 // Test that the use counts of the data models are updated as expected when
@@ -585,11 +597,14 @@
 
   TestPaymentRequest payment_request(web_payment_request,
                                      &personal_data_manager);
+  payments::AutofillPaymentInstrument* payment_instrument =
+      static_cast<payments::AutofillPaymentInstrument*>(
+          payment_request.selected_payment_method());
   EXPECT_EQ(address.guid(),
             payment_request.selected_shipping_profile()->guid());
   EXPECT_EQ(contact_info.guid(),
             payment_request.selected_contact_profile()->guid());
-  EXPECT_EQ(credit_card.guid(), payment_request.selected_credit_card()->guid());
+  EXPECT_EQ(credit_card.guid(), payment_instrument->credit_card()->guid());
 
   EXPECT_CALL(personal_data_manager, RecordUseOf(GuidMatches(address.guid())))
       .Times(1);
@@ -618,10 +633,13 @@
 
   TestPaymentRequest payment_request(web_payment_request,
                                      &personal_data_manager);
+  payments::AutofillPaymentInstrument* payment_instrument =
+      static_cast<payments::AutofillPaymentInstrument*>(
+          payment_request.selected_payment_method());
   EXPECT_EQ(address.guid(),
             payment_request.selected_shipping_profile()->guid());
   EXPECT_EQ(address.guid(), payment_request.selected_contact_profile()->guid());
-  EXPECT_EQ(credit_card.guid(), payment_request.selected_credit_card()->guid());
+  EXPECT_EQ(credit_card.guid(), payment_instrument->credit_card()->guid());
 
   // Even though |address| is used for contact info, shipping address, and
   // credit_card's billing address, the stats should be updated only once.
@@ -652,10 +670,13 @@
 
   TestPaymentRequest payment_request(web_payment_request,
                                      &personal_data_manager);
+  payments::AutofillPaymentInstrument* payment_instrument =
+      static_cast<payments::AutofillPaymentInstrument*>(
+          payment_request.selected_payment_method());
   EXPECT_EQ(address.guid(),
             payment_request.selected_shipping_profile()->guid());
   EXPECT_EQ(nullptr, payment_request.selected_contact_profile());
-  EXPECT_EQ(credit_card.guid(), payment_request.selected_credit_card()->guid());
+  EXPECT_EQ(credit_card.guid(), payment_instrument->credit_card()->guid());
 
   EXPECT_CALL(personal_data_manager, RecordUseOf(GuidMatches(address.guid())))
       .Times(1);
@@ -682,9 +703,12 @@
 
   TestPaymentRequest payment_request(web_payment_request,
                                      &personal_data_manager);
+  payments::AutofillPaymentInstrument* payment_instrument =
+      static_cast<payments::AutofillPaymentInstrument*>(
+          payment_request.selected_payment_method());
   EXPECT_EQ(nullptr, payment_request.selected_shipping_profile());
   EXPECT_EQ(address.guid(), payment_request.selected_contact_profile()->guid());
-  EXPECT_EQ(credit_card.guid(), payment_request.selected_credit_card()->guid());
+  EXPECT_EQ(credit_card.guid(), payment_instrument->credit_card()->guid());
 
   EXPECT_CALL(personal_data_manager, RecordUseOf(GuidMatches(address.guid())))
       .Times(1);
@@ -714,9 +738,12 @@
 
   TestPaymentRequest payment_request(web_payment_request,
                                      &personal_data_manager);
+  payments::AutofillPaymentInstrument* payment_instrument =
+      static_cast<payments::AutofillPaymentInstrument*>(
+          payment_request.selected_payment_method());
   EXPECT_EQ(nullptr, payment_request.selected_shipping_profile());
   EXPECT_EQ(nullptr, payment_request.selected_contact_profile());
-  EXPECT_EQ(credit_card.guid(), payment_request.selected_credit_card()->guid());
+  EXPECT_EQ(credit_card.guid(), payment_instrument->credit_card()->guid());
 
   EXPECT_CALL(personal_data_manager, RecordUseOf(GuidMatches(address.guid())))
       .Times(0);
diff --git a/ios/chrome/browser/payments/payment_request_util.h b/ios/chrome/browser/payments/payment_request_util.h
index 3bbb6d6..ddf6ab2e 100644
--- a/ios/chrome/browser/payments/payment_request_util.h
+++ b/ios/chrome/browser/payments/payment_request_util.h
@@ -14,9 +14,12 @@
 
 namespace autofill {
 class AutofillProfile;
-class CreditCard;
 }  // namespace autofill
 
+namespace payments {
+class PaymentInstrument;
+}  // namespace payments
+
 class PaymentRequest;
 
 namespace payment_request_util {
@@ -52,16 +55,10 @@
     PaymentRequest& payment_request,
     const autofill::AutofillProfile& profile);
 
-// Returns whether the credit card is complete to be used as a payment method
-// without further editing.
-BOOL IsCreditCardCompleteForPayment(
-    const autofill::CreditCard& credit_card,
-    const std::vector<autofill::AutofillProfile*>& billing_profiles);
-
 // Helper function to create a notification label for what's missing from a
-// credit card. Returns nil if the resulting label is empty.
-NSString* GetPaymentMethodNotificationLabelFromCreditCard(
-    const autofill::CreditCard& credit_card,
+// payment method. Returns nil if the resulting label is empty.
+NSString* GetPaymentMethodNotificationLabelFromPaymentMethod(
+    payments::PaymentInstrument& payment_method,
     const std::vector<autofill::AutofillProfile*>& billing_profiles);
 
 // Returns the title for the shipping section of the payment summary view given
diff --git a/ios/chrome/browser/payments/payment_request_util.mm b/ios/chrome/browser/payments/payment_request_util.mm
index 0759516..7464e1f6 100644
--- a/ios/chrome/browser/payments/payment_request_util.mm
+++ b/ios/chrome/browser/payments/payment_request_util.mm
@@ -13,6 +13,7 @@
 #include "components/autofill/core/browser/field_types.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
 #include "components/autofill/core/browser/validation.h"
+#include "components/payments/core/payment_instrument.h"
 #include "components/payments/core/payment_request_data_util.h"
 #include "components/payments/core/strings_util.h"
 #include "components/strings/grit/components_strings.h"
@@ -73,23 +74,10 @@
   return !label.empty() ? base::SysUTF16ToNSString(label) : nil;
 }
 
-BOOL IsCreditCardCompleteForPayment(
-    const autofill::CreditCard& credit_card,
+NSString* GetPaymentMethodNotificationLabelFromPaymentMethod(
+    payments::PaymentInstrument& payment_method,
     const std::vector<autofill::AutofillProfile*>& billing_profiles) {
-  // EXPIRED cards are considered valid for payment. The user will be prompted
-  // to enter the new expiration at the CVC step.
-  return autofill::GetCompletionStatusForCard(
-             credit_card, GetApplicationContext()->GetApplicationLocale(),
-             billing_profiles) <= autofill::CREDIT_CARD_EXPIRED;
-}
-
-NSString* GetPaymentMethodNotificationLabelFromCreditCard(
-    const autofill::CreditCard& credit_card,
-    const std::vector<autofill::AutofillProfile*>& billing_profiles) {
-  base::string16 label = autofill::GetCompletionMessageForCard(
-      autofill::GetCompletionStatusForCard(
-          credit_card, GetApplicationContext()->GetApplicationLocale(),
-          billing_profiles));
+  base::string16 label = payment_method.GetMissingInfoLabel();
   return !label.empty() ? base::SysUTF16ToNSString(label) : nil;
 }
 
diff --git a/ios/chrome/browser/payments/test_payment_request.h b/ios/chrome/browser/payments/test_payment_request.h
index 6d0415f..fa0ff0e 100644
--- a/ios/chrome/browser/payments/test_payment_request.h
+++ b/ios/chrome/browser/payments/test_payment_request.h
@@ -84,8 +84,8 @@
   // Removes all the contact profiles.
   void ClearContactProfiles();
 
-  // Removes all the credit cards.
-  void ClearCreditCards();
+  // Removes all the payment methods.
+  void ClearPaymentMethods();
 
   // Sets the currently selected shipping option for this PaymentRequest flow.
   void set_selected_shipping_option(web::PaymentShippingOption* option) {
diff --git a/ios/chrome/browser/payments/test_payment_request.mm b/ios/chrome/browser/payments/test_payment_request.mm
index 171ba59..21ac093 100644
--- a/ios/chrome/browser/payments/test_payment_request.mm
+++ b/ios/chrome/browser/payments/test_payment_request.mm
@@ -22,8 +22,8 @@
   contact_profiles_.clear();
 }
 
-void TestPaymentRequest::ClearCreditCards() {
-  credit_cards_.clear();
+void TestPaymentRequest::ClearPaymentMethods() {
+  payment_methods_.clear();
 }
 
 autofill::RegionDataLoader* TestPaymentRequest::GetRegionDataLoader() {
diff --git a/ios/chrome/browser/ui/payments/credit_card_edit_coordinator.h b/ios/chrome/browser/ui/payments/credit_card_edit_coordinator.h
index 3797cb9..5a37438 100644
--- a/ios/chrome/browser/ui/payments/credit_card_edit_coordinator.h
+++ b/ios/chrome/browser/ui/payments/credit_card_edit_coordinator.h
@@ -10,9 +10,9 @@
 #import "ios/chrome/browser/ui/payments/billing_address_selection_coordinator.h"
 #import "ios/chrome/browser/ui/payments/payment_request_edit_view_controller.h"
 
-namespace autofill {
-class CreditCard;
-}  // namespace autofill
+namespace payments {
+class AutofillPaymentInstrument;
+}
 
 class PaymentRequest;
 
@@ -22,11 +22,12 @@
 @protocol CreditCardEditCoordinatorDelegate<NSObject>
 
 // Notifies the delegate that the user has finished editing or creating
-// |creditCard|. |creditCard| will be a new credit card owned by the
-// PaymentRequest object if no credit card instance was provided to the
+// |paymentMethod|. |paymentMethod| will be a new payment method owned by the
+// PaymentRequest object if no payment method instance was provided to the
 // coordinator. Otherwise, it will be the same edited instance.
 - (void)creditCardEditCoordinator:(CreditCardEditCoordinator*)coordinator
-       didFinishEditingCreditCard:(autofill::CreditCard*)creditCard;
+    didFinishEditingPaymentMethod:
+        (payments::AutofillPaymentInstrument*)paymentMethod;
 
 // Notifies the delegate that the user has chosen to cancel editing or creating
 // a credit card and return to the previous screen.
@@ -44,9 +45,9 @@
                         PaymentRequestEditViewControllerDelegate,
                         PaymentRequestEditViewControllerValidator>
 
-// The credit card to be edited, if any. This pointer is not owned by this class
-// and should outlive it.
-@property(nonatomic, assign) autofill::CreditCard* creditCard;
+// The payment method to be edited, if any. This pointer is not owned by this
+// class and should outlive it.
+@property(nonatomic, assign) payments::AutofillPaymentInstrument* paymentMethod;
 
 // The PaymentRequest object owning an instance of web::PaymentRequest as
 // provided by the page invoking the Payment Request API. This pointer is not
diff --git a/ios/chrome/browser/ui/payments/credit_card_edit_coordinator.mm b/ios/chrome/browser/ui/payments/credit_card_edit_coordinator.mm
index b31867e..24a3f9d 100644
--- a/ios/chrome/browser/ui/payments/credit_card_edit_coordinator.mm
+++ b/ios/chrome/browser/ui/payments/credit_card_edit_coordinator.mm
@@ -15,6 +15,8 @@
 #include "components/autofill/core/common/autofill_clock.h"
 #include "components/autofill/core/common/autofill_constants.h"
 #import "components/autofill/ios/browser/credit_card_util.h"
+#include "components/payments/core/autofill_payment_instrument.h"
+#include "components/payments/core/payment_instrument.h"
 #include "components/strings/grit/components_strings.h"
 #include "ios/chrome/browser/application_context.h"
 #include "ios/chrome/browser/payments/payment_request.h"
@@ -69,6 +71,8 @@
 
 @interface CreditCardEditCoordinator ()
 
+@property(nonatomic, assign) autofill::CreditCard* creditCard;
+
 @property(nonatomic, strong)
     BillingAddressSelectionCoordinator* billingAddressSelectionCoordinator;
 
@@ -85,9 +89,10 @@
 
 @implementation CreditCardEditCoordinator
 
-@synthesize creditCard = _creditCard;
+@synthesize paymentMethod = _paymentMethod;
 @synthesize paymentRequest = _paymentRequest;
 @synthesize delegate = _delegate;
+@synthesize creditCard = _creditCard;
 @synthesize billingAddressSelectionCoordinator =
     _billingAddressSelectionCoordinator;
 @synthesize addressEditCoordinator = _addressEditCoordinator;
@@ -96,6 +101,8 @@
 @synthesize mediator = _mediator;
 
 - (void)start {
+  _creditCard = _paymentMethod ? _paymentMethod->credit_card() : nil;
+
   _editViewController = [[PaymentRequestEditViewController alloc] init];
   // TODO(crbug.com/602666): Title varies depending on the missing fields.
   NSString* title = _creditCard
@@ -238,8 +245,8 @@
     if (saveCreditCard)
       _paymentRequest->GetPersonalDataManager()->AddCreditCard(creditCard);
 
-    // Add the credit card to the list of credit cards in |_paymentRequest|.
-    _creditCard = _paymentRequest->AddCreditCard(creditCard);
+    // Add the credit card to the list of payment methods in |_paymentRequest|.
+    _paymentMethod = _paymentRequest->AddAutofillPaymentInstrument(creditCard);
   } else {
     // Override the origin.
     creditCard.set_origin(autofill::kSettingsOrigin);
@@ -257,7 +264,7 @@
   }
 
   [_delegate creditCardEditCoordinator:self
-            didFinishEditingCreditCard:_creditCard];
+         didFinishEditingPaymentMethod:_paymentMethod];
 }
 
 - (void)paymentRequestEditViewControllerDidCancel:
diff --git a/ios/chrome/browser/ui/payments/credit_card_edit_coordinator_unittest.mm b/ios/chrome/browser/ui/payments/credit_card_edit_coordinator_unittest.mm
index 282645f0..abef7ab 100644
--- a/ios/chrome/browser/ui/payments/credit_card_edit_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/payments/credit_card_edit_coordinator_unittest.mm
@@ -12,6 +12,8 @@
 #include "base/test/scoped_task_environment.h"
 #include "components/autofill/core/browser/credit_card.h"
 #include "components/autofill/core/browser/test_personal_data_manager.h"
+#include "components/payments/core/autofill_payment_instrument.h"
+#include "components/payments/core/payment_instrument.h"
 #include "ios/chrome/browser/payments/payment_request_test_util.h"
 #include "ios/chrome/browser/payments/test_payment_request.h"
 #import "ios/chrome/browser/ui/autofill/autofill_ui_type.h"
@@ -40,8 +42,9 @@
   MockPaymentRequest(web::PaymentRequest web_payment_request,
                      autofill::PersonalDataManager* personal_data_manager)
       : TestPaymentRequest(web_payment_request, personal_data_manager) {}
-  MOCK_METHOD1(AddCreditCard,
-               autofill::CreditCard*(const autofill::CreditCard&));
+  MOCK_METHOD1(
+      AddAutofillPaymentInstrument,
+      payments::AutofillPaymentInstrument*(const autofill::CreditCard&));
 };
 
 MATCHER_P5(CreditCardMatches,
@@ -151,10 +154,10 @@
 }
 
 // Tests that calling the view controller delegate method which signals that the
-// user has finished creating a new credit card, causes the credit card to be
-// added to the PaymentRequest instance and the corresponding coordinator
-// delegate method to get called. The new credit card is expected to get added
-// to the PersonalDataManager if user chooses to save it locally.
+// user has finished creating a new payment method, causes the payment method to
+// be added to the PaymentRequest instance and the corresponding coordinator
+// delegate method to get called. The new payment method is expected to get
+// added to the PersonalDataManager if user chooses to save it locally.
 TEST_F(PaymentRequestCreditCardEditCoordinatorTest, DidFinishCreatingWithSave) {
   UIViewController* base_view_controller = [[UIViewController alloc] init];
   ScopedKeyWindow scoped_key_window_;
@@ -168,9 +171,10 @@
   id delegate = [OCMockObject
       mockForProtocol:@protocol(CreditCardEditCoordinatorDelegate)];
   [[delegate expect]
-       creditCardEditCoordinator:coordinator
-      didFinishEditingCreditCard:static_cast<autofill::CreditCard*>(
-                                     [OCMArg anyPointer])];
+          creditCardEditCoordinator:coordinator
+      didFinishEditingPaymentMethod:static_cast<
+                                        payments::AutofillPaymentInstrument*>(
+                                        [OCMArg anyPointer])];
   [coordinator setDelegate:delegate];
 
   EXPECT_EQ(nil, base_view_controller.presentedViewController);
@@ -180,17 +184,17 @@
   base::test::ios::SpinRunLoopWithMaxDelay(base::TimeDelta::FromSecondsD(1.0));
   EXPECT_NE(nil, base_view_controller.presentedViewController);
 
-  // Expect a credit card to be added to the PaymentRequest.
+  // Expect a payment method to be added to the PaymentRequest.
   EXPECT_CALL(*payment_request_,
-              AddCreditCard(CreditCardMatches("4111111111111111", "John Doe",
-                                              "12", "2090", "12345")))
+              AddAutofillPaymentInstrument(CreditCardMatches(
+                  "4111111111111111", "John Doe", "12", "2090", "12345")))
       .Times(1);
-  // Expect a credit card to be added to the PersonalDataManager.
+  // Expect a payment method to be added to the PersonalDataManager.
   EXPECT_CALL(personal_data_manager_,
               AddCreditCard(CreditCardMatches("4111111111111111", "John Doe",
                                               "12", "2090", "12345")))
       .Times(1);
-  // No credit card should get updated in the PersonalDataManager.
+  // No payment method should get updated in the PersonalDataManager.
   EXPECT_CALL(personal_data_manager_, UpdateCreditCard(_)).Times(0);
 
   // Call the controller delegate method.
@@ -209,9 +213,9 @@
 }
 
 // Tests that calling the view controller delegate method which signals that the
-// user has finished creating a new credit card, causes the credit card to be
-// added to the PaymentRequest instance and the corresponding coordinator
-// delegate method to get called. The new credit card should not get added to
+// user has finished creating a new payment method, causes the payment method to
+// be added to the PaymentRequest instance and the corresponding coordinator
+// delegate method to get called. The new payment method should not get added to
 // the PersonalDataManager if user chooses to not to save it locally.
 TEST_F(PaymentRequestCreditCardEditCoordinatorTest, DidFinishCreatingNoSave) {
   UIViewController* base_view_controller = [[UIViewController alloc] init];
@@ -226,9 +230,10 @@
   id delegate = [OCMockObject
       mockForProtocol:@protocol(CreditCardEditCoordinatorDelegate)];
   [[delegate expect]
-       creditCardEditCoordinator:coordinator
-      didFinishEditingCreditCard:static_cast<autofill::CreditCard*>(
-                                     [OCMArg anyPointer])];
+          creditCardEditCoordinator:coordinator
+      didFinishEditingPaymentMethod:static_cast<
+                                        payments::AutofillPaymentInstrument*>(
+                                        [OCMArg anyPointer])];
   [coordinator setDelegate:delegate];
 
   EXPECT_EQ(nil, base_view_controller.presentedViewController);
@@ -238,14 +243,14 @@
   base::test::ios::SpinRunLoopWithMaxDelay(base::TimeDelta::FromSecondsD(1.0));
   EXPECT_NE(nil, base_view_controller.presentedViewController);
 
-  // Expect a credit card to be added to the PaymentRequest.
+  // Expect a payment method to be added to the PaymentRequest.
   EXPECT_CALL(*payment_request_,
-              AddCreditCard(CreditCardMatches("4111111111111111", "John Doe",
-                                              "12", "2090", "12345")))
+              AddAutofillPaymentInstrument(CreditCardMatches(
+                  "4111111111111111", "John Doe", "12", "2090", "12345")))
       .Times(1);
-  // No credit card should get added to the PersonalDataManager.
+  // No payment method should get added to the PersonalDataManager.
   EXPECT_CALL(personal_data_manager_, AddCreditCard(_)).Times(0);
-  // No credit card should get updated in the PersonalDataManager.
+  // No payment method should get updated in the PersonalDataManager.
   EXPECT_CALL(personal_data_manager_, UpdateCreditCard(_)).Times(0);
 
   // Call the controller delegate method.
@@ -264,9 +269,9 @@
 }
 
 // Tests that calling the view controller delegate method which signals that the
-// user has finished editing a credit card, causes the corresponding coordinator
-// delegate method to get called. The credit card should not get re-added to the
-// PaymentRequest nor the PersonalDataManager.
+// user has finished editing a payment method, causes the corresponding
+// coordinator delegate method to get called. The payment method should not get
+// re-added to the PaymentRequest nor the PersonalDataManager.
 TEST_F(PaymentRequestCreditCardEditCoordinatorTest, DidFinishEditing) {
   UIViewController* base_view_controller = [[UIViewController alloc] init];
   ScopedKeyWindow scoped_key_window_;
@@ -276,17 +281,20 @@
       initWithBaseViewController:base_view_controller];
   [coordinator setPaymentRequest:payment_request_.get()];
 
-  // Set the credit card to be edited.
+  // Set the payment method to be edited.
   autofill::CreditCard credit_card;
-  [coordinator setCreditCard:&credit_card];
+  payments::AutofillPaymentInstrument payment_method(
+      "", credit_card, false, payment_request_->billing_profiles(), "", nil);
+  [coordinator setPaymentMethod:&payment_method];
 
   // Mock the coordinator delegate.
   id delegate = [OCMockObject
       mockForProtocol:@protocol(CreditCardEditCoordinatorDelegate)];
   [[delegate expect]
-       creditCardEditCoordinator:coordinator
-      didFinishEditingCreditCard:static_cast<autofill::CreditCard*>(
-                                     [OCMArg anyPointer])];
+          creditCardEditCoordinator:coordinator
+      didFinishEditingPaymentMethod:static_cast<
+                                        payments::AutofillPaymentInstrument*>(
+                                        [OCMArg anyPointer])];
   [coordinator setDelegate:delegate];
 
   EXPECT_EQ(nil, base_view_controller.presentedViewController);
@@ -296,11 +304,11 @@
   base::test::ios::SpinRunLoopWithMaxDelay(base::TimeDelta::FromSecondsD(1.0));
   EXPECT_NE(nil, base_view_controller.presentedViewController);
 
-  // No credit card should get added to the PaymentRequest.
-  EXPECT_CALL(*payment_request_, AddCreditCard(_)).Times(0);
-  // No credit card should get added to the PersonalDataManager.
+  // No payment method should get added to the PaymentRequest.
+  EXPECT_CALL(*payment_request_, AddAutofillPaymentInstrument(_)).Times(0);
+  // No payment method should get added to the PersonalDataManager.
   EXPECT_CALL(personal_data_manager_, AddCreditCard(_)).Times(0);
-  // Expect a credit card to be updated in the PersonalDataManager.
+  // Expect a payment method to be updated in the PersonalDataManager.
   EXPECT_CALL(personal_data_manager_,
               UpdateCreditCard(CreditCardMatches("4111111111111111", "John Doe",
                                                  "12", "2090", "12345")))
@@ -322,7 +330,7 @@
 }
 
 // Tests that calling the view controller delegate method which signals that the
-// user has chosen to cancel creating/editing a credit card, causes the
+// user has chosen to cancel creating/editing a payment method, causes the
 // corresponding coordinator delegate method to get called.
 TEST_F(PaymentRequestCreditCardEditCoordinatorTest, DidCancel) {
   UIViewController* base_view_controller = [[UIViewController alloc] init];
diff --git a/ios/chrome/browser/ui/payments/full_card_requester.h b/ios/chrome/browser/ui/payments/full_card_requester.h
index ea6f1a0..c6092c7 100644
--- a/ios/chrome/browser/ui/payments/full_card_requester.h
+++ b/ios/chrome/browser/ui/payments/full_card_requester.h
@@ -5,14 +5,16 @@
 #ifndef IOS_CHROME_BROWSER_UI_PAYMENTS_FULL_CARD_REQUESTER_H_
 #define IOS_CHROME_BROWSER_UI_PAYMENTS_FULL_CARD_REQUESTER_H_
 
+#include <string>
+
 #import <UIKit/UIKit.h>
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "base/strings/string16.h"
 #include "components/autofill/core/browser/autofill_client.h"
 #include "components/autofill/core/browser/payments/full_card_request.h"
 #include "components/autofill/core/browser/ui/card_unmask_prompt_controller_impl.h"
+#include "components/payments/core/payment_instrument.h"
 
 namespace autofill {
 class AutofillManager;
@@ -25,19 +27,17 @@
 
 @protocol FullCardRequesterConsumer
 
-// Called when a credit card has been successfully unmasked. Note that |card|
-// may be different from what passed to GetFullCard method of FullCardRequester,
-// because CVC unmasking process may update the credit card number and
-// expiration date.
-- (void)fullCardRequestDidSucceedWithCard:(const autofill::CreditCard&)card
-                         verificationCode:
-                             (const base::string16&)verificationCode;
+// Called when a credit card has been successfully unmasked. Should be
+// called wth method name (e.g., "visa") and json-serialized details.
+- (void)fullCardRequestDidSucceedWithMethodName:(const std::string&)methodName
+                             stringifiedDetails:
+                                 (const std::string&)stringifiedDetails;
 
 @end
 
 // Receives the full credit card details. Also displays the unmask prompt UI.
 class FullCardRequester
-    : public autofill::payments::FullCardRequest::ResultDelegate,
+    : public payments::PaymentInstrument::Delegate,
       public autofill::payments::FullCardRequest::UIDelegate,
       public base::SupportsWeakPtr<FullCardRequester> {
  public:
@@ -45,14 +45,17 @@
                     UIViewController* base_view_controller,
                     ios::ChromeBrowserState* browser_state);
 
-  void GetFullCard(autofill::CreditCard* card,
-                   autofill::AutofillManager* autofill_manager);
-
-  // payments::FullCardRequest::ResultDelegate:
-  void OnFullCardRequestSucceeded(
+  void GetFullCard(
       const autofill::CreditCard& card,
-      const base::string16& verificationCode) override;
-  void OnFullCardRequestFailed() override;
+      autofill::AutofillManager* autofill_manager,
+      base::WeakPtr<autofill::payments::FullCardRequest::ResultDelegate>
+          result_delegate);
+
+  // payments::PaymentInstrument::Delegate:
+  void OnInstrumentDetailsReady(
+      const std::string& method_name,
+      const std::string& stringified_details) override;
+  void OnInstrumentDetailsError() override{};
 
   // payments::FullCardRequest::UIDelegate:
   void ShowUnmaskPrompt(
diff --git a/ios/chrome/browser/ui/payments/full_card_requester.mm b/ios/chrome/browser/ui/payments/full_card_requester.mm
index 74cc6e4..55e0995 100644
--- a/ios/chrome/browser/ui/payments/full_card_requester.mm
+++ b/ios/chrome/browser/ui/payments/full_card_requester.mm
@@ -51,27 +51,22 @@
                          browser_state->IsOffTheRecord()) {}
 
 void FullCardRequester::GetFullCard(
-    autofill::CreditCard* card,
-    autofill::AutofillManager* autofill_manager) {
-  DCHECK(card);
-  DCHECK(autofill_manager);
-  autofill_manager->GetOrCreateFullCardRequest()->GetFullCard(
-      *card, autofill::AutofillClient::UNMASK_FOR_PAYMENT_REQUEST, AsWeakPtr(),
-      AsWeakPtr());
-}
-
-void FullCardRequester::OnFullCardRequestSucceeded(
     const autofill::CreditCard& card,
-    const base::string16& verificationCode) {
-  [consumer_ fullCardRequestDidSucceedWithCard:card
-                              verificationCode:verificationCode];
+    autofill::AutofillManager* autofill_manager,
+    base::WeakPtr<autofill::payments::FullCardRequest::ResultDelegate>
+        result_delegate) {
+  DCHECK(autofill_manager);
+  DCHECK(result_delegate);
+  autofill_manager->GetOrCreateFullCardRequest()->GetFullCard(
+      card, autofill::AutofillClient::UNMASK_FOR_PAYMENT_REQUEST,
+      result_delegate, AsWeakPtr());
 }
 
-void FullCardRequester::OnFullCardRequestFailed() {
-  // No action is required here. PRCardUnmaskPromptViewBridge manages its own
-  // life cycle. When the prompt is explicitly dismissed via tapping the close
-  // button (either in presence or absence of an error), the unmask prompt
-  // dialog pops itself and the user is back to the Payment Request UI.
+void FullCardRequester::OnInstrumentDetailsReady(
+    const std::string& method_name,
+    const std::string& stringified_details) {
+  [consumer_ fullCardRequestDidSucceedWithMethodName:method_name
+                                  stringifiedDetails:stringified_details];
 }
 
 void FullCardRequester::ShowUnmaskPrompt(
diff --git a/ios/chrome/browser/ui/payments/full_card_requester_unittest.mm b/ios/chrome/browser/ui/payments/full_card_requester_unittest.mm
index 13de877..d84f5e08 100644
--- a/ios/chrome/browser/ui/payments/full_card_requester_unittest.mm
+++ b/ios/chrome/browser/ui/payments/full_card_requester_unittest.mm
@@ -4,6 +4,10 @@
 
 #import "ios/chrome/browser/ui/payments/full_card_requester.h"
 
+#include <string>
+
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
 #include "base/logging.h"
 #include "base/strings/string16.h"
 #include "base/strings/utf_string_conversions.h"
@@ -12,6 +16,9 @@
 #include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/browser/credit_card.h"
 #include "components/autofill/ios/browser/autofill_driver_ios.h"
+#include "components/payments/core/autofill_payment_instrument.h"
+#include "components/payments/core/basic_card_response.h"
+#include "components/payments/core/payment_request_data_util.h"
 #import "ios/chrome/browser/autofill/autofill_agent.h"
 #import "ios/chrome/browser/autofill/autofill_controller.h"
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
@@ -34,18 +41,40 @@
 
 @implementation FullCardRequesterConsumerMock
 
-typedef void (^mock_full_card_request_did_succeed)(const autofill::CreditCard&,
-                                                   const base::string16&);
+typedef void (^mock_full_card_request_did_succeed_with_method_name)(
+    const std::string&,
+    const std::string&);
 
-- (void)fullCardRequestDidSucceedWithCard:(const autofill::CreditCard&)card
-                         verificationCode:
-                             (const base::string16&)verificationCode {
-  return static_cast<mock_full_card_request_did_succeed>(
-      [self blockForSelector:_cmd])(card, verificationCode);
+- (void)fullCardRequestDidSucceedWithMethodName:(const std::string&)methodName
+                             stringifiedDetails:
+                                 (const std::string&)stringifiedDetails {
+  return static_cast<mock_full_card_request_did_succeed_with_method_name>(
+      [self blockForSelector:_cmd])(methodName, stringifiedDetails);
 }
 
 @end
 
+class FakeResultDelegate
+    : public autofill::payments::FullCardRequest::ResultDelegate {
+ public:
+  FakeResultDelegate() : weak_ptr_factory_(this) {}
+  ~FakeResultDelegate() override {}
+
+  void OnFullCardRequestSucceeded(const autofill::CreditCard& card,
+                                  const base::string16& cvc) override {}
+
+  void OnFullCardRequestFailed() override {}
+
+  base::WeakPtr<FakeResultDelegate> GetWeakPtr() {
+    return weak_ptr_factory_.GetWeakPtr();
+  }
+
+ private:
+  base::WeakPtrFactory<FakeResultDelegate> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeResultDelegate);
+};
+
 class PaymentRequestFullCardRequesterTest : public ChromeWebTest {
  protected:
   PaymentRequestFullCardRequesterTest()
@@ -98,7 +127,9 @@
   autofill::AutofillManager* autofill_manager =
       autofill::AutofillDriverIOS::FromWebState(web_state())
           ->autofill_manager();
-  full_card_requester.GetFullCard(&credit_card_, autofill_manager);
+  FakeResultDelegate* fake_result_delegate = new FakeResultDelegate;
+  full_card_requester.GetFullCard(credit_card_, autofill_manager,
+                                  fake_result_delegate->GetWeakPtr());
 
   // Spin the run loop to trigger the animation.
   base::test::ios::SpinRunLoopWithMaxDelay(base::TimeDelta::FromSecondsD(1.0));
@@ -119,23 +150,38 @@
 // Tests that calling the FullCardRequester's delegate method which signals that
 // the full credit card details have been successfully received, causes the
 // FullCardRequester's delegate method to get called.
-TEST_F(PaymentRequestFullCardRequesterTest, FullCardRequestSucceeded) {
+TEST_F(PaymentRequestFullCardRequesterTest, InstrumentDetailsReady) {
   // Mock the consumer.
   id consumer =
       [OCMockObject mockForProtocol:@protocol(FullCardRequesterConsumer)];
   id consumer_mock([[FullCardRequesterConsumerMock alloc]
       initWithRepresentedObject:consumer]);
-  SEL selector = @selector(fullCardRequestDidSucceedWithCard:verificationCode:);
+  SEL selector =
+      @selector(fullCardRequestDidSucceedWithMethodName:stringifiedDetails:);
   [consumer_mock onSelector:selector
-       callBlockExpectation:^(const autofill::CreditCard& card,
-                              const base::string16& verificationCode) {
-         EXPECT_EQ(credit_card_, card);
-         EXPECT_EQ(base::ASCIIToUTF16("123"), verificationCode);
+       callBlockExpectation:^(const std::string& methodName,
+                              const std::string& stringifiedDetails) {
+         EXPECT_EQ("visa", methodName);
+
+         std::string cvc;
+         std::unique_ptr<base::DictionaryValue> detailsDict =
+             base::DictionaryValue::From(
+                 base::JSONReader::Read(stringifiedDetails));
+         detailsDict->GetString("cardSecurityCode", &cvc);
+         EXPECT_EQ("123", cvc);
        }];
 
   FullCardRequester full_card_requester(consumer_mock, nil,
                                         chrome_browser_state_.get());
 
-  full_card_requester.OnFullCardRequestSucceeded(credit_card_,
-                                                 base::ASCIIToUTF16("123"));
+  autofill::AutofillProfile billing_address;
+
+  std::unique_ptr<base::DictionaryValue> response_value =
+      payments::data_util::GetBasicCardResponseFromAutofillCreditCard(
+          credit_card_, base::ASCIIToUTF16("123"), billing_address, "en-US")
+          .ToDictionaryValue();
+  std::string stringifiedDetails;
+  base::JSONWriter::Write(*response_value, &stringifiedDetails);
+
+  full_card_requester.OnInstrumentDetailsReady("visa", stringifiedDetails);
 }
diff --git a/ios/chrome/browser/ui/payments/payment_items_display_coordinator.mm b/ios/chrome/browser/ui/payments/payment_items_display_coordinator.mm
index 027990af..44a8349 100644
--- a/ios/chrome/browser/ui/payments/payment_items_display_coordinator.mm
+++ b/ios/chrome/browser/ui/payments/payment_items_display_coordinator.mm
@@ -26,7 +26,7 @@
 @synthesize delegate = _delegate;
 
 - (void)start {
-  BOOL payButtonEnabled = _paymentRequest->selected_credit_card() != nil;
+  BOOL payButtonEnabled = _paymentRequest->selected_payment_method() != nil;
   _viewController = [[PaymentItemsDisplayViewController alloc]
       initWithPayButtonEnabled:payButtonEnabled];
   [_viewController setDelegate:self];
diff --git a/ios/chrome/browser/ui/payments/payment_method_selection_coordinator.h b/ios/chrome/browser/ui/payments/payment_method_selection_coordinator.h
index c644675..fd1b1c1f 100644
--- a/ios/chrome/browser/ui/payments/payment_method_selection_coordinator.h
+++ b/ios/chrome/browser/ui/payments/payment_method_selection_coordinator.h
@@ -14,9 +14,9 @@
 
 class PaymentRequest;
 
-namespace autofill {
-class CreditCard;
-}  // namespace autofill
+namespace payments {
+class PaymentInstrument;
+}  // namespace payments
 
 @class PaymentMethodSelectionCoordinator;
 
@@ -26,7 +26,8 @@
 // Notifies the delegate that the user has selected a payment method.
 - (void)paymentMethodSelectionCoordinator:
             (PaymentMethodSelectionCoordinator*)coordinator
-                   didSelectPaymentMethod:(autofill::CreditCard*)paymentMethod;
+                   didSelectPaymentMethod:
+                       (payments::PaymentInstrument*)paymentMethod;
 
 // Notifies the delegate that the user has chosen to return to the previous
 // screen without making a selection.
diff --git a/ios/chrome/browser/ui/payments/payment_method_selection_coordinator.mm b/ios/chrome/browser/ui/payments/payment_method_selection_coordinator.mm
index 8441248..f4e2771 100644
--- a/ios/chrome/browser/ui/payments/payment_method_selection_coordinator.mm
+++ b/ios/chrome/browser/ui/payments/payment_method_selection_coordinator.mm
@@ -8,6 +8,8 @@
 
 #include "base/logging.h"
 #include "components/autofill/core/browser/credit_card.h"
+#include "components/payments/core/autofill_payment_instrument.h"
+#include "components/payments/core/payment_instrument.h"
 #include "components/strings/grit/components_strings.h"
 #include "ios/chrome/browser/payments/payment_request.h"
 #import "ios/chrome/browser/ui/payments/cells/payment_method_item.h"
@@ -33,16 +35,17 @@
 
 @property(nonatomic, strong) PaymentMethodSelectionMediator* mediator;
 
-// Initializes and starts the CreditCardEditCoordinator. Sets |creditCard| as
-// the credit card to be edited.
-- (void)startCreditCardEditCoordinatorWithCreditCard:
-    (autofill::CreditCard*)creditCard;
+// Initializes and starts the CreditCardEditCoordinator. Sets
+// |autofillInstrument| as the autofill payment instrument to be edited.
+- (void)startCreditCardEditCoordinatorWithAutofillPaymentInstrument:
+    (payments::AutofillPaymentInstrument*)autofillInstrument;
 
 // Called when the user selects a payment method. The cell is checked, the
 // UI is locked so that the user can't interact with it, then the delegate is
 // notified. The delay is here to let the user get a visual feedback of the
 // selection before this view disappears.
-- (void)delayedNotifyDelegateOfSelection:(autofill::CreditCard*)paymentMethod;
+- (void)delayedNotifyDelegateOfSelection:
+    (payments::PaymentInstrument*)paymentMethod;
 
 @end
 
@@ -83,20 +86,25 @@
 - (BOOL)paymentRequestSelectorViewController:
             (PaymentRequestSelectorViewController*)controller
                         didSelectItemAtIndex:(NSUInteger)index {
-  DCHECK(index < self.paymentRequest->credit_cards().size());
-  autofill::CreditCard* creditCard = self.paymentRequest->credit_cards()[index];
+  DCHECK(index < self.paymentRequest->payment_methods().size());
+  payments::PaymentInstrument* paymentMethod =
+      self.paymentRequest->payment_methods()[index];
 
   // Proceed with item selection only if the item has all required info, or
-  // else bring up the credit card editor.
+  // else bring up the credit card editor. A payment method can be incomplete
+  // only if it is an AutofillPaymentInstrument.
   CollectionViewItem<PaymentsIsSelectable>* selectedItem =
       self.mediator.selectableItems[index];
   if (selectedItem.complete) {
     // Update the data source with the selection.
     self.mediator.selectedItemIndex = index;
-    [self delayedNotifyDelegateOfSelection:creditCard];
+    [self delayedNotifyDelegateOfSelection:paymentMethod];
     return YES;
   } else {
-    [self startCreditCardEditCoordinatorWithCreditCard:creditCard];
+    DCHECK(paymentMethod->type() ==
+           payments::PaymentInstrument::Type::AUTOFILL);
+    [self startCreditCardEditCoordinatorWithAutofillPaymentInstrument:
+              static_cast<payments::AutofillPaymentInstrument*>(paymentMethod)];
     return NO;
   }
 }
@@ -108,7 +116,7 @@
 
 - (void)paymentRequestSelectorViewControllerDidSelectAddItem:
     (PaymentRequestSelectorViewController*)controller {
-  [self startCreditCardEditCoordinatorWithCreditCard:nil];
+  [self startCreditCardEditCoordinatorWithAutofillPaymentInstrument:nil];
 }
 
 - (void)paymentRequestSelectorViewControllerDidToggleEditingMode:
@@ -120,33 +128,41 @@
 - (void)paymentRequestSelectorViewController:
             (PaymentRequestSelectorViewController*)controller
               didSelectItemAtIndexForEditing:(NSUInteger)index {
-  DCHECK(index < self.paymentRequest->credit_cards().size());
-  [self
-      startCreditCardEditCoordinatorWithCreditCard:self.paymentRequest
-                                                       ->credit_cards()[index]];
+  DCHECK(index < self.paymentRequest->payment_methods().size());
+
+  // We should only edit the payment instrument if it is an
+  // AutofillPaymentInstrument.
+  if (self.paymentRequest->payment_methods()[index]->type() ==
+      payments::PaymentInstrument::Type::AUTOFILL) {
+    [self startCreditCardEditCoordinatorWithAutofillPaymentInstrument:
+              static_cast<payments::AutofillPaymentInstrument*>(
+                  self.paymentRequest->payment_methods()[index])];
+  }
 }
 
 #pragma mark - CreditCardEditCoordinatorDelegate
 
 - (void)creditCardEditCoordinator:(CreditCardEditCoordinator*)coordinator
-       didFinishEditingCreditCard:(autofill::CreditCard*)creditCard {
+    didFinishEditingPaymentMethod:
+        (payments::AutofillPaymentInstrument*)creditCard {
   // Update the data source with the new data.
   [self.mediator loadItems];
 
-  const std::vector<autofill::CreditCard*>& creditCards =
-      self.paymentRequest->credit_cards();
-  auto position = std::find(creditCards.begin(), creditCards.end(), creditCard);
-  DCHECK(position != creditCards.end());
+  const std::vector<payments::PaymentInstrument*>& paymentMethods =
+      self.paymentRequest->payment_methods();
+  auto position =
+      std::find(paymentMethods.begin(), paymentMethods.end(), creditCard);
+  DCHECK(position != paymentMethods.end());
 
   // Mark the edited item as complete meaning all required information has been
   // filled out.
   CollectionViewItem<PaymentsIsSelectable>* editedItem =
-      self.mediator.selectableItems[position - creditCards.begin()];
+      self.mediator.selectableItems[position - paymentMethods.begin()];
   editedItem.complete = YES;
 
   if (![self.viewController isEditing]) {
     // Update the data source with the selection.
-    self.mediator.selectedItemIndex = position - creditCards.begin();
+    self.mediator.selectedItemIndex = position - paymentMethods.begin();
   }
 
   [self.viewController loadModel];
@@ -170,17 +186,18 @@
 
 #pragma mark - Helper methods
 
-- (void)startCreditCardEditCoordinatorWithCreditCard:
-    (autofill::CreditCard*)creditCard {
+- (void)startCreditCardEditCoordinatorWithAutofillPaymentInstrument:
+    (payments::AutofillPaymentInstrument*)autofillInstrument {
   self.creditCardEditCoordinator = [[CreditCardEditCoordinator alloc]
       initWithBaseViewController:self.viewController];
   self.creditCardEditCoordinator.paymentRequest = self.paymentRequest;
-  self.creditCardEditCoordinator.creditCard = creditCard;
+  self.creditCardEditCoordinator.paymentMethod = autofillInstrument;
   self.creditCardEditCoordinator.delegate = self;
   [self.creditCardEditCoordinator start];
 }
 
-- (void)delayedNotifyDelegateOfSelection:(autofill::CreditCard*)paymentMethod {
+- (void)delayedNotifyDelegateOfSelection:
+    (payments::PaymentInstrument*)paymentMethod {
   self.viewController.view.userInteractionEnabled = NO;
   __weak PaymentMethodSelectionCoordinator* weakSelf = self;
   dispatch_after(
diff --git a/ios/chrome/browser/ui/payments/payment_method_selection_coordinator_unittest.mm b/ios/chrome/browser/ui/payments/payment_method_selection_coordinator_unittest.mm
index 145efaf..bfa1995 100644
--- a/ios/chrome/browser/ui/payments/payment_method_selection_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/payments/payment_method_selection_coordinator_unittest.mm
@@ -12,6 +12,7 @@
 #include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/browser/credit_card.h"
 #include "components/autofill/core/browser/test_personal_data_manager.h"
+#include "components/payments/core/payment_instrument.h"
 #include "ios/chrome/browser/payments/payment_request.h"
 #include "ios/chrome/browser/payments/payment_request_test_util.h"
 #include "ios/chrome/browser/payments/test_payment_request.h"
@@ -106,10 +107,10 @@
       mockForProtocol:@protocol(PaymentMethodSelectionCoordinatorDelegate)];
   [[delegate expect]
       paymentMethodSelectionCoordinator:coordinator
-                 didSelectPaymentMethod:payment_request_->credit_cards()[0]];
+                 didSelectPaymentMethod:payment_request_->payment_methods()[0]];
   [[delegate reject]
       paymentMethodSelectionCoordinator:coordinator
-                 didSelectPaymentMethod:payment_request_->credit_cards()[1]];
+                 didSelectPaymentMethod:payment_request_->payment_methods()[1]];
   [coordinator setDelegate:delegate];
 
   EXPECT_EQ(1u, navigation_controller.viewControllers.count);
diff --git a/ios/chrome/browser/ui/payments/payment_method_selection_mediator.mm b/ios/chrome/browser/ui/payments/payment_method_selection_mediator.mm
index 4513447e..1706d06 100644
--- a/ios/chrome/browser/ui/payments/payment_method_selection_mediator.mm
+++ b/ios/chrome/browser/ui/payments/payment_method_selection_mediator.mm
@@ -8,10 +8,11 @@
 
 #include "base/strings/string16.h"
 #include "base/strings/sys_string_conversions.h"
-#include "components/autofill/core/browser/autofill_data_util.h"
 #include "components/autofill/core/browser/autofill_profile.h"
 #include "components/autofill/core/browser/credit_card.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
+#include "components/payments/core/autofill_payment_instrument.h"
+#include "components/payments/core/payment_instrument.h"
 #include "components/payments/core/strings_util.h"
 #include "components/strings/grit/components_strings.h"
 #include "ios/chrome/browser/payments/payment_request.h"
@@ -28,6 +29,8 @@
 
 namespace {
 using ::payment_request_util::GetBillingAddressLabelFromAutofillProfile;
+using ::payment_request_util::
+    GetPaymentMethodNotificationLabelFromPaymentMethod;
 }  // namespace
 
 @interface PaymentMethodSelectionMediator ()
@@ -90,39 +93,36 @@
 #pragma mark - Public methods
 
 - (void)loadItems {
-  const std::vector<autofill::CreditCard*>& paymentMethods =
-      _paymentRequest->credit_cards();
+  const std::vector<payments::PaymentInstrument*>& paymentMethods =
+      _paymentRequest->payment_methods();
   _items = [NSMutableArray arrayWithCapacity:paymentMethods.size()];
   for (size_t index = 0; index < paymentMethods.size(); ++index) {
-    autofill::CreditCard* paymentMethod = paymentMethods[index];
+    payments::PaymentInstrument* paymentMethod = paymentMethods[index];
     DCHECK(paymentMethod);
     PaymentMethodItem* item = [[PaymentMethodItem alloc] init];
-    item.methodID =
-        base::SysUTF16ToNSString(paymentMethod->NetworkAndLastFourDigits());
-    item.methodDetail = base::SysUTF16ToNSString(
-        paymentMethod->GetRawInfo(autofill::CREDIT_CARD_NAME_FULL));
-    item.notification =
-        payment_request_util::GetPaymentMethodNotificationLabelFromCreditCard(
-            *paymentMethod, _paymentRequest->billing_profiles());
-    item.complete = payment_request_util::IsCreditCardCompleteForPayment(
+    item.methodID = base::SysUTF16ToNSString(paymentMethod->GetLabel());
+    item.methodDetail = base::SysUTF16ToNSString(paymentMethod->GetSublabel());
+    item.notification = GetPaymentMethodNotificationLabelFromPaymentMethod(
         *paymentMethod, _paymentRequest->billing_profiles());
+    item.complete = paymentMethod->IsCompleteForPayment();
 
-    autofill::AutofillProfile* billingAddress =
-        autofill::PersonalDataManager::GetProfileFromProfilesByGUID(
-            paymentMethod->billing_address_id(),
-            _paymentRequest->billing_profiles());
-    if (billingAddress) {
-      item.methodAddress =
-          GetBillingAddressLabelFromAutofillProfile(*billingAddress);
+    if (paymentMethod->type() == payments::PaymentInstrument::Type::AUTOFILL) {
+      payments::AutofillPaymentInstrument* autofillInstrument =
+          static_cast<payments::AutofillPaymentInstrument*>(paymentMethod);
+      autofill::AutofillProfile* billingAddress =
+          autofill::PersonalDataManager::GetProfileFromProfilesByGUID(
+              autofillInstrument->credit_card()->billing_address_id(),
+              _paymentRequest->billing_profiles());
+      if (billingAddress) {
+        item.methodAddress =
+            GetBillingAddressLabelFromAutofillProfile(*billingAddress);
+      }
     }
 
-    int methodTypeIconID =
-        autofill::data_util::GetPaymentRequestData(paymentMethod->network())
-            .icon_resource_id;
-    item.methodTypeIcon = NativeImage(methodTypeIconID);
+    item.methodTypeIcon = NativeImage(paymentMethod->icon_resource_id());
 
     item.reserveRoomForAccessoryType = YES;
-    if (_paymentRequest->selected_credit_card() == paymentMethod)
+    if (_paymentRequest->selected_payment_method() == paymentMethod)
       _selectedItemIndex = index;
 
     [_items addObject:item];
diff --git a/ios/chrome/browser/ui/payments/payment_request_coordinator.h b/ios/chrome/browser/ui/payments/payment_request_coordinator.h
index aa70e22..90779d4b 100644
--- a/ios/chrome/browser/ui/payments/payment_request_coordinator.h
+++ b/ios/chrome/browser/ui/payments/payment_request_coordinator.h
@@ -9,6 +9,7 @@
 
 #include "base/ios/block_types.h"
 #include "base/strings/string16.h"
+#include "components/autofill/core/browser/payments/full_card_request.h"
 #import "ios/chrome/browser/chrome_coordinator.h"
 #import "ios/chrome/browser/ui/payments/address_edit_coordinator.h"
 #import "ios/chrome/browser/ui/payments/contact_info_edit_coordinator.h"
@@ -51,10 +52,11 @@
 - (void)paymentRequestCoordinatorDidSelectSettings:
     (PaymentRequestCoordinator*)coordinator;
 
-// Notifies the delegate that the user has completed the payment request.
+// Notifies the delegate that the full payment method name and details
+// have been receieved.
 - (void)paymentRequestCoordinator:(PaymentRequestCoordinator*)coordinator
-    didCompletePaymentRequestWithCard:(const autofill::CreditCard&)card
-                     verificationCode:(const base::string16&)verificationCode;
+         didReceiveFullMethodName:(const std::string&)methodName
+               stringifiedDetails:(const std::string&)stringifiedDetails;
 
 // Notifies the delegate that the user has selected a shipping address.
 - (void)paymentRequestCoordinator:(PaymentRequestCoordinator*)coordinator
@@ -115,8 +117,12 @@
 // The delegate to be notified when the user confirms or cancels the request.
 @property(nonatomic, weak) id<PaymentRequestCoordinatorDelegate> delegate;
 
-// Initiates the UI that will process payment with a payment method.
-- (void)sendPaymentResponse;
+// Initiates the UI that will request card details from the user.
+- (void)
+requestFullCreditCard:(const autofill::CreditCard&)card
+       resultDelegate:
+           (base::WeakPtr<autofill::payments::FullCardRequest::ResultDelegate>)
+               resultDelegate;
 
 // Updates the payment details of the PaymentRequest and updates the UI.
 - (void)updatePaymentDetails:(web::PaymentDetails)paymentDetails;
diff --git a/ios/chrome/browser/ui/payments/payment_request_coordinator.mm b/ios/chrome/browser/ui/payments/payment_request_coordinator.mm
index f316eef..415e014 100644
--- a/ios/chrome/browser/ui/payments/payment_request_coordinator.mm
+++ b/ios/chrome/browser/ui/payments/payment_request_coordinator.mm
@@ -4,10 +4,14 @@
 
 #import "ios/chrome/browser/ui/payments/payment_request_coordinator.h"
 
+#include "base/json/json_reader.h"
 #include "base/memory/ptr_util.h"
 #include "components/autofill/core/browser/autofill_profile.h"
 #include "components/autofill/core/browser/credit_card.h"
+#include "components/payments/core/autofill_payment_instrument.h"
 #include "components/payments/core/payment_address.h"
+#include "components/payments/core/payment_instrument.h"
+#include "components/payments/core/payment_request_data_util.h"
 #include "components/strings/grit/components_strings.h"
 #include "ios/chrome/browser/application_context.h"
 #include "ios/chrome/browser/payments/payment_request.h"
@@ -80,6 +84,9 @@
       setModalTransitionStyle:UIModalTransitionStyleCoverVertical];
   [_navigationController setNavigationBarHidden:YES];
 
+  _fullCardRequester = base::MakeUnique<FullCardRequester>(
+      self, _navigationController, _browserState);
+
   [[self baseViewController] presentViewController:_navigationController
                                           animated:YES
                                         completion:nil];
@@ -113,27 +120,27 @@
   _navigationController = nil;
 }
 
-- (void)sendPaymentResponse {
-  DCHECK(_paymentRequest->selected_credit_card());
-  autofill::CreditCard* card = _paymentRequest->selected_credit_card();
-  _fullCardRequester = base::MakeUnique<FullCardRequester>(
-      self, _navigationController, _browserState);
-  _fullCardRequester->GetFullCard(card, _autofillManager);
+- (void)
+requestFullCreditCard:(const autofill::CreditCard&)card
+       resultDelegate:
+           (base::WeakPtr<autofill::payments::FullCardRequest::ResultDelegate>)
+               resultDelegate {
+  _fullCardRequester->GetFullCard(card, _autofillManager, resultDelegate);
 }
 
 #pragma mark - FullCardRequesterConsumer
 
-- (void)fullCardRequestDidSucceedWithCard:(const autofill::CreditCard&)card
-                         verificationCode:
-                             (const base::string16&)verificationCode {
+- (void)fullCardRequestDidSucceedWithMethodName:(const std::string&)methodName
+                             stringifiedDetails:
+                                 (const std::string&)stringifiedDetails {
   _viewController.view.userInteractionEnabled = NO;
   [_viewController setPending:YES];
   [_viewController loadModel];
   [[_viewController collectionView] reloadData];
 
   [_delegate paymentRequestCoordinator:self
-      didCompletePaymentRequestWithCard:card
-                       verificationCode:verificationCode];
+              didReceiveFullMethodName:methodName
+                    stringifiedDetails:stringifiedDetails];
 }
 
 #pragma mark - Public methods
@@ -210,7 +217,9 @@
 
 - (void)paymentRequestViewControllerDidConfirm:
     (PaymentRequestViewController*)controller {
-  [self sendPaymentResponse];
+  DCHECK(_paymentRequest->selected_payment_method());
+  _paymentRequest->selected_payment_method()->InvokePaymentApp(
+      _fullCardRequester.get());
 }
 
 - (void)paymentRequestViewControllerDidSelectSettings:
@@ -286,7 +295,7 @@
 
 - (void)paymentRequestViewControllerDidSelectPaymentMethodItem:
     (PaymentRequestViewController*)controller {
-  if (_paymentRequest->credit_cards().empty()) {
+  if (_paymentRequest->payment_methods().empty()) {
     _creditCardEditCoordinator = [[CreditCardEditCoordinator alloc]
         initWithBaseViewController:_viewController];
     [_creditCardEditCoordinator setPaymentRequest:_paymentRequest];
@@ -327,7 +336,9 @@
 
 - (void)paymentItemsDisplayCoordinatorDidConfirm:
     (PaymentItemsDisplayCoordinator*)coordinator {
-  [self sendPaymentResponse];
+  DCHECK(_paymentRequest->selected_payment_method());
+  _paymentRequest->selected_payment_method()->InvokePaymentApp(
+      _fullCardRequester.get());
 }
 
 #pragma mark - ContactInfoSelectionCoordinatorDelegate
@@ -421,11 +432,11 @@
 
 - (void)paymentMethodSelectionCoordinator:
             (PaymentMethodSelectionCoordinator*)coordinator
-                   didSelectPaymentMethod:(autofill::CreditCard*)creditCard {
-  DCHECK(creditCard);
-  DCHECK(payment_request_util::IsCreditCardCompleteForPayment(
-      *creditCard, _paymentRequest->billing_profiles()));
-  _paymentRequest->set_selected_credit_card(creditCard);
+                   didSelectPaymentMethod:
+                       (payments::PaymentInstrument*)paymentMethod {
+  DCHECK(paymentMethod);
+  DCHECK(paymentMethod->IsCompleteForPayment());
+  _paymentRequest->set_selected_payment_method(paymentMethod);
   [_viewController updatePaymentMethodSection];
 
   [_methodSelectionCoordinator stop];
@@ -441,9 +452,11 @@
 #pragma mark - CreditCardEditCoordinatorDelegate
 
 - (void)creditCardEditCoordinator:(CreditCardEditCoordinator*)coordinator
-       didFinishEditingCreditCard:(autofill::CreditCard*)creditCard {
-  DCHECK(creditCard);
-  _paymentRequest->set_selected_credit_card(creditCard);
+    didFinishEditingPaymentMethod:
+        (payments::AutofillPaymentInstrument*)paymentMethod {
+  DCHECK(paymentMethod);
+  DCHECK(paymentMethod->IsCompleteForPayment());
+  _paymentRequest->set_selected_payment_method(paymentMethod);
   [_viewController updatePaymentMethodSection];
 
   [_creditCardEditCoordinator stop];
diff --git a/ios/chrome/browser/ui/payments/payment_request_coordinator_unittest.mm b/ios/chrome/browser/ui/payments/payment_request_coordinator_unittest.mm
index 1f6c030..736c14b6 100644
--- a/ios/chrome/browser/ui/payments/payment_request_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/payments/payment_request_coordinator_unittest.mm
@@ -4,15 +4,21 @@
 
 #import "ios/chrome/browser/ui/payments/payment_request_coordinator.h"
 
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
 #include "base/mac/foundation_util.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/ios/wait_util.h"
+#include "base/test/scoped_task_environment.h"
 #include "components/autofill/core/browser/autofill_profile.h"
 #include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/browser/credit_card.h"
 #include "components/autofill/core/browser/test_personal_data_manager.h"
+#include "components/payments/core/autofill_payment_instrument.h"
 #include "components/payments/core/payment_address.h"
+#include "components/payments/core/payment_instrument.h"
+#include "components/payments/core/payment_request_data_util.h"
 #include "components/payments/core/payments_test_util.h"
 #include "components/prefs/pref_service.h"
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
@@ -43,8 +49,8 @@
 
 typedef void (^mock_coordinator_cancel)(PaymentRequestCoordinator*);
 typedef void (^mock_coordinator_complete)(PaymentRequestCoordinator*,
-                                          const autofill::CreditCard&,
-                                          const base::string16&);
+                                          const std::string&,
+                                          const std::string&);
 typedef void (^mock_coordinator_select_shipping_address)(
     PaymentRequestCoordinator*,
     const autofill::AutofillProfile&);
@@ -59,10 +65,10 @@
 }
 
 - (void)paymentRequestCoordinator:(PaymentRequestCoordinator*)coordinator
-    didCompletePaymentRequestWithCard:(const autofill::CreditCard&)card
-                     verificationCode:(const base::string16&)verificationCode {
+         didReceiveFullMethodName:(const std::string&)methodName
+               stringifiedDetails:(const std::string&)stringifiedDetails {
   return static_cast<mock_coordinator_complete>([self blockForSelector:_cmd])(
-      coordinator, card, verificationCode);
+      coordinator, methodName, stringifiedDetails);
 }
 
 - (void)paymentRequestCoordinator:(PaymentRequestCoordinator*)coordinator
@@ -102,6 +108,8 @@
     payment_request_->SetPrefService(pref_service_.get());
   }
 
+  base::test::ScopedTaskEnvironment scoped_task_evironment_;
+
   autofill::AutofillProfile autofill_profile_;
   autofill::CreditCard credit_card_;
   std::unique_ptr<PrefService> pref_service_;
@@ -147,7 +155,7 @@
 // Tests that calling the FullCardRequesterConsumer delegate method which
 // notifies the coordinator about successful unmasking of a credit card invokes
 // the appropriate coordinator delegate method with the expected information.
-TEST_F(PaymentRequestCoordinatorTest, FullCardRequestDidSucceed) {
+TEST_F(PaymentRequestCoordinatorTest, FullCardRequestDidSucceedWithMethodName) {
   UIViewController* base_view_controller = [[UIViewController alloc] init];
   ScopedKeyWindow scoped_key_window_;
   [scoped_key_window_.Get() setRootViewController:base_view_controller];
@@ -160,22 +168,38 @@
       mockForProtocol:@protocol(PaymentMethodSelectionCoordinatorDelegate)];
   id delegate_mock([[PaymentRequestCoordinatorDelegateMock alloc]
       initWithRepresentedObject:delegate]);
-  SEL selector =
-      @selector(paymentRequestCoordinator:didCompletePaymentRequestWithCard
-                                         :verificationCode:);
+  SEL selector = @selector
+      (paymentRequestCoordinator:didReceiveFullMethodName:stringifiedDetails:);
   [delegate_mock onSelector:selector
        callBlockExpectation:^(PaymentRequestCoordinator* callerCoordinator,
-                              const autofill::CreditCard& card,
-                              const base::string16& verificationCode) {
-         EXPECT_EQ(credit_card_, card);
-         EXPECT_EQ(base::ASCIIToUTF16("123"), verificationCode);
+                              const std::string& methodName,
+                              const std::string& stringifiedDetails) {
+         EXPECT_EQ("visa", methodName);
+
+         std::string cvc;
+         std::unique_ptr<base::DictionaryValue> detailsDict =
+             base::DictionaryValue::From(
+                 base::JSONReader::Read(stringifiedDetails));
+         detailsDict->GetString("cardSecurityCode", &cvc);
+         EXPECT_EQ("123", cvc);
+
          EXPECT_EQ(coordinator, callerCoordinator);
        }];
   [coordinator setDelegate:delegate_mock];
 
+  std::string methodName = "visa";
+  std::string appLocale = "";
+
+  std::unique_ptr<base::DictionaryValue> response_value =
+      payments::data_util::GetBasicCardResponseFromAutofillCreditCard(
+          credit_card_, base::ASCIIToUTF16("123"), autofill_profile_, appLocale)
+          .ToDictionaryValue();
+  std::string stringifiedDetails;
+  base::JSONWriter::Write(*response_value, &stringifiedDetails);
+
   // Call the card unmasking delegate method.
-  [coordinator fullCardRequestDidSucceedWithCard:credit_card_
-                                verificationCode:base::ASCIIToUTF16("123")];
+  [coordinator fullCardRequestDidSucceedWithMethodName:methodName
+                                    stringifiedDetails:stringifiedDetails];
 }
 
 // Tests that calling the ShippingAddressSelectionCoordinator delegate method
diff --git a/ios/chrome/browser/ui/payments/payment_request_manager.mm b/ios/chrome/browser/ui/payments/payment_request_manager.mm
index 1a5be16f..8ca02c5 100644
--- a/ios/chrome/browser/ui/payments/payment_request_manager.mm
+++ b/ios/chrome/browser/ui/payments/payment_request_manager.mm
@@ -8,6 +8,7 @@
 
 #include "base/ios/block_types.h"
 #include "base/ios/ios_util.h"
+#include "base/json/json_reader.h"
 #import "base/mac/bind_objc_block.h"
 #include "base/mac/foundation_util.h"
 #include "base/memory/ptr_util.h"
@@ -77,9 +78,8 @@
 NSString* kCancelMessage = @"The payment request was canceled.";
 
 struct PendingPaymentResponse {
-  autofill::CreditCard creditCard;
-  base::string16 verificationCode;
-  autofill::AutofillProfile billingAddress;
+  std::string methodName;
+  std::string stringifiedDetails;
   autofill::AutofillProfile shippingAddress;
   autofill::AutofillProfile contactAddress;
 };
@@ -658,8 +658,13 @@
 
 #pragma mark - PaymentRequestUIDelegate
 
-- (void)openFullCardRequestUI {
-  [_paymentRequestCoordinator sendPaymentResponse];
+- (void)
+requestFullCreditCard:(const autofill::CreditCard&)creditCard
+       resultDelegate:
+           (base::WeakPtr<autofill::payments::FullCardRequest::ResultDelegate>)
+               resultDelegate {
+  [_paymentRequestCoordinator requestFullCreditCard:creditCard
+                                     resultDelegate:resultDelegate];
 }
 
 #pragma mark - PaymentRequestCoordinatorDelegate methods
@@ -683,19 +688,10 @@
 }
 
 - (void)paymentRequestCoordinator:(PaymentRequestCoordinator*)coordinator
-    didCompletePaymentRequestWithCard:(const autofill::CreditCard&)card
-                     verificationCode:(const base::string16&)verificationCode {
-  _pendingPaymentResponse.creditCard = card;
-  _pendingPaymentResponse.verificationCode = verificationCode;
-
-  DCHECK(!card.billing_address_id().empty());
-  autofill::AutofillProfile* billingAddress =
-      autofill::PersonalDataManager::GetProfileFromProfilesByGUID(
-          card.billing_address_id(), _paymentRequest->billing_profiles());
-  DCHECK(billingAddress);
-  _pendingPaymentResponse.billingAddress = *billingAddress;
-  _addressNormalizationManager->StartNormalizingAddress(
-      &_pendingPaymentResponse.billingAddress);
+         didReceiveFullMethodName:(const std::string&)methodName
+               stringifiedDetails:(const std::string&)stringifiedDetails {
+  _pendingPaymentResponse.methodName = methodName;
+  _pendingPaymentResponse.stringifiedDetails = stringifiedDetails;
 
   if (_paymentRequest->request_shipping()) {
     // TODO(crbug.com/602666): User should get here only if they have selected
@@ -729,24 +725,10 @@
 - (void)paymentRequestAddressNormalizationDidComplete {
   web::PaymentResponse paymentResponse;
 
-  // If the merchant specified the card network as part of the "basic-card"
-  // payment method, return "basic-card" as the method_name. Otherwise, return
-  // the name of the network directly.
-  std::string issuer_network = autofill::data_util::GetPaymentRequestData(
-                                   _pendingPaymentResponse.creditCard.network())
-                                   .basic_card_issuer_network;
   paymentResponse.method_name =
-      _paymentRequest->basic_card_specified_networks().find(issuer_network) !=
-              _paymentRequest->basic_card_specified_networks().end()
-          ? base::ASCIIToUTF16("basic-card")
-          : base::ASCIIToUTF16(issuer_network);
+      base::ASCIIToUTF16(_pendingPaymentResponse.methodName);
 
-  paymentResponse.details =
-      payments::data_util::GetBasicCardResponseFromAutofillCreditCard(
-          _pendingPaymentResponse.creditCard,
-          _pendingPaymentResponse.verificationCode,
-          _pendingPaymentResponse.billingAddress,
-          GetApplicationContext()->GetApplicationLocale());
+  paymentResponse.details = _pendingPaymentResponse.stringifiedDetails;
 
   if (_paymentRequest->request_shipping()) {
     paymentResponse.shipping_address =
diff --git a/ios/chrome/browser/ui/payments/payment_request_mediator.mm b/ios/chrome/browser/ui/payments/payment_request_mediator.mm
index e9c2cb6..6add498 100644
--- a/ios/chrome/browser/ui/payments/payment_request_mediator.mm
+++ b/ios/chrome/browser/ui/payments/payment_request_mediator.mm
@@ -12,6 +12,7 @@
 #include "components/autofill/core/browser/autofill_profile.h"
 #include "components/autofill/core/browser/credit_card.h"
 #include "components/autofill/core/browser/field_types.h"
+#include "components/payments/core/autofill_payment_instrument.h"
 #include "components/payments/core/currency_formatter.h"
 #include "components/payments/core/payment_prefs.h"
 #include "components/payments/core/strings_util.h"
@@ -73,7 +74,7 @@
 #pragma mark - PaymentRequestViewControllerDataSource
 
 - (BOOL)canPay {
-  return self.paymentRequest->selected_credit_card() != nullptr &&
+  return self.paymentRequest->selected_payment_method() != nullptr &&
          (self.paymentRequest->selected_shipping_option() != nullptr ||
           ![self requestShipping]) &&
          (self.paymentRequest->selected_shipping_profile() != nullptr ||
@@ -174,7 +175,7 @@
 }
 
 - (CollectionViewItem*)paymentMethodSectionHeaderItem {
-  if (!self.paymentRequest->selected_credit_card())
+  if (!self.paymentRequest->selected_payment_method())
     return nil;
   PaymentsTextItem* item = [[PaymentsTextItem alloc] init];
   item.text =
@@ -183,18 +184,13 @@
 }
 
 - (CollectionViewItem*)paymentMethodItem {
-  const autofill::CreditCard* creditCard =
-      self.paymentRequest->selected_credit_card();
-  if (creditCard) {
+  const payments::PaymentInstrument* paymentMethod =
+      self.paymentRequest->selected_payment_method();
+  if (paymentMethod) {
     PaymentMethodItem* item = [[PaymentMethodItem alloc] init];
-    item.methodID =
-        base::SysUTF16ToNSString(creditCard->NetworkAndLastFourDigits());
-    item.methodDetail = base::SysUTF16ToNSString(
-        creditCard->GetRawInfo(autofill::CREDIT_CARD_NAME_FULL));
-    int issuerNetworkIconID =
-        autofill::data_util::GetPaymentRequestData(creditCard->network())
-            .icon_resource_id;
-    item.methodTypeIcon = NativeImage(issuerNetworkIconID);
+    item.methodID = base::SysUTF16ToNSString(paymentMethod->GetLabel());
+    item.methodDetail = base::SysUTF16ToNSString(paymentMethod->GetSublabel());
+    item.methodTypeIcon = NativeImage(paymentMethod->icon_resource_id());
     item.accessoryType = MDCCollectionViewCellAccessoryDisclosureIndicator;
     return item;
   }
@@ -202,7 +198,7 @@
   CollectionViewDetailItem* item = [[CollectionViewDetailItem alloc] init];
   item.text =
       l10n_util::GetNSString(IDS_PAYMENT_REQUEST_PAYMENT_METHOD_SECTION_NAME);
-  if (self.paymentRequest->credit_cards().empty()) {
+  if (self.paymentRequest->payment_methods().empty()) {
     item.detailText = [l10n_util::GetNSString(IDS_ADD)
         uppercaseStringWithLocale:[NSLocale currentLocale]];
   } else {
diff --git a/ios/chrome/browser/ui/payments/payment_request_mediator_unittest.mm b/ios/chrome/browser/ui/payments/payment_request_mediator_unittest.mm
index ca07b06..ef3e1b2c 100644
--- a/ios/chrome/browser/ui/payments/payment_request_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/payments/payment_request_mediator_unittest.mm
@@ -14,6 +14,7 @@
 #include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/browser/credit_card.h"
 #include "components/autofill/core/browser/test_personal_data_manager.h"
+#include "components/payments/core/payment_instrument.h"
 #include "components/payments/core/payment_prefs.h"
 #include "components/payments/core/payments_test_util.h"
 #include "components/payments/core/strings_util.h"
@@ -89,20 +90,20 @@
 
 // Tests whether payment can be completed when expected.
 TEST_F(PaymentRequestMediatorTest, TestCanPay) {
-  EXPECT_TRUE(payment_request_->selected_credit_card());
+  EXPECT_TRUE(payment_request_->selected_payment_method());
   EXPECT_TRUE(payment_request_->selected_shipping_profile());
   EXPECT_TRUE(payment_request_->selected_shipping_option());
   EXPECT_TRUE(payment_request_->selected_contact_profile());
   EXPECT_TRUE([GetPaymentRequestMediator() canPay]);
 
-  // Payment cannot be completed if there is no selected credit card.
-  autofill::CreditCard* selected_credit_card =
-      payment_request_->selected_credit_card();
-  payment_request_->set_selected_credit_card(nullptr);
+  // Payment cannot be completed if there is no selected payment method.
+  payments::PaymentInstrument* selected_payment_method =
+      payment_request_->selected_payment_method();
+  payment_request_->set_selected_payment_method(nullptr);
   EXPECT_FALSE([GetPaymentRequestMediator() canPay]);
 
-  // Restore the selected credit card.
-  payment_request_->set_selected_credit_card(selected_credit_card);
+  // Restore the selected payment method.
+  payment_request_->set_selected_payment_method(selected_payment_method);
   EXPECT_TRUE([GetPaymentRequestMediator() canPay]);
 
   // Payment cannot be completed if there is no selected shipping profile,
@@ -308,11 +309,11 @@
   EXPECT_EQ(MDCCollectionViewCellAccessoryDisclosureIndicator,
             payment_method_item.accessoryType);
 
-  // Reset the selected credit card.
-  payment_request_->set_selected_credit_card(nullptr);
+  // Reset the selected payment method.
+  payment_request_->set_selected_payment_method(nullptr);
 
-  // When there is no selected credit card, the Payment Method item should be of
-  // type CollectionViewDetailItem.
+  // When there is no selected payment method, the Payment Method item should be
+  // of type CollectionViewDetailItem.
   item = [GetPaymentRequestMediator() paymentMethodItem];
   ASSERT_TRUE([item isMemberOfClass:[CollectionViewDetailItem class]]);
   CollectionViewDetailItem* add_payment_method_item =
@@ -324,8 +325,8 @@
   EXPECT_EQ(MDCCollectionViewCellAccessoryDisclosureIndicator,
             add_payment_method_item.accessoryType);
 
-  // Remove the credit cards.
-  payment_request_->ClearCreditCards();
+  // Remove the payment methods.
+  payment_request_->ClearPaymentMethods();
 
   // No accessory view indicates there are no payment methods to choose from.
   item = [GetPaymentRequestMediator() paymentMethodItem];
diff --git a/ios/chrome/browser/ui/settings/password_details_collection_view_controller.mm b/ios/chrome/browser/ui/settings/password_details_collection_view_controller.mm
index b9271376..27f5628e 100644
--- a/ios/chrome/browser/ui/settings/password_details_collection_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/password_details_collection_view_controller.mm
@@ -144,6 +144,7 @@
   CollectionViewTextItem* siteHeader =
       [[CollectionViewTextItem alloc] initWithType:ItemTypeHeader];
   siteHeader.text = l10n_util::GetNSString(IDS_IOS_SHOW_PASSWORD_VIEW_SITE);
+  siteHeader.textColor = [[MDCPalette greyPalette] tint500];
   [model setHeader:siteHeader forSectionWithIdentifier:SectionIdentifierSite];
   PasswordDetailsItem* siteItem =
       [[PasswordDetailsItem alloc] initWithType:ItemTypeSite];
diff --git a/ios/chrome/browser/web/progress_indicator_egtest.mm b/ios/chrome/browser/web/progress_indicator_egtest.mm
index 5f5daf7..f82a548e 100644
--- a/ios/chrome/browser/web/progress_indicator_egtest.mm
+++ b/ios/chrome/browser/web/progress_indicator_egtest.mm
@@ -160,8 +160,7 @@
 
 // Tests that the progress indicator is shown and has expected progress value
 // after a form is submitted, and the toolbar is visible.
-// TODO(crbug.com/734874): Reenable test when fixed.
-- (void)DISABLED_testProgressIndicatorShownOnFormSubmit {
+- (void)testProgressIndicatorShownOnFormSubmit {
   if (IsIPadIdiom()) {
     EARL_GREY_TEST_SKIPPED(@"Skipped for iPad (no progress view in tablet)");
   }
diff --git a/ios/web/payments/payment_request.cc b/ios/web/payments/payment_request.cc
index 8b6d3b2..7798b25 100644
--- a/ios/web/payments/payment_request.cc
+++ b/ios/web/payments/payment_request.cc
@@ -362,7 +362,7 @@
 
   result->SetString(kPaymentRequestId, this->payment_request_id);
   result->SetString(kPaymentResponseMethodName, this->method_name);
-  result->Set(kPaymentResponseDetails, this->details.ToDictionaryValue());
+  result->SetString(kPaymentResponseDetails, this->details);
   if (!this->shipping_address.ToDictionaryValue()->empty()) {
     result->Set(kPaymentResponseShippingAddress,
                 this->shipping_address.ToDictionaryValue());
diff --git a/ios/web/payments/payment_request_unittest.cc b/ios/web/payments/payment_request_unittest.cc
index 3aeaee1..c80ccc4 100644
--- a/ios/web/payments/payment_request_unittest.cc
+++ b/ios/web/payments/payment_request_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <vector>
 
+#include "base/json/json_writer.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
 #include "components/payments/core/basic_card_response.h"
@@ -263,9 +264,7 @@
 TEST(PaymentRequestTest, EmptyResponseDictionary) {
   base::DictionaryValue expected_value;
 
-  std::unique_ptr<base::DictionaryValue> details(new base::DictionaryValue);
-  details->SetString("cardNumber", "");
-  expected_value.Set("details", std::move(details));
+  expected_value.SetString("details", "");
   expected_value.SetString("paymentRequestID", "");
   expected_value.SetString("methodName", "");
 
@@ -289,7 +288,9 @@
       new base::DictionaryValue);
   billing_address->SetString("postalCode", "90210");
   details->Set("billingAddress", std::move(billing_address));
-  expected_value.Set("details", std::move(details));
+  std::string stringified_details;
+  base::JSONWriter::Write(*details, &stringified_details);
+  expected_value.SetString("details", stringified_details);
   expected_value.SetString("paymentRequestID", "12345");
   expected_value.SetString("methodName", "American Express");
   std::unique_ptr<base::DictionaryValue> shipping_address(
@@ -304,14 +305,23 @@
   PaymentResponse payment_response;
   payment_response.payment_request_id = base::ASCIIToUTF16("12345");
   payment_response.method_name = base::ASCIIToUTF16("American Express");
-  payment_response.details.card_number =
+
+  payments::BasicCardResponse payment_response_details;
+  payment_response_details.card_number =
       base::ASCIIToUTF16("1111-1111-1111-1111");
-  payment_response.details.cardholder_name = base::ASCIIToUTF16("Jon Doe");
-  payment_response.details.expiry_month = base::ASCIIToUTF16("02");
-  payment_response.details.expiry_year = base::ASCIIToUTF16("2090");
-  payment_response.details.card_security_code = base::ASCIIToUTF16("111");
-  payment_response.details.billing_address.postal_code =
+  payment_response_details.cardholder_name = base::ASCIIToUTF16("Jon Doe");
+  payment_response_details.expiry_month = base::ASCIIToUTF16("02");
+  payment_response_details.expiry_year = base::ASCIIToUTF16("2090");
+  payment_response_details.card_security_code = base::ASCIIToUTF16("111");
+  payment_response_details.billing_address.postal_code =
       base::ASCIIToUTF16("90210");
+  std::unique_ptr<base::DictionaryValue> response_value =
+      payment_response_details.ToDictionaryValue();
+  std::string payment_response_stringified_details;
+  base::JSONWriter::Write(*response_value,
+                          &payment_response_stringified_details);
+  payment_response.details = payment_response_stringified_details;
+
   payment_response.shipping_address.postal_code = base::ASCIIToUTF16("94115");
   payment_response.shipping_option = base::ASCIIToUTF16("666");
   payment_response.payer_name = base::ASCIIToUTF16("Jane Doe");
@@ -630,13 +640,21 @@
 
   payments::BasicCardResponse card_response1;
   card_response1.card_number = base::ASCIIToUTF16("1234");
+  std::unique_ptr<base::DictionaryValue> response_value1 =
+      card_response1.ToDictionaryValue();
+  std::string stringified_card_response1;
+  base::JSONWriter::Write(*response_value1, &stringified_card_response1);
   payments::BasicCardResponse card_response2;
   card_response2.card_number = base::ASCIIToUTF16("8888");
-  response1.details = card_response1;
+  std::unique_ptr<base::DictionaryValue> response_value2 =
+      card_response2.ToDictionaryValue();
+  std::string stringified_card_response2;
+  base::JSONWriter::Write(*response_value2, &stringified_card_response2);
+  response1.details = stringified_card_response1;
   EXPECT_NE(response1, response2);
-  response2.details = card_response2;
+  response2.details = stringified_card_response2;
   EXPECT_NE(response1, response2);
-  response2.details = card_response1;
+  response2.details = stringified_card_response1;
   EXPECT_EQ(response1, response2);
 }
 
diff --git a/ios/web/public/payments/payment_request.h b/ios/web/public/payments/payment_request.h
index 4452690..76e80fe2 100644
--- a/ios/web/public/payments/payment_request.h
+++ b/ios/web/public/payments/payment_request.h
@@ -264,7 +264,7 @@
 
   // A credit card response object used by the merchant to process the
   // transaction and determine successful fund transfer.
-  payments::BasicCardResponse details;
+  std::string details;
 
   // If request_shipping was set to true in the PaymentOptions passed to the
   // PaymentRequest constructor, this will be the full and final shipping
diff --git a/ipc/README.md b/ipc/README.md
index 2b1f9f2..be5624d 100644
--- a/ipc/README.md
+++ b/ipc/README.md
@@ -604,7 +604,7 @@
 to define the mapping:
 
 ```
-mojom = "//content/common/url_loader.mojom"
+mojom = "//content/public/common/url_loader.mojom"
 public_headers = [ "//content/common/resource_request.h" ]
 traits_headers = [ "//content/common/resource_messages.h" ]
 ...
diff --git a/media/capture/video_capturer_source.cc b/media/capture/video_capturer_source.cc
index 7ff9915..bafdf42 100644
--- a/media/capture/video_capturer_source.cc
+++ b/media/capture/video_capturer_source.cc
@@ -13,12 +13,4 @@
 // to generate symbols across linking units.
 VideoCapturerSource::~VideoCapturerSource() {}
 
-void VideoCapturerSource::GetCurrentSupportedFormats(
-    int max_requested_width,
-    int max_requested_height,
-    double max_requested_frame_rate,
-    const VideoCaptureDeviceFormatsCB& callback) {
-  callback.Run(GetPreferredFormats());
-}
-
 }  // namespace media
diff --git a/media/capture/video_capturer_source.h b/media/capture/video_capturer_source.h
index 2cb9c02..c4cc854 100644
--- a/media/capture/video_capturer_source.h
+++ b/media/capture/video_capturer_source.h
@@ -50,18 +50,6 @@
 
   using RunningCallback = base::Callback<void(bool)>;
 
-  // Collects the formats that can currently be used.
-  // |max_requested_height|, |max_requested_width|, and
-  // |max_requested_frame_rate| is used by Tab and Screen capture to decide what
-  // resolution/framerate to generate. |callback| is triggered when the formats
-  // have been collected.
-  // TODO(guidou): Remove this method. http://crbug.com/706408
-  virtual void GetCurrentSupportedFormats(
-      int max_requested_width,
-      int max_requested_height,
-      double max_requested_frame_rate,
-      const VideoCaptureDeviceFormatsCB& callback);
-
   // Returns formats that are preferred and can currently be used. May be empty
   // if no formats are available or known.
   virtual VideoCaptureFormats GetPreferredFormats() = 0;
diff --git a/media/filters/frame_processor.cc b/media/filters/frame_processor.cc
index 6bb03a0..41d79c8 100644
--- a/media/filters/frame_processor.cc
+++ b/media/filters/frame_processor.cc
@@ -58,6 +58,10 @@
     needs_random_access_point_ = needs_random_access_point;
   }
 
+  DecodeTimestamp last_processed_decode_timestamp() const {
+    return last_processed_decode_timestamp_;
+  }
+
   // Gets a pointer to this track's ChunkDemuxerStream.
   ChunkDemuxerStream* stream() const { return stream_; }
 
@@ -82,11 +86,24 @@
   // |processed_frames_| is cleared in both cases.
   bool FlushProcessedFrames();
 
+  // Signals this track buffer's stream that a coded frame group is starting
+  // with decode timestamp |start_timestamp|.
+  void NotifyStartOfCodedFrameGroup(DecodeTimestamp start_time);
+
  private:
   // The decode timestamp of the last coded frame appended in the current coded
   // frame group. Initially kNoTimestamp, meaning "unset".
   DecodeTimestamp last_decode_timestamp_;
 
+  // On signalling the stream of a new coded frame group start time, this is
+  // reset to that start time. Any buffers subsequently enqueued for emission to
+  // the stream update this. This is managed separately from
+  // |last_decode_timestamp_| because |last_processed_decode_timestamp_| is not
+  // reset during Reset(), to especially be able to track the need to signal
+  // coded frame group start time for muxed post-discontiuity edge cases. See
+  // also FrameProcessor::ProcessFrame().
+  DecodeTimestamp last_processed_decode_timestamp_;
+
   // The coded frame duration of the last coded frame appended in the current
   // coded frame group. Initially kNoTimestamp, meaning "unset".
   base::TimeDelta last_frame_duration_;
@@ -116,6 +133,7 @@
 
 MseTrackBuffer::MseTrackBuffer(ChunkDemuxerStream* stream)
     : last_decode_timestamp_(kNoDecodeTimestamp()),
+      last_processed_decode_timestamp_(DecodeTimestamp()),
       last_frame_duration_(kNoTimestamp),
       highest_presentation_timestamp_(kNoTimestamp),
       needs_random_access_point_(true),
@@ -146,6 +164,7 @@
 
 void MseTrackBuffer::EnqueueProcessedFrame(
     const scoped_refptr<StreamParserBuffer>& frame) {
+  last_processed_decode_timestamp_ = frame->GetDecodeTimestamp();
   processed_frames_.push_back(frame);
 }
 
@@ -162,6 +181,11 @@
   return result;
 }
 
+void MseTrackBuffer::NotifyStartOfCodedFrameGroup(DecodeTimestamp start_time) {
+  last_processed_decode_timestamp_ = start_time;
+  stream_->OnStartOfCodedFrameGroup(start_time);
+}
+
 FrameProcessor::FrameProcessor(const UpdateDurationCB& update_duration_cb,
                                MediaLog* media_log)
     : group_start_timestamp_(kNoTimestamp),
@@ -188,7 +212,7 @@
   } else if (sequence_mode_) {
     // We're switching from 'sequence' to 'segments' mode. Be safe and signal a
     // new coded frame group on the next frame emitted.
-    coded_frame_group_last_dts_ = kNoDecodeTimestamp();
+    pending_notify_all_group_start_ = true;
   }
 
   // Step 8: Update the attribute to new mode.
@@ -314,13 +338,13 @@
     itr->second->Reset();
   }
 
-  // Maintain current |coded_frame_group_last_dts_| state for Reset() during
+  // Maintain current |pending_notify_all_group_start_| state for Reset() during
   // sequence mode. Reset it here only if in segments mode. In sequence mode,
   // the current coded frame group may be continued across Reset() operations to
   // allow the stream to coalesce what might otherwise be gaps in the buffered
-  // ranges. See also the declaration for |coded_frame_group_last_dts_|.
+  // ranges. See also the declaration for |pending_notify_all_group_start_|.
   if (!sequence_mode_) {
-    coded_frame_group_last_dts_ = kNoDecodeTimestamp();
+    pending_notify_all_group_start_ = true;
     return;
   }
 
@@ -357,7 +381,7 @@
   DVLOG(2) << __func__ << "(" << start_timestamp.InSecondsF() << ")";
 
   for (auto itr = track_buffers_.begin(); itr != track_buffers_.end(); ++itr) {
-    itr->second->stream()->OnStartOfCodedFrameGroup(start_timestamp);
+    itr->second->NotifyStartOfCodedFrameGroup(start_timestamp);
   }
 }
 
@@ -610,7 +634,7 @@
           decode_timestamp - track_last_decode_timestamp;
       if (track_dts_delta < base::TimeDelta() ||
           track_dts_delta > 2 * track_buffer->last_frame_duration()) {
-        DCHECK(coded_frame_group_last_dts_ != kNoDecodeTimestamp());
+        DCHECK(!pending_notify_all_group_start_);
         // 6.1. If mode equals "segments": Set group end timestamp to
         //      presentation timestamp.
         //      If mode equals "sequence": Set group start timestamp equal to
@@ -619,8 +643,8 @@
           group_end_timestamp_ = presentation_timestamp;
           // This triggers a discontinuity so we need to treat the next frames
           // appended within the append window as if they were the beginning of
-          // a new coded frame group. |coded_frame_group_last_dts_| is reset in
-          // Reset(), below, for "segments" mode.
+          // a new coded frame group. |pending_notify_all_group_start_| is reset
+          // in Reset(), below, for "segments" mode.
         } else {
           DVLOG(3) << __func__ << " : Sequence mode discontinuity, GETS: "
                    << group_end_timestamp_.InSecondsF();
@@ -711,33 +735,43 @@
 
     // We now have a processed buffer to append to the track buffer's stream.
     // If it is the first in a new coded frame group (such as following a
-    // discontinuity), notify all the track buffers' streams that a coded frame
-    // group is starting.
-    // If in 'sequence' appendMode, also check to make sure we don't need to
-    // signal the start of a new coded frame group in the case where
-    // timestampOffset adjustments by the app may cause this coded frame to be
-    // in the timeline prior to the last frame processed.
-    if (coded_frame_group_last_dts_ == kNoDecodeTimestamp() ||
-        (sequence_mode_ && coded_frame_group_last_dts_ > decode_timestamp)) {
+    // segments append mode discontinuity, or following a switch to segments
+    // append mode from sequence append mode), notify all the track buffers
+    // that a coded frame group is starting.
+    //
+    // Otherwise, if the buffer's DTS indicates that a new coded frame group
+    // needs signalling, signal just the buffer's track buffer. This can
+    // happen in both sequence and segments append modes when the first
+    // processed track's frame following a discontinuity has a higher DTS than
+    // this later processed track's first frame following that discontinuity.
+    if (pending_notify_all_group_start_ ||
+        track_buffer->last_processed_decode_timestamp() > decode_timestamp) {
+      DCHECK(frame->is_key_frame());
+
       // First, complete the append to track buffer streams of the previous
       // coded frame group's frames, if any.
       if (!FlushProcessedFrames())
         return false;
 
-      // TODO(wolenetz): This should be changed to a presentation timestamp. See
-      // http://crbug.com/402502
-      NotifyStartOfCodedFrameGroup(decode_timestamp);
+      if (pending_notify_all_group_start_) {
+        // TODO(wolenetz): This should be changed to a presentation timestamp.
+        // See http://crbug.com/402502
+        NotifyStartOfCodedFrameGroup(decode_timestamp);
+        pending_notify_all_group_start_ = false;
+      } else {
+        // TODO(wolenetz): This should be changed to a presentation timestamp.
+        // See http://crbug.com/402502
+        track_buffer->NotifyStartOfCodedFrameGroup(decode_timestamp);
+      }
     }
 
-    coded_frame_group_last_dts_ = decode_timestamp;
-
     DVLOG(3) << __func__ << ": Sending processed frame to stream, "
              << "PTS=" << presentation_timestamp.InSecondsF()
              << ", DTS=" << decode_timestamp.InSecondsF();
 
     // Steps 11-16: Note, we optimize by appending groups of contiguous
     // processed frames for each track buffer at end of ProcessFrames() or prior
-    // to NotifyStartOfCodedFrameGroup().
+    // to signalling coded frame group starts.
     track_buffer->EnqueueProcessedFrame(frame);
 
     // 17. Set last decode timestamp for track buffer to decode timestamp.
diff --git a/media/filters/frame_processor.h b/media/filters/frame_processor.h
index cd8d662..2a7d33d 100644
--- a/media/filters/frame_processor.h
+++ b/media/filters/frame_processor.h
@@ -145,17 +145,14 @@
   // set to false ("segments").
   bool sequence_mode_ = false;
 
-  // Tracks whether or not the next processed frame is a continuation of a coded
-  // frame group (see https://w3c.github.io/media-source/#coded-frame-group).
-  // Resets to kNoDecodeTimestamp() upon detection of 'segments' mode
-  // discontinuity, parser reset during 'segments' mode, or switching from
-  // 'sequence' to 'segments' mode.
-  // Once a processed coded frame is emitted for the current coded frame group,
-  // tracks the decode timestamp of the last frame emitted.
-  // Explicit setting of timestampOffset will trigger subsequent notification of
-  // a new coded frame start to the tracks' streams, even in 'sequence' mode, if
-  // the resulting frame has a DTS less than this.
-  DecodeTimestamp coded_frame_group_last_dts_ = kNoDecodeTimestamp();
+  // Tracks whether or not we need to notify all track buffers of a new coded
+  // frame group (see https://w3c.github.io/media-source/#coded-frame-group)
+  // upon the next successfully processed frame.  Set true initially and upon
+  // detection of 'segments' mode discontinuity, parser reset during 'segments'
+  // mode, or switching from 'sequence' to 'segments' mode.  Individual track
+  // buffers can also be notified of an updated coded frame group start in edge
+  // cases. See further comments in ProcessFrame().
+  bool pending_notify_all_group_start_ = true;
 
   // Tracks the MSE coded frame processing variable of same name.
   // Initially kNoTimestamp, meaning "unset".
diff --git a/media/filters/frame_processor_unittest.cc b/media/filters/frame_processor_unittest.cc
index 679a6f4..7c5fb88 100644
--- a/media/filters/frame_processor_unittest.cc
+++ b/media/filters/frame_processor_unittest.cc
@@ -253,8 +253,7 @@
   // thereof of new coded frame group by the FrameProcessor. See
   // https://crbug.com/580613.
   bool in_coded_frame_group() {
-    return frame_processor_->coded_frame_group_last_dts_ !=
-           kNoDecodeTimestamp();
+    return !frame_processor_->pending_notify_all_group_start_;
   }
 
   void seek(ChunkDemuxerStream* stream, base::TimeDelta seek_time) {
@@ -652,7 +651,15 @@
     // This causes [55,85) to merge with [100,230) here for audio, and similar
     // for video. See also https://crbug.com/620523.
     CheckExpectedRangesByTimestamp(audio_.get(), "{ [55,230) }");
-    CheckExpectedRangesByTimestamp(video_.get(), "{ [55,240) }");
+    // Note that 'sequence' mode group start signalling (if the decode time goes
+    // into the past) is per-track after the first frame has been processed.
+    // Hence 65, not 55 here. Similarly, 'segments' mode muxed tracks where
+    // discontinuity is followed by tracks whose first frames decrease in DTS
+    // relative to each other (allowed since media segments are not required to
+    // contain frames for every track) could result in decreasing range start
+    // times for those later tracks. See
+    // AudioVideo_OutOfSequence_After_Discontinuity for deeper verification.
+    CheckExpectedRangesByTimestamp(video_.get(), "{ [65,240) }");
   } else {
     CheckExpectedRangesByTimestamp(audio_.get(),
                                    "{ [55,85) [100,130) [200,230) }");
@@ -691,6 +698,124 @@
   }
 }
 
+TEST_P(FrameProcessorTest, AudioVideo_OutOfSequence_After_Discontinuity) {
+  // Once a discontinuity is detected (and all tracks drop everything until the
+  // next keyframe per each track), we should gracefully handle the case where
+  // some tracks' first keyframe after the discontinuity are appended after, but
+  // end up earlier in timeline than some other track(s). In particular, we
+  // shouldn't notify all tracks that a new coded frame group is starting and
+  // begin dropping leading non-keyframes from all tracks.  Rather, we should
+  // notify just the track encountering this new type of discontinuity.  Since
+  // MSE doesn't require all media segments to contain media from every track,
+  // these append sequences can occur.
+  InSequence s;
+  AddTestTracks(HAS_AUDIO | HAS_VIDEO);
+  bool using_sequence_mode = GetParam();
+  frame_processor_->SetSequenceMode(using_sequence_mode);
+
+  // Begin with a simple set of appends for all tracks.
+  if (using_sequence_mode) {
+    // Allow room in the timeline for the last audio append (50K, below) in this
+    // test to remain within default append window [0, +Infinity]. Moving the
+    // sequence mode appends to begin at time 100ms, the same time as the first
+    // append, below, results in a -20ms offset (instead of a -120ms offset)
+    // applied to frames beginning at the first frame after the discontinuity
+    // caused by the video append at 160K, below.
+    SetTimestampOffset(frame_duration_ * 10);
+  }
+  EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 14));
+  ProcessFrames("100K 110K 120K", "110K 120K 130K");
+  EXPECT_TRUE(in_coded_frame_group());
+  EXPECT_EQ(base::TimeDelta(), timestamp_offset_);
+  CheckExpectedRangesByTimestamp(audio_.get(), "{ [100,130) }");
+  CheckExpectedRangesByTimestamp(video_.get(), "{ [100,140) }");
+
+  // Trigger (normal) discontinuity with one track (video).
+  if (using_sequence_mode)
+    EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 15));
+  else
+    EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 17));
+
+  ProcessFrames("", "160K");
+  EXPECT_TRUE(in_coded_frame_group());
+
+  if (using_sequence_mode) {
+    // The new video buffer is relocated into [140,150).
+    EXPECT_EQ(frame_duration_ * -2, timestamp_offset_);
+    CheckExpectedRangesByTimestamp(audio_.get(), "{ [100,130) }");
+    CheckExpectedRangesByTimestamp(video_.get(), "{ [100,150) }");
+  } else {
+    // The new video buffer is at [160,170).
+    EXPECT_EQ(base::TimeDelta(), timestamp_offset_);
+    CheckExpectedRangesByTimestamp(audio_.get(), "{ [100,130) }");
+    CheckExpectedRangesByTimestamp(video_.get(), "{ [100,140) [160,170) }");
+  }
+
+  // Append to the other track (audio) with lower time than the video frame we
+  // just appended. Append with a timestamp such that segments mode demonstrates
+  // we don't retroactively extend the new video buffer appended above's range
+  // start back to this audio start time.
+  if (using_sequence_mode)
+    EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 15));
+  else
+    EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 17));
+
+  ProcessFrames("50K", "");
+  EXPECT_TRUE(in_coded_frame_group());
+
+  // Because this is the first audio buffer appended following the discontinuity
+  // detected while appending the video frame, above, a new coded frame group
+  // for video is not triggered.
+  if (using_sequence_mode) {
+    // The new audio buffer is relocated into [30,40). Note the muxed 'sequence'
+    // mode append mode results in a buffered range gap in this case.
+    EXPECT_EQ(frame_duration_ * -2, timestamp_offset_);
+    CheckExpectedRangesByTimestamp(audio_.get(), "{ [30,40) [100,130) }");
+    CheckExpectedRangesByTimestamp(video_.get(), "{ [100,150) }");
+  } else {
+    EXPECT_EQ(base::TimeDelta(), timestamp_offset_);
+    CheckExpectedRangesByTimestamp(audio_.get(), "{ [50,60) [100,130) }");
+    CheckExpectedRangesByTimestamp(video_.get(), "{ [100,140) [160,170) }");
+  }
+
+  // Finally, append a non-keyframe to the first track (video), to continue the
+  // GOP that started the normal discontinuity on the previous video append.
+  if (using_sequence_mode)
+    EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 16));
+  else
+    EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 18));
+
+  ProcessFrames("", "170");
+  EXPECT_TRUE(in_coded_frame_group());
+
+  // Verify the final buffers. First, re-seek audio since we appended data
+  // earlier than what already satisfied our initial seek to start. We satisfy
+  // the seek with the first buffer in [0,1000).
+  seek(audio_.get(), base::TimeDelta());
+  if (using_sequence_mode) {
+    // The new video buffer is relocated into [150,160).
+    EXPECT_EQ(frame_duration_ * -2, timestamp_offset_);
+    CheckExpectedRangesByTimestamp(audio_.get(), "{ [30,40) [100,130) }");
+    CheckReadsThenReadStalls(audio_.get(), "30:50");
+    seek(audio_.get(), 10 * frame_duration_);
+    CheckReadsThenReadStalls(audio_.get(), "100 110 120");
+
+    CheckExpectedRangesByTimestamp(video_.get(), "{ [100,160) }");
+    CheckReadsThenReadStalls(video_.get(), "110 120 130 140:160 150:170");
+  } else {
+    EXPECT_EQ(base::TimeDelta(), timestamp_offset_);
+    CheckExpectedRangesByTimestamp(audio_.get(), "{ [50,60) [100,130) }");
+    CheckReadsThenReadStalls(audio_.get(), "50");
+    seek(audio_.get(), 10 * frame_duration_);
+    CheckReadsThenReadStalls(audio_.get(), "100 110 120");
+
+    CheckExpectedRangesByTimestamp(video_.get(), "{ [100,140) [160,180) }");
+    CheckReadsThenReadStalls(video_.get(), "110 120 130");
+    seek(video_.get(), 16 * frame_duration_);
+    CheckReadsThenReadStalls(video_.get(), "160 170");
+  }
+}
+
 TEST_P(FrameProcessorTest,
        AppendWindowFilterOfNegativeBufferTimestampsWithPrerollDiscard) {
   InSequence s;
diff --git a/net/nqe/network_quality_estimator.cc b/net/nqe/network_quality_estimator.cc
index c0316253..5c13b47 100644
--- a/net/nqe/network_quality_estimator.cc
+++ b/net/nqe/network_quality_estimator.cc
@@ -766,8 +766,7 @@
 
   bool private_network_request = nqe::internal::IsPrivateHost(
       request.context()->host_resolver(),
-      HostPortPair(request.url().host(), request.url().EffectiveIntPort()),
-      net_log_);
+      HostPortPair(request.url().host(), request.url().EffectiveIntPort()));
 
   return (use_localhost_requests_ || !private_network_request) &&
          // Verify that response headers are received, so it can be ensured that
diff --git a/net/nqe/network_quality_estimator_util.cc b/net/nqe/network_quality_estimator_util.cc
index bffaac2..34e6b77e 100644
--- a/net/nqe/network_quality_estimator_util.cc
+++ b/net/nqe/network_quality_estimator_util.cc
@@ -10,6 +10,7 @@
 #include "net/base/ip_endpoint.h"
 #include "net/base/net_errors.h"
 #include "net/dns/host_resolver.h"
+#include "net/log/net_log_with_source.h"
 
 namespace net {
 
@@ -18,14 +19,14 @@
 namespace internal {
 
 bool IsPrivateHost(HostResolver* host_resolver,
-                   const HostPortPair& host_port_pair,
-                   const NetLogWithSource& net_log) {
+                   const HostPortPair& host_port_pair) {
   // Try resolving |host_port_pair.host()| synchronously.
   HostResolver::RequestInfo resolve_info(host_port_pair);
   resolve_info.set_allow_cached_response(true);
   AddressList addresses;
   // Resolve synchronously using the resolver's cache.
-  int rv = host_resolver->ResolveFromCache(resolve_info, &addresses, net_log);
+  int rv = host_resolver->ResolveFromCache(resolve_info, &addresses,
+                                           NetLogWithSource());
 
   DCHECK_NE(rv, ERR_IO_PENDING);
   if (rv == OK && !addresses.empty()) {
diff --git a/net/nqe/network_quality_estimator_util.h b/net/nqe/network_quality_estimator_util.h
index 94289c1e..02448241 100644
--- a/net/nqe/network_quality_estimator_util.h
+++ b/net/nqe/network_quality_estimator_util.h
@@ -11,7 +11,6 @@
 
 class HostPortPair;
 class HostResolver;
-class NetLogWithSource;
 
 namespace nqe {
 
@@ -28,8 +27,7 @@
 // |host_port_pair|. The method is synchronous.
 // |host_resolver| must not be null.
 NET_EXPORT_PRIVATE bool IsPrivateHost(HostResolver* host_resolver,
-                                      const HostPortPair& host_port_pair,
-                                      const NetLogWithSource& net_log);
+                                      const HostPortPair& host_port_pair);
 
 }  // namespace internal
 
diff --git a/net/nqe/network_quality_estimator_util_unittest.cc b/net/nqe/network_quality_estimator_util_unittest.cc
index 0f80d78..896ac7b7 100644
--- a/net/nqe/network_quality_estimator_util_unittest.cc
+++ b/net/nqe/network_quality_estimator_util_unittest.cc
@@ -29,8 +29,6 @@
 TEST(NetworkQualityEstimatorUtilTest, ReservedHost) {
   std::unique_ptr<BoundTestNetLog> net_log =
       base::MakeUnique<BoundTestNetLog>();
-  BoundTestNetLog* net_log_ptr = net_log.get();
-
   MockCachingHostResolver mock_host_resolver;
 
   scoped_refptr<net::RuleBasedHostResolverProc> rules(
@@ -75,28 +73,23 @@
   EXPECT_EQ(2u, mock_host_resolver.num_resolve());
 
   EXPECT_FALSE(IsPrivateHost(&mock_host_resolver,
-                             HostPortPair("2607:f8b0:4006:819::200e", 80),
-                             net_log_ptr->bound()));
+                             HostPortPair("2607:f8b0:4006:819::200e", 80)));
   EXPECT_EQ(1u, mock_host_resolver.num_resolve_from_cache());
 
-  EXPECT_TRUE(IsPrivateHost(&mock_host_resolver,
-                            HostPortPair("192.168.0.1", 443),
-                            net_log_ptr->bound()));
+  EXPECT_TRUE(
+      IsPrivateHost(&mock_host_resolver, HostPortPair("192.168.0.1", 443)));
   EXPECT_EQ(2u, mock_host_resolver.num_resolve_from_cache());
 
-  EXPECT_FALSE(IsPrivateHost(&mock_host_resolver,
-                             HostPortPair("92.168.0.1", 443),
-                             net_log_ptr->bound()));
+  EXPECT_FALSE(
+      IsPrivateHost(&mock_host_resolver, HostPortPair("92.168.0.1", 443)));
   EXPECT_EQ(3u, mock_host_resolver.num_resolve_from_cache());
 
-  EXPECT_TRUE(IsPrivateHost(&mock_host_resolver,
-                            HostPortPair("example1.com", 443),
-                            net_log_ptr->bound()));
+  EXPECT_TRUE(
+      IsPrivateHost(&mock_host_resolver, HostPortPair("example1.com", 443)));
   EXPECT_EQ(4u, mock_host_resolver.num_resolve_from_cache());
 
-  EXPECT_FALSE(IsPrivateHost(&mock_host_resolver,
-                             HostPortPair("example2.com", 443),
-                             net_log_ptr->bound()));
+  EXPECT_FALSE(
+      IsPrivateHost(&mock_host_resolver, HostPortPair("example2.com", 443)));
   EXPECT_EQ(5u, mock_host_resolver.num_resolve_from_cache());
 
   // IsPrivateHost() should have queried only the resolver's cache.
@@ -109,8 +102,6 @@
 TEST(NetworkQualityEstimatorUtilTest, ReservedHostUncached) {
   std::unique_ptr<BoundTestNetLog> net_log =
       base::MakeUnique<BoundTestNetLog>();
-  BoundTestNetLog* net_log_ptr = net_log.get();
-
   MockCachingHostResolver mock_host_resolver;
 
   scoped_refptr<net::RuleBasedHostResolverProc> rules(
@@ -121,9 +112,8 @@
   mock_host_resolver.set_rules(rules.get());
 
   // Not in DNS host cache, so should not be marked as private.
-  EXPECT_FALSE(IsPrivateHost(&mock_host_resolver,
-                             HostPortPair("example3.com", 443),
-                             net_log_ptr->bound()));
+  EXPECT_FALSE(
+      IsPrivateHost(&mock_host_resolver, HostPortPair("example3.com", 443)));
   EXPECT_EQ(0u, mock_host_resolver.num_resolve());
   EXPECT_EQ(1u, mock_host_resolver.num_resolve_from_cache());
 
@@ -140,9 +130,8 @@
     EXPECT_EQ(OK, callback.WaitForResult());
     EXPECT_EQ(1u, mock_host_resolver.num_resolve());
   }
-  EXPECT_TRUE(IsPrivateHost(&mock_host_resolver,
-                            HostPortPair("example3.com", 443),
-                            net_log_ptr->bound()));
+  EXPECT_TRUE(
+      IsPrivateHost(&mock_host_resolver, HostPortPair("example3.com", 443)));
 
   // IsPrivateHost() should have queried only the resolver's cache.
   EXPECT_EQ(1u, mock_host_resolver.num_resolve());
@@ -163,18 +152,12 @@
   scoped_refptr<net::RuleBasedHostResolverProc> rules(
       new net::RuleBasedHostResolverProc(nullptr));
 
-  EXPECT_TRUE(IsPrivateHost(&resolver, HostPortPair("localhost", 443),
-                            net_log_ptr->bound()));
-  EXPECT_TRUE(IsPrivateHost(&resolver, HostPortPair("localhost6", 443),
-                            net_log_ptr->bound()));
-  EXPECT_TRUE(IsPrivateHost(&resolver, HostPortPair("127.0.0.1", 80),
-                            net_log_ptr->bound()));
-  EXPECT_TRUE(IsPrivateHost(&resolver, HostPortPair("0.0.0.0", 80),
-                            net_log_ptr->bound()));
-  EXPECT_TRUE(
-      IsPrivateHost(&resolver, HostPortPair("::1", 80), net_log_ptr->bound()));
-  EXPECT_FALSE(IsPrivateHost(&resolver, HostPortPair("google.com", 80),
-                             net_log_ptr->bound()));
+  EXPECT_TRUE(IsPrivateHost(&resolver, HostPortPair("localhost", 443)));
+  EXPECT_TRUE(IsPrivateHost(&resolver, HostPortPair("localhost6", 443)));
+  EXPECT_TRUE(IsPrivateHost(&resolver, HostPortPair("127.0.0.1", 80)));
+  EXPECT_TRUE(IsPrivateHost(&resolver, HostPortPair("0.0.0.0", 80)));
+  EXPECT_TRUE(IsPrivateHost(&resolver, HostPortPair("::1", 80)));
+  EXPECT_FALSE(IsPrivateHost(&resolver, HostPortPair("google.com", 80)));
 }
 
 }  // namespace
diff --git a/net/nqe/throughput_analyzer.cc b/net/nqe/throughput_analyzer.cc
index 2c4d1b8..96880ed 100644
--- a/net/nqe/throughput_analyzer.cc
+++ b/net/nqe/throughput_analyzer.cc
@@ -263,8 +263,7 @@
 
   bool private_network_request = nqe::internal::IsPrivateHost(
       request.context()->host_resolver(),
-      HostPortPair(request.url().host(), request.url().EffectiveIntPort()),
-      net_log_);
+      HostPortPair(request.url().host(), request.url().EffectiveIntPort()));
 
   return !(use_localhost_requests_for_tests_ || !private_network_request) ||
          request.creation_time() < last_connection_change_;
diff --git a/net/nqe/throughput_analyzer_unittest.cc b/net/nqe/throughput_analyzer_unittest.cc
index ca7091f..d3011cd 100644
--- a/net/nqe/throughput_analyzer_unittest.cc
+++ b/net/nqe/throughput_analyzer_unittest.cc
@@ -119,11 +119,11 @@
                                 ? "http://127.0.0.1/test.html"
                                 : "http://example.com/test.html";
 
-    EXPECT_EQ(test.use_local_requests,
-              nqe::internal::IsPrivateHost(
-                  context.host_resolver(),
-                  HostPortPair(GURL(url).host(), GURL(url).EffectiveIntPort()),
-                  base::MakeUnique<BoundTestNetLog>()->bound()));
+    EXPECT_EQ(
+        test.use_local_requests,
+        nqe::internal::IsPrivateHost(
+            context.host_resolver(),
+            HostPortPair(GURL(url).host(), GURL(url).EffectiveIntPort())));
     for (size_t i = 0; i < 1000; ++i) {
       std::unique_ptr<URLRequest> request(
           context.CreateRequest(GURL(url), DEFAULT_PRIORITY, &test_delegate,
diff --git a/net/quic/core/congestion_control/bbr_sender_test.cc b/net/quic/core/congestion_control/bbr_sender_test.cc
index 68b37d9..dc91c20 100644
--- a/net/quic/core/congestion_control/bbr_sender_test.cc
+++ b/net/quic/core/congestion_control/bbr_sender_test.cc
@@ -314,7 +314,6 @@
             sender_->ExportDebugState().max_bandwidth);
   // TODO(ianswett): Expect 0 packets are lost once BBR no longer measures
   // bandwidth higher than the link rate.
-  EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);
   // The margin here is high, because the aggregation greatly increases
   // smoothed rtt.
   EXPECT_GE(kTestRtt * 4, rtt_stats_->smoothed_rtt());
@@ -378,11 +377,10 @@
             sender_->ExportDebugState().max_bandwidth);
   // TODO(ianswett): Expect 0 packets are lost once BBR no longer measures
   // bandwidth higher than the link rate.
-  EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);
   // The margin here is high, because the aggregation greatly increases
   // smoothed rtt.
   EXPECT_GE(kTestRtt * 4, rtt_stats_->smoothed_rtt());
-  ExpectApproxEq(kTestRtt, rtt_stats_->min_rtt(), 0.12f);
+  ExpectApproxEq(kTestRtt, rtt_stats_->min_rtt(), 0.33f);
 }
 
 // Test a simple long data transfer with 2 rtts of aggregation.
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index 967b5bd..78405b7 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -1601,6 +1601,48 @@
       }
     ]
   },
+  "Android WebView L (dbg)": {
+    "cts_tests": [
+      {
+        "arch": "arm64",
+        "platform": "L"
+      }
+    ],
+    "instrumentation_tests": [
+      {
+        "test": "system_webview_shell_layout_test_apk"
+      }
+    ]
+  },
+  "Android WebView M (dbg)": {
+    "cts_tests": [
+      {
+        "arch": "arm64",
+        "platform": "M"
+      }
+    ],
+    "instrumentation_tests": [
+      {
+        "test": "system_webview_shell_layout_test_apk"
+      },
+      {
+        "test": "webview_ui_test_app_test_apk"
+      }
+    ]
+  },
+  "Android WebView N (dbg)": {
+    "cts_tests": [
+      {
+        "arch": "arm64",
+        "platform": "N"
+      }
+    ],
+    "instrumentation_tests": [
+      {
+        "test": "system_webview_shell_layout_test_apk"
+      }
+    ]
+  },
   "Android arm Builder (dbg)": {
     "additional_compile_targets": [
       "dump_syms",
diff --git a/testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter b/testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter
index 37c9525..8285dec5 100644
--- a/testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter
+++ b/testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter
@@ -150,6 +150,7 @@
 -WebContentsImplBrowserTest.GetSizeForNewRenderView
 -WebContentsImplBrowserTest.UserAgentOverride
 -WebContentsViewAuraTest.ScreenshotForSwappedOutRenderViews
+-WebRtcGetUserMediaOldConstraintsBrowserTest.GetUserMediaWithInvalidMandatorySourceID
 -WebRtcGetUserMediaOldConstraintsBrowserTest.TwoGetUserMediaWithFirstHdSecondVga
 -WebRtcGetUserMediaOldConstraintsBrowserTest.TwoGetUserMediaWithSecondVideoCropped
 -WebRtcVideoCaptureBrowserTest.RecoverFromCrashInVideoCaptureProcess
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 9c3385a..83c4156 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -403,6 +403,25 @@
             ]
         }
     ],
+    "CaptivePortalCertificateList": [
+        {
+            "platforms": [
+                "android",
+                "chromeos",
+                "linux",
+                "mac",
+                "win"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "CaptivePortalCertificateList"
+                    ]
+                }
+            ]
+        }
+    ],
     "CaptivePortalInterstitial": [
         {
             "platforms": [
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
index 82f6d37..517751a2 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
@@ -16369,8 +16369,8 @@
 crbug.com/591099 inspector-protocol/css/cssom-matching-rules.html [ Failure ]
 crbug.com/591099 inspector-protocol/css/cssom-modify-rule-and-get-rule-list.html [ Failure ]
 crbug.com/591099 inspector-protocol/css/media-query-listener-exception.html [ Failure ]
-crbug.com/591099 inspector-protocol/debugger/debugger-step-into-dedicated-worker.html [ Failure ]
-crbug.com/591099 inspector-protocol/debugger/suspend-setTimeout-on-pause-in-dedicated-worker.html [ Failure ]
+crbug.com/591099 inspector-protocol/debugger/debugger-step-into-dedicated-worker.js [ Failure ]
+crbug.com/591099 inspector-protocol/debugger/suspend-setTimeout-on-pause-in-dedicated-worker.js [ Failure ]
 crbug.com/591099 inspector-protocol/dom-snapshot/dom-snapshot-getSnapshot.js [ Crash Failure Timeout ]
 crbug.com/591099 inspector-protocol/emulation/device-emulation-small-dw.js [ Failure ]
 crbug.com/591099 inspector-protocol/emulation/device-emulation-small.js [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService b/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService
index 17e0c13..344378c 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService
@@ -384,6 +384,7 @@
 Bug(none) external/wpt/html/browsers/history/the-location-interface/location-protocol-setter-non-broken.html [ Failure Timeout ]
 Bug(none) external/wpt/html/browsers/history/the-location-interface/location-prototype-setting-cross-origin.sub.html [ Failure Timeout ]
 Bug(none) external/wpt/html/browsers/history/the-location-interface/location-prototype-setting-goes-cross-origin-domain.sub.html [ Failure Timeout ]
+Bug(none) external/wpt/html/browsers/offline/appcache/workers/appcache-worker.html [ Timeout ]
 Bug(none) external/wpt/html/browsers/offline/application-cache-api/api_status_idle.html [ Failure Timeout ]
 Bug(none) external/wpt/html/browsers/the-window-object/Window-document.html [ Failure Timeout ]
 Bug(none) external/wpt/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/creating_browsing_context_test_01.html [ Failure Timeout ]
@@ -2448,7 +2449,7 @@
 Bug(none) http/tests/security/media-element-audio-source-node-cross-origin-allowed.html [ Timeout ]
 Bug(none) http/tests/security/media-element-audio-source-node-cross-origin-with-credentials.html [ Failure Timeout ]
 Bug(none) http/tests/security/media-element-audio-source-node-cross-origin.html [ Timeout ]
-Bug(none) http/tests/security/media-element-audio-source-node-same-origin.html [ Timeout ]
+Bug(none) http/tests/security/media-element-audio-source-node-same-origin.html [ Failure Pass ]
 Bug(none) http/tests/security/mime-type-execute-as-html-01.html [ Failure ]
 Bug(none) http/tests/security/mime-type-execute-as-html-04.html [ Failure ]
 Bug(none) http/tests/security/mixedContent/about-blank-iframe-in-main-frame.html [ Timeout ]
@@ -3357,3 +3358,4 @@
 Bug(none) webshare/share-nonutf8-encoding.html [ Timeout ]
 Bug(none) webshare/share-success.html [ Timeout ]
 Bug(none) webshare/share-types.html [ Timeout ]
+Bug(none) webshare/share-url-relative.html [ Timeout ]
diff --git a/third_party/WebKit/LayoutTests/SlowTests b/third_party/WebKit/LayoutTests/SlowTests
index 89e2d00d..d68e0905 100644
--- a/third_party/WebKit/LayoutTests/SlowTests
+++ b/third_party/WebKit/LayoutTests/SlowTests
@@ -268,7 +268,7 @@
 crbug.com/480769 virtual/mojo-loading/http/tests/inspector/service-workers/service-workers-redundant.html [ Slow ]
 crbug.com/480769 http/tests/inspector/service-workers/service-worker-agents.html [ Slow ]
 crbug.com/480769 virtual/mojo-loading/http/tests/inspector/service-workers/service-worker-agents.html [ Slow ]
-crbug.com/504703 inspector-protocol/debugger/debugger-step-into-dedicated-worker.html [ Slow ]
+crbug.com/504703 inspector-protocol/debugger/debugger-step-into-dedicated-worker.js [ Slow ]
 crbug.com/535478 [ Win ] virtual/threaded/inspector/tracing/decode-resize.html [ Slow ]
 crbug.com/548765 http/tests/inspector/console-fetch-logging.html [ Slow ]
 crbug.com/548765 virtual/mojo-loading/http/tests/inspector/console-fetch-logging.html [ Slow ]
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 0fd4cfe1..9600407 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -402,7 +402,7 @@
 crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/vertical-align-sub-001.xht [ Failure ]
 crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/vertical-align-super-001.xht [ Failure ]
 
-# Atomic inline margins.
+# Inline: abspos inline static positions.
 crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/vertical-align-baseline-004a.xht [ Failure ]
 crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/vertical-align-baseline-005a.xht [ Failure ]
 
@@ -430,9 +430,6 @@
 crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/normal-flow/block-non-replaced-height-005.xht [ Failure ]
 crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/normal-flow/inline-table-zorder-005.xht [ Failure ]
 
-# Inline: atomic inline margins.
-crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/normal-flow/inline-block-non-replaced-height-002.xht [ Failure ]
-
 # Inline: atomic inline preferred width.
 crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/normal-flow/inline-block-valign-001.xht [ Failure ]
 crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/normal-flow/inline-block-valign-002.xht [ Failure ]
@@ -914,8 +911,8 @@
 crbug.com/692560 external/wpt/html/semantics/document-metadata/styling/LinkStyle.html [ Failure Pass ]
 crbug.com/627706 external/wpt/html/semantics/embedded-content/the-img-element/invalid-src.html [ Skip ]
 # MANIFEST.json contains any *-manual.*. https://github.com/w3c/web-platform-tests/issues/4137
-Bug(github) external/wpt/uievents/keyboard/key-manual.css [ Skip ]
-Bug(github) external/wpt/uievents/keyboard/key-manual.js [ Skip ]
+Bug(github) external/wpt/uievents/keyboard/key.css [ Skip ]
+Bug(github) external/wpt/uievents/keyboard/key.js [ Skip ]
 
 crbug.com/698135 external/wpt/editing/run/delete.html [ Crash Failure Timeout ]
 crbug.com/698165 external/wpt/editing/run/forwarddelete.html [ Pass Timeout ]
diff --git a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
index 093e9f8..97cce6be 100644
--- a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
+++ b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
@@ -3967,18 +3967,6 @@
      {}
     ]
    ],
-   "uievents/keyboard/key-manual.css": [
-    [
-     "/uievents/keyboard/key-manual.css",
-     {}
-    ]
-   ],
-   "uievents/keyboard/key-manual.js": [
-    [
-     "/uievents/keyboard/key-manual.js",
-     {}
-    ]
-   ],
    "uievents/mouse/mouseevent_move_button-manual.html": [
     [
      "/uievents/mouse/mouseevent_move_button-manual.html",
@@ -87640,6 +87628,36 @@
      {}
     ]
    ],
+   "html/browsers/offline/appcache/workers/resources/appcache-dedicated-worker-not-in-cache.js": [
+    [
+     {}
+    ]
+   ],
+   "html/browsers/offline/appcache/workers/resources/appcache-shared-worker-not-in-cache.js": [
+    [
+     {}
+    ]
+   ],
+   "html/browsers/offline/appcache/workers/resources/appcache-worker-data.py": [
+    [
+     {}
+    ]
+   ],
+   "html/browsers/offline/appcache/workers/resources/appcache-worker-import.py": [
+    [
+     {}
+    ]
+   ],
+   "html/browsers/offline/appcache/workers/resources/appcache-worker.manifest": [
+    [
+     {}
+    ]
+   ],
+   "html/browsers/offline/appcache/workers/resources/appcache-worker.py": [
+    [
+     {}
+    ]
+   ],
    "html/browsers/offline/application-cache-api/.gitkeep": [
     [
      {}
@@ -105400,6 +105418,16 @@
      {}
     ]
    ],
+   "uievents/keyboard/key.css": [
+    [
+     {}
+    ]
+   ],
+   "uievents/keyboard/key.js": [
+    [
+     {}
+    ]
+   ],
    "uievents/legacy-domevents-tests/Status.html": [
     [
      {}
@@ -130450,6 +130478,12 @@
      {}
     ]
    ],
+   "html/browsers/offline/appcache/workers/appcache-worker.html": [
+    [
+     "/html/browsers/offline/appcache/workers/appcache-worker.html",
+     {}
+    ]
+   ],
    "html/browsers/offline/application-cache-api/api_status_idle.html": [
     [
      "/html/browsers/offline/application-cache-api/api_status_idle.html",
@@ -225787,6 +225821,34 @@
    "da39a3ee5e6b4b0d3255bfef95601890afd80709",
    "support"
   ],
+  "html/browsers/offline/appcache/workers/appcache-worker.html": [
+   "d4dc559a600b1d197e7b31f960b396da3faa4e42",
+   "testharness"
+  ],
+  "html/browsers/offline/appcache/workers/resources/appcache-dedicated-worker-not-in-cache.js": [
+   "4f2e89ef4b23ff17d6c333831cdd81ebf46a9315",
+   "support"
+  ],
+  "html/browsers/offline/appcache/workers/resources/appcache-shared-worker-not-in-cache.js": [
+   "e0ca1027ad1077119b95f6a56055417452ab4b04",
+   "support"
+  ],
+  "html/browsers/offline/appcache/workers/resources/appcache-worker-data.py": [
+   "5686f9536fc98675e737d703d054370b1398399a",
+   "support"
+  ],
+  "html/browsers/offline/appcache/workers/resources/appcache-worker-import.py": [
+   "8dced5671671d536c1e0bb554bf919f7f2f3861e",
+   "support"
+  ],
+  "html/browsers/offline/appcache/workers/resources/appcache-worker.manifest": [
+   "284dcbf9adad4bc5d823e654ca889c08f679735c",
+   "support"
+  ],
+  "html/browsers/offline/appcache/workers/resources/appcache-worker.py": [
+   "8681ab72485c543d1e22de75cee685478090a848",
+   "support"
+  ],
   "html/browsers/offline/application-cache-api/.gitkeep": [
    "da39a3ee5e6b4b0d3255bfef95601890afd80709",
    "support"
@@ -228644,7 +228706,7 @@
    "support"
   ],
   "html/editing/activation/OWNERS": [
-   "e3e7f3973cf8f9b466d4f22d1ec3b9b9241fb906",
+   "2764978ea7b3de2789f3e5c8ad8e819a697cedd9",
    "support"
   ],
   "html/editing/activation/click-manual.html": [
@@ -232016,7 +232078,7 @@
    "support"
   ],
   "html/editing/editing-0/contenteditable/OWNERS": [
-   "e3e7f3973cf8f9b466d4f22d1ec3b9b9241fb906",
+   "2764978ea7b3de2789f3e5c8ad8e819a697cedd9",
    "support"
   ],
   "html/editing/editing-0/contenteditable/contentEditable-invalidvalue.html": [
@@ -232036,7 +232098,7 @@
    "support"
   ],
   "html/editing/editing-0/making-entire-documents-editable-the-designmode-idl-attribute/OWNERS": [
-   "e3e7f3973cf8f9b466d4f22d1ec3b9b9241fb906",
+   "2764978ea7b3de2789f3e5c8ad8e819a697cedd9",
    "support"
   ],
   "html/editing/editing-0/making-entire-documents-editable-the-designmode-idl-attribute/original-id.json": [
@@ -232052,7 +232114,7 @@
    "support"
   ],
   "html/editing/editing-0/spelling-and-grammar-checking/OWNERS": [
-   "e3e7f3973cf8f9b466d4f22d1ec3b9b9241fb906",
+   "2764978ea7b3de2789f3e5c8ad8e819a697cedd9",
    "support"
   ],
   "html/editing/editing-0/spelling-and-grammar-checking/references/spelling-markers-001-ref.html": [
@@ -232112,7 +232174,7 @@
    "support"
   ],
   "html/editing/focus/document-level-focus-apis/OWNERS": [
-   "e3e7f3973cf8f9b466d4f22d1ec3b9b9241fb906",
+   "2764978ea7b3de2789f3e5c8ad8e819a697cedd9",
    "support"
   ],
   "html/editing/focus/document-level-focus-apis/document-level-apis.html": [
@@ -232132,7 +232194,7 @@
    "support"
   ],
   "html/editing/focus/focus-management/OWNERS": [
-   "e3e7f3973cf8f9b466d4f22d1ec3b9b9241fb906",
+   "2764978ea7b3de2789f3e5c8ad8e819a697cedd9",
    "support"
   ],
   "html/editing/focus/focus-management/focus-event-targets-simple.html": [
@@ -232152,7 +232214,7 @@
    "support"
   ],
   "html/editing/focus/sequential-focus-navigation-and-the-tabindex-attribute/OWNERS": [
-   "e3e7f3973cf8f9b466d4f22d1ec3b9b9241fb906",
+   "2764978ea7b3de2789f3e5c8ad8e819a697cedd9",
    "support"
   ],
   "html/editing/focus/sequential-focus-navigation-and-the-tabindex-attribute/focus-tabindex-default-value.html": [
@@ -262048,20 +262110,20 @@
    "support"
   ],
   "uievents/keyboard/key-101en-us-manual.html": [
-   "8c4bedcea88fd39cfe291e40b11c3b0cb510267a",
+   "0b54b880246409466d57dcbe941b89d74587e8e4",
    "manual"
   ],
   "uievents/keyboard/key-102fr-fr-manual.html": [
-   "74ec4ae6f0cfa8ddc2f296d058a70d64977eb28a",
+   "65b31e543203b36d6a1026ba9da3e60de507cf67",
    "manual"
   ],
-  "uievents/keyboard/key-manual.css": [
+  "uievents/keyboard/key.css": [
    "54d5f8a452d8bbbd040e66af7594debacb6d0377",
-   "manual"
+   "support"
   ],
-  "uievents/keyboard/key-manual.js": [
+  "uievents/keyboard/key.js": [
    "d189977c13fcff73f9604a56a919676930176db4",
-   "manual"
+   "support"
   ],
   "uievents/legacy-domevents-tests/Status.html": [
    "b4cfc46993f2349beca9aa583b1eaf24a1bdfe63",
diff --git a/third_party/WebKit/LayoutTests/external/wpt/uievents/keyboard/key-101en-us-manual.html b/third_party/WebKit/LayoutTests/external/wpt/uievents/keyboard/key-101en-us-manual.html
index 8b9cb46..3228c65 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/uievents/keyboard/key-101en-us-manual.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/uievents/keyboard/key-101en-us-manual.html
@@ -5,8 +5,8 @@
 
 <title>Keyboard Event Manual Test</title>
 <meta http-equiv="content-type" content="text/html;charset=utf-8" />
-<script type="text/javascript" src="key-manual.js" ></script>
-<link rel="stylesheet" type="text/css" href="key-manual.css" />
+<script type="text/javascript" src="key.js" ></script>
+<link rel="stylesheet" type="text/css" href="key.css" />
 
 </head>
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/uievents/keyboard/key-102fr-fr-manual.html b/third_party/WebKit/LayoutTests/external/wpt/uievents/keyboard/key-102fr-fr-manual.html
index 6521681..c5b51c47 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/uievents/keyboard/key-102fr-fr-manual.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/uievents/keyboard/key-102fr-fr-manual.html
@@ -5,8 +5,8 @@
 
 <title>Keyboard Event Manual Test</title>
 <meta http-equiv="content-type" content="text/html;charset=utf-8" />
-<script type="text/javascript" src="key-manual.js" ></script>
-<link rel="stylesheet" type="text/css" href="key-manual.css" />
+<script type="text/javascript" src="key.js" ></script>
+<link rel="stylesheet" type="text/css" href="key.css" />
 
 </head>
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/uievents/keyboard/key-manual.css b/third_party/WebKit/LayoutTests/external/wpt/uievents/keyboard/key.css
similarity index 100%
rename from third_party/WebKit/LayoutTests/external/wpt/uievents/keyboard/key-manual.css
rename to third_party/WebKit/LayoutTests/external/wpt/uievents/keyboard/key.css
diff --git a/third_party/WebKit/LayoutTests/external/wpt/uievents/keyboard/key-manual.js b/third_party/WebKit/LayoutTests/external/wpt/uievents/keyboard/key.js
similarity index 100%
rename from third_party/WebKit/LayoutTests/external/wpt/uievents/keyboard/key-manual.js
rename to third_party/WebKit/LayoutTests/external/wpt/uievents/keyboard/key.js
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/access-obsolete-frame-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/access-obsolete-frame-expected.txt
index 856e153..87741ca 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/access-obsolete-frame-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/access-obsolete-frame-expected.txt
@@ -1,3 +1,4 @@
+
 Paused on 'debugger;'
 resume
 restartFrame
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/access-obsolete-frame.html b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/access-obsolete-frame.html
deleted file mode 100644
index 7d1ba5d..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/access-obsolete-frame.html
+++ /dev/null
@@ -1,76 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-
-function testFunction()
-{
-    debugger;
-}
-
-function test()
-{
-    InspectorTest.sendCommand("Debugger.enable", {});
-
-    InspectorTest.eventHandler["Debugger.paused"] = handleDebuggerPausedOne;
-
-    InspectorTest.sendCommand("Runtime.evaluate", { "expression": "setTimeout(testFunction, 0)" });
-
-    var obsoleteTopFrameId;
-
-    function handleDebuggerPausedOne(messageObject)
-    {
-        InspectorTest.log("Paused on 'debugger;'");
-
-        var topFrame = messageObject.params.callFrames[0];
-        obsoleteTopFrameId = topFrame.callFrameId;
-
-        InspectorTest.eventHandler["Debugger.paused"] = undefined;
-
-        InspectorTest.sendCommand("Debugger.resume", { }, callbackResume);
-    }
-
-    function callbackResume(response)
-    {
-        InspectorTest.log("resume");
-        InspectorTest.log("restartFrame");
-        InspectorTest.sendCommand("Debugger.restartFrame", { callFrameId: obsoleteTopFrameId }, callbackRestartFrame);
-    }
-
-    function callbackRestartFrame(response)
-    {
-        logErrorResponse(response);
-        InspectorTest.log("evaluateOnFrame");
-        InspectorTest.sendCommand("Debugger.evaluateOnCallFrame", { callFrameId: obsoleteTopFrameId, expression: "0"} , callbackEvaluate);
-    }
-
-    function callbackEvaluate(response)
-    {
-        logErrorResponse(response);
-        InspectorTest.log("setVariableValue");
-        InspectorTest.sendCommand("Debugger.setVariableValue", { callFrameId: obsoleteTopFrameId, scopeNumber: 0, variableName: "a", newValue: { value: 0 } }, callbackSetVariableValue);
-    }
-
-    function callbackSetVariableValue(response)
-    {
-        logErrorResponse(response);
-        InspectorTest.completeTest();
-    }
-
-    function logErrorResponse(response)
-    {
-        if (response.error) {
-            if (response.error.message.indexOf("Can only perform operation while paused.") != -1) {
-                InspectorTest.log("PASS, error message as expected");
-                return;
-            }
-        }
-        InspectorTest.log("FAIL, unexpected error message");
-        InspectorTest.log(JSON.stringify(response));
-    }
-}
-</script>
-</head>
-<body onLoad="runTest();">
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/access-obsolete-frame.js b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/access-obsolete-frame.js
new file mode 100644
index 0000000..b9ec2d0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/access-obsolete-frame.js
@@ -0,0 +1,35 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startBlank('');
+
+  function logErrorResponse(response) {
+    if (response.error) {
+      if (response.error.message.indexOf('Can only perform operation while paused.') != -1) {
+        testRunner.log('PASS, error message as expected');
+        return;
+      }
+    }
+    testRunner.log('FAIL, unexpected error message');
+    testRunner.log(JSON.stringify(response));
+  }
+
+  dp.Debugger.enable();
+  dp.Runtime.evaluate({expression: 'setTimeout(() => { debugger; }, 0)' });
+
+  var messageObject = await dp.Debugger.oncePaused();
+  testRunner.log(`Paused on 'debugger;'`);
+  var topFrame = messageObject.params.callFrames[0];
+  var obsoleteTopFrameId = topFrame.callFrameId;
+
+  await dp.Debugger.resume();
+  testRunner.log('resume');
+  testRunner.log('restartFrame');
+
+  logErrorResponse(await dp.Debugger.restartFrame({callFrameId: obsoleteTopFrameId}));
+  testRunner.log('evaluateOnFrame');
+
+  logErrorResponse(await dp.Debugger.evaluateOnCallFrame({callFrameId: obsoleteTopFrameId, expression: '0'}));
+  testRunner.log('setVariableValue');
+
+  logErrorResponse(await dp.Debugger.setVariableValue({callFrameId: obsoleteTopFrameId, scopeNumber: 0, variableName: 'a', newValue: { value: 0 }}));
+  testRunner.completeTest();
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/call-frame-functionLocation-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/call-frame-functionLocation-expected.txt
index d309d42..ccc8515 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/call-frame-functionLocation-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/call-frame-functionLocation-expected.txt
@@ -1,3 +1,4 @@
+
 Paused on 'debugger;'
 Top frame location: {"scriptId":"42","lineNumber":8,"columnNumber":4}
 Top frame functionLocation: {"scriptId":"42","lineNumber":5,"columnNumber":21}
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/call-frame-functionLocation.html b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/call-frame-functionLocation.html
deleted file mode 100644
index 84fab01dd..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/call-frame-functionLocation.html
+++ /dev/null
@@ -1,33 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-
-function testFunction()
-{
-    var a = 2;
-    debugger;
-}
-
-function test()
-{
-    InspectorTest.sendCommand("Debugger.enable", {});
-    InspectorTest.eventHandler["Debugger.paused"] = handleDebuggerPaused;
-    InspectorTest.sendCommand("Runtime.evaluate", { "expression": "setTimeout(testFunction, 0)" });
-
-    function handleDebuggerPaused(messageObject)
-    {
-        InspectorTest.log("Paused on 'debugger;'");
-        var topFrame = messageObject.params.callFrames[0];
-        topFrame.location.scriptId = "42";
-        topFrame.functionLocation.scriptId = "42";
-        InspectorTest.log("Top frame location: " + JSON.stringify(topFrame.location));
-        InspectorTest.log("Top frame functionLocation: " + JSON.stringify(topFrame.functionLocation));
-        InspectorTest.completeTest();
-    }
-}
-</script>
-</head>
-<body onLoad="runTest();">
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/call-frame-functionLocation.js b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/call-frame-functionLocation.js
new file mode 100644
index 0000000..f7f3db31
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/call-frame-functionLocation.js
@@ -0,0 +1,26 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startBlank('');
+
+  dp.Debugger.enable();
+  dp.Runtime.evaluate({expression: `
+
+
+
+
+function testFunction()
+{
+    var a = 2;
+    debugger;
+}
+setTimeout(testFunction, 0);
+  `});
+
+  var messageObject = await dp.Debugger.oncePaused();
+  testRunner.log(`Paused on 'debugger;'`);
+  var topFrame = messageObject.params.callFrames[0];
+  topFrame.location.scriptId = '42';
+  topFrame.functionLocation.scriptId = '42';
+  testRunner.log('Top frame location: ' + JSON.stringify(topFrame.location));
+  testRunner.log('Top frame functionLocation: ' + JSON.stringify(topFrame.functionLocation));
+  testRunner.completeTest();
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/continueToLocation-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/continueToLocation-expected.txt
index d0c6ce7..404cd9ac 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/continueToLocation-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/continueToLocation-expected.txt
@@ -1,3 +1,4 @@
+
 Paused on debugger statement
 Paused after continueToLocation
 Stopped on line 8, expected 8, requested 8, (0-based numbers).
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/continueToLocation.html b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/continueToLocation.html
deleted file mode 100644
index 5ae2992..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/continueToLocation.html
+++ /dev/null
@@ -1,103 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script type="text/javascript" src="resources/statements.js"></script>
-<script>
-
-function test()
-{
-    var scenario = [
-        // requested line number, expected control parameter 'step', expected line number
-        [ 8, 1, 8 ],
-        [ 8, 1, 8 ],
-        [ 12, 6, 17 ],
-        [ 13, 6, 17 ],
-        [ 17, 6, 17 ],
-        [ 17, 6, 17 ],
-    ];
-
-    InspectorTest.sendCommand("Debugger.enable", {});
-
-    InspectorTest.sendCommand("Runtime.evaluate", { "expression": "statementsExample" }, callbackEvalFunctionObject);
-
-    function callbackEvalFunctionObject(response)
-    {
-        var functionObjectId = response.result.result.objectId;
-        InspectorTest.sendCommand("Runtime.getProperties", { objectId: functionObjectId }, callbackFunctionDetails);
-    }
-
-    function callbackFunctionDetails(response)
-    {
-        var result = response.result;
-        var scriptId;
-        for (var prop of result.internalProperties) {
-            if (prop.name === "[[FunctionLocation]]")
-                scriptId = prop.value.value.scriptId;
-        }
-
-        nextScenarioStep(0);
-
-        function nextScenarioStep(pos)
-        {
-            if (pos < scenario.length)
-                gotoSinglePassChain(scriptId, scenario[pos][0], scenario[pos][1], scenario[pos][2], nextScenarioStep.bind(this, pos + 1));
-            else
-                InspectorTest.completeTest();
-        }
-    }
-
-    function gotoSinglePassChain(scriptId, lineNumber, expectedResult, expectedLineNumber, next)
-    {
-        InspectorTest.eventHandler["Debugger.paused"] = handleDebuggerPausedOne;
-
-        InspectorTest.sendCommand("Runtime.evaluate", { "expression": "setTimeout(statementsExample, 0)" });
-
-        function handleDebuggerPausedOne(messageObject)
-        {
-            InspectorTest.log("Paused on debugger statement");
-
-            InspectorTest.eventHandler["Debugger.paused"] = handleDebuggerPausedTwo;
-
-            InspectorTest.sendCommand("Debugger.continueToLocation", { location: { scriptId: scriptId, lineNumber: lineNumber, columnNumber: 0} }, logContinueToLocation);
-
-            function logContinueToLocation(response)
-            {
-                if (response.error) {
-                    InspectorTest.log("Failed to execute continueToLocation " + JSON.stringify(response.error));
-                    InspectorTest.completeTest();
-                }
-            }
-        }
-        function handleDebuggerPausedTwo(messageObject)
-        {
-            InspectorTest.log("Paused after continueToLocation");
-            var actualLineNumber = messageObject.params.callFrames[0].location.lineNumber;
-
-            InspectorTest.log("Stopped on line " + actualLineNumber + ", expected " + expectedLineNumber + ", requested " + lineNumber + ", (0-based numbers).");
-
-            InspectorTest.eventHandler["Debugger.paused"] = handleDebuggerPausedUnexpected;
-
-            InspectorTest.sendCommand("Runtime.evaluate", { "expression": "statementsExample.step" }, callbackStepEvaluate);
-        }
-
-        function callbackStepEvaluate(response)
-        {
-            var resultValue = response.result.result.value;
-            InspectorTest.log("Control parameter 'step' calculation result: " + resultValue + ", expected: " + expectedResult);
-            InspectorTest.log(resultValue == expectedResult ? "SUCCESS" : "FAIL");
-            InspectorTest.sendCommand("Debugger.resume", { });
-            next();
-        }
-
-        function handleDebuggerPausedUnexpected(messageObject)
-        {
-            InspectorTest.log("Unexpected debugger pause");
-            InspectorTest.completeTest();
-        }
-    }
-}
-</script>
-</head>
-<body onLoad="runTest();">
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/continueToLocation.js b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/continueToLocation.js
new file mode 100644
index 0000000..466535a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/continueToLocation.js
@@ -0,0 +1,79 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startBlank('');
+
+  function statementsExample()
+  {
+      var self = arguments.callee;
+
+      debugger;
+
+      self.step = 1;
+
+      self.step = 2;
+
+      void [
+          self.step = 3,
+          self.step = 4,
+          self.step = 5,
+          self.step = 6
+      ];
+
+      self.step = 7;
+  }
+
+  var scenarios = [
+      // requested line number, expected control parameter 'step', expected line number
+      [ 8, 1, 8 ],
+      [ 8, 1, 8 ],
+      [ 12, 6, 17 ],
+      [ 13, 6, 17 ],
+      [ 17, 6, 17 ],
+      [ 17, 6, 17 ],
+  ];
+
+  dp.Debugger.enable();
+  var functionResponse = await dp.Runtime.evaluate({expression: statementsExample.toString() + '; statementsExample'});
+  var functionObjectId = functionResponse.result.result.objectId;
+
+  var detailsResponse = await dp.Runtime.getProperties({objectId: functionObjectId});
+  var scriptId;
+  for (var prop of detailsResponse.result.internalProperties) {
+    if (prop.name === '[[FunctionLocation]]')
+      scriptId = prop.value.value.scriptId;
+  }
+
+  for (var scenario of scenarios) {
+    var lineNumber = scenario[0];
+    var expectedResult = scenario[1];
+    var expectedLineNumber = scenario[2];
+    dp.Runtime.evaluate({expression: 'setTimeout(statementsExample, 0)' });
+    await dp.Debugger.oncePaused();
+    testRunner.log('Paused on debugger statement');
+
+    var continueToLocationResponse = await dp.Debugger.continueToLocation({location: {scriptId, lineNumber, columnNumber: 0}});
+    if (continueToLocationResponse.error) {
+      testRunner.log('Failed to execute continueToLocation ' + JSON.stringify(continueToLocationResponse.error));
+      testRunner.completeTest();
+      return;
+    }
+
+    var messageObject = await dp.Debugger.oncePaused();
+    testRunner.log('Paused after continueToLocation');
+    var actualLineNumber = messageObject.params.callFrames[0].location.lineNumber;
+    testRunner.log('Stopped on line ' + actualLineNumber + ', expected ' + expectedLineNumber + ', requested ' + lineNumber + ', (0-based numbers).');
+
+    dp.Debugger.onPaused(handleDebuggerPausedUnexpected);
+    var resultValue = (await dp.Runtime.evaluate({expression: 'statementsExample.step' })).result.result.value;
+    testRunner.log(`Control parameter 'step' calculation result: ${resultValue}, expected: ${expectedResult}`);
+    testRunner.log(resultValue == expectedResult ? 'SUCCESS' : 'FAIL');
+    dp.Debugger.resume();
+    dp.Debugger.offPaused(handleDebuggerPausedUnexpected);
+
+    function handleDebuggerPausedUnexpected() {
+      testRunner.log('Unexpected debugger pause');
+      testRunner.completeTest();
+    }
+  }
+
+  testRunner.completeTest();
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-doesnt-step-into-injected-script.html b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-doesnt-step-into-injected-script.html
deleted file mode 100644
index 5bacfae..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-doesnt-step-into-injected-script.html
+++ /dev/null
@@ -1,39 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-
-function foo()
-{
-    return 239;
-}
-
-function test()
-{
-    InspectorTest.sendCommandOrDie("Debugger.enable", {});
-    InspectorTest.eventHandler["Debugger.paused"] = debuggerPaused;
-    InspectorTest.sendCommandOrDie("Runtime.evaluate", { "expression": "(function boo() { setTimeout(foo, 0); debugger; })()" });
-
-    var actions = [ "stepInto", "stepInto", "stepInto" ];
-    function debuggerPaused(result)
-    {
-        InspectorTest.log("Stack trace:");
-        for (var callFrame of result.params.callFrames)
-            InspectorTest.log(callFrame.functionName + ':' + callFrame.location.lineNumber + ":" + callFrame.location.columnNumber);
-        InspectorTest.log("");
-
-        var action = actions.shift();
-        if (!action) {
-            InspectorTest.sendCommandOrDie("Debugger.resume", {}, () => InspectorTest.completeTest());
-            return;
-        }
-        InspectorTest.log("Perform " + action);
-        InspectorTest.sendCommandOrDie("Debugger." + action, {});
-    }
-}
-</script>
-</head>
-<body onLoad="runTest();">
-Check that stepInto at then end of the script go to next user script instead InjectedScriptSource.js.
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-doesnt-step-into-injected-script.js b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-doesnt-step-into-injected-script.js
new file mode 100644
index 0000000..64fd2d3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-doesnt-step-into-injected-script.js
@@ -0,0 +1,38 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startBlank('Check that stepInto at then end of the script go to next user script instead InjectedScriptSource.js.');
+
+  function logStack(event) {
+    testRunner.log('Stack trace:');
+    for (var callFrame of event.params.callFrames)
+      testRunner.log(callFrame.functionName + ':' + callFrame.location.lineNumber + ':' + callFrame.location.columnNumber);
+    testRunner.log('');
+  }
+
+  dp.Debugger.enable();
+  await session.evaluate(`
+
+
+
+
+
+  function foo() {
+    return 239;
+  }`);
+  dp.Runtime.evaluate({expression: '(function boo() { setTimeout(foo, 0); debugger; })()' });
+
+  logStack(await dp.Debugger.oncePaused());
+  testRunner.log('Perform stepInto');
+  dp.Debugger.stepInto();
+
+  logStack(await dp.Debugger.oncePaused());
+  testRunner.log('Perform stepInto');
+  dp.Debugger.stepInto();
+
+  logStack(await dp.Debugger.oncePaused());
+  testRunner.log('Perform stepInto');
+  dp.Debugger.stepInto();
+
+  logStack(await dp.Debugger.oncePaused());
+  await dp.Debugger.resume();
+  testRunner.completeTest();
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-evaluate-in-worker-while-pause-in-page-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-evaluate-in-worker-while-pause-in-page-expected.txt
index 81e00ce..aa6355b6 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-evaluate-in-worker-while-pause-in-page-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-evaluate-in-worker-while-pause-in-page-expected.txt
@@ -1,3 +1,4 @@
+
 Started worker
 Paused on 'debugger;'
 Worker created
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-evaluate-in-worker-while-pause-in-page.html b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-evaluate-in-worker-while-pause-in-page.html
deleted file mode 100644
index ddae332..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-evaluate-in-worker-while-pause-in-page.html
+++ /dev/null
@@ -1,80 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-
-var worker;
-
-function testFunction()
-{
-    debugger;
-}
-
-function startWorkerAndRunTest()
-{
-    worker = new Worker("resources/dedicated-worker.js");
-    worker.onmessage = function(event) { };
-    worker.postMessage(1);
-    log("Started worker");
-    runTest();
-}
-
-function test()
-{
-    var workerId;
-    var workerRequestId = 1;
-
-    function sendCommandToWorker(method, params)
-    {
-        InspectorTest.sendCommand("Target.sendMessageToTarget",
-            {
-                "targetId": workerId,
-                "message": JSON.stringify({ "method": method,
-                                            "params": params,
-                                            "id": workerRequestId })
-            });
-        return workerRequestId++;
-    }
-
-    InspectorTest.sendCommand("Debugger.enable", {});
-    InspectorTest.eventHandler["Debugger.paused"] = handleDebuggerPausedInTestFunction;
-    InspectorTest.sendCommand("Runtime.evaluate", { "expression": "testFunction()" });
-
-    function handleDebuggerPausedInTestFunction(messageObject)
-    {
-        InspectorTest.log("Paused on 'debugger;'");
-        InspectorTest.sendCommand("Target.setAutoAttach", {autoAttach: true, waitForDebuggerOnStart: false}, didEnableWorkerDebugging);
-    }
-
-    function didEnableWorkerDebugging(messageObject)
-    {
-        if ("error" in messageObject) {
-            InspectorTest.log("FAIL: Couldn't enable worker debugger: " + messageObject.error.message);
-            InspectorTest.completeTest();
-        }
-    }
-
-    var savedWorkerRequestId = -1;
-    InspectorTest.eventHandler["Target.attachedToTarget"] = function(messageObject)
-    {
-        workerId = messageObject["params"]["targetInfo"]["targetId"];
-        InspectorTest.log("Worker created");
-        InspectorTest.log("didConnectToWorker");
-        savedWorkerRequestId = sendCommandToWorker("Runtime.evaluate", { "expression": "1+1"});
-    }
-
-    InspectorTest.eventHandler["Target.receivedMessageFromTarget"] = function(messageObject)
-    {
-        var message = JSON.parse(messageObject["params"]["message"]);
-        if (message["id"] === savedWorkerRequestId) {
-            var value = message["result"]["result"]["value"];
-            InspectorTest.log("Successfully evaluated, result: " + value);
-            InspectorTest.completeTest();
-        }
-    }
-}
-</script>
-</head>
-<body onLoad="startWorkerAndRunTest();">
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-evaluate-in-worker-while-pause-in-page.js b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-evaluate-in-worker-while-pause-in-page.js
new file mode 100644
index 0000000..76ede5e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-evaluate-in-worker-while-pause-in-page.js
@@ -0,0 +1,39 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startBlank('');
+
+  await session.evaluate(`
+    window.worker = new Worker('${testRunner.url('resources/dedicated-worker.js')}');
+    window.worker.onmessage = function(event) { };
+    window.worker.postMessage(1);
+  `);
+  testRunner.log('Started worker');
+
+  var workerRequestId = 1;
+  function sendCommandToWorker(method, params) {
+    var message = {method, params, id: workerRequestId};
+    dp.Target.sendMessageToTarget({targetId: workerId, message: JSON.stringify(message)});
+    return workerRequestId++;
+  }
+
+  dp.Debugger.enable();
+  dp.Runtime.evaluate({expression: 'debugger;' });
+  await dp.Debugger.oncePaused();
+  testRunner.log(`Paused on 'debugger;'`);
+
+  dp.Target.setAutoAttach({autoAttach: true, waitForDebuggerOnStart: false});
+
+  var messageObject = await dp.Target.onceAttachedToTarget();
+  var workerId = messageObject.params.targetInfo.targetId;
+  testRunner.log('Worker created');
+  testRunner.log('didConnectToWorker');
+
+  var savedWorkerRequestId = sendCommandToWorker('Runtime.evaluate', {expression: '1+1'});
+  dp.Target.onReceivedMessageFromTarget(messageObject => {
+    var message = JSON.parse(messageObject.params.message);
+    if (message.id === savedWorkerRequestId) {
+      var value = message.result.result.value;
+      testRunner.log('Successfully evaluated, result: ' + value);
+      testRunner.completeTest();
+    }
+  });
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-pause-dedicated-worker-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-pause-dedicated-worker-expected.txt
index e412394..bd6d733 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-pause-dedicated-worker-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-pause-dedicated-worker-expected.txt
@@ -1,3 +1,4 @@
+
 Started worker
 Worker created
 didConnectToWorker
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-pause-dedicated-worker-loop-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-pause-dedicated-worker-loop-expected.txt
index ab9d5310a..0b8048b 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-pause-dedicated-worker-loop-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-pause-dedicated-worker-loop-expected.txt
@@ -1,3 +1,4 @@
+
 Started worker
 Worker created
 didConnectToWorker
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-pause-dedicated-worker-loop.html b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-pause-dedicated-worker-loop.html
deleted file mode 100644
index 5d3c19d..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-pause-dedicated-worker-loop.html
+++ /dev/null
@@ -1,86 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-
-var worker;
-function startWorkerAndRunTest()
-{
-    worker = new Worker("resources/dedicated-worker-loop.js");
-    worker.onmessage = function(event)
-    {
-        if (event.data === "WorkerMessageReceived")
-            evaluateInFrontend("InspectorTest.workerMessageReceived()");
-    };
-    log("Started worker");
-    runTest();
-}
-
-function test()
-{
-    var workerId;
-    var workerRequestId = 1;
-    function sendCommandToWorker(method, params)
-    {
-        InspectorTest.sendCommand("Target.sendMessageToTarget",
-            {
-                "targetId": workerId,
-                "message": JSON.stringify({ "method": method,
-                                            "params": params,
-                                            "id": workerRequestId })
-            });
-        return workerRequestId++;
-    }
-
-    function didEnableWorkerDebugging(messageObject)
-    {
-        if ("error" in messageObject) {
-            InspectorTest.log("FAIL: Couldn't enable worker debugger: " + messageObject.error.message);
-            InspectorTest.completeTest();
-        }
-    }
-    InspectorTest.sendCommand("Target.setAutoAttach", {autoAttach: true, waitForDebuggerOnStart: false}, didEnableWorkerDebugging);
-
-    var evaluateRequestId = -1;
-    var debuggerEnableRequestId = -1;
-    InspectorTest.eventHandler["Target.attachedToTarget"] = function(messageObject)
-    {
-        workerId = messageObject["params"]["targetInfo"]["targetId"];
-        InspectorTest.log("Worker created");
-        InspectorTest.log("didConnectToWorker");
-        // Enable debugger so that V8 can interrupt and handle inspector commands while there is a script running in a tight loop.
-        debuggerEnableRequestId = sendCommandToWorker("Debugger.enable", {});
-    }
-
-    InspectorTest.workerMessageReceived = function()
-    {
-        evaluateRequestId = sendCommandToWorker("Runtime.evaluate", { "expression": "message_id > 1"});
-    }
-
-    InspectorTest.eventHandler["Target.receivedMessageFromTarget"] = function(messageObject)
-    {
-        var message = JSON.parse(messageObject["params"]["message"]);
-        if (message["id"] === debuggerEnableRequestId) {
-            InspectorTest.log("Did enable debugger");
-            // Start tight loop in the worker.
-            InspectorTest.sendCommand("Runtime.evaluate", { "expression": "worker.postMessage(1)" }, didPostMessageToWorker);
-            function didPostMessageToWorker()
-            {
-                InspectorTest.log("Did post message to worker");
-            }
-        }
-        if (message["id"] === evaluateRequestId) {
-            var value = message["result"]["result"]["value"];
-            if (value === true)
-                InspectorTest.log("SUCCESS: evaluated, result: " + value);
-            else
-                InspectorTest.log("FAIL: evaluated, result: " + value);
-            InspectorTest.completeTest();
-        }
-    }
-}
-</script>
-</head>
-<body onLoad="startWorkerAndRunTest();">
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-pause-dedicated-worker-loop.js b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-pause-dedicated-worker-loop.js
new file mode 100644
index 0000000..ec3325f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-pause-dedicated-worker-loop.js
@@ -0,0 +1,54 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startBlank('');
+
+  await session.evaluate(`
+    window.worker = new Worker('${testRunner.url('resources/dedicated-worker-loop.js')}');
+    var resolve;
+    window.workerMessageReceivedPromise = new Promise(f => resolve = f);
+    window.worker.onmessage = function(event) {
+      if (event.data === 'WorkerMessageReceived')
+        resolve();
+    };
+  `);
+  testRunner.log('Started worker');
+
+  var workerId;
+  var workerRequestId = 1;
+  function sendCommandToWorker(method, params) {
+    var message = {method, params, id: workerRequestId};
+    dp.Target.sendMessageToTarget({targetId: workerId, message: JSON.stringify(message)});
+    return workerRequestId++;
+  }
+
+  dp.Target.setAutoAttach({autoAttach: true, waitForDebuggerOnStart: false});
+
+  var debuggerEnableRequestId = -1;
+  var evaluateRequestId = -1;
+
+  dp.Target.onReceivedMessageFromTarget(async messageObject => {
+    var message = JSON.parse(messageObject.params.message);
+    if (message.id === debuggerEnableRequestId) {
+      testRunner.log('Did enable debugger');
+      // Start tight loop in the worker.
+      await dp.Runtime.evaluate({expression: 'worker.postMessage(1)' });
+      testRunner.log('Did post message to worker');
+    }
+    if (message.id === evaluateRequestId) {
+      var value = message.result.result.value;
+      if (value === true)
+        testRunner.log('SUCCESS: evaluated, result: ' + value);
+      else
+        testRunner.log('FAIL: evaluated, result: ' + value);
+      testRunner.completeTest();
+    }
+  });
+
+  workerId = (await dp.Target.onceAttachedToTarget()).params.targetInfo.targetId;
+  testRunner.log('Worker created');
+  testRunner.log('didConnectToWorker');
+  // Enable debugger so that V8 can interrupt and handle inspector commands while there is a script running in a tight loop.
+  debuggerEnableRequestId = sendCommandToWorker('Debugger.enable', {});
+
+  await session.evaluateAsync('workerMessageReceivedPromise');
+  evaluateRequestId = sendCommandToWorker('Runtime.evaluate', { 'expression': 'message_id > 1'});
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-pause-dedicated-worker.html b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-pause-dedicated-worker.html
deleted file mode 100644
index 1d33b8d..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-pause-dedicated-worker.html
+++ /dev/null
@@ -1,69 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-
-
-var worker;
-
-function startWorkerAndRunTest()
-{
-    worker = new Worker("resources/dedicated-worker.js");
-    worker.onmessage = function(event) { };
-    worker.postMessage(1);
-    log("Started worker");
-    runTest();
-}
-
-
-function test()
-{
-
-    var workerId;
-    var workerRequestId = 1;
-    function sendCommandToWorker(method, params)
-    {
-        InspectorTest.sendCommand("Target.sendMessageToTarget",
-            {
-                "targetId": workerId,
-                "message": JSON.stringify({ "method": method,
-                                            "params": params,
-                                            "id": workerRequestId++ })
-            });
-    }
-
-    function didEnableWorkerDebugging(messageObject)
-    {
-        if ("error" in messageObject) {
-            InspectorTest.log("FAIL: Couldn't enable worker debugger: " + messageObject.error.message);
-            InspectorTest.completeTest();
-        }
-    }
-    InspectorTest.sendCommand("Target.setAutoAttach", {autoAttach: true, waitForDebuggerOnStart: false}, didEnableWorkerDebugging);
-
-
-    InspectorTest.eventHandler["Target.attachedToTarget"] = function(messageObject)
-    {
-        workerId = messageObject["params"]["targetInfo"]["targetId"];
-        InspectorTest.log("Worker created");
-        InspectorTest.log("didConnectToWorker");
-        sendCommandToWorker("Debugger.enable", {});
-        sendCommandToWorker("Debugger.pause", {});
-    }
-
-    InspectorTest.eventHandler["Target.receivedMessageFromTarget"] = function(messageObject)
-    {
-        var message = JSON.parse(messageObject["params"]["message"]);
-        if (message["method"] === "Debugger.paused") {
-            InspectorTest.log("SUCCESS: Worker paused");
-            sendCommandToWorker("Debugger.disable", {});
-            InspectorTest.completeTest();
-        }
-    }
-
-}
-</script>
-</head>
-<body onLoad="startWorkerAndRunTest();">
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-pause-dedicated-worker.js b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-pause-dedicated-worker.js
new file mode 100644
index 0000000..90542a2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-pause-dedicated-worker.js
@@ -0,0 +1,35 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startBlank('');
+
+  await session.evaluate(`
+    window.worker = new Worker('${testRunner.url('resources/dedicated-worker.js')}');
+    window.worker.onmessage = function(event) { };
+    window.worker.postMessage(1);
+  `);
+  testRunner.log('Started worker');
+
+  var workerRequestId = 1;
+  function sendCommandToWorker(method, params) {
+    var message = {method, params, id: workerRequestId};
+    dp.Target.sendMessageToTarget({targetId: workerId, message: JSON.stringify(message)});
+    return workerRequestId++;
+  }
+
+  dp.Target.setAutoAttach({autoAttach: true, waitForDebuggerOnStart: false});
+
+  var messageObject = await dp.Target.onceAttachedToTarget();
+  var workerId = messageObject.params.targetInfo.targetId;
+  testRunner.log('Worker created');
+  testRunner.log('didConnectToWorker');
+  sendCommandToWorker('Debugger.enable', {});
+  sendCommandToWorker('Debugger.pause', {});
+
+  dp.Target.onReceivedMessageFromTarget(messageObject => {
+    var message = JSON.parse(messageObject.params.message);
+    if (message.method === 'Debugger.paused') {
+      testRunner.log('SUCCESS: Worker paused');
+      sendCommandToWorker('Debugger.disable', {});
+      testRunner.completeTest();
+    }
+  });
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-pause-in-tight-loop-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-pause-in-tight-loop-expected.txt
index 938e588..a769f3e 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-pause-in-tight-loop-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-pause-in-tight-loop-expected.txt
@@ -1,4 +1,6 @@
+
 didEval
 didFireTimer
 SUCCESS: Paused
+SUCCESS: Resumed
 
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-pause-in-tight-loop.html b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-pause-in-tight-loop.html
deleted file mode 100644
index fac7b51..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-pause-in-tight-loop.html
+++ /dev/null
@@ -1,62 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-
-function testFunction()
-{
-    setTimeout(hotFunction, 0);
-}
-
-var terminated = false;
-function hotFunction() {
-  evaluateInFrontend("InspectorTest.didFireTimer()");
-
-  var message_id = 1;
-  var ts = Date.now();
-  while (!terminated) {
-      // Without this try/catch v8 will optimize the function and break will not work.
-      try {
-          if (Date.now() - ts > 1000) {
-              ts = Date.now();
-              console.error("Message #" + message_id++);
-          }
-      } catch (e) {
-      }
-  }
-}
-
-function test()
-{
-    InspectorTest.sendCommand("Inspector.enable", {});
-    InspectorTest.sendCommand("Debugger.enable", {}, didEnableDebugger);
-    function didEnableDebugger()
-    {
-        // Start tight loop in page.
-        InspectorTest.sendCommand("Runtime.evaluate", { "expression": "testFunction()" }, didEval);
-        function didEval()
-        {
-            InspectorTest.log("didEval");
-        }
-    }
-
-    InspectorTest.didFireTimer = function()
-    {
-        InspectorTest.log("didFireTimer");
-        InspectorTest.sendCommand("Debugger.pause", { });
-    }
-
-    InspectorTest.eventHandler["Debugger.paused"] = function(messageObject)
-    {
-        var message = messageObject["params"]["message"];
-        InspectorTest.log("SUCCESS: Paused");
-        InspectorTest.sendCommand("Runtime.evaluate", { "expression": "terminated = true;" });
-        InspectorTest.sendCommand("Debugger.resume", { });
-        InspectorTest.completeTest();
-    }
-}
-</script>
-</head>
-<body onLoad="runTest();">
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-pause-in-tight-loop.js b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-pause-in-tight-loop.js
new file mode 100644
index 0000000..f56dc97e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-pause-in-tight-loop.js
@@ -0,0 +1,41 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startBlank('');
+
+  await dp.Runtime.enable();
+  await dp.Debugger.enable();
+
+  // Start tight loop in page.
+  session.evaluate(`
+    var terminated = false;
+    function hotFunction() {
+      console.log('hi');
+      var message_id = 1;
+      var ts = Date.now();
+      while (!terminated) {
+        // Without this try/catch v8 will optimize the function and break will not work.
+        try {
+          if (Date.now() - ts > 1000) {
+            ts = Date.now();
+            console.error('Message #' + message_id++);
+          }
+        } catch (e) {
+        }
+      }
+    }
+    setTimeout(hotFunction, 0);
+  `);
+  testRunner.log('didEval');
+
+  await dp.Runtime.onceConsoleAPICalled();
+  testRunner.log('didFireTimer');
+
+  dp.Debugger.pause();
+  await dp.Debugger.oncePaused();
+  testRunner.log('SUCCESS: Paused');
+
+  dp.Runtime.evaluate({expression: 'terminated = true;' });
+  dp.Debugger.resume();
+  await dp.Debugger.onceResumed();
+  testRunner.log('SUCCESS: Resumed');
+  testRunner.completeTest();
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-scope-skip-variables-with-empty-name-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-scope-skip-variables-with-empty-name-expected.txt
index 4b7e1a3..f1a2108 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-scope-skip-variables-with-empty-name-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-scope-skip-variables-with-empty-name-expected.txt
@@ -1,3 +1,4 @@
+
 {
     result : [
         [0] : {
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-scope-skip-variables-with-empty-name.html b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-scope-skip-variables-with-empty-name.html
deleted file mode 100644
index a0357b3..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-scope-skip-variables-with-empty-name.html
+++ /dev/null
@@ -1,50 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-
-function testFunction()
-{
-    for (var a of [1]) {
-        ++a;
-        debugger;
-    }
-}
-
-function test()
-{
-    InspectorTest.sendCommandOrDie("Debugger.enable", {});
-    InspectorTest.eventHandler["Debugger.paused"] = dumpScopeOnPause;
-    InspectorTest.sendCommandOrDie("Runtime.evaluate", { "expression": "testFunction()" });
-
-    var waitScopeObjects = 0;
-    function dumpScopeOnPause(message)
-    {
-        var scopeChain = message.params.callFrames[0].scopeChain;
-        var localScopeObjectIds = [];
-        for (var scope of scopeChain) {
-            if (scope.type === "local")
-                localScopeObjectIds.push(scope.object.objectId);
-        }
-        waitScopeObjects = localScopeObjectIds.length;
-        if (!waitScopeObjects) {
-            InspectorTest.completeTest();
-        } else {
-            for (var objectId of localScopeObjectIds)
-                InspectorTest.sendCommandOrDie("Runtime.getProperties", { "objectId" : objectId }, dumpProperties);
-        }
-    }
-
-    function dumpProperties(message)
-    {
-        InspectorTest.logObject(message);
-        --waitScopeObjects;
-        if (!waitScopeObjects)
-            InspectorTest.sendCommandOrDie("Debugger.resume", {}, () => InspectorTest.completeTest());
-    }
-}
-</script>
-</head>
-<body onLoad="runTest();">
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-scope-skip-variables-with-empty-name.js b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-scope-skip-variables-with-empty-name.js
new file mode 100644
index 0000000..a9e8871
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-scope-skip-variables-with-empty-name.js
@@ -0,0 +1,28 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startBlank('');
+
+  dp.Debugger.enable();
+  dp.Runtime.evaluate({expression: `
+    function testFunction() {
+      for (var a of [1]) {
+        ++a;
+        debugger;
+      }
+    }
+    testFunction();
+  `});
+
+  var message = await dp.Debugger.oncePaused();
+  var scopeChain = message.params.callFrames[0].scopeChain;
+  var localScopeObjectIds = [];
+  for (var scope of scopeChain) {
+    if (scope.type === 'local')
+      localScopeObjectIds.push(scope.object.objectId);
+  }
+
+  for (var objectId of localScopeObjectIds)
+    testRunner.logObject((await dp.Runtime.getProperties({objectId})).result);
+
+  await dp.Debugger.resume();
+  testRunner.completeTest();
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-setBlackboxPatterns-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-setBlackboxPatterns-expected.txt
index 9b5bec7..287712b 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-setBlackboxPatterns-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-setBlackboxPatterns-expected.txt
@@ -1,25 +1,26 @@
+
 Pattern parser error: Uncaught SyntaxError: Invalid regular expression: /(foo([)/: Unterminated character class
 Paused in
 (...):1
 Paused in
 (...):1
 Paused in
-qwe:21
-baz:29
+qwe:4
+baz:4
 (...):1
 Paused in
-bar:7
-foo:13
-qwe:21
-baz:29
+bar:4
+foo:4
+qwe:4
+baz:4
 (...):1
 Paused in
-qwe:22
-baz:29
+qwe:5
+baz:4
 (...):1
 Paused in
-qwe:23
-baz:29
+qwe:6
+baz:4
 (...):1
 Paused in
 (...):1
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-setBlackboxPatterns.html b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-setBlackboxPatterns.html
deleted file mode 100644
index 07ec913271..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-setBlackboxPatterns.html
+++ /dev/null
@@ -1,67 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-function bar()
-{
-    return 42;
-}
-</script>
-<script>
-function foo()
-{
-    var a = bar();
-    return a + 1;
-}
-//# sourceURL=foo.js
-</script>
-<script>
-function qwe()
-{
-    var a = foo();
-    return a + 1;
-}
-//# sourceURL=qwe.js
-</script>
-<script>
-function baz()
-{
-    var a = qwe();
-    return a + 1;
-}
-//# sourceURL=baz.js
-</script>
-<script>
-function test()
-{
-    InspectorTest.sendCommand("Debugger.enable", {});
-    InspectorTest.sendCommand("Debugger.setBlackboxPatterns", { patterns: [ "foo([" ] }, dumpError);
-
-    function dumpError(message)
-    {
-        InspectorTest.log(message.error.message);
-        InspectorTest.eventHandler["Debugger.paused"] = dumpStackAndRunNextCommand;
-        InspectorTest.sendCommandOrDie("Debugger.setBlackboxPatterns", { patterns: [ "baz\.js", "foo\.js" ] });
-        InspectorTest.sendCommandOrDie("Runtime.evaluate", { "expression": "debugger;baz()" });
-    }
-
-    var commands = [ "stepInto", "stepInto", "stepInto", "stepOut", "stepInto", "stepInto" ];
-    function dumpStackAndRunNextCommand(message)
-    {
-        InspectorTest.log("Paused in");
-        var callFrames = message.params.callFrames;
-        for (var callFrame of callFrames)
-            InspectorTest.log((callFrame.functionName || "(...)") + ":" + (callFrame.location.lineNumber + 1));
-        var command = commands.shift();
-        if (!command) {
-            InspectorTest.completeTest();
-            return;
-        }
-        InspectorTest.sendCommandOrDie("Debugger." + command, {});
-    }
-
-}
-</script>
-</head>
-<body onLoad="runTest();"></body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-setBlackboxPatterns.js b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-setBlackboxPatterns.js
new file mode 100644
index 0000000..65cc402
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-setBlackboxPatterns.js
@@ -0,0 +1,74 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startBlank('');
+
+  await session.evaluate(`
+    function bar()
+    {
+        return 42;
+    }
+  `);
+
+  await session.evaluate(`
+    function foo()
+    {
+        var a = bar();
+        return a + 1;
+    }
+    //# sourceURL=foo.js
+  `);
+
+  await session.evaluate(`
+    function qwe()
+    {
+        var a = foo();
+        return a + 1;
+    }
+    //# sourceURL=qwe.js
+  `);
+
+  await session.evaluate(`
+    function baz()
+    {
+        var a = qwe();
+        return a + 1;
+    }
+    //# sourceURL=baz.js
+  `);
+
+  function logStack(message) {
+    testRunner.log('Paused in');
+    var callFrames = message.params.callFrames;
+    for (var callFrame of callFrames)
+      testRunner.log((callFrame.functionName || '(...)') + ':' + (callFrame.location.lineNumber + 1));
+  }
+
+  dp.Debugger.enable();
+  var message = await dp.Debugger.setBlackboxPatterns({patterns: ['foo([']});
+  testRunner.log(message.error.message);
+
+  dp.Debugger.setBlackboxPatterns({patterns: ['baz\.js', 'foo\.js']});
+  dp.Runtime.evaluate({expression: 'debugger;baz()' });
+  logStack(await dp.Debugger.oncePaused());
+
+  dp.Debugger.stepInto();
+  logStack(await dp.Debugger.oncePaused());
+
+  dp.Debugger.stepInto();
+  logStack(await dp.Debugger.oncePaused());
+
+  dp.Debugger.stepInto();
+  logStack(await dp.Debugger.oncePaused());
+
+  dp.Debugger.stepOut();
+  logStack(await dp.Debugger.oncePaused());
+
+  dp.Debugger.stepInto();
+  logStack(await dp.Debugger.oncePaused());
+
+  dp.Debugger.stepInto();
+  logStack(await dp.Debugger.oncePaused());
+
+  await dp.Debugger.resume();
+  testRunner.completeTest();
+})
+
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-setEventListenerBreakpoint-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-setEventListenerBreakpoint-expected.txt
index 3b141a7..77c15196 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-setEventListenerBreakpoint-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-setEventListenerBreakpoint-expected.txt
@@ -1,3 +1,4 @@
+
 PASS: Debugger was enabled
 Error on attempt to set event listener breakpoint when DOM is disabled: undefined
 PASS: DOM was enabled
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-setEventListenerBreakpoint.html b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-setEventListenerBreakpoint.html
deleted file mode 100644
index cafebf4f..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-setEventListenerBreakpoint.html
+++ /dev/null
@@ -1,69 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-
-function test()
-{
-    InspectorTest.sendCommand("Debugger.enable", {}, setEventListenerBreakpointWhenDOMDisabled);
-
-    function finishIfError(message)
-    {
-        if (message.result)
-            return;
-        InspectorTest.log("FAIL: " + JSON.stringify(message));
-        InspectorTest.completeTest();
-    }
-
-    function setEventListenerBreakpointWhenDOMDisabled(message)
-    {
-        finishIfError(message);
-        InspectorTest.log("PASS: Debugger was enabled");
-        InspectorTest.sendCommand("DOMDebugger.setEventListenerBreakpoint", {'eventName':'click'}, enableDOMAgent);
-    }
-
-    function enableDOMAgent(message)
-    {
-        InspectorTest.log("Error on attempt to set event listener breakpoint when DOM is disabled: " + JSON.stringify(message.error));
-        InspectorTest.sendCommand("DOM.enable", {}, setEventListenerBreakpoint);
-    }
-
-    function setEventListenerBreakpoint(message)
-    {
-        finishIfError(message);
-        InspectorTest.log("PASS: DOM was enabled");
-        InspectorTest.sendCommand("DOMDebugger.setEventListenerBreakpoint", {'eventName':'click'}, disableDOMAgent);
-    }
-
-
-    function disableDOMAgent(message)
-    {
-        finishIfError(message);
-        InspectorTest.log("PASS: Listener was set.");
-        InspectorTest.sendCommand("DOM.disable", {}, disableDOMAgent2);
-    }
-
-    function disableDOMAgent2(message)
-    {
-        finishIfError(message);
-        InspectorTest.log("PASS: DOM agent was disabled successfully.");
-        InspectorTest.sendCommand("DOM.disable", {}, finish);
-    }
-
-    function finish(message)
-    {
-        if (!message.error) {
-            InspectorTest.log("FAIL: we expected an error but it wasn't happen.");
-            InspectorTest.completeTest();
-            return;
-        }
-
-        InspectorTest.log("PASS: The second attempt to disable DOM agent failed as expected.");
-        InspectorTest.completeTest();
-    }
-}
-</script>
-</head>
-<body onLoad="runTest();">
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-setEventListenerBreakpoint.js b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-setEventListenerBreakpoint.js
new file mode 100644
index 0000000..15ff82f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-setEventListenerBreakpoint.js
@@ -0,0 +1,32 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startBlank('');
+
+  function finishIfError(message) {
+    if (message.result)
+      return;
+    testRunner.log('FAIL: ' + JSON.stringify(message));
+    testRunner.completeTest();
+  }
+
+  finishIfError(await dp.Debugger.enable());
+  testRunner.log('PASS: Debugger was enabled');
+
+  var errorResponse = dp.DOMDebugger.setEventListenerBreakpoint({eventName: 'click'});
+  testRunner.log('Error on attempt to set event listener breakpoint when DOM is disabled: ' + JSON.stringify(errorResponse.error));
+
+  finishIfError(await dp.DOM.enable());
+  testRunner.log('PASS: DOM was enabled');
+
+  finishIfError(await dp.DOMDebugger.setEventListenerBreakpoint({eventName:'click'}));
+  testRunner.log('PASS: Listener was set.');
+
+  finishIfError(await dp.DOM.disable());
+  testRunner.log('PASS: DOM agent was disabled successfully.');
+
+  var message = await dp.DOM.disable();
+  if (!message.error)
+    testRunner.log(`FAIL: we expected an error but it wasn't happen.`);
+  else
+    testRunner.log('PASS: The second attempt to disable DOM agent failed as expected.');
+  testRunner.completeTest();
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-setTimeout-sourceUrl-dedicated-worker-loop-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-setTimeout-sourceUrl-dedicated-worker-loop-expected.txt
index 1eb72fb6..c4ac494d 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-setTimeout-sourceUrl-dedicated-worker-loop-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-setTimeout-sourceUrl-dedicated-worker-loop-expected.txt
@@ -1,3 +1,4 @@
+
 Started worker
 Worker created
 didConnectToWorker
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-setTimeout-sourceUrl-dedicated-worker-loop.html b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-setTimeout-sourceUrl-dedicated-worker-loop.html
deleted file mode 100644
index fd20f46..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-setTimeout-sourceUrl-dedicated-worker-loop.html
+++ /dev/null
@@ -1,78 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-
-var worker;
-function startWorkerAndRunTest()
-{
-    worker = new Worker("resources/dedicated-worker-string-setTimeout.js");
-    log("Started worker");
-    runTest();
-}
-
-function test()
-{
-    var workerId;
-    var workerRequestId = 1;
-    function sendCommandToWorker(method, params)
-    {
-        InspectorTest.sendCommand("Target.sendMessageToTarget",
-            {
-                "targetId": workerId,
-                "message": JSON.stringify({ "method": method,
-                                            "params": params,
-                                            "id": workerRequestId })
-            });
-        return workerRequestId++;
-    }
-
-    function didEnableWorkerDebugging(messageObject)
-    {
-        if ("error" in messageObject) {
-            InspectorTest.log("FAIL: Couldn't enable worker debugger: " + messageObject.error.message);
-            InspectorTest.completeTest();
-        }
-    }
-    InspectorTest.sendCommand("Target.setAutoAttach", {autoAttach: true, waitForDebuggerOnStart: false}, didEnableWorkerDebugging);
-
-    var debuggerEnableRequestId = -1;
-    InspectorTest.eventHandler["Target.attachedToTarget"] = function(messageObject)
-    {
-        workerId = messageObject["params"]["targetInfo"]["targetId"];
-        InspectorTest.log("Worker created");
-        InspectorTest.log("didConnectToWorker");
-        debuggerEnableRequestId = sendCommandToWorker("Debugger.enable", {});
-    }
-
-    var postMessageToWorker = false;
-
-    InspectorTest.eventHandler["Target.receivedMessageFromTarget"] = function(messageObject)
-    {
-        var message = JSON.parse(messageObject["params"]["message"]);
-        if (message["id"] === debuggerEnableRequestId) {
-            InspectorTest.log("Did enable debugger");
-            // Start setTimeout.
-            InspectorTest.sendCommand("Runtime.evaluate", { "expression": "worker.postMessage(1)" }, didPostMessageToWorker);
-            function didPostMessageToWorker()
-            {
-                postMessageToWorker = true;
-                InspectorTest.log("Did post message to worker");
-            }
-        }
-
-        if (postMessageToWorker && message["method"] === "Debugger.scriptParsed") {
-            var sourceUrl = message["params"]["url"];
-            if (!sourceUrl)
-                InspectorTest.log("SUCCESS: script created from string parameter of setTimeout has no url");
-            else
-                InspectorTest.log("FAIL: script created from string parameter of setTimeout has url " + sourceUrl);
-            InspectorTest.completeTest();
-        }
-    }
-}
-</script>
-</head>
-<body onLoad="startWorkerAndRunTest();">
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-setTimeout-sourceUrl-dedicated-worker-loop.js b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-setTimeout-sourceUrl-dedicated-worker-loop.js
new file mode 100644
index 0000000..7133bb2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-setTimeout-sourceUrl-dedicated-worker-loop.js
@@ -0,0 +1,47 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startBlank('');
+
+  await session.evaluate(`
+    window.worker = new Worker('${testRunner.url('resources/dedicated-worker-string-setTimeout.js')}');
+    window.worker.onmessage = function(event) { };
+    window.worker.postMessage(1);
+  `);
+  testRunner.log('Started worker');
+
+  var workerId;
+  var workerRequestId = 1;
+  function sendCommandToWorker(method, params) {
+    var message = {method, params, id: workerRequestId};
+    dp.Target.sendMessageToTarget({targetId: workerId, message: JSON.stringify(message)});
+    return workerRequestId++;
+  }
+
+  dp.Target.setAutoAttach({autoAttach: true, waitForDebuggerOnStart: false});
+
+  var messageObject = await dp.Target.onceAttachedToTarget();
+  workerId = messageObject.params.targetInfo.targetId;
+  testRunner.log('Worker created');
+  testRunner.log('didConnectToWorker');
+
+  var debuggerEnableRequestId = sendCommandToWorker('Debugger.enable', {});
+  var postMessageToWorker = false;
+  dp.Target.onReceivedMessageFromTarget(async messageObject => {
+    var message = JSON.parse(messageObject.params.message);
+    if (message.id === debuggerEnableRequestId) {
+      testRunner.log('Did enable debugger');
+      // Start setTimeout.
+      await dp.Runtime.evaluate({expression: 'worker.postMessage(1)'});
+      postMessageToWorker = true;
+      testRunner.log('Did post message to worker');
+    }
+
+    if (postMessageToWorker && message.method === 'Debugger.scriptParsed') {
+      var sourceUrl = message.params.url;
+      if (!sourceUrl)
+        testRunner.log('SUCCESS: script created from string parameter of setTimeout has no url');
+      else
+        testRunner.log('FAIL: script created from string parameter of setTimeout has url ' + sourceUrl);
+      testRunner.completeTest();
+    }
+  });
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-step-into-dedicated-worker-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-step-into-dedicated-worker-expected.txt
index ecdb5d20..776e622 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-step-into-dedicated-worker-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-step-into-dedicated-worker-expected.txt
@@ -1,5 +1,4 @@
 Tests that dedicated worker won't crash on attempt to step into.Bug 232392.
-
 Started worker
 Worker created
 SUCCESS: Worker paused
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-step-into-dedicated-worker.html b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-step-into-dedicated-worker.html
deleted file mode 100644
index 6822980..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-step-into-dedicated-worker.html
+++ /dev/null
@@ -1,73 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-var worker;
-
-function startWorker()
-{
-    worker = new Worker("resources/dedicated-worker-step-into.js");
-    worker.onmessage = function(event) { };
-    worker.postMessage(1);
-    log("Started worker");
-}
-
-
-function test()
-{
-
-    var workerId;
-    var workerRequestId = 1;
-    function sendCommandToWorker(method, params)
-    {
-        InspectorTest.sendCommand("Target.sendMessageToTarget",
-            {
-                "targetId": workerId,
-                "message": JSON.stringify({ "method": method,
-                                            "params": params,
-                                            "id": workerRequestId++ })
-            });
-    }
-
-    function didEnableWorkerDebugging(messageObject)
-    {
-        if ("error" in messageObject) {
-            InspectorTest.log("FAIL: Couldn't enable worker debugger: " + messageObject.error.message);
-            InspectorTest.completeTest();
-        }
-    }
-    InspectorTest.sendCommand("Target.setAutoAttach", {autoAttach: true, waitForDebuggerOnStart: true}, didEnableWorkerDebugging);
-    InspectorTest.sendCommand("Runtime.evaluate", { "expression": "startWorker()" });
-
-    InspectorTest.eventHandler["Target.attachedToTarget"] = function(messageObject)
-    {
-        workerId = messageObject["params"]["targetInfo"]["targetId"];
-        InspectorTest.log("Worker created");
-        sendCommandToWorker("Debugger.enable", {});
-        sendCommandToWorker("Runtime.runIfWaitingForDebugger", {});
-    }
-
-    var pauseCount = 0;
-    InspectorTest.eventHandler["Target.receivedMessageFromTarget"] = function(messageObject)
-    {
-        var message = JSON.parse(messageObject["params"]["message"]);
-        if (message["method"] === "Debugger.paused") {
-            InspectorTest.log("SUCCESS: Worker paused");
-            if (++pauseCount === 1) {
-                InspectorTest.log("Stepping into...");
-                sendCommandToWorker("Debugger.stepInto", {});
-            } else {
-                sendCommandToWorker("Debugger.disable", {});
-                InspectorTest.completeTest();
-            }
-        }
-    }
-
-}
-</script>
-</head>
-<body onLoad="runTest();">
-<p>Tests that dedicated worker won't crash on attempt to step into.<a href="https://code.google.com/p/chromium/issues/detail?id=232392">Bug 232392.</a>
-</p>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-step-into-dedicated-worker.js b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-step-into-dedicated-worker.js
new file mode 100644
index 0000000..bfb43c7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-step-into-dedicated-worker.js
@@ -0,0 +1,41 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startBlank(`Tests that dedicated worker won't crash on attempt to step into.Bug 232392.`);
+
+  var workerId;
+  var workerRequestId = 1;
+  function sendCommandToWorker(method, params) {
+    var message = {method, params, id: workerRequestId};
+    dp.Target.sendMessageToTarget({targetId: workerId, message: JSON.stringify(message)});
+    return workerRequestId++;
+  }
+
+  dp.Target.setAutoAttach({autoAttach: true, waitForDebuggerOnStart: true});
+  await session.evaluate(`
+    window.worker = new Worker('${testRunner.url('resources/dedicated-worker-step-into.js')}');
+    window.worker.onmessage = function(event) { };
+    window.worker.postMessage(1);
+  `);
+  testRunner.log('Started worker');
+
+  var messageObject = await dp.Target.onceAttachedToTarget();
+  workerId = messageObject.params.targetInfo.targetId;
+  testRunner.log('Worker created');
+
+  sendCommandToWorker('Debugger.enable', {});
+  sendCommandToWorker('Runtime.runIfWaitingForDebugger', {});
+
+  var pauseCount = 0;
+  dp.Target.onReceivedMessageFromTarget(async messageObject => {
+    var message = JSON.parse(messageObject.params.message);
+    if (message.method === 'Debugger.paused') {
+      testRunner.log('SUCCESS: Worker paused');
+      if (++pauseCount === 1) {
+        testRunner.log('Stepping into...');
+        sendCommandToWorker('Debugger.stepInto', {});
+      } else {
+        sendCommandToWorker('Debugger.disable', {});
+        testRunner.completeTest();
+      }
+    }
+  });
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-terminate-dedicated-worker-while-paused.html b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-terminate-dedicated-worker-while-paused.html
deleted file mode 100644
index 3919a0f..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-terminate-dedicated-worker-while-paused.html
+++ /dev/null
@@ -1,75 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-
-
-var worker;
-
-function startWorkerAndRunTest()
-{
-    worker = new Worker("resources/dedicated-worker.js");
-    worker.onmessage = function(event) { };
-    worker.postMessage(1);
-    log("Started worker");
-    runTest();
-}
-
-
-function test()
-{
-
-    var workerId;
-    var workerRequestId = 1;
-    function sendCommandToWorker(method, params)
-    {
-        InspectorTest.sendCommand("Target.sendMessageToTarget",
-            {
-                "targetId": workerId,
-                "message": JSON.stringify({ "method": method,
-                                            "params": params,
-                                            "id": workerRequestId++ })
-            });
-    }
-
-    function didEnableWorkerDebugging(messageObject)
-    {
-        if ("error" in messageObject) {
-            InspectorTest.log("FAIL: Couldn't enable worker debugger: " + messageObject.error.message);
-            InspectorTest.completeTest();
-        }
-    }
-    InspectorTest.sendCommand("Target.setAutoAttach", {autoAttach: true, waitForDebuggerOnStart: false}, didEnableWorkerDebugging);
-
-
-    InspectorTest.eventHandler["Target.attachedToTarget"] = function(messageObject)
-    {
-        workerId = messageObject["params"]["targetInfo"]["targetId"];
-        InspectorTest.log("Worker created");
-        InspectorTest.log("didConnectToWorker");
-        sendCommandToWorker("Debugger.enable", {});
-        sendCommandToWorker("Debugger.pause", {});
-    }
-
-    InspectorTest.eventHandler["Target.receivedMessageFromTarget"] = function(messageObject)
-    {
-        var message = JSON.parse(messageObject["params"]["message"]);
-        if (message["method"] === "Debugger.paused") {
-            InspectorTest.log("Worker paused");
-            InspectorTest.sendCommand("Runtime.evaluate", { "expression": "worker.terminate()" }, didTerminateWorker);
-        }
-    }
-
-    function didTerminateWorker(messageObject)
-    {
-        InspectorTest.log("SUCCESS: Did terminate paused worker");
-        InspectorTest.completeTest();
-    }
-
-}
-</script>
-</head>
-<body onLoad="startWorkerAndRunTest();">Test that inspected page won't crash if inspected worker is terminated while it is paused. Test passes if it doesn't crash.
-<a href="https://bugs.webkit.org/show_bug.cgi?id=101065">Bug 101065.</a>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-terminate-dedicated-worker-while-paused.js b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-terminate-dedicated-worker-while-paused.js
new file mode 100644
index 0000000..9c7bf596
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/debugger-terminate-dedicated-worker-while-paused.js
@@ -0,0 +1,37 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startBlank(
+      `Test that inspected page won't crash if inspected worker is terminated while it is paused. Test passes if it doesn't crash. Bug 101065.`);
+
+  await session.evaluate(`
+    window.worker = new Worker('${testRunner.url('resources/dedicated-worker.js')}');
+    window.worker.onmessage = function(event) { };
+    window.worker.postMessage(1);
+  `);
+  testRunner.log('Started worker');
+
+  var workerRequestId = 1;
+  function sendCommandToWorker(method, params) {
+    var message = {method, params, id: workerRequestId};
+    dp.Target.sendMessageToTarget({targetId: workerId, message: JSON.stringify(message)});
+    return workerRequestId++;
+  }
+
+  dp.Target.setAutoAttach({autoAttach: true, waitForDebuggerOnStart: false});
+
+  var messageObject = await dp.Target.onceAttachedToTarget();
+  var workerId = messageObject.params.targetInfo.targetId;
+  testRunner.log('Worker created');
+  testRunner.log('didConnectToWorker');
+  sendCommandToWorker('Debugger.enable', {});
+  sendCommandToWorker('Debugger.pause', {});
+
+  dp.Target.onReceivedMessageFromTarget(async messageObject => {
+    var message = JSON.parse(messageObject.params.message);
+    if (message.method === 'Debugger.paused') {
+      testRunner.log('Worker paused');
+      await dp.Runtime.evaluate({expression: 'worker.terminate()' });
+      testRunner.log('SUCCESS: Did terminate paused worker');
+      testRunner.completeTest();
+    }
+  });
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/domdebugger-getEventListeners-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/domdebugger-getEventListeners-expected.txt
index c2d3373..231a947 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/domdebugger-getEventListeners-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/domdebugger-getEventListeners-expected.txt
@@ -1,3 +1,4 @@
+
 Event listeners of window
   type:click
   useCapture:true
@@ -15,14 +16,6 @@
   handler.className:Function
   handler.description:function hoverHandler(event) { console.log("hover - button - bubbling"); }
 
-  type:load
-  useCapture:false
-  lineNumber:164
-  columnNumber:18
-  handler.type:function
-  handler.className:Function
-  handler.description:function onload(event) {  runTest();}
-
   type:scroll
   useCapture:false
   lineNumber:1
@@ -59,8 +52,8 @@
 
   type:load
   useCapture:false
-  lineNumber:162
-  columnNumber:33
+  lineNumber:0
+  columnNumber:38
   handler.type:function
   handler.className:Function
   handler.description:function onload(event) {  return 42;}
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/domdebugger-getEventListeners.html b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/domdebugger-getEventListeners.html
deleted file mode 100644
index 856dfc7..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/domdebugger-getEventListeners.html
+++ /dev/null
@@ -1,167 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-
-function test()
-{
-    // A general-purpose engine for sending a sequence of protocol commands.
-    // The clients provide requests and response handlers, while the engine catches
-    // errors and makes sure that once there's nothing to do completeTest() is called.
-    // @param step is an object with command, params and callback fields.
-    function runRequestSeries(step)
-    {
-        processStep(step);
-
-        function processStep(s)
-        {
-            try {
-                processStepOrFail(s);
-            } catch (e) {
-                InspectorTest.log(e.stack);
-                InspectorTest.completeTest();
-            }
-        }
-
-        function processStepOrFail(s)
-        {
-            if (!s) {
-                InspectorTest.completeTest();
-                return;
-            }
-            if (!s.command) {
-                // A simple loopback step.
-                var next = s.callback();
-                processStep(next);
-                return;
-            }
-
-            var innerCallback = function(response)
-            {
-                if ("error" in response) {
-                    InspectorTest.log(response.error.message);
-                    InspectorTest.completeTest();
-                    return;
-                }
-                var next;
-                try {
-                    next = s.callback(response.result);
-                } catch (e) {
-                    InspectorTest.log(e.stack);
-                    InspectorTest.completeTest();
-                    return;
-                }
-                processStep(next);
-            }
-            InspectorTest.sendCommand(s.command, s.params, innerCallback);
-        }
-    }
-
-    runRequestSeries({ callback: callbackStartWindow });
-
-    function callbackStartWindow()
-    {
-        var expression = "(function(){\n\
-            window.addEventListener('scroll', function(){ consol.log(42) }, false);\n\
-            window.addEventListener('scroll', function(){ consol.log(42) }, false);\n\
-            function clickHandler(event) { console.log('click - button - bubbling (registered before attribute)'); }\n\
-            window.addEventListener(\"click\", clickHandler, true);\n\
-            window.addEventListener(\"hover\", function hoverHandler(event) { console.log(\"hover - button - bubbling\"); }, true);\n\
-            return window;\n\
-        })()";
-        return { command: "Runtime.evaluate", params: {expression: expression, objectGroup: "event-listeners-test"}, callback: callbackEvalWindow };
-    }
-    function callbackEvalWindow(result)
-    {
-        var id = result.result.objectId;
-        if (id === undefined)
-            throw new Error("objectId is expected");
-        return {
-            command: "DOMDebugger.getEventListeners", params: {objectId: id}, callback: callbackListenersWindow
-        };
-    }
-    function callbackListenersWindow(result)
-    {
-        logGetListenersResult("window", result);
-        return {callback: calbackStartDivWithListeners};
-    }
-
-    function calbackStartDivWithListeners()
-    {
-        var expression = "(function(){\n\
-            var div = document.getElementById(\"listeners1\");\n\
-            function clickHandler(event) { console.log('click - button - bubbling (registered before attribute)'); }\n\
-            div.addEventListener(\"click\", clickHandler, true);\n\
-            div.addEventListener(\"hover\", function hoverHandler(event) { console.log(\"hover - button - bubbling\"); }, true);\n\
-            return div;\n\
-        })()";
-        return { command: "Runtime.evaluate", params: {expression: expression, objectGroup: "event-listeners-test"}, callback: callbackEvalDivWithListeners };
-    }
-    function callbackEvalDivWithListeners(result)
-    {
-        var id = result.result.objectId;
-        if (id === undefined)
-            throw new Error("objectId is expected");
-        return {
-            command: "DOMDebugger.getEventListeners", params: {objectId: id}, callback: callbackListenersDivWithListeners
-        };
-    }
-    function callbackListenersDivWithListeners(result)
-    {
-        logGetListenersResult("div#listeners1", result);
-        return {callback: calbackStartDivWithoutListeners};
-    }
-
-    function calbackStartDivWithoutListeners()
-    {
-        var expression = "(function(){\n\
-            return document.getElementById(\"listeners2\");\n\
-        })()";
-        return { command: "Runtime.evaluate", params: {expression: expression, objectGroup: "event-listeners-test"}, callback: callbackEvalDivWithoutListeners };
-    }
-    function callbackEvalDivWithoutListeners(result)
-    {
-        var id = result.result.objectId;
-        if (id === undefined)
-            throw new Error("objectId is expected");
-        return {
-            command: "DOMDebugger.getEventListeners", params: {objectId: id}, callback: callbackListenersDivWithoutListeners
-        };
-    }
-    function callbackListenersDivWithoutListeners(result)
-    {
-        logGetListenersResult("div#listeners2", result);
-    }
-
-    function logGetListenersResult(title, protocolResult)
-    {
-        InspectorTest.log("Event listeners of " + title);
-        var listenersArray = protocolResult.listeners;
-        listenersArray.sort(TypedThingComparator);
-        for (var i = 0; i < listenersArray.length; i++) {
-            var l = listenersArray[i];
-            InspectorTest.log("  type:" + l.type);
-            InspectorTest.log("  useCapture:" + l.useCapture);
-            InspectorTest.log("  lineNumber:" + l.lineNumber);
-            InspectorTest.log("  columnNumber:" + l.columnNumber);
-            if (l.handler) {
-                InspectorTest.log("  handler.type:" + l.handler.type);
-                InspectorTest.log("  handler.className:" + l.handler.className);
-                InspectorTest.log("  handler.description:" + l.handler.description.replace(/(\r\n|\n|\r)/gm,""));
-            }
-            InspectorTest.log("");
-        }
-        InspectorTest.log("");
-        function TypedThingComparator(o1, o2)
-        {
-            return o1.type === o2.type ? 0 : (o1.type < o2.type ? -1 : 1);
-        }
-    }
-}
-</script>
-</head>
-<div id="listeners1" onload="return 42;"></div>
-<div id="listeners2"></div>
-<body onLoad="runTest();">
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/domdebugger-getEventListeners.js b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/domdebugger-getEventListeners.js
new file mode 100644
index 0000000..3a7b1dd2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/domdebugger-getEventListeners.js
@@ -0,0 +1,57 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startHTML(`
+    <div id='listeners1' onload='return 42;'></div>
+    <div id='listeners2'></div>
+  `, ``);
+
+  function logGetListenersResult(title, response) {
+    testRunner.log('Event listeners of ' + title);
+    var listenersArray = response.result.listeners;
+    listenersArray.sort((o1, o2) => o1.type === o2.type ? 0 : (o1.type < o2.type ? -1 : 1));
+    for (var l of listenersArray) {
+      testRunner.log('  type:' + l.type);
+      testRunner.log('  useCapture:' + l.useCapture);
+      testRunner.log('  lineNumber:' + l.lineNumber);
+      testRunner.log('  columnNumber:' + l.columnNumber);
+      if (l.handler) {
+        testRunner.log('  handler.type:' + l.handler.type);
+        testRunner.log('  handler.className:' + l.handler.className);
+        testRunner.log('  handler.description:' + l.handler.description.replace(/(\r\n|\n|\r)/gm,''));
+      }
+      testRunner.log('');
+    }
+    testRunner.log('');
+  }
+
+  var objectId = (await dp.Runtime.evaluate({expression:
+    `(function(){
+            window.addEventListener('scroll', function(){ consol.log(42) }, false);
+            window.addEventListener('scroll', function(){ consol.log(42) }, false);
+            function clickHandler(event) { console.log('click - button - bubbling (registered before attribute)'); }
+            window.addEventListener('click', clickHandler, true);
+            window.addEventListener('hover', function hoverHandler(event) { console.log("hover - button - bubbling"); }, true);
+            return window;
+    })()
+  `, objectGroup: 'event-listeners-test'})).result.result.objectId;
+  logGetListenersResult('window', await dp.DOMDebugger.getEventListeners({objectId}));
+
+  var objectId = (await dp.Runtime.evaluate({expression:
+    `(function(){
+            var div = document.getElementById('listeners1');
+            function clickHandler(event) { console.log('click - button - bubbling (registered before attribute)'); }
+            div.addEventListener('click', clickHandler, true);
+            div.addEventListener('hover', function hoverHandler(event) { console.log("hover - button - bubbling"); }, true);
+            return div;
+    })()
+  `, objectGroup: 'event-listeners-test'})).result.result.objectId;
+  logGetListenersResult('div#listeners1', await dp.DOMDebugger.getEventListeners({objectId}));
+
+  var objectId = (await dp.Runtime.evaluate({expression:
+    `(function(){
+      return document.getElementById('listeners2');
+    })()
+  `, objectGroup: 'event-listeners-test'})).result.result.objectId;
+  logGetListenersResult('div#listeners2', await dp.DOMDebugger.getEventListeners({objectId}));
+
+  testRunner.completeTest();
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/domdebugger-script-blocked-by-csp-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/domdebugger-script-blocked-by-csp-expected.txt
index 0c0a485..a6902f5 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/domdebugger-script-blocked-by-csp-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/domdebugger-script-blocked-by-csp-expected.txt
@@ -1,10 +1,4 @@
-CONSOLE ERROR: line 2: Refused to execute inline event handler because it violates the following Content Security Policy directive: "script-src 'self'". Either the 'unsafe-inline' keyword, a hash ('sha256-...'), or a nonce ('nonce-...') is required to enable inline execution.
 
-CONSOLE ERROR: line 4: Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self'". Either the 'unsafe-inline' keyword, a hash ('sha256-bhHHL3z2vDgxUt0W3dWQOrprscmda2Y5pLsLg4GF+pI='), or a nonce ('nonce-...') is required to enable inline execution.
-
-CONSOLE ERROR: line 5: Refused to execute JavaScript URL because it violates the following Content Security Policy directive: "script-src 'self'". Either the 'unsafe-inline' keyword, a hash ('sha256-...'), or a nonce ('nonce-...') is required to enable inline execution.
-
-Button
 
 -------
 blockedEventHandler
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/domdebugger-script-blocked-by-csp.html b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/domdebugger-script-blocked-by-csp.html
deleted file mode 100644
index 1fb01011..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/domdebugger-script-blocked-by-csp.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<html>
-<head>
-<meta http-equiv="Content-Security-Policy" content="script-src 'self';">
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script type="text/javascript" src="resources/script-blocked-by-csp.js"></script>
-</head>
-<body>
-<button id="testButton" onclick="alert(1);">Button</button>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/domdebugger-script-blocked-by-csp.js b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/domdebugger-script-blocked-by-csp.js
new file mode 100644
index 0000000..8f700db
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/domdebugger-script-blocked-by-csp.js
@@ -0,0 +1,55 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startHTML(`
+    <html>
+    <head>
+    <meta http-equiv='Content-Security-Policy' content="script-src 'self';">
+    </head>
+    <body>
+    <button id='testButton' onclick='alert(1);'>Button</button>
+    </body>
+    </html>
+  `, ``);
+
+  dp.Debugger.enable();
+  dp.DOM.enable();
+  dp.DOMDebugger.enable();
+  dp.DOMDebugger.setInstrumentationBreakpoint({eventName: 'scriptBlockedByCSP'});
+
+  var expressions = [
+    `
+    document.getElementById('testButton').click();
+    `,
+
+    `
+    var script = document.createElement('script');
+    script.innerText = 'alert(1)';
+    document.body.appendChild(script);
+    `,
+
+    `
+    var a = document.createElement('a');
+    a.setAttribute('href', 'javascript:alert(1);');
+    var dummy = 1;
+    document.body.appendChild(a); a.click();
+    `
+  ];
+  var descriptions = [
+    'blockedEventHandler',
+    'blockedScriptInjection',
+    'blockedScriptUrl'
+  ];
+
+  for (var i = 0; i < expressions.length; i++) {
+    var funcName = descriptions[i];
+    testRunner.log('\n-------\n' + funcName);
+    dp.Runtime.evaluate({expression: 'function ' + funcName + '() {' + expressions[i] + '}\n' + funcName + '()'});
+    var messageObject = await dp.Debugger.oncePaused();
+    var params = messageObject.params;
+    testRunner.log('Paused at: ' + params.callFrames[0].functionName + '@' + params.callFrames[0].location.lineNumber);
+    testRunner.log('Reason: ' + params.reason + '; Data:');
+    testRunner.logObject(params.data);
+    await dp.Debugger.resume();
+  }
+
+  testRunner.completeTest();
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/domdebugger-setInnerHTML-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/domdebugger-setInnerHTML-expected.txt
index a97b8e52..4dce98314 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/domdebugger-setInnerHTML-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/domdebugger-setInnerHTML-expected.txt
@@ -1,3 +1,3 @@
-innerHTML
+
 Paused on the innerHTML assignment: modifyHTML@:7
 
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/domdebugger-setInnerHTML.html b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/domdebugger-setInnerHTML.html
deleted file mode 100644
index bb8c99d63..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/domdebugger-setInnerHTML.html
+++ /dev/null
@@ -1,36 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-
-function modifyHTML()
-{
-    document.getElementById("divUnderTest").innerHTML = "innerHTML";
-}
-
-function test()
-{
-    InspectorTest.sendCommand("Debugger.enable", {});
-    InspectorTest.sendCommand("DOM.enable", {});
-    InspectorTest.sendCommand("DOMDebugger.enable", {});
-    InspectorTest.sendCommand("DOMDebugger.setInstrumentationBreakpoint", {"eventName":"Element.setInnerHTML"});
-    InspectorTest.sendCommand("Runtime.evaluate", { "expression": "modifyHTML()" });
-    InspectorTest.eventHandler["Debugger.paused"] = handleDebuggerPausedOne;
-
-    function handleDebuggerPausedOne(messageObject)
-    {
-        var callFrame = messageObject.params.callFrames[0];
-        InspectorTest.log("Paused on the innerHTML assignment: " + callFrame.functionName + "@:" + callFrame.location.lineNumber);
-        InspectorTest.sendCommand("Debugger.resume", { }, didResume);
-        function didResume()
-        {
-            InspectorTest.completeTest();
-        }
-    }
-}
-</script>
-</head>
-<body onLoad="runTest();">
-<div id="divUnderTest"></div>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/domdebugger-setInnerHTML.js b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/domdebugger-setInnerHTML.js
new file mode 100644
index 0000000..486242e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/domdebugger-setInnerHTML.js
@@ -0,0 +1,25 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startHTML(`
+    <div id='divUnderTest'></div>
+  `, ``);
+
+  dp.Debugger.enable();
+  dp.DOM.enable();
+  dp.DOMDebugger.enable();
+  dp.DOMDebugger.setInstrumentationBreakpoint({eventName: 'Element.setInnerHTML'});
+  dp.Runtime.evaluate({expression: `
+
+
+
+
+
+    (function modifyHTML() {
+      document.getElementById('divUnderTest').innerHTML = 'innerHTML';
+    })()
+  ` });
+  var messageObject = await dp.Debugger.oncePaused();
+  var callFrame = messageObject.params.callFrames[0];
+  testRunner.log('Paused on the innerHTML assignment: ' + callFrame.functionName + '@:' + callFrame.location.lineNumber);
+  await dp.Debugger.resume();
+  testRunner.completeTest();
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/postMessage-on-pause-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/postMessage-on-pause-expected.txt
index 568693f..96cf4515 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/postMessage-on-pause-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/postMessage-on-pause-expected.txt
@@ -1,3 +1,4 @@
+
 Paused on 'debugger;'
 PASS: message has not been dispatched yet.
 PASS: message has not been dispatched yet.
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/postMessage-on-pause.html b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/postMessage-on-pause.html
deleted file mode 100644
index 7cb5fbc..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/postMessage-on-pause.html
+++ /dev/null
@@ -1,72 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-
-var messageDispatched = false;
-window.addEventListener("message", function(event)
-{
-    messageDispatched = true;
-    debugger;
-}, true);
-
-function testFunction()
-{
-    window.postMessage("test", "*");
-    debugger;
-}
-
-function test()
-{
-    InspectorTest.sendCommand("Debugger.enable", {});
-    InspectorTest.eventHandler["Debugger.paused"] = handleDebuggerPausedInTestFunction;
-    InspectorTest.sendCommand("Runtime.evaluate", { "expression": "testFunction()" });
-
-    function handleDebuggerPausedInTestFunction(messageObject)
-    {
-        InspectorTest.log("Paused on 'debugger;'");
-        InspectorTest.sendCommand("Runtime.evaluate", { "expression": "messageDispatched" }, didEvaluateOnPause1);
-    }
-
-    function didEvaluateOnPause1(messageObject)
-    {
-        var r = messageObject.result.result;
-        if (r.type === "boolean" && r.value === false)
-            InspectorTest.log("PASS: message has not been dispatched yet.");
-        else
-            InspectorTest.log("FAIL: unexpected response " + JSON.stringify(messageObject, null, 2));
-        InspectorTest.sendCommand("Runtime.evaluate", { "expression": "messageDispatched" }, didEvaluateOnPause2);
-    }
-
-    function didEvaluateOnPause2(messageObject)
-    {
-        var r = messageObject.result.result;
-        if (r.type === "boolean" && r.value === false)
-            InspectorTest.log("PASS: message has not been dispatched yet.");
-        else
-            InspectorTest.log("FAIL: unexpected response " + JSON.stringify(messageObject, null, 2));
-        InspectorTest.sendCommand("Debugger.resume", { }, didResume);
-    }
-
-    function didResume(messageObject)
-    {
-        InspectorTest.log("Resumed, now waiting for pause in the event listener...");
-        InspectorTest.eventHandler["Debugger.paused"] = handleDebuggerPausedInEventListener;
-    }
-
-    function handleDebuggerPausedInEventListener(messageObject)
-    {
-        InspectorTest.log("PASS: pasued in the event listener.");
-        InspectorTest.sendCommand("Debugger.resume", { }, didResume2);
-    }
-
-    function didResume2(messageObject)
-    {
-        InspectorTest.completeTest();
-    }
-}
-</script>
-</head>
-<body onLoad="runTest();">
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/postMessage-on-pause.js b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/postMessage-on-pause.js
new file mode 100644
index 0000000..6a2b7b1
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/postMessage-on-pause.js
@@ -0,0 +1,42 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startBlank('');
+
+  dp.Debugger.enable();
+  dp.Runtime.evaluate({expression: `
+    var messageDispatched = false;
+    window.addEventListener('message', event => {
+      messageDispatched = true;
+      debugger;
+    }, true);
+
+    (function testFunction() {
+      window.postMessage('test', '*');
+      debugger;
+    })()
+  `});
+
+  await dp.Debugger.oncePaused();
+  testRunner.log(`Paused on 'debugger;'`);
+
+  var messageObject = await dp.Runtime.evaluate({expression: 'messageDispatched' });
+  var r = messageObject.result.result;
+  if (r.type === 'boolean' && r.value === false)
+    testRunner.log('PASS: message has not been dispatched yet.');
+  else
+    testRunner.log('FAIL: unexpected response ' + JSON.stringify(messageObject, null, 2));
+
+  messageObject = await dp.Runtime.evaluate({expression: 'messageDispatched' });
+  r = messageObject.result.result;
+  if (r.type === 'boolean' && r.value === false)
+    testRunner.log('PASS: message has not been dispatched yet.');
+  else
+    testRunner.log('FAIL: unexpected response ' + JSON.stringify(messageObject, null, 2));
+
+  await dp.Debugger.resume();
+  testRunner.log('Resumed, now waiting for pause in the event listener...');
+
+  await dp.Debugger.oncePaused();
+  testRunner.log('PASS: pasued in the event listener.');
+  await dp.Debugger.resume();
+  testRunner.completeTest();
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/resources/blackboxed.js b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/resources/blackboxed.js
deleted file mode 100644
index 51da71a..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/resources/blackboxed.js
+++ /dev/null
@@ -1,7 +0,0 @@
-function blackboxedBoo()
-{
-    var a = 42;
-    var b = foo();
-    return a + b;
-}
-//# sourceURL=blackboxed-script.js
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/resources/mixed.js b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/resources/mixed.js
deleted file mode 100644
index df01a0e9..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/resources/mixed.js
+++ /dev/null
@@ -1,21 +0,0 @@
-function notBlackboxedFoo()
-{
-    var a = 42;
-    var b = blackboxedBoo();
-    return a + b;
-}
-
-function blackboxedFoo()
-{
-    var a = 42;
-    var b = notBlackboxedFoo();
-    return a + b;
-}
-
-function notBlackboxedBoo()
-{
-    var a = 42;
-    var b = blackboxedFoo();
-    return a + b;
-}
-//# sourceURL=mixed-source.js
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/resources/script-blocked-by-csp.js b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/resources/script-blocked-by-csp.js
deleted file mode 100644
index 31a5ce5..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/resources/script-blocked-by-csp.js
+++ /dev/null
@@ -1,50 +0,0 @@
-function test()
-{
-    InspectorTest.sendCommand("Debugger.enable", {});
-    InspectorTest.sendCommand("DOM.enable", {});
-    InspectorTest.sendCommand("DOMDebugger.enable", {});
-    InspectorTest.sendCommand("DOMDebugger.setInstrumentationBreakpoint", {"eventName":"scriptBlockedByCSP"});
-    InspectorTest.eventHandler["Debugger.paused"] = handleDebuggerPaused;
-
-    var expressions = [
-        "\n document.getElementById('testButton').click();",
-
-        "\n var script = document.createElement('script');" +
-        "\n script.innerText = 'alert(1)';" +
-        "\n document.body.appendChild(script);",
-
-        "\n var a = document.createElement('a');" +
-        "\n a.setAttribute('href', 'javascript:alert(1);');" +
-        "\n var dummy = 1; " +
-        "\n document.body.appendChild(a); a.click();"
-    ];
-    var descriptions = [
-        "blockedEventHandler",
-        "blockedScriptInjection",
-        "blockedScriptUrl"
-    ];
-
-    function nextExpression()
-    {
-        if (!expressions.length) {
-            InspectorTest.completeTest();
-            return;
-        }
-        var description = descriptions.shift();
-        InspectorTest.log("\n-------\n" + description);
-        InspectorTest.sendCommand("Runtime.evaluate", { "expression": "function " + description + "() {" + expressions.shift() + "}\n" + description + "()"});
-    }
-
-    function handleDebuggerPaused(messageObject)
-    {
-        var params = messageObject.params;
-        InspectorTest.log("Paused at: " + params.callFrames[0].functionName + "@" + params.callFrames[0].location.lineNumber);
-        InspectorTest.log("Reason: " + params.reason + "; Data:");
-        InspectorTest.logObject(params.data);
-        InspectorTest.sendCommand("Debugger.resume", { }, nextExpression);
-    }
-
-    nextExpression();
-}
-
-window.addEventListener("load", runTest.bind(null, false));
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/resources/statements.js b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/resources/statements.js
deleted file mode 100644
index cec8a12..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/resources/statements.js
+++ /dev/null
@@ -1,19 +0,0 @@
-function statementsExample()
-{
-    var self = arguments.callee;
-
-    debugger;
-
-    self.step = 1;
-
-    self.step = 2;
-
-    void [
-        self.step = 3,
-        self.step = 4,
-        self.step = 5,
-        self.step = 6
-    ];
-
-    self.step = 7;
-}
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/scriptParsedHash-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/scriptParsedHash-expected.txt
index 237f325..b5a0f54 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/scriptParsedHash-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/scriptParsedHash-expected.txt
@@ -1,3 +1,4 @@
+
 Hash received: 1C6D2E82E4E4F1BA4CB5762843D429DC872EBA18
 Hash received: EBF1ECD351E7A3294CB5762843D429DC872EBA18
 Hash received: 22D0043331237371241FC675A984B967025A3DC0
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/scriptParsedHash.html b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/scriptParsedHash.html
deleted file mode 100644
index d441cb1..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/scriptParsedHash.html
+++ /dev/null
@@ -1,37 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-function test()
-{
-    var hashes = new Set(["1C6D2E82E4E4F1BA4CB5762843D429DC872EBA18",
-                          "EBF1ECD351E7A3294CB5762843D429DC872EBA18",
-                          "22D0043331237371241FC675A984B967025A3DC0"]);
-    InspectorTest.sendCommandOrDie("Debugger.enable", {}, function() {
-        InspectorTest.eventHandler["Debugger.scriptParsed"] = function(messageObject)
-        {
-            if (hashes.has(messageObject.params.hash))
-                InspectorTest.log("Hash received: " + messageObject.params.hash);
-        }
-    });
-
-    function longScript() {
-        var longScript = "var b = 1;";
-        for (var i = 0; i < 2024; ++i)
-            longScript += "++b;";
-    }
-
-    InspectorTest.sendCommandOrDie("Runtime.evaluate", { expression: "1" });
-    InspectorTest.sendCommandOrDie("Runtime.evaluate", { expression: "239" });
-    InspectorTest.sendCommandOrDie("Runtime.evaluate", { expression: "(" + longScript + ")()" }, step2);
-
-    function step2()
-    {
-        InspectorTest.completeTest();
-    }
-}
-</script>
-</head>
-<body onLoad="runTest();">
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/scriptParsedHash.js b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/scriptParsedHash.js
new file mode 100644
index 0000000..8e5db35c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/scriptParsedHash.js
@@ -0,0 +1,24 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startBlank('');
+
+  var hashes = new Set(['1C6D2E82E4E4F1BA4CB5762843D429DC872EBA18',
+                        'EBF1ECD351E7A3294CB5762843D429DC872EBA18',
+                        '22D0043331237371241FC675A984B967025A3DC0']);
+
+  dp.Debugger.enable();
+  dp.Debugger.onScriptParsed(messageObject => {
+    if (hashes.has(messageObject.params.hash))
+      testRunner.log('Hash received: ' + messageObject.params.hash);
+  });
+
+  function longScript() {
+        var longScript = "var b = 1;";
+        for (var i = 0; i < 2024; ++i)
+            longScript += "++b;";
+  }
+
+  dp.Runtime.evaluate({expression: '1'});
+  dp.Runtime.evaluate({expression: '239'});
+  await dp.Runtime.evaluate({expression: '(' + longScript + ')()' });
+  testRunner.completeTest();
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/setScriptSource-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/setScriptSource-expected.txt
index 1b76ec5..9e355e2 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/setScriptSource-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/setScriptSource-expected.txt
@@ -1,3 +1,4 @@
+
 Function evaluate: {"type":"number","value":6,"description":"6"}
 PASS, result value: 6
 Function evaluate: {"type":"number","value":8,"description":"8"}
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/setScriptSource.html b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/setScriptSource.html
deleted file mode 100644
index 9bdb671..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/setScriptSource.html
+++ /dev/null
@@ -1,156 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script type="text/javascript" src="resources/liveedit-me.js"></script>
-<script>
-
-function test()
-{
-    // A general-purpose engine for sending a sequence of protocol commands.
-    // The clients provide requests and response handlers, while the engine catches
-    // errors and makes sure that once there's nothing to do completeTest() is called.
-    // @param step is an object with command, params and callback fields
-    function runRequestSeries(step) {
-        processStep(step);
-
-        function processStep(currentStep) {
-            try {
-                processStepOrFail(currentStep);
-            } catch (e) {
-                InspectorTest.log(e.stack);
-                InspectorTest.completeTest();
-            }
-        }
-
-        function processStepOrFail(currentStep) {
-            if (!currentStep) {
-                InspectorTest.completeTest();
-                return;
-            }
-            if (!currentStep.command) {
-                // A simple loopback step.
-                var next = currentStep.callback();
-                processStep(next);
-                return;
-            }
-
-            var innerCallback = function(response) {
-                var next;
-                if ("error" in response) {
-                    if (!("errorHandler" in currentStep)) {
-                        // Error message is not logged intentionally, it may be platform-specific.
-                        InspectorTest.log("Protocol command '" + currentStep.command + "' failed");
-                        InspectorTest.completeTest();
-                        return;
-                    }
-                    try {
-                        next = currentStep.errorHandler(response.error);
-                    } catch (e) {
-                        InspectorTest.log(e.stack);
-                        InspectorTest.completeTest();
-                        return;
-                    }
-                } else {
-                    try {
-                        next = currentStep.callback(response.result);
-                    } catch (e) {
-                        InspectorTest.log(e.stack);
-                        InspectorTest.completeTest();
-                        return;
-                    }
-                }
-                processStep(next);
-            }
-            InspectorTest.sendCommand(currentStep.command, currentStep.params, innerCallback);
-        }
-    }
-
-    function logEqualsCheck(actual, expected)
-    {
-        if (actual == expected) {
-            InspectorTest.log("PASS, result value: " + actual);
-        } else {
-            InspectorTest.log("FAIL, actual value: " + actual + ", expected: " + expected);
-        }
-    }
-    function logCheck(description, success)
-    {
-        InspectorTest.log(description + ": " + (success ? "PASS" : "FAIL"));
-    }
-
-    var firstStep = { callback: enableDebugger };
-
-    runRequestSeries(firstStep);
-
-    function enableDebugger() {
-        return { command: "Debugger.enable", params: {}, callback: evalFunction };
-    }
-
-    function evalFunction(response) {
-        var expression = "TestExpression(2, 4)";
-        return { command: "Runtime.evaluate", params: { expression: expression }, callback: callbackEvalFunction };
-    }
-
-    function callbackEvalFunction(result) {
-        InspectorTest.log("Function evaluate: " + JSON.stringify(result.result));
-        logEqualsCheck(result.result.value, 6);
-
-        return { command: "Runtime.evaluate", params: { expression: "TestExpression" }, callback: callbackEvalFunctionObject };
-    }
-
-    function callbackEvalFunctionObject(result) {
-        return { command: "Runtime.getProperties", params: { objectId: result.result.objectId }, callback: callbackFunctionDetails };
-    }
-
-    function callbackFunctionDetails(result)
-    {
-        var scriptId;
-        for (var prop of result.internalProperties) {
-            if (prop.name === "[[FunctionLocation]]")
-                scriptId = prop.value.value.scriptId;
-        }
-        return createScriptManipulationArc(scriptId, null);
-    }
-
-    // Several steps with scriptId in context.
-    function createScriptManipulationArc(scriptId, next) {
-        return { command: "Debugger.getScriptSource", params: { scriptId: scriptId }, callback: callbackGetScriptSource };
-
-        var originalText;
-
-        function callbackGetScriptSource(result) {
-            originalText = result.scriptSource;
-            var patched = originalText.replace("a + b", "a * b");
-
-            return { command: "Debugger.setScriptSource", params: { scriptId: scriptId, scriptSource: patched }, callback: callbackSetScriptSource };
-        }
-
-        function callbackSetScriptSource(result) {
-            var expression = "TestExpression(2, 4)";
-            return { command: "Runtime.evaluate", params: { expression: expression }, callback: callbackEvalFunction2 };
-        }
-
-        function callbackEvalFunction2(result) {
-            InspectorTest.log("Function evaluate: " + JSON.stringify(result.result));
-            logEqualsCheck(result.result.value, 8);
-
-            var patched = originalText.replace("a + b", "a # b");
-
-            return { command: "Debugger.setScriptSource", params: { scriptId: scriptId, scriptSource: patched }, callback: errorCallbackSetScriptSource2 };
-        }
-
-        function errorCallbackSetScriptSource2(result) {
-            var exceptionDetails = result.exceptionDetails;
-            logCheck("Has error reported", !!exceptionDetails);
-            logCheck("Reported error is a compile error", !!exceptionDetails);
-            if (exceptionDetails)
-                logEqualsCheck(exceptionDetails.lineNumber, 1);
-            return next;
-        }
-    }
-}
-</script>
-</head>
-<body onLoad="runTest();">
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/setScriptSource.js b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/setScriptSource.js
new file mode 100644
index 0000000..31b4f61
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/setScriptSource.js
@@ -0,0 +1,46 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startBlank('');
+
+  function logEqualsCheck(actual, expected) {
+    if (actual == expected) {
+      testRunner.log('PASS, result value: ' + actual);
+    } else {
+      testRunner.log('FAIL, actual value: ' + actual + ', expected: ' + expected);
+    }
+  }
+
+  await session.evaluate(
+    `function TestExpression(a, b) {
+      return a + b;
+    }`);
+
+  await dp.Debugger.enable();
+
+  var response = await dp.Runtime.evaluate({expression: 'TestExpression(2, 4)' });
+  testRunner.log('Function evaluate: ' + JSON.stringify(response.result.result));
+  logEqualsCheck(response.result.result.value, 6);
+
+  var functionObjectId = (await dp.Runtime.evaluate({expression: 'TestExpression' })).result.result.objectId;
+  var result = (await dp.Runtime.getProperties({ objectId: functionObjectId})).result;
+  var scriptId;
+  for (var prop of result.internalProperties) {
+    if (prop.name === '[[FunctionLocation]]')
+      scriptId = prop.value.value.scriptId;
+  }
+
+  var originalText = (await dp.Debugger.getScriptSource({scriptId})).result.scriptSource;
+  var patched = originalText.replace('a + b', 'a * b');
+  await dp.Debugger.setScriptSource({scriptId, scriptSource: patched});
+
+  var response = await dp.Runtime.evaluate({expression: 'TestExpression(2, 4)' });
+  testRunner.log('Function evaluate: ' + JSON.stringify(response.result.result));
+  logEqualsCheck(response.result.result.value, 8);
+
+  patched = originalText.replace('a + b', 'a # b');
+  var exceptionDetails = (await dp.Debugger.setScriptSource({scriptId, scriptSource: patched})).result.exceptionDetails;
+  testRunner.log(`Has error reported: ${!!exceptionDetails ? 'PASS' : 'FAIL'}`, );
+  testRunner.log(`Reported error is a compile error: ${!!exceptionDetails ? 'PASS' : 'FAIL'}`, );
+  if (exceptionDetails)
+    logEqualsCheck(exceptionDetails.lineNumber, 1);
+  testRunner.completeTest();
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/step-into-inline-event-handler.html b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/step-into-inline-event-handler.html
deleted file mode 100644
index e120131..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/step-into-inline-event-handler.html
+++ /dev/null
@@ -1,54 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-
-function testFunction()
-{
-    var e = document.getElementById("div");
-    debugger;
-    e.click();
-}
-
-function shouldNotBeThisFunction()
-{
-    return 239;
-}
-
-function test()
-{
-    InspectorTest.waitForEventPromise("Debugger.paused").then(makeStepping);
-
-    InspectorTest.sendCommandPromise("Debugger.enable", {})
-        .then((result) => InspectorTest.sendCommandPromise("Runtime.evaluate", { expression: "testFunction()" }))
-        .then(() => InspectorTest.completeTest());
-
-    function makeStepping()
-    {
-        sendCommandAndWaitForPause("Debugger.stepInto")
-            .then(() => sendCommandAndWaitForPause("Debugger.stepInto"))
-            .then((result) => dumpTopCallFrame(result))
-            .then(() => InspectorTest.sendCommandPromise("Debugger.resume"));
-    }
-
-    function sendCommandAndWaitForPause(command)
-    {
-        InspectorTest.sendCommand(command, {});
-        return InspectorTest.waitForEventPromise("Debugger.paused");
-    }
-
-    function dumpTopCallFrame(result)
-    {
-        var frame = result.params.callFrames[0];
-        InspectorTest.log("functionName (should be empty): " + (frame.functionName.length ? frame.functionName : "empty"));
-    }
-}
-</script>
-</head>
-<div id="div" onclick="shouldNotBeThisFunction()">
-</div>
-<body onLoad="runTest();">
-Tests that Debugger.stepInto doesn't ignore inline event listeners.
-</body>
-</html>
-
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/step-into-inline-event-handler.js b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/step-into-inline-event-handler.js
new file mode 100644
index 0000000..9d71b342
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/step-into-inline-event-handler.js
@@ -0,0 +1,35 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startHTML(`
+    <script>
+    function testFunction() {
+      var e = document.getElementById('div');
+      debugger;
+      e.click();
+    }
+
+    function shouldNotBeThisFunction() {
+      return 239;
+    }
+    </script>
+    <div id='div' onclick='shouldNotBeThisFunction()'></div>
+  `, `Tests that Debugger.stepInto doesn't ignore inline event listeners.`);
+
+
+  function dumpTopCallFrame(result) {
+    var frame = result.params.callFrames[0];
+    testRunner.log('functionName (should be empty): ' + (frame.functionName.length ? frame.functionName : 'empty'));
+  }
+
+  await dp.Debugger.enable();
+  var finished = dp.Runtime.evaluate({expression: 'testFunction()'});
+
+  await dp.Debugger.oncePaused();
+  dp.Debugger.stepInto();
+  await dp.Debugger.oncePaused();
+  dp.Debugger.stepInto();
+  dumpTopCallFrame(await dp.Debugger.oncePaused());
+  dp.Debugger.resume();
+
+  await finished;
+  testRunner.completeTest();
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/step-over-caught-exception-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/step-over-caught-exception-expected.txt
index 78aaf35..e6ce4af3 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/step-over-caught-exception-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/step-over-caught-exception-expected.txt
@@ -1,5 +1,4 @@
-CONSOLE MESSAGE: line 16: completed
-CONSOLE MESSAGE: line 16: completed
+
 testFunction:13
 testFunction:15
 testFunction:13
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/step-over-caught-exception.html b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/step-over-caught-exception.html
deleted file mode 100644
index 1da33950..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/step-over-caught-exception.html
+++ /dev/null
@@ -1,83 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-function testFunction()
-{
-    function foo()
-    {
-        try {
-            throw new Error();
-        } catch (e) {
-        }
-    }
-    debugger;
-    foo();
-    console.log("completed");
-}
-</script>
-<script>
-function test()
-{
-    InspectorTest.sendCommandOrDie("Debugger.enable", {} );
-    InspectorTest.sendCommandOrDie("Runtime.enable", {} );
-    step1();
-
-    function step1()
-    {
-        InspectorTest.sendCommandOrDie("Runtime.evaluate", { "expression": "setTimeout(testFunction, 0);"} );
-        var commands = [ "Print", "stepOver", "stepOver", "Print", "resume" ];
-        InspectorTest.eventHandler["Debugger.paused"] = function(messageObject)
-        {
-            var command = commands.shift();
-            if (command === "Print") {
-                var callFrames = messageObject.params.callFrames;
-                for (var callFrame of callFrames)
-                    InspectorTest.log(callFrame.functionName + ":" + callFrame.location.lineNumber);
-                command = commands.shift();
-            }
-            if (command)
-                InspectorTest.sendCommandOrDie("Debugger." + command, {});
-        }
-
-        InspectorTest.eventHandler["Runtime.consoleAPICalled"] = function(messageObject)
-        {
-            if (messageObject.params.args[0].value === "completed") {
-                if (commands.length)
-                    InspectorTest.log("[FAIL]: execution was resumed too earlier.")
-                step2();
-            }
-        }
-    }
-
-    function step2()
-    {
-        InspectorTest.sendCommandOrDie("Runtime.evaluate", { "expression": "setTimeout(testFunction, 0);"} );
-        var commands = [ "Print", "stepOver", "stepInto", "stepOver", "stepOver", "Print", "resume" ];
-        InspectorTest.eventHandler["Debugger.paused"] = function(messageObject)
-        {
-            var command = commands.shift();
-            if (command === "Print") {
-                var callFrames = messageObject.params.callFrames;
-                for (var callFrame of callFrames)
-                    InspectorTest.log(callFrame.functionName + ":" + callFrame.location.lineNumber);
-                command = commands.shift();
-            }
-            if (command)
-                InspectorTest.sendCommandOrDie("Debugger." + command, {});
-        }
-
-        InspectorTest.eventHandler["Runtime.consoleAPICalled"] = function(messageObject)
-        {
-            if (messageObject.params.args[0].value === "completed") {
-                if (commands.length)
-                    InspectorTest.log("[FAIL]: execution was resumed too earlier.")
-                InspectorTest.completeTest();
-            }
-        }
-    }
-}
-</script>
-</head>
-<body onLoad="runTest();"></body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/step-over-caught-exception.js b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/step-over-caught-exception.js
new file mode 100644
index 0000000..a30526d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/step-over-caught-exception.js
@@ -0,0 +1,54 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startBlank('');
+
+  dp.Debugger.enable();
+  dp.Runtime.enable();
+  dp.Runtime.evaluate({expression: `
+
+
+
+
+
+    function testFunction() {
+      function foo() {
+        try {
+            throw new Error();
+        } catch (e) {
+        }
+      }
+      debugger;
+      foo();
+      console.log('completed');
+    }
+    setTimeout(testFunction, 0);
+  `});
+
+  function printCallFrames(messageObject) {
+    var callFrames = messageObject.params.callFrames;
+    for (var callFrame of callFrames)
+      testRunner.log(callFrame.functionName + ':' + callFrame.location.lineNumber);
+  }
+
+  printCallFrames(await dp.Debugger.oncePaused());
+  dp.Debugger.stepOver();
+  await dp.Debugger.oncePaused();
+  dp.Debugger.stepOver();
+  printCallFrames(await dp.Debugger.oncePaused());
+  dp.Debugger.resume();
+  await dp.Runtime.onceConsoleAPICalled(messageObject => messageObject.params.args[0].value === 'completed');
+
+  dp.Runtime.evaluate({expression: 'setTimeout(testFunction, 0);'} );
+  printCallFrames(await dp.Debugger.oncePaused());
+  dp.Debugger.stepOver();
+  await dp.Debugger.oncePaused();
+  dp.Debugger.stepInto();
+  await dp.Debugger.oncePaused();
+  dp.Debugger.stepOver();
+  await dp.Debugger.oncePaused();
+  dp.Debugger.stepOver();
+  printCallFrames(await dp.Debugger.oncePaused());
+  dp.Debugger.resume();
+  await dp.Runtime.onceConsoleAPICalled(messageObject => messageObject.params.args[0].value === 'completed');
+
+  testRunner.completeTest();
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/stepping-with-blackboxed-ranges-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/stepping-with-blackboxed-ranges-expected.txt
index 2969deb4..10cc9e4 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/stepping-with-blackboxed-ranges-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/stepping-with-blackboxed-ranges-expected.txt
@@ -1,3 +1,4 @@
+
 foo: 14:4
 blackboxedBoo: 3:12
 notBlackboxedFoo: 3:12
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/stepping-with-blackboxed-ranges.html b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/stepping-with-blackboxed-ranges.html
deleted file mode 100644
index cbb17ec..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/stepping-with-blackboxed-ranges.html
+++ /dev/null
@@ -1,104 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script type="text/javascript" src="resources/blackboxed.js"></script>
-<script type="text/javascript" src="resources/mixed.js"></script>
-<script>
-function testFunction()
-{
-    notBlackboxedBoo(); // for setup ranges and stepOut
-    notBlackboxedBoo(); // for stepIn
-}
-
-function foo()
-{
-    debugger;
-    return 239;
-}
-</script>
-<script>
-function test()
-{
-    InspectorTest.eventHandler["Debugger.paused"] = setBlackboxedScriptRanges;
-    InspectorTest.sendCommandOrDie("Debugger.enable", {}, callTestFunction);
-
-    function callTestFunction(response)
-    {
-        InspectorTest.evaluateInInspectedPage("setTimeout(testFunction, 0);");
-    }
-
-    function setBlackboxedScriptRanges(response)
-    {
-        var callFrames = response.params.callFrames;
-        printCallFrames(callFrames);
-        InspectorTest.sendCommand("Debugger.setBlackboxedRanges", {
-            scriptId: callFrames[1].location.scriptId,
-            positions: [ { lineNumber: 0, columnNumber: 0 } ] // blackbox ranges for blackboxed.js
-        }, setIncorrectRanges.bind(null, callFrames[2].location.scriptId));
-    }
-
-    var incorrectPositions = [
-        [ { lineNumber: 0, columnNumber: 0 }, { lineNumber: 0, columnNumber: 0 } ],
-        [ { lineNumber: 0, columnNumber: 1 }, { lineNumber: 0, columnNumber: 0 } ],
-        [ { lineNumber: 0, columnNumber: -1 } ],
-    ];
-
-    function setIncorrectRanges(scriptId, response)
-    {
-        if (response.error)
-            InspectorTest.log(response.error.message);
-        var positions = incorrectPositions.shift();
-        if (!positions) {
-            setMixedSourceRanges(scriptId);
-            return;
-        }
-        InspectorTest.log("Try to set positions: " + JSON.stringify(positions));
-        InspectorTest.sendCommand("Debugger.setBlackboxedRanges", {
-            scriptId: scriptId,
-            positions: positions
-        }, setIncorrectRanges.bind(null, scriptId));
-    }
-
-    function setMixedSourceRanges(scriptId)
-    {
-        InspectorTest.eventHandler["Debugger.paused"] = runAction;
-        InspectorTest.sendCommandOrDie("Debugger.setBlackboxedRanges", {
-            scriptId: scriptId,
-            positions: [ { lineNumber: 6, columnNumber: 0 }, { lineNumber: 14, columnNumber: 0 } ] // blackbox ranges for mixed.js
-        }, runAction);
-    }
-
-    var actions = [ "stepOut", "print", "stepOut", "print", "stepOut", "print",
-        "stepInto", "print", "stepOver", "stepInto", "print", "stepOver", "stepInto", "print",
-        "stepOver", "stepInto", "print" ];
-
-    function runAction(response)
-    {
-        var action = actions.shift();
-        if (!action)
-            InspectorTest.completeTest();
-
-        if (action === "print") {
-            printCallFrames(response.params.callFrames);
-            runAction({});
-        } else {
-            InspectorTest.log("action: " + action);
-            InspectorTest.sendCommandOrDie("Debugger." + action, {});
-        }
-    }
-
-    function printCallFrames(callFrames)
-    {
-        var topCallFrame = callFrames[0];
-        if (topCallFrame.functionName.startsWith("blackboxed"))
-            InspectorTest.log("FAIL: blackboxed function in top call frame");
-        for (var callFrame of callFrames)
-            InspectorTest.log(callFrame.functionName + ': ' + callFrame.location.lineNumber + ":" + callFrame.location.columnNumber);
-        InspectorTest.log("");
-    }
-}
-</script>
-</head>
-<body onload="runTest()">
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/stepping-with-blackboxed-ranges.js b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/stepping-with-blackboxed-ranges.js
new file mode 100644
index 0000000..49757284
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/stepping-with-blackboxed-ranges.js
@@ -0,0 +1,131 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startBlank('');
+
+  function printCallFrames(response) {
+    var callFrames = response.params.callFrames;
+    var topCallFrame = callFrames[0];
+    if (topCallFrame.functionName.startsWith('blackboxed'))
+      testRunner.log('FAIL: blackboxed function in top call frame');
+    for (var callFrame of callFrames)
+      testRunner.log(callFrame.functionName + ': ' + callFrame.location.lineNumber + ':' + callFrame.location.columnNumber);
+    testRunner.log('');
+  }
+
+  function printError(response) {
+    if (response.error)
+      testRunner.log(response.error.message);
+  }
+
+  await session.evaluate(
+`function blackboxedBoo()
+{
+    var a = 42;
+    var b = foo();
+    return a + b;
+}
+//# sourceURL=blackboxed-script.js
+`);
+
+  await session.evaluate(
+`function notBlackboxedFoo()
+{
+    var a = 42;
+    var b = blackboxedBoo();
+    return a + b;
+}
+
+function blackboxedFoo()
+{
+    var a = 42;
+    var b = notBlackboxedFoo();
+    return a + b;
+}
+
+function notBlackboxedBoo()
+{
+    var a = 42;
+    var b = blackboxedFoo();
+    return a + b;
+}
+//# sourceURL=mixed-source.js
+`);
+
+  await session.evaluate(`
+
+
+
+
+
+function testFunction()
+{
+    notBlackboxedBoo(); // for setup ranges and stepOut
+    notBlackboxedBoo(); // for stepIn
+}
+
+function foo()
+{
+    debugger;
+    return 239;
+}
+  `);
+
+  await dp.Debugger.enable();
+  session.evaluate('setTimeout(testFunction, 0);');
+
+  var response = await dp.Debugger.oncePaused();
+  printCallFrames(response);
+  var scriptId = response.params.callFrames[2].location.scriptId;
+
+  printError(await dp.Debugger.setBlackboxedRanges({
+    scriptId: response.params.callFrames[1].location.scriptId,
+    positions: [{lineNumber: 0, columnNumber: 0}] // blackbox ranges for blackboxed.js
+  }));
+
+  var incorrectPositions = [
+    [{lineNumber: 0, columnNumber: 0}, {lineNumber: 0, columnNumber: 0}],
+    [{lineNumber: 0, columnNumber: 1}, {lineNumber: 0, columnNumber: 0}],
+    [{lineNumber: 0, columnNumber: -1}],
+  ];
+  for (var positions of incorrectPositions) {
+    testRunner.log('Try to set positions: ' + JSON.stringify(positions));
+    printError(await dp.Debugger.setBlackboxedRanges({scriptId, positions}));
+  }
+
+  await dp.Debugger.setBlackboxedRanges({
+    scriptId,
+    positions: [{lineNumber: 6, columnNumber: 0}, {lineNumber: 14, columnNumber: 0}] // blackbox ranges for mixed.js
+  });
+
+  testRunner.log('action: stepOut');
+  dp.Debugger.stepOut();
+  printCallFrames(await dp.Debugger.oncePaused());
+  testRunner.log('action: stepOut');
+  dp.Debugger.stepOut();
+  printCallFrames(await dp.Debugger.oncePaused());
+  testRunner.log('action: stepOut');
+  dp.Debugger.stepOut();
+  printCallFrames(await dp.Debugger.oncePaused());
+  testRunner.log('action: stepInto');
+  dp.Debugger.stepInto();
+  printCallFrames(await dp.Debugger.oncePaused());
+  testRunner.log('action: stepOver');
+  dp.Debugger.stepOver();
+  await dp.Debugger.oncePaused();
+  testRunner.log('action: stepInto');
+  dp.Debugger.stepInto();
+  printCallFrames(await dp.Debugger.oncePaused());
+  testRunner.log('action: stepOver');
+  dp.Debugger.stepOver();
+  await dp.Debugger.oncePaused();
+  testRunner.log('action: stepInto');
+  dp.Debugger.stepInto();
+  printCallFrames(await dp.Debugger.oncePaused());
+  testRunner.log('action: stepOver');
+  dp.Debugger.stepOver();
+  await dp.Debugger.oncePaused();
+  testRunner.log('action: stepInto');
+  dp.Debugger.stepInto();
+  printCallFrames(await dp.Debugger.oncePaused());
+  await dp.Debugger.resume();
+  testRunner.completeTest();
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/suspend-setTimeout-on-pause-in-dedicated-worker-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/suspend-setTimeout-on-pause-in-dedicated-worker-expected.txt
index d6da87d7..492170a 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/suspend-setTimeout-on-pause-in-dedicated-worker-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/suspend-setTimeout-on-pause-in-dedicated-worker-expected.txt
@@ -1,5 +1,4 @@
 Tests that setTimeout callback will not fire while script execution is paused.Bug 377926.
-
 Started worker
 Worker created
 SUCCESS: Worker paused
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/suspend-setTimeout-on-pause-in-dedicated-worker.html b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/suspend-setTimeout-on-pause-in-dedicated-worker.html
deleted file mode 100644
index 7853e5d..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/suspend-setTimeout-on-pause-in-dedicated-worker.html
+++ /dev/null
@@ -1,82 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-var worker;
-
-function startWorker()
-{
-    worker = new Worker("resources/dedicated-worker-suspend-setTimeout.js");
-    worker.onmessage = function(event) { };
-    worker.postMessage(1);
-    log("Started worker");
-}
-
-
-function test()
-{
-
-    var workerId;
-    var workerRequestId = 1;
-    function sendCommandToWorker(method, params)
-    {
-        var requestId = workerRequestId++;
-        InspectorTest.sendCommand("Target.sendMessageToTarget",
-            {
-                "targetId": workerId,
-                "message": JSON.stringify({ "method": method,
-                                            "params": params,
-                                            "id": requestId })
-            });
-        return requestId;
-    }
-
-    function didEnableWorkerDebugging(messageObject)
-    {
-        if ("error" in messageObject) {
-            InspectorTest.log("FAIL: Couldn't enable worker debugger: " + messageObject.error.message);
-            InspectorTest.completeTest();
-        }
-    }
-    InspectorTest.sendCommand("Target.setAutoAttach", {autoAttach: true, waitForDebuggerOnStart: true}, didEnableWorkerDebugging);
-    InspectorTest.sendCommand("Runtime.evaluate", { "expression": "startWorker()" });
-
-    InspectorTest.eventHandler["Target.attachedToTarget"] = function(messageObject)
-    {
-        workerId = messageObject["params"]["targetInfo"]["targetId"];
-        InspectorTest.log("Worker created");
-        sendCommandToWorker("Debugger.enable", {});
-        sendCommandToWorker("Runtime.runIfWaitingForDebugger", {});
-    }
-
-    var pauseCount = 0;
-    var evalRequestId;
-    InspectorTest.eventHandler["Target.receivedMessageFromTarget"] = function(messageObject)
-    {
-        var message = JSON.parse(messageObject["params"]["message"]);
-        if (message["method"] === "Debugger.paused") {
-            InspectorTest.log("SUCCESS: Worker paused");
-            if (++pauseCount === 1) {
-                evalRequestId = sendCommandToWorker("Runtime.evaluate", { "expression": "global_value" });
-            } else {
-                InspectorTest.log("FAIL: debugger paused second time");
-                InspectorTest.completeTest();
-            }
-        } else if (evalRequestId && message["id"] === evalRequestId) {
-            var value = message["result"]["result"]["value"];
-            if (value === 1)
-                InspectorTest.log("SUCCESS: global_value is 1");
-            else
-                InspectorTest.log("FAIL: setTimeout callback fired while script execution was paused");
-            sendCommandToWorker("Debugger.disable", {});
-            InspectorTest.completeTest();
-        }
-    }
-}
-</script>
-</head>
-<body onLoad="runTest();">
-<p>Tests that setTimeout callback will not fire while script execution is paused.<a href="https://code.google.com/p/chromium/issues/detail?id=377926">Bug 377926.</a>
-</p>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/suspend-setTimeout-on-pause-in-dedicated-worker.js b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/suspend-setTimeout-on-pause-in-dedicated-worker.js
new file mode 100644
index 0000000..f5ffb967
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/suspend-setTimeout-on-pause-in-dedicated-worker.js
@@ -0,0 +1,49 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startBlank('Tests that setTimeout callback will not fire while script execution is paused.Bug 377926.');
+
+  var workerId;
+  var workerRequestId = 1;
+  function sendCommandToWorker(method, params) {
+    var message = {method, params, id: workerRequestId};
+    dp.Target.sendMessageToTarget({targetId: workerId, message: JSON.stringify(message)});
+    return workerRequestId++;
+  }
+
+  dp.Target.setAutoAttach({autoAttach: true, waitForDebuggerOnStart: true});
+  await session.evaluate(`
+    window.worker = new Worker('${testRunner.url('resources/dedicated-worker-suspend-setTimeout.js')}');
+    window.worker.onmessage = function(event) { };
+    window.worker.postMessage(1);
+  `);
+  testRunner.log('Started worker');
+
+  var messageObject = await dp.Target.onceAttachedToTarget();
+  workerId = messageObject.params.targetInfo.targetId;
+  testRunner.log('Worker created');
+
+  sendCommandToWorker('Debugger.enable', {});
+  sendCommandToWorker('Runtime.runIfWaitingForDebugger', {});
+
+  var pauseCount = 0;
+  var evalRequestId;
+  dp.Target.onReceivedMessageFromTarget(async messageObject => {
+    var message = JSON.parse(messageObject.params.message);
+    if (message.method === 'Debugger.paused') {
+      testRunner.log('SUCCESS: Worker paused');
+      if (++pauseCount === 1) {
+        evalRequestId = sendCommandToWorker('Runtime.evaluate', {expression: 'global_value'});
+      } else {
+        testRunner.log('FAIL: debugger paused second time');
+        testRunner.completeTest();
+      }
+    } else if (evalRequestId && message.id === evalRequestId) {
+      var value = message.result.result.value;
+      if (value === 1)
+        testRunner.log('SUCCESS: global_value is 1');
+      else
+        testRunner.log('FAIL: setTimeout callback fired while script execution was paused');
+      sendCommandToWorker('Debugger.disable', {});
+      testRunner.completeTest();
+    }
+  });
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/updateCallFrameScopes-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/updateCallFrameScopes-expected.txt
index ed52d23..55d32b9 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/updateCallFrameScopes-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/updateCallFrameScopes-expected.txt
@@ -1,3 +1,4 @@
+
 Paused on 'debugger;'
 Variable value changed
 Stacktrace re-read again
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/updateCallFrameScopes.html b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/updateCallFrameScopes.html
deleted file mode 100644
index 20004b4..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/updateCallFrameScopes.html
+++ /dev/null
@@ -1,72 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-
-function TestFunction()
-{
-    var a = 2;
-    debugger;
-    debugger;
-}
-
-function test()
-{
-    var newVariableValue = 55;
-
-    InspectorTest.sendCommand("Debugger.enable", {});
-
-    InspectorTest.eventHandler["Debugger.paused"] = handleDebuggerPaused;
-
-    InspectorTest.sendCommand("Runtime.evaluate", { "expression": "setTimeout(TestFunction, 0)" });
-
-    function handleDebuggerPaused(messageObject)
-    {
-        InspectorTest.log("Paused on 'debugger;'");
-        InspectorTest.eventHandler["Debugger.paused"] = undefined;
-
-        var topFrame = messageObject.params.callFrames[0];
-        var topFrameId = topFrame.callFrameId;
-        InspectorTest.sendCommand("Debugger.evaluateOnCallFrame", { "callFrameId": topFrameId, "expression": "a = " + newVariableValue }, callbackChangeValue);
-    }
-
-    function callbackChangeValue(response)
-    {
-        InspectorTest.log("Variable value changed");
-        InspectorTest.eventHandler["Debugger.paused"] = callbackGetBacktrace;
-        InspectorTest.sendCommand("Debugger.resume", { });
-    }
-
-    function callbackGetBacktrace(response)
-    {
-        InspectorTest.log("Stacktrace re-read again");
-        var localScope = response.params.callFrames[0].scopeChain[0];
-        InspectorTest.sendCommand("Runtime.getProperties", { "objectId": localScope.object.objectId }, callbackGetProperties);
-    }
-
-    function callbackGetProperties(response)
-    {
-        InspectorTest.log("Scope variables downloaded anew");
-        var varNamedA;
-        var propertyList = response.result.result;
-        for (var i = 0; i < propertyList.length; i++) {
-            if (propertyList[i].name === "a") {
-                varNamedA = propertyList[i];
-                break;
-            }
-        }
-        if (varNamedA) {
-            var actualValue = varNamedA.value.value;
-            InspectorTest.log("New variable is " + actualValue + ", expected is " + newVariableValue + ", old was: 2");
-            InspectorTest.log(actualValue == newVariableValue ? "SUCCESS" : "FAIL");
-        } else {
-            InspectorTest.log("Failed to find variable in scope");
-        }
-        InspectorTest.completeTest();
-    }
-}
-</script>
-</head>
-<body onLoad="runTest();">
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/debugger/updateCallFrameScopes.js b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/updateCallFrameScopes.js
new file mode 100644
index 0000000..09e007d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/debugger/updateCallFrameScopes.js
@@ -0,0 +1,41 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startBlank('');
+
+  var newVariableValue = 55;
+
+  dp.Debugger.enable();
+  dp.Runtime.evaluate({expression: `
+    function TestFunction() {
+      var a = 2;
+      debugger;
+      debugger;
+    }
+    setTimeout(TestFunction, 0);
+  `});
+
+  var messageObject = await dp.Debugger.oncePaused();
+  testRunner.log(`Paused on 'debugger;'`);
+  var topFrame = messageObject.params.callFrames[0];
+  var topFrameId = topFrame.callFrameId;
+
+  dp.Debugger.evaluateOnCallFrame({callFrameId: topFrameId, expression: 'a = ' + newVariableValue });
+  testRunner.log('Variable value changed');
+  dp.Debugger.resume();
+
+  var response = await dp.Debugger.oncePaused();
+  testRunner.log('Stacktrace re-read again');
+  var localScope = response.params.callFrames[0].scopeChain[0];
+
+  response = await dp.Runtime.getProperties({objectId: localScope.object.objectId });
+  testRunner.log('Scope variables downloaded anew');
+  var propertyList = response.result.result;
+  var varNamedA = propertyList.find(x => x.name === 'a');
+  if (varNamedA) {
+    var actualValue = varNamedA.value.value;
+    testRunner.log('New variable is ' + actualValue + ', expected is ' + newVariableValue + ', old was: 2');
+    testRunner.log(actualValue == newVariableValue ? 'SUCCESS' : 'FAIL');
+  } else {
+    testRunner.log('Failed to find variable in scope');
+  }
+  testRunner.completeTest();
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/resources/inspector-protocol-test.js b/third_party/WebKit/LayoutTests/inspector-protocol/resources/inspector-protocol-test.js
index ca43106..f2c60bad 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/resources/inspector-protocol-test.js
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/resources/inspector-protocol-test.js
@@ -312,7 +312,7 @@
         var eventName = match[3];
         eventName = eventName.charAt(0).toLowerCase() + eventName.slice(1);
         if (match[1] === 'once')
-          return () => this._waitForEvent(`${agentName}.${eventName}`);
+          return eventMatcher => this._waitForEvent(`${agentName}.${eventName}`, eventMatcher);
         if (match[1] === 'off')
           return listener => this._removeEventHandler(`${agentName}.${eventName}`, listener);
         return listener => this._addEventHandler(`${agentName}.${eventName}`, listener);
@@ -335,9 +335,11 @@
     this._eventHandlers.set(eventName, handlers);
   }
 
-  _waitForEvent(eventName) {
+  _waitForEvent(eventName, eventMatcher) {
     return new Promise(callback => {
       var handler = result => {
+        if (eventMatcher && !eventMatcher(result))
+          return;
         this._removeEventHandler(eventName, handler);
         callback(result);
       };
diff --git a/third_party/WebKit/Source/core/layout/ng/geometry/ng_box_strut.h b/third_party/WebKit/Source/core/layout/ng/geometry/ng_box_strut.h
index 5b70b24..6b91aca 100644
--- a/third_party/WebKit/Source/core/layout/ng/geometry/ng_box_strut.h
+++ b/third_party/WebKit/Source/core/layout/ng/geometry/ng_box_strut.h
@@ -26,6 +26,13 @@
         block_start(block_start),
         block_end(block_end) {}
 
+  LayoutUnit LineLeft(TextDirection direction) const {
+    return IsLtr(direction) ? inline_start : inline_end;
+  }
+  LayoutUnit LineRight(TextDirection direction) const {
+    return IsLtr(direction) ? inline_end : inline_start;
+  }
+
   LayoutUnit InlineSum() const { return inline_start + inline_end; }
   LayoutUnit BlockSum() const { return block_start + block_end; }
 
diff --git a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_box_state.cc b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_box_state.cc
index 4a35415f1..250b93f 100644
--- a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_box_state.cc
+++ b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_box_state.cc
@@ -86,7 +86,8 @@
 
   // Compute box properties regardless of needs_box_fragment since close tag may
   // also set needs_box_fragment.
-  box->line_left_position = position + item_result.margins.inline_start;
+  box->line_left_position =
+      position + item_result.margins.LineLeft(item.Style()->Direction());
   box->borders_paddings_block_start = item_result.borders_paddings_block_start;
   box->borders_paddings_block_end = item_result.borders_paddings_block_end;
   return box;
@@ -155,7 +156,8 @@
     const NGInlineItemResult& item_result,
     LayoutUnit position) {
   DCHECK(needs_box_fragment);
-  line_right_position = position - item_result.margins.inline_end;
+  line_right_position =
+      position - item_result.margins.LineRight(item.Style()->Direction());
   // We have right edge on close tag, and if the box does not have a
   // continuation.
   // TODO(kojii): Needs review when we change SplitInlines().
diff --git a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.cc b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.cc
index 01b1c7f..d704839 100644
--- a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.cc
+++ b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.cc
@@ -282,6 +282,11 @@
     NGTextFragmentBuilder* text_builder) {
   DCHECK(item_result->layout_result);
 
+  // The input |position| is the line-left edge of the margin box.
+  // Adjust it to the border box by adding the line-left margin.
+  const ComputedStyle& style = *item.Style();
+  position += item_result->margins.LineLeft(style.Direction());
+
   NGInlineBoxState* box =
       box_states_.OnOpenTag(item, *item_result, line_box, position);
 
@@ -312,7 +317,7 @@
   // TODO(kojii): Try to eliminate the wrapping text fragment and use the
   // |fragment| directly. Currently |CopyFragmentDataToLayoutBlockFlow|
   // requires a text fragment.
-  text_builder->SetDirection(item.Style()->Direction());
+  text_builder->SetDirection(style.Direction());
   text_builder->SetSize({fragment.InlineSize(), block_size});
   LayoutUnit line_top = item_result->margins.block_start - metrics.ascent;
   RefPtr<NGPhysicalTextFragment> text_fragment = text_builder->ToTextFragment(
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_block_node.cc b/third_party/WebKit/Source/core/layout/ng/ng_block_node.cc
index 9857544..771d4b4 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_block_node.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_block_node.cc
@@ -255,6 +255,22 @@
   intrinsic_logical_height -= border_scrollbar_padding.BlockSum();
   box_->SetIntrinsicContentLogicalHeight(intrinsic_logical_height);
 
+  // LayoutBox::Margin*() should be used value, while we set computed value
+  // here. This is not entirely correct, but these values are not used for
+  // layout purpose.
+  // BaselinePosition() relies on margins set to the box, and computed value is
+  // good enough for it to work correctly.
+  // Set this only for atomic inlines, or we end up adding margins twice.
+  if (box_->IsAtomicInlineLevel()) {
+    NGBoxStrut margins =
+        ComputeMargins(constraint_space, Style(),
+                       constraint_space.WritingMode(), Style().Direction());
+    box_->SetMarginBefore(margins.block_start);
+    box_->SetMarginAfter(margins.block_end);
+    box_->SetMarginStart(margins.inline_start);
+    box_->SetMarginEnd(margins.inline_end);
+  }
+
   // TODO(ikilpatrick) is this the right thing to do?
   if (box_->IsLayoutBlockFlow()) {
     ToLayoutBlockFlow(box_)->RemoveFloatingObjects();
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index df9d421..f213a33 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -20410,6 +20410,32 @@
   <int value="1" label="identical"/>
 </enum>
 
+<enum name="InstantTethering_ConnectionToHostResult_Failure">
+  <int value="0" label="Unknown error"/>
+  <int value="1" label="Tethering timed out"/>
+  <int value="2" label="Client connection error"/>
+</enum>
+
+<enum name="InstantTethering_ConnectionToHostResult_Failure_ClientConnection">
+  <int value="0" label="Timeout while waiting to connect"/>
+  <int value="1" label="Canceled by a new connection attempt"/>
+</enum>
+
+<enum name="InstantTethering_ConnectionToHostResult_Failure_TetheringTimeout">
+  <int value="0" label="First-time setup was required"/>
+  <int value="1" label="First-time setup was not required"/>
+</enum>
+
+<enum name="InstantTethering_ConnectionToHostResult_ProvisioningFailureRate">
+  <int value="0" label="Provisioning failed"/>
+  <int value="1" label="Other"/>
+</enum>
+
+<enum name="InstantTethering_ConnectionToHostResult_SuccessRate">
+  <int value="0" label="Success"/>
+  <int value="1" label="Failure"/>
+</enum>
+
 <enum name="IntelMaxMicroArchitecture">
   <int value="0" label="Pentium"/>
   <int value="1" label="SSE"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 18ce1dad..d304889b 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -26619,6 +26619,96 @@
   </summary>
 </histogram>
 
+<histogram name="InstantTethering.ConnectionToHostResult.Failure"
+    enum="InstantTethering_ConnectionToHostResult_Failure">
+  <owner>hansberry@chromium.com</owner>
+  <summary>
+    Provides a top-level breakdown of the times a connection to a host has
+    failed.
+
+    An &quot;unknown error&quot; is generally caused by the host returning an
+    &quot;unknown error&quot; response code. Tethering timing out and client
+    connection error and 3) are both broken down further in
+    InstantTethering.ConnectionToHostResult.Failure.TetheringTimeout and
+    InstantTethering.ConnectionToHostResult.Failure.ClientConnection,
+    respectively.
+  </summary>
+</histogram>
+
+<histogram
+    name="InstantTethering.ConnectionToHostResult.Failure.ClientConnection"
+    enum="InstantTethering_ConnectionToHostResult_Failure_ClientConnection">
+  <owner>hansberry@chromium.com</owner>
+  <summary>
+    Breaks down the reasons why the client failed to connect to the hotspot, and
+    the number of times they occurred. This histogram captures 2 reasons: 1) the
+    connection attempt to the hotspot timed out, or 2) any stage of the
+    connection attempt was canceled by a new connection attempt.
+
+    This histogram breaks down the &quot;client connection error&quot; count of
+    InstantTethering.ConnectionToHostResult.Failure.
+  </summary>
+</histogram>
+
+<histogram
+    name="InstantTethering.ConnectionToHostResult.Failure.TetheringTimeout"
+    enum="InstantTethering_ConnectionToHostResult_Failure_TetheringTimeout">
+  <owner>hansberry@chromium.com</owner>
+  <summary>
+    Captures the number of times of whether or not first-time setup was required
+    when tethering timed out.
+
+    Starting tethering on the host works like so: if first-time setup is
+    required on the host, a first-time setup UI is shown on the host; once the
+    user interacts with it, the provisioning app is called. If first-time setup
+    is not required, then the provisioning app is directly called. To be clear:
+    the provisioning app is always run.
+
+    There are two possible ways for tethering to time out: either the
+    provisioning app flaked (crashed or hung, and never called back to Instant
+    Tethering), or the user never interacted with the first-time setup UI flow
+    (assuming first-time setup was required).
+
+    Because it's very unlikely for the provisioning app to flake, we can read
+    the &quot;was first-time setup&quot; count as almost always indicative of
+    the user not interacting with the first-time setup UI. We expect the
+    &quot;was not first-time setup&quot; count to be low (because, as mentioned,
+    it's very unlikely for the provisioning app to flake).
+
+    This histogram breaks down the &quot;tethering timed out&quot; count of
+    InstantTethering.ConnectionToHostResult.Failure.
+  </summary>
+</histogram>
+
+<histogram
+    name="InstantTethering.ConnectionToHostResult.ProvisioningFailureRate"
+    enum="InstantTethering_ConnectionToHostResult_ProvisioningFailureRate">
+  <owner>hansberry@chromium.org</owner>
+  <summary>
+    Provides a breakdown of the times a connection to a host was either
+    unsuccessful due to provisioning failure (the carrier disallows tethering)
+    or &quot;other&quot; (this is captured under
+    InstantTethering.ConnectionToHostResult.SuccessRate).
+
+    This metric captures the rough percentage of connections which are
+    unsuccessful due to provisioning failure. It is separate from
+    InstantTethering.ConnectionToHostResult.SuccessRate and the metrics related
+    to it because provisioning failure is neither truly a success nor an error.
+  </summary>
+</histogram>
+
+<histogram name="InstantTethering.ConnectionToHostResult.SuccessRate"
+    enum="InstantTethering_ConnectionToHostResult_SuccessRate">
+  <owner>hansberry@chromium.com</owner>
+  <summary>
+    Captures the count of successful and failed connection attempts.
+
+    This metric provides an immediate understanding of the Instant Tethering
+    connection success rate. The counts of failure are broken down in
+    InstantTethering.ConnectionToHostResult.Failure.
+  </summary>
+</histogram>
+
 <histogram name="InterProcessTimeTicks.BrowserAhead" units="ms">
   <owner>ppi@chromium.org</owner>
   <summary>
@@ -87566,6 +87656,14 @@
   </summary>
 </histogram>
 
+<histogram name="WinJumplist.NotificationTimeInterval" units="ms">
+  <owner>chengx@chromium.org</owner>
+  <summary>
+    The time interval between two adjacent update notifications if it's less
+    than 3500 ms.
+  </summary>
+</histogram>
+
 <histogram name="WinJumplist.OnFaviconDataAvailableDuration" units="ms">
   <obsolete>
     Obsolete 06/22/2017 as it's no longer needed.
@@ -96576,6 +96674,7 @@
   <suffix name="OfflinePageMetadata" label="OfflinePageMetadata"/>
   <suffix name="Passwords" label="Passwords"/>
   <suffix name="Precache" label="Precache"/>
+  <suffix name="PrefetchStore" label="PrefetchStore"/>
   <suffix name="Predictor" label="Predictor"/>
   <suffix name="PreviewsOptOut" label="PreviewsOptOut"/>
   <suffix name="Quota" label="Quota"/>
diff --git a/ui/accessibility/platform/ax_platform_node_mac.mm b/ui/accessibility/platform/ax_platform_node_mac.mm
index 2b09d34d..2eb4dba0 100644
--- a/ui/accessibility/platform/ax_platform_node_mac.mm
+++ b/ui/accessibility/platform/ax_platform_node_mac.mm
@@ -311,9 +311,6 @@
 // NSAccessibility informal protocol implementation.
 
 - (BOOL)accessibilityIsIgnored {
-  if (!node_)
-    return YES;
-
   return [[self AXRole] isEqualToString:NSAccessibilityUnknownRole] ||
          node_->GetData().HasState(ui::AX_STATE_INVISIBLE);
 }
@@ -333,13 +330,10 @@
 }
 
 - (id)accessibilityFocusedUIElement {
-  return node_ ? node_->GetDelegate()->GetFocus() : nil;
+  return node_->GetDelegate()->GetFocus();
 }
 
 - (NSArray*)accessibilityActionNames {
-  if (!node_)
-    return @[];
-
   base::scoped_nsobject<NSMutableArray> axActions(
       [[NSMutableArray alloc] init]);
 
@@ -361,11 +355,7 @@
 }
 
 - (void)accessibilityPerformAction:(NSString*)action {
-  // Actions are performed asynchronously, so it's always possible for an object
-  // to change its mind after previously reporting an action as available.
-  if (![[self accessibilityActionNames] containsObject:action])
-    return;
-
+  DCHECK([[self accessibilityActionNames] containsObject:action]);
   ui::AXActionData data;
   if ([action isEqualToString:NSAccessibilityShowMenuAction] &&
       AlsoUseShowMenuActionForDefaultAction(node_->GetData())) {
@@ -388,9 +378,6 @@
 }
 
 - (NSArray*)accessibilityAttributeNames {
-  if (!node_)
-    return @[];
-
   // These attributes are required on all accessibility objects.
   NSArray* const kAllRoleAttributes = @[
     NSAccessibilityChildrenAttribute,
@@ -460,7 +447,7 @@
 
 - (NSArray*)accessibilityParameterizedAttributeNames {
   if (!node_)
-    return @[];
+    return nil;
 
   static NSArray* const kSelectableTextAttributes = [@[
     NSAccessibilityLineForIndexParameterizedAttribute,
@@ -485,9 +472,6 @@
 }
 
 - (BOOL)accessibilityIsAttributeSettable:(NSString*)attributeName {
-  if (!node_)
-    return NO;
-
   if (node_->GetData().HasState(ui::AX_STATE_DISABLED))
     return NO;
 
@@ -525,9 +509,6 @@
 }
 
 - (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute {
-  if (!node_)
-    return;
-
   ui::AXActionData data;
 
   // Check for attributes first. Only the |data.action| should be set here - any
@@ -566,9 +547,6 @@
 }
 
 - (id)accessibilityAttributeValue:(NSString*)attribute {
-  if (!node_)
-    return nil;  // Return nil when detached. Even for AXRole.
-
   SEL selector = NSSelectorFromString(attribute);
   if ([self respondsToSelector:selector])
     return [self performSelector:selector];
@@ -577,9 +555,6 @@
 
 - (id)accessibilityAttributeValue:(NSString*)attribute
                      forParameter:(id)parameter {
-  if (!node_)
-    return nil;
-
   SEL selector = NSSelectorFromString([attribute stringByAppendingString:@":"]);
   if ([self respondsToSelector:selector])
     return [self performSelector:selector withObject:parameter];
@@ -592,7 +567,6 @@
 - (NSString*)AXRole {
   if (!node_)
     return nil;
-
   return [[self class] nativeRoleFromAXRole:node_->GetData().role];
 }
 
@@ -660,8 +634,7 @@
 
 - (NSArray*)AXChildren {
   if (!node_)
-    return @[];
-
+    return nil;
   int count = node_->GetChildCount();
   NSMutableArray* children = [NSMutableArray arrayWithCapacity:count];
   for (int i = 0; i < count; ++i)
diff --git a/ui/app_list/BUILD.gn b/ui/app_list/BUILD.gn
index 979cde4..5ff7cec 100644
--- a/ui/app_list/BUILD.gn
+++ b/ui/app_list/BUILD.gn
@@ -76,6 +76,7 @@
     "//base/third_party/dynamic_annotations",
     "//components/keyed_service/core",
     "//components/sync",
+    "//components/wallpaper",
     "//skia",
     "//third_party/icu",
     "//ui/accessibility",
diff --git a/ui/app_list/search_box_model.cc b/ui/app_list/search_box_model.cc
index 877d677..790d9fab 100644
--- a/ui/app_list/search_box_model.cc
+++ b/ui/app_list/search_box_model.cc
@@ -81,6 +81,16 @@
     observer.Update();
 }
 
+void SearchBoxModel::SetWallpaperProminentColors(
+    const std::vector<SkColor>& colors) {
+  if (wallpaper_prominent_colors_ == colors)
+    return;
+
+  wallpaper_prominent_colors_ = colors;
+  for (auto& observer : observers_)
+    observer.WallpaperProminentColorsChanged();
+}
+
 void SearchBoxModel::AddObserver(SearchBoxModelObserver* observer) {
   observers_.AddObserver(observer);
 }
diff --git a/ui/app_list/search_box_model.h b/ui/app_list/search_box_model.h
index fe53744..36b27781 100644
--- a/ui/app_list/search_box_model.h
+++ b/ui/app_list/search_box_model.h
@@ -74,6 +74,12 @@
   const base::string16& text() const { return text_; }
   bool is_voice_query() const { return is_voice_query_; }
 
+  // Sets/gets the wallpaper prominent colors.
+  void SetWallpaperProminentColors(const std::vector<SkColor>& colors);
+  const std::vector<SkColor>& wallpaper_prominent_colors() const {
+    return wallpaper_prominent_colors_;
+  }
+
   void AddObserver(SearchBoxModelObserver* observer);
   void RemoveObserver(SearchBoxModelObserver* observer);
 
@@ -84,6 +90,7 @@
   gfx::SelectionModel selection_model_;
   base::string16 text_;
   bool is_voice_query_ = false;
+  std::vector<SkColor> wallpaper_prominent_colors_;
 
   base::ObserverList<SearchBoxModelObserver> observers_;
 
diff --git a/ui/app_list/search_box_model_observer.h b/ui/app_list/search_box_model_observer.h
index 9dfd2e8..090f87a 100644
--- a/ui/app_list/search_box_model_observer.h
+++ b/ui/app_list/search_box_model_observer.h
@@ -24,6 +24,9 @@
   // Invoked when text or voice search flag is changed.
   virtual void Update() = 0;
 
+  // Invoked when wallpaper prominent colors are changed.
+  virtual void WallpaperProminentColorsChanged() = 0;
+
  protected:
   virtual ~SearchBoxModelObserver() {}
 };
diff --git a/ui/app_list/views/search_box_view.cc b/ui/app_list/views/search_box_view.cc
index 48a22b3..45f30f1 100644
--- a/ui/app_list/views/search_box_view.cc
+++ b/ui/app_list/views/search_box_view.cc
@@ -9,6 +9,7 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "build/build_config.h"
+#include "components/wallpaper/wallpaper_color_profile.h"
 #include "ui/app_list/app_list_constants.h"
 #include "ui/app_list/app_list_features.h"
 #include "ui/app_list/app_list_model.h"
@@ -26,6 +27,7 @@
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/events/event.h"
 #include "ui/gfx/canvas.h"
+#include "ui/gfx/color_utils.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/gfx/shadow_value.h"
@@ -40,6 +42,8 @@
 #include "ui/views/shadow_border.h"
 #include "ui/views/widget/widget.h"
 
+using wallpaper::ColorProfileType;
+
 namespace app_list {
 
 namespace {
@@ -62,14 +66,17 @@
 constexpr SkColor kDefaultSearchboxColor =
     SkColorSetARGBMacro(0xDE, 0x00, 0x00, 0x00);
 
+constexpr int kLightVibrantBlendAlpha = 0xB3;
+
 // A background that paints a solid white rounded rect with a thin grey border.
 class SearchBoxBackground : public views::Background {
  public:
-  SearchBoxBackground()
+  explicit SearchBoxBackground(SkColor color)
       : background_border_corner_radius_(
             features::IsFullscreenAppListEnabled()
                 ? kBackgroundBorderCornerRadiusFullscreen
-                : kBackgroundBorderCornerRadius) {}
+                : kBackgroundBorderCornerRadius),
+        color_(color) {}
   ~SearchBoxBackground() override {}
 
  private:
@@ -79,11 +86,12 @@
 
     cc::PaintFlags flags;
     flags.setAntiAlias(true);
-    flags.setColor(kSearchBoxBackgroundDefault);
+    flags.setColor(color_);
     canvas->DrawRoundRect(bounds, background_border_corner_radius_, flags);
   }
 
   const int background_border_corner_radius_;
+  const SkColor color_;
 
   DISALLOW_COPY_AND_ASSIGN(SearchBoxBackground);
 };
@@ -156,7 +164,8 @@
   AddChildView(content_container_);
 
   SetShadow(GetShadowForZHeight(2));
-  content_container_->SetBackground(base::MakeUnique<SearchBoxBackground>());
+  content_container_->SetBackground(
+      base::MakeUnique<SearchBoxBackground>(kSearchBoxBackgroundDefault));
 
   views::BoxLayout* layout = new views::BoxLayout(
       views::BoxLayout::kHorizontal, gfx::Insets(0, kPadding),
@@ -216,6 +225,7 @@
   model_->search_box()->AddObserver(this);
   SpeechRecognitionButtonPropChanged();
   HintTextChanged();
+  WallpaperProminentColorsChanged();
 }
 
 bool SearchBoxView::HasSearch() const {
@@ -498,6 +508,46 @@
   NotifyQueryChanged();
 }
 
+void SearchBoxView::WallpaperProminentColorsChanged() {
+  if (!is_fullscreen_app_list_enabled_)
+    return;
+
+  const std::vector<SkColor> prominent_colors =
+      model_->search_box()->wallpaper_prominent_colors();
+  if (prominent_colors.empty())
+    return;
+
+  DCHECK_EQ(static_cast<size_t>(ColorProfileType::NUM_OF_COLOR_PROFILES),
+            prominent_colors.size());
+  const SkColor dark_muted =
+      prominent_colors[static_cast<int>(ColorProfileType::DARK_MUTED)];
+  const bool dark_muted_available = SK_ColorTRANSPARENT != dark_muted;
+  google_icon_->SetImage(gfx::CreateVectorIcon(
+      kIcGoogleBlackIcon, kGoogleIconSize,
+      dark_muted_available ? dark_muted : kDefaultSearchboxColor));
+  speech_button_->SetImage(
+      views::Button::STATE_NORMAL,
+      gfx::CreateVectorIcon(
+          kIcMicBlackIcon, kMicIconSize,
+          dark_muted_available ? dark_muted : kDefaultSearchboxColor));
+  search_box_->set_placeholder_text_color(
+      dark_muted_available ? dark_muted : kDefaultSearchboxColor);
+
+  const SkColor light_vibrant =
+      prominent_colors[static_cast<int>(ColorProfileType::LIGHT_VIBRANT)];
+  const SkColor light_vibrant_mixed = color_utils::AlphaBlend(
+      SK_ColorWHITE, light_vibrant, kLightVibrantBlendAlpha);
+  const bool light_vibrant_available = SK_ColorTRANSPARENT != light_vibrant;
+  content_container_->SetBackground(base::MakeUnique<SearchBoxBackground>(
+      light_vibrant_available ? light_vibrant_mixed
+                              : kSearchBoxBackgroundDefault));
+  search_box_->SetBackgroundColor(light_vibrant_available
+                                      ? light_vibrant_mixed
+                                      : kSearchBoxBackgroundDefault);
+
+  SchedulePaint();
+}
+
 void SearchBoxView::OnSpeechRecognitionStateChanged(
     SpeechRecognitionState new_state) {
   SpeechRecognitionButtonPropChanged();
diff --git a/ui/app_list/views/search_box_view.h b/ui/app_list/views/search_box_view.h
index d2cee93..a8e8486 100644
--- a/ui/app_list/views/search_box_view.h
+++ b/ui/app_list/views/search_box_view.h
@@ -110,6 +110,7 @@
   void HintTextChanged() override;
   void SelectionModelChanged() override;
   void Update() override;
+  void WallpaperProminentColorsChanged() override;
 
   // Overridden from SpeechUIModelObserver:
   void OnSpeechRecognitionStateChanged(
diff --git a/ui/base/BUILD.gn b/ui/base/BUILD.gn
index 710731ee..4441ecb 100644
--- a/ui/base/BUILD.gn
+++ b/ui/base/BUILD.gn
@@ -6,6 +6,7 @@
 import("//build/config/compiler/compiler.gni")
 import("//build/config/sanitizers/sanitizers.gni")
 import("//build/config/ui.gni")
+import("//build/util/branding.gni")
 import("//testing/test.gni")
 import("//ui/base/ui_features.gni")
 import("//ui/ozone/ozone.gni")
@@ -1014,6 +1015,7 @@
       "//ui/resources:ui_test_pak_bundle_data",
     ]
     info_plist = "test/framework-Info.plist"
+    extra_substitutions = [ "CHROMIUM_BUNDLE_ID=$chrome_mac_bundle_id" ]
     output_name = "ui_unittests Framework"
   }
 }
diff --git a/ui/base/test/framework-Info.plist b/ui/base/test/framework-Info.plist
index f3b7fc725b..3d749a5 100644
--- a/ui/base/test/framework-Info.plist
+++ b/ui/base/test/framework-Info.plist
@@ -7,7 +7,7 @@
 	<key>CFBundleExecutable</key>
 	<string>${EXECUTABLE_NAME}</string>
 	<key>CFBundleIdentifier</key>
-	<string>${CHROMIUM_BUNDLE_ID}.framework</string>
+	<string>${CHROMIUM_BUNDLE_ID}.ui_unittests.framework</string>
 	<key>CFBundleInfoDictionaryVersion</key>
 	<string>6.0</string>
 	<key>CFBundlePackageType</key>
diff --git a/ui/views/widget/native_widget_mac_accessibility_unittest.mm b/ui/views/widget/native_widget_mac_accessibility_unittest.mm
index 7988061f..d195839 100644
--- a/ui/views/widget/native_widget_mac_accessibility_unittest.mm
+++ b/ui/views/widget/native_widget_mac_accessibility_unittest.mm
@@ -140,86 +140,6 @@
 
 }  // namespace
 
-// Test that all methods in the NSAccessibility informal protocol can be called
-// on a retained accessibility object after the source view is deleted.
-TEST_F(NativeWidgetMacAccessibilityTest, Lifetime) {
-  Textfield* view = AddChildTextfield(widget()->GetContentsView()->size());
-  base::scoped_nsobject<NSObject> ax_node(view->GetNativeViewAccessible(),
-                                          base::scoped_policy::RETAIN);
-
-  NSString* kAttribute = NSAccessibilityValueAttribute;
-  NSString* kParamAttribute =
-      NSAccessibilityStringForRangeParameterizedAttribute;
-  NSString* kAction = NSAccessibilityShowMenuAction;
-
-  EXPECT_TRUE(
-      [[ax_node accessibilityAttributeNames] containsObject:kAttribute]);
-  EXPECT_NSEQ(kTestStringValue,
-              [ax_node accessibilityAttributeValue:kAttribute]);
-  EXPECT_TRUE([ax_node accessibilityIsAttributeSettable:kAttribute]);
-  EXPECT_TRUE([[ax_node accessibilityActionNames] containsObject:kAction]);
-  EXPECT_FALSE([ax_node accessibilityIsIgnored]);
-
-  // Not implemented, but be sure to update this test if it ever is.
-  EXPECT_FALSE(
-      [ax_node respondsToSelector:@selector(accessibilityActionDescription:)]);
-
-  EXPECT_TRUE([[ax_node accessibilityParameterizedAttributeNames]
-      containsObject:kParamAttribute]);
-  NSValue* range = [NSValue valueWithRange:NSMakeRange(0, kTestStringLength)];
-  EXPECT_NSEQ(
-      kTestStringValue,
-      [ax_node accessibilityAttributeValue:kParamAttribute forParameter:range]);
-
-  // The following is also "not implemented", but the informal protocol category
-  // provides a default implementation.
-  EXPECT_EQ(NSNotFound, [ax_node accessibilityIndexOfChild:nil]);
-
-  // The only usually available array attribute is AXChildren, so go up a level
-  // to the Widget to test that a bit. The default implementation just gets the
-  // attribute normally and returns its size (if it's an array).
-  NSString* kChildren = NSAccessibilityChildrenAttribute;
-  base::scoped_nsobject<NSObject> ax_parent(
-      [ax_node accessibilityAttributeValue:NSAccessibilityParentAttribute],
-      base::scoped_policy::RETAIN);
-  EXPECT_EQ(1u, [ax_parent accessibilityArrayAttributeCount:kChildren]);
-  EXPECT_EQ(
-      ax_node.get(),
-      [ax_parent accessibilityArrayAttributeValues:kChildren index:0
-                                          maxCount:1][0]);
-
-  // If it is not an array, the default implementation throws an exception, so
-  // it's impossible to test these methods further on |ax_node|, apart from the
-  // following, before deleting the view.
-  EXPECT_EQ(0u, [ax_node accessibilityArrayAttributeCount:kChildren]);
-
-  delete view;
-
-  EXPECT_TRUE(
-      [ax_node respondsToSelector:@selector(accessibilityAttributeNames)]);
-  EXPECT_EQ(@[], [ax_node accessibilityAttributeNames]);
-  EXPECT_EQ(nil, [ax_node accessibilityAttributeValue:kAttribute]);
-  EXPECT_FALSE([ax_node accessibilityIsAttributeSettable:kAttribute]);
-  [ax_node accessibilitySetValue:kTestStringValue forAttribute:kAttribute];
-
-  EXPECT_EQ(@[], [ax_node accessibilityActionNames]);
-  [ax_node accessibilityPerformAction:kAction];
-
-  EXPECT_TRUE([ax_node accessibilityIsIgnored]);
-  EXPECT_EQ(nil, [ax_node accessibilityHitTest:NSZeroPoint]);
-  EXPECT_EQ(nil, [ax_node accessibilityFocusedUIElement]);
-
-  EXPECT_EQ(@[], [ax_node accessibilityParameterizedAttributeNames]);
-  EXPECT_NSEQ(nil, [ax_node accessibilityAttributeValue:kParamAttribute
-                                           forParameter:range]);
-
-  // Test the attributes with default implementations provided.
-  EXPECT_EQ(NSNotFound, [ax_node accessibilityIndexOfChild:nil]);
-
-  // The Widget is currently still around, but the child should be gone.
-  EXPECT_EQ(0u, [ax_parent accessibilityArrayAttributeCount:kChildren]);
-}
-
 // Check that potentially keyboard-focusable elements are always leaf nodes.
 TEST_F(NativeWidgetMacAccessibilityTest, FocusableElementsAreLeafNodes) {
   // LabelButtons will have a label inside the button. The label should be