diff --git a/BUILD.gn b/BUILD.gn
index e2df773a..6eefc1b 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -101,6 +101,7 @@
       "//chrome",
       "//chrome/installer/zucchini:zucchini",
       "//chrome/installer/zucchini:zucchini_unittests",
+      "//chrome/profiling:unit_tests",
       "//chrome/test:browser_tests",
       "//chrome/test:interactive_ui_tests",
       "//chrome/test:sync_integration_tests",
diff --git a/DEPS b/DEPS
index 2ebfe06..351485ff 100644
--- a/DEPS
+++ b/DEPS
@@ -40,7 +40,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '69fd008199989c5a5a96f992dcaa4089b63f490f',
+  'skia_revision': '5eb8fc585e9b3c9ccc82b0921986e1020ddaff23',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # 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': '656df4e2fcc877c34809f6018ab30ba5ddd0d623',
+  'catapult_revision': 'bd05965e45572ae06d1a7139759a85a3d9a1a27c',
   # 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/android_webview/renderer/aw_content_renderer_client.cc b/android_webview/renderer/aw_content_renderer_client.cc
index 027a22c..9e1df3b44 100644
--- a/android_webview/renderer/aw_content_renderer_client.cc
+++ b/android_webview/renderer/aw_content_renderer_client.cc
@@ -24,8 +24,6 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
-#include "components/autofill/content/renderer/autofill_agent.h"
-#include "components/autofill/content/renderer/password_autofill_agent.h"
 #include "components/printing/renderer/print_render_frame_helper.h"
 #include "components/safe_browsing/renderer/renderer_url_loader_throttle.h"
 #include "components/safe_browsing/renderer/websocket_sb_handshake_throttle.h"
@@ -178,15 +176,6 @@
         parent_frame->GetRoutingID(), render_frame->GetRoutingID()));
   }
 
-  registry_ = base::MakeUnique<service_manager::BinderRegistry>();
-
-  // TODO(sgurun) do not create a password autofill agent (change
-  // autofill agent to store a weakptr).
-  autofill::PasswordAutofillAgent* password_autofill_agent =
-      new autofill::PasswordAutofillAgent(render_frame, registry_.get());
-  new autofill::AutofillAgent(render_frame, password_autofill_agent, nullptr,
-                              registry_.get());
-
 #if BUILDFLAG(ENABLE_SPELLCHECK)
   new SpellCheckProvider(render_frame, spellcheck_.get());
 #endif
diff --git a/android_webview/renderer/aw_content_renderer_client.h b/android_webview/renderer/aw_content_renderer_client.h
index fa815b28..e4ff745 100644
--- a/android_webview/renderer/aw_content_renderer_client.h
+++ b/android_webview/renderer/aw_content_renderer_client.h
@@ -15,7 +15,6 @@
 #include "components/spellcheck/spellcheck_build_features.h"
 #include "components/web_restrictions/interfaces/web_restrictions.mojom.h"
 #include "content/public/renderer/content_renderer_client.h"
-#include "services/service_manager/public/cpp/binder_registry.h"
 
 #if BUILDFLAG(ENABLE_SPELLCHECK)
 class SpellCheck;
@@ -81,8 +80,6 @@
   std::unique_ptr<SpellCheck> spellcheck_;
 #endif
 
-  std::unique_ptr<service_manager::BinderRegistry> registry_;
-
   DISALLOW_COPY_AND_ASSIGN(AwContentRendererClient);
 };
 
diff --git a/android_webview/renderer/aw_render_frame_ext.cc b/android_webview/renderer/aw_render_frame_ext.cc
index 284399f..860e420 100644
--- a/android_webview/renderer/aw_render_frame_ext.cc
+++ b/android_webview/renderer/aw_render_frame_ext.cc
@@ -2,10 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "android_webview/renderer/aw_render_frame_ext.h"
+
 #include "android_webview/common/aw_hit_test_data.h"
 #include "android_webview/common/render_view_messages.h"
-#include "android_webview/renderer/aw_render_frame_ext.h"
 #include "base/strings/utf_string_conversions.h"
+#include "components/autofill/content/renderer/autofill_agent.h"
+#include "components/autofill/content/renderer/password_autofill_agent.h"
 #include "content/public/renderer/document_state.h"
 #include "content/public/renderer/render_frame.h"
 #include "content/public/renderer/render_view.h"
@@ -137,11 +140,25 @@
 
 AwRenderFrameExt::AwRenderFrameExt(content::RenderFrame* render_frame)
     : content::RenderFrameObserver(render_frame) {
+  registry_ = base::MakeUnique<service_manager::BinderRegistry>();
+
+  // TODO(sgurun) do not create a password autofill agent (change
+  // autofill agent to store a weakptr).
+  autofill::PasswordAutofillAgent* password_autofill_agent =
+      new autofill::PasswordAutofillAgent(render_frame, registry_.get());
+  new autofill::AutofillAgent(render_frame, password_autofill_agent, nullptr,
+                              registry_.get());
 }
 
 AwRenderFrameExt::~AwRenderFrameExt() {
 }
 
+void AwRenderFrameExt::OnInterfaceRequestForFrame(
+    const std::string& interface_name,
+    mojo::ScopedMessagePipeHandle* interface_pipe) {
+  registry_->TryBindInterface(interface_name, interface_pipe);
+}
+
 void AwRenderFrameExt::DidCommitProvisionalLoad(
     bool is_new_navigation,
     bool is_same_document_navigation) {
diff --git a/android_webview/renderer/aw_render_frame_ext.h b/android_webview/renderer/aw_render_frame_ext.h
index 9fd50194..55d4b38 100644
--- a/android_webview/renderer/aw_render_frame_ext.h
+++ b/android_webview/renderer/aw_render_frame_ext.h
@@ -7,6 +7,8 @@
 
 #include "base/macros.h"
 #include "content/public/renderer/render_frame_observer.h"
+#include "services/service_manager/public/cpp/binder_registry.h"
+#include "third_party/skia/include/core/SkColor.h"
 #include "ui/gfx/geometry/point_f.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/geometry/size_f.h"
@@ -31,6 +33,9 @@
   ~AwRenderFrameExt() override;
 
   // RenderFrameObserver:
+  void OnInterfaceRequestForFrame(
+      const std::string& interface_name,
+      mojo::ScopedMessagePipeHandle* interface_pipe) override;
   void DidCommitProvisionalLoad(bool is_new_navigation,
                                 bool is_same_document_navigation) override;
 
@@ -57,11 +62,11 @@
 
   url::Origin last_origin_;
 
+  std::unique_ptr<service_manager::BinderRegistry> registry_;
+
   DISALLOW_COPY_AND_ASSIGN(AwRenderFrameExt);
 };
 
 }  // namespace android_webview
 
 #endif  // ANDROID_WEBVIEW_RENDERER_AW_RENDER_FRAME_EXT_H_
-
-
diff --git a/ash/system/network/network_icon.cc b/ash/system/network/network_icon.cc
index 36d0b0d..91f0f437 100644
--- a/ash/system/network/network_icon.cc
+++ b/ash/system/network/network_icon.cc
@@ -884,8 +884,6 @@
   if (!mobile_networks.empty())
     return 0;
 
-  // TODO(lesliewatkins): Only return this message when Tether is uninitialized
-  // due to no Bluetooth (dependent on codereview.chromium.org/2969493002/).
   if (handler->GetTechnologyState(NetworkTypePattern::Tether()) ==
       NetworkStateHandler::TECHNOLOGY_UNINITIALIZED) {
     s_uninitialized_msg = IDS_ASH_STATUS_TRAY_ENABLE_BLUETOOTH;
diff --git a/ash/system/network/network_list.cc b/ash/system/network/network_list.cc
index 147e7aa..a0418c8c 100644
--- a/ash/system/network/network_list.cc
+++ b/ash/system/network/network_list.cc
@@ -118,6 +118,11 @@
     toggle_->SetIsOn(enabled, true);
   }
 
+  void SetToggleVisible(bool visible) {
+    toggle_->SetVisible(visible);
+    toggle_->set_accepts_events(visible);
+  }
+
  protected:
   // This is called before the toggle button is added to give subclasses an
   // opportunity to add more buttons before the toggle button. Subclasses can
@@ -460,17 +465,6 @@
       UpdateNetworkChildren(NetworkInfo::Type::UNKNOWN, index);
   index += new_guids->size();
 
-  if (handler->IsTechnologyAvailable(NetworkTypePattern::Cellular()) ||
-      handler->IsTechnologyAvailable(NetworkTypePattern::Tether())) {
-    bool is_enabled =
-        handler->IsTechnologyEnabled(NetworkTypePattern::Cellular()) ||
-        handler->IsTechnologyEnabled(NetworkTypePattern::Tether());
-
-    index = UpdateSectionHeaderRow(NetworkTypePattern::Cellular(), is_enabled,
-                                   index, &mobile_header_view_,
-                                   &mobile_separator_view_);
-  }
-
   // Cellular initializing.
   int cellular_message_id = network_icon::GetMobileUninitializedMsg();
   if (!cellular_message_id &&
@@ -478,6 +472,23 @@
       !handler->FirstNetworkByType(NetworkTypePattern::Mobile())) {
     cellular_message_id = IDS_ASH_STATUS_TRAY_NO_MOBILE_NETWORKS;
   }
+
+  if (handler->IsTechnologyAvailable(NetworkTypePattern::Cellular()) ||
+      handler->IsTechnologyAvailable(NetworkTypePattern::Tether())) {
+    bool is_enabled =
+        handler->IsTechnologyEnabled(NetworkTypePattern::Cellular()) ||
+        handler->IsTechnologyEnabled(NetworkTypePattern::Tether());
+
+    // The toggle is not visible in the case when Bluetooth must be
+    // enabled in order to enable Tether.
+    bool toggle_is_visible =
+        cellular_message_id != IDS_ASH_STATUS_TRAY_ENABLE_BLUETOOTH;
+
+    index = UpdateSectionHeaderRow(
+        NetworkTypePattern::Cellular(), is_enabled, toggle_is_visible, index,
+        &mobile_header_view_, &mobile_separator_view_);
+  }
+
   UpdateInfoLabel(cellular_message_id, index, &no_mobile_networks_view_);
   if (cellular_message_id)
     ++index;
@@ -490,8 +501,8 @@
 
   index = UpdateSectionHeaderRow(
       NetworkTypePattern::WiFi(),
-      handler->IsTechnologyEnabled(NetworkTypePattern::WiFi()), index,
-      &wifi_header_view_, &wifi_separator_view_);
+      handler->IsTechnologyEnabled(NetworkTypePattern::WiFi()),
+      true /* show_toggle */, index, &wifi_header_view_, &wifi_separator_view_);
 
   // "Wifi Enabled / Disabled".
   int wifi_message_id = 0;
@@ -662,6 +673,7 @@
 
 int NetworkListView::UpdateSectionHeaderRow(NetworkTypePattern pattern,
                                             bool enabled,
+                                            bool show_toggle,
                                             int child_index,
                                             SectionHeaderRowView** view,
                                             views::Separator** separator_view) {
@@ -687,6 +699,7 @@
   }
 
   (*view)->SetIsOn(enabled);
+  (*view)->SetToggleVisible(show_toggle);
   PlaceViewAtIndex(*view, child_index++);
   return child_index;
 }
diff --git a/ash/system/network/network_list.h b/ash/system/network/network_list.h
index 09b18a4..1ebe4c22 100644
--- a/ash/system/network/network_list.h
+++ b/ash/system/network/network_list.h
@@ -114,6 +114,7 @@
   // be inserted, i.e., the index directly after the last inserted child.
   int UpdateSectionHeaderRow(chromeos::NetworkTypePattern pattern,
                              bool enabled,
+                             bool show_toggle,
                              int child_index,
                              SectionHeaderRowView** view,
                              views::Separator** separator_view);
diff --git a/base/metrics/histogram.cc b/base/metrics/histogram.cc
index 19891b8b..7780df528 100644
--- a/base/metrics/histogram.cc
+++ b/base/metrics/histogram.cc
@@ -545,21 +545,24 @@
 bool Histogram::ValidateHistogramContents(bool crash_if_invalid,
                                           int corrupted_count) const {
   enum Fields : int {
-    kBucketRangesField,
+    kUnloggedBucketRangesField,
     kUnloggedSamplesField,
     kLoggedSamplesField,
     kIdField,
     kHistogramNameField,
     kFlagsField,
+    kLoggedBucketRangesField,
   };
 
   uint32_t bad_fields = 0;
   if (!unlogged_samples_)
     bad_fields |= 1 << kUnloggedSamplesField;
-  else if (!bucket_ranges())
-    bad_fields |= 1 << kBucketRangesField;
+  else if (!unlogged_samples_->bucket_ranges())
+    bad_fields |= 1 << kUnloggedBucketRangesField;
   if (!logged_samples_)
     bad_fields |= 1 << kLoggedSamplesField;
+  else if (!logged_samples_->bucket_ranges())
+    bad_fields |= 1 << kLoggedBucketRangesField;
   else if (logged_samples_->id() == 0)
     bad_fields |= 1 << kIdField;
   else if (HashMetricName(histogram_name()) != logged_samples_->id())
diff --git a/base/threading/sequenced_worker_pool.cc b/base/threading/sequenced_worker_pool.cc
index e36999a..130fd19 100644
--- a/base/threading/sequenced_worker_pool.cc
+++ b/base/threading/sequenced_worker_pool.cc
@@ -366,8 +366,6 @@
 
   void Shutdown(int max_blocking_tasks_after_shutdown);
 
-  bool IsShutdownInProgress();
-
   // Runs the worker loop on the background thread.
   void ThreadLoop(Worker* this_worker);
 
@@ -967,11 +965,6 @@
 #endif
 }
 
-bool SequencedWorkerPool::Inner::IsShutdownInProgress() {
-    AutoLock lock(lock_);
-    return shutdown_called_;
-}
-
 void SequencedWorkerPool::Inner::ThreadLoop(Worker* this_worker) {
   DCHECK_EQ(AllPoolsState::USE_WORKER_POOL, g_all_pools_state);
   {
@@ -1623,10 +1616,6 @@
   inner_->Shutdown(max_new_blocking_tasks_after_shutdown);
 }
 
-bool SequencedWorkerPool::IsShutdownInProgress() {
-  return inner_->IsShutdownInProgress();
-}
-
 bool SequencedWorkerPool::IsRunningSequenceOnCurrentThread(
     SequenceToken sequence_token) const {
   return inner_->IsRunningSequenceOnCurrentThread(sequence_token);
diff --git a/base/threading/sequenced_worker_pool.h b/base/threading/sequenced_worker_pool.h
index d95d7f2..005b4867 100644
--- a/base/threading/sequenced_worker_pool.h
+++ b/base/threading/sequenced_worker_pool.h
@@ -365,12 +365,6 @@
   // Must be called from the same thread this object was constructed on.
   void Shutdown(int max_new_blocking_tasks_after_shutdown);
 
-  // Check if Shutdown was called for given threading pool. This method is used
-  // for aborting time consuming operation to avoid blocking shutdown.
-  //
-  // Can be called from any thread.
-  bool IsShutdownInProgress();
-
  protected:
   ~SequencedWorkerPool() override;
 
diff --git a/build/fuchsia/test_runner.py b/build/fuchsia/test_runner.py
index cc47f5dc9..0ba1b4c 100755
--- a/build/fuchsia/test_runner.py
+++ b/build/fuchsia/test_runner.py
@@ -183,6 +183,8 @@
   # that to align it properly after the frame index.
   addr2line_filtered = addr2line_output.strip().replace(
       '(inlined', ' ' * len(prefix) + '(inlined')
+  if '??' in addr2line_filtered:
+    addr2line_filtered = "%s+%s" % (os.path.basename(entry[1]), entry[2])
   return '%s%s' % (prefix, addr2line_filtered)
 
 
@@ -345,9 +347,10 @@
     line = qemu_popen.stdout.readline().strip()
     if not line:
       break
-    print line
     if 'SUCCESS: all tests passed.' in line:
       success = True
+
+    # Check for an end-of-backtrace marker.
     if bt_end_re.match(line):
       if bt_entries:
         print '----- start symbolized stack'
@@ -355,18 +358,24 @@
           print processed
         print '----- end symbolized stack'
       bt_entries = []
-    else:
-      # Try to parse this as a Fuchsia system backtrace.
-      m = bt_with_offset_re.match(line)
-      if m:
-        bt_entries.append((m.group(1), args.test_name, m.group(4)))
-        continue
+      continue
 
-      # Try to parse the line as an in-process backtrace entry.
-      m = in_process_re.match(line)
-      if m:
-        bt_entries.append((m.group(1), args.test_name, m.group(2)))
-        continue
+    # Try to parse this as a Fuchsia system backtrace.
+    m = bt_with_offset_re.match(line)
+    if m:
+      bt_entries.append((m.group(1), args.test_name, m.group(4)))
+      continue
+
+    # Try to parse the line as an in-process backtrace entry.
+    m = in_process_re.match(line)
+    if m:
+      bt_entries.append((m.group(1), args.test_name, m.group(2)))
+      continue
+
+    # Some other line, so print it. Back-traces should not be interleaved with
+    # other output, so while this may re-order lines we see, it should actually
+    # make things more readable.
+    print line
 
   qemu_popen.wait()
 
diff --git a/cc/paint/paint_canvas.h b/cc/paint/paint_canvas.h
index 57ee5a1..b21d0a6 100644
--- a/cc/paint/paint_canvas.h
+++ b/cc/paint/paint_canvas.h
@@ -115,10 +115,6 @@
   virtual void drawDRRect(const SkRRect& outer,
                           const SkRRect& inner,
                           const PaintFlags& flags) = 0;
-  virtual void drawCircle(SkScalar cx,
-                          SkScalar cy,
-                          SkScalar radius,
-                          const PaintFlags& flags) = 0;
   virtual void drawArc(const SkRect& oval,
                        SkScalar start_angle,
                        SkScalar sweep_angle,
diff --git a/cc/paint/paint_op_buffer.cc b/cc/paint/paint_op_buffer.cc
index 16175bb..3bc927a 100644
--- a/cc/paint/paint_op_buffer.cc
+++ b/cc/paint/paint_op_buffer.cc
@@ -183,7 +183,6 @@
   M(ClipRRectOp)      \
   M(ConcatOp)         \
   M(DrawArcOp)        \
-  M(DrawCircleOp)     \
   M(DrawColorOp)      \
   M(DrawDRRectOp)     \
   M(DrawImageOp)      \
@@ -354,8 +353,6 @@
       return "Concat";
     case PaintOpType::DrawArc:
       return "DrawArc";
-    case PaintOpType::DrawCircle:
-      return "DrawCircle";
     case PaintOpType::DrawColor:
       return "DrawColor";
     case PaintOpType::DrawDRRect:
@@ -480,19 +477,6 @@
   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,
@@ -816,26 +800,6 @@
   return op;
 }
 
-PaintOp* DrawCircleOp::Deserialize(const void* input,
-                                   size_t input_size,
-                                   void* output,
-                                   size_t output_size) {
-  DCHECK_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->IsValid()) {
-    op->~DrawCircleOp();
-    return nullptr;
-  }
-  UpdateTypeAndSkip(op);
-  return op;
-}
-
 PaintOp* DrawColorOp::Deserialize(const void* input,
                                   size_t input_size,
                                   void* output,
@@ -1189,14 +1153,6 @@
                   paint);
 }
 
-void DrawCircleOp::RasterWithFlags(const DrawCircleOp* op,
-                                   const PaintFlags* flags,
-                                   SkCanvas* canvas,
-                                   const PlaybackParams& params) {
-  SkPaint paint = flags->ToSkPaint();
-  canvas->drawCircle(op->cx, op->cy, op->radius, paint);
-}
-
 void DrawColorOp::Raster(const DrawColorOp* op,
                          SkCanvas* canvas,
                          const PlaybackParams& params) {
@@ -1486,14 +1442,6 @@
       rect->sort();
       return true;
     }
-    case PaintOpType::DrawCircle: {
-      auto* circle_op = static_cast<const DrawCircleOp*>(op);
-      *rect = SkRect::MakeXYWH(circle_op->cx - circle_op->radius,
-                               circle_op->cy - circle_op->radius,
-                               2 * circle_op->radius, 2 * circle_op->radius);
-      rect->sort();
-      return true;
-    }
     case PaintOpType::DrawColor:
       return false;
     case PaintOpType::DrawDRRect: {
diff --git a/cc/paint/paint_op_buffer.h b/cc/paint/paint_op_buffer.h
index fb3a773..4e545d8 100644
--- a/cc/paint/paint_op_buffer.h
+++ b/cc/paint/paint_op_buffer.h
@@ -63,7 +63,6 @@
   ClipRRect,
   Concat,
   DrawArc,
-  DrawCircle,
   DrawColor,
   DrawDRRect,
   DrawImage,
@@ -367,30 +366,6 @@
   DrawArcOp() = default;
 };
 
-class CC_PAINT_EXPORT DrawCircleOp final : public PaintOpWithFlags {
- public:
-  static constexpr PaintOpType kType = PaintOpType::DrawCircle;
-  static constexpr bool kIsDrawOp = true;
-  DrawCircleOp(SkScalar cx,
-               SkScalar cy,
-               SkScalar radius,
-               const PaintFlags& flags)
-      : PaintOpWithFlags(flags), cx(cx), cy(cy), radius(radius) {}
-  static void RasterWithFlags(const DrawCircleOp* op,
-                              const PaintFlags* flags,
-                              SkCanvas* canvas,
-                              const PlaybackParams& params);
-  bool IsValid() const { return flags.IsValid(); }
-  HAS_SERIALIZATION_FUNCTIONS();
-
-  SkScalar cx;
-  SkScalar cy;
-  SkScalar radius;
-
- private:
-  DrawCircleOp() = default;
-};
-
 class CC_PAINT_EXPORT DrawColorOp final : public PaintOp {
  public:
   static constexpr PaintOpType kType = PaintOpType::DrawColor;
diff --git a/cc/paint/paint_op_buffer_unittest.cc b/cc/paint/paint_op_buffer_unittest.cc
index 8a91197..2c4ead41 100644
--- a/cc/paint/paint_op_buffer_unittest.cc
+++ b/cc/paint/paint_op_buffer_unittest.cc
@@ -1663,14 +1663,6 @@
   }
 }
 
-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));
@@ -1896,16 +1888,6 @@
   EXPECT_EQ(original->use_center, written->use_center);
 }
 
-void CompareDrawCircleOp(const DrawCircleOp* original,
-                         const DrawCircleOp* written) {
-  EXPECT_TRUE(original->IsValid());
-  EXPECT_TRUE(written->IsValid());
-  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_TRUE(original->IsValid());
@@ -2124,9 +2106,6 @@
       case PaintOpType::DrawArc:
         PushDrawArcOps(&buffer_);
         break;
-      case PaintOpType::DrawCircle:
-        PushDrawCircleOps(&buffer_);
-        break;
       case PaintOpType::DrawColor:
         PushDrawColorOps(&buffer_);
         break;
@@ -2228,10 +2207,6 @@
         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));
@@ -2705,27 +2680,6 @@
   }
 }
 
-TEST(PaintOpBufferTest, BoundingRect_DrawCircleOp) {
-  PaintOpBuffer buffer;
-  PaintFlags flags;
-  buffer.push<DrawCircleOp>(0.f, 0.f, 5.f, flags);
-  buffer.push<DrawCircleOp>(-1.f, 4.f, 44.f, flags);
-  buffer.push<DrawCircleOp>(-99.f, -32.f, 100.f, flags);
-
-  SkRect rect;
-  for (auto* base_op : PaintOpBuffer::Iterator(&buffer)) {
-    auto* op = static_cast<DrawCircleOp*>(base_op);
-
-    SkScalar dimension = 2 * op->radius;
-    SkScalar x = op->cx - op->radius;
-    SkScalar y = op->cy - op->radius;
-    SkRect circle_rect = SkRect::MakeXYWH(x, y, dimension, dimension);
-
-    ASSERT_TRUE(PaintOp::GetBounds(op, &rect));
-    EXPECT_EQ(rect, circle_rect.makeSorted());
-  }
-}
-
 TEST(PaintOpBufferTest, BoundingRect_DrawImageOp) {
   PaintOpBuffer buffer;
   PushDrawImageOps(&buffer);
diff --git a/cc/paint/record_paint_canvas.cc b/cc/paint/record_paint_canvas.cc
index aaa02a9e..8b43504 100644
--- a/cc/paint/record_paint_canvas.cc
+++ b/cc/paint/record_paint_canvas.cc
@@ -241,13 +241,6 @@
   list_->push<DrawDRRectOp>(outer, inner, flags);
 }
 
-void RecordPaintCanvas::drawCircle(SkScalar cx,
-                                   SkScalar cy,
-                                   SkScalar radius,
-                                   const PaintFlags& flags) {
-  list_->push<DrawCircleOp>(cx, cy, radius, flags);
-}
-
 void RecordPaintCanvas::drawArc(const SkRect& oval,
                                 SkScalar start_angle,
                                 SkScalar sweep_angle,
diff --git a/cc/paint/record_paint_canvas.h b/cc/paint/record_paint_canvas.h
index de10644..6d72986 100644
--- a/cc/paint/record_paint_canvas.h
+++ b/cc/paint/record_paint_canvas.h
@@ -74,10 +74,6 @@
   void drawDRRect(const SkRRect& outer,
                   const SkRRect& inner,
                   const PaintFlags& flags) override;
-  void drawCircle(SkScalar cx,
-                  SkScalar cy,
-                  SkScalar radius,
-                  const PaintFlags& flags) override;
   void drawArc(const SkRect& oval,
                SkScalar start_angle,
                SkScalar sweep_angle,
diff --git a/cc/paint/skia_paint_canvas.cc b/cc/paint/skia_paint_canvas.cc
index 967ea16..2f1904f 100644
--- a/cc/paint/skia_paint_canvas.cc
+++ b/cc/paint/skia_paint_canvas.cc
@@ -203,14 +203,6 @@
   canvas_->drawDRRect(outer, inner, paint);
 }
 
-void SkiaPaintCanvas::drawCircle(SkScalar cx,
-                                 SkScalar cy,
-                                 SkScalar radius,
-                                 const PaintFlags& flags) {
-  SkPaint paint = flags.ToSkPaint();
-  canvas_->drawCircle(cx, cy, radius, paint);
-}
-
 void SkiaPaintCanvas::drawArc(const SkRect& oval,
                               SkScalar start_angle,
                               SkScalar sweep_angle,
diff --git a/cc/paint/skia_paint_canvas.h b/cc/paint/skia_paint_canvas.h
index 6ce174b..2777f1e 100644
--- a/cc/paint/skia_paint_canvas.h
+++ b/cc/paint/skia_paint_canvas.h
@@ -82,10 +82,6 @@
   void drawDRRect(const SkRRect& outer,
                   const SkRRect& inner,
                   const PaintFlags& flags) override;
-  void drawCircle(SkScalar cx,
-                  SkScalar cy,
-                  SkScalar radius,
-                  const PaintFlags& flags) override;
   void drawArc(const SkRect& oval,
                SkScalar start_angle,
                SkScalar sweep_angle,
diff --git a/cc/paint/solid_color_analyzer.cc b/cc/paint/solid_color_analyzer.cc
index 7b556e0..510f73b 100644
--- a/cc/paint/solid_color_analyzer.cc
+++ b/cc/paint/solid_color_analyzer.cc
@@ -198,7 +198,6 @@
 
       // Any of the following ops result in non solid content.
       case PaintOpType::DrawArc:
-      case PaintOpType::DrawCircle:
       case PaintOpType::DrawDRRect:
       case PaintOpType::DrawImage:
       case PaintOpType::DrawImageRect:
diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc
index 78865db..10b3be3 100644
--- a/cc/trees/layer_tree_host.cc
+++ b/cc/trees/layer_tree_host.cc
@@ -967,7 +967,11 @@
   SetNeedsCommit();
 }
 
-void LayerTreeHost::SetViewportSize(const gfx::Size& device_viewport_size) {
+void LayerTreeHost::SetViewportSize(
+    const gfx::Size& device_viewport_size,
+    const viz::LocalSurfaceId& local_surface_id) {
+  if (settings_.enable_surface_synchronization)
+    SetLocalSurfaceId(local_surface_id);
   if (device_viewport_size_ == device_viewport_size)
     return;
 
diff --git a/cc/trees/layer_tree_host.h b/cc/trees/layer_tree_host.h
index a68f671..2b5f0aca 100644
--- a/cc/trees/layer_tree_host.h
+++ b/cc/trees/layer_tree_host.h
@@ -285,7 +285,9 @@
     return event_listener_properties_[static_cast<size_t>(event_class)];
   }
 
-  void SetViewportSize(const gfx::Size& device_viewport_size);
+  void SetViewportSize(
+      const gfx::Size& device_viewport_size,
+      const viz::LocalSurfaceId& local_surface_id = viz::LocalSurfaceId());
   gfx::Size device_viewport_size() const { return device_viewport_size_; }
 
   void SetBrowserControlsHeight(float top_height,
diff --git a/cc/trees/layer_tree_host_pixeltest_masks.cc b/cc/trees/layer_tree_host_pixeltest_masks.cc
index efb1d16..ee78966 100644
--- a/cc/trees/layer_tree_host_pixeltest_masks.cc
+++ b/cc/trees/layer_tree_host_pixeltest_masks.cc
@@ -247,10 +247,13 @@
     PaintFlags flags;
     flags.setStyle(PaintFlags::kFill_Style);
     flags.setColor(SK_ColorWHITE);
-    display_list->push<DrawCircleOp>(bounds_.width() / 2.f,
-                                     bounds_.height() / 2.f,
-                                     bounds_.width() / 4.f, flags);
-
+    float radius = bounds_.width() / 4.f;
+    float circle_x = bounds_.width() / 2.f;
+    float circle_y = bounds_.height() / 2.f;
+    display_list->push<DrawOvalOp>(
+        SkRect::MakeLTRB(circle_x - radius, circle_y - radius,
+                         circle_x + radius, circle_y + radius),
+        flags);
     display_list->push<RestoreOp>();
     display_list->EndPaintOfUnpaired(PaintableRegion());
     display_list->Finalize();
diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc
index 81e6527..3270fca 100644
--- a/cc/trees/layer_tree_host_unittest.cc
+++ b/cc/trees/layer_tree_host_unittest.cc
@@ -3010,21 +3010,23 @@
   }
 
   void WillBeginMainFrame() override {
-    EXPECT_TRUE(allow_commits_);
+    EXPECT_TRUE(IsCommitAllowed());
     num_send_begin_main_frame_++;
     EndTest();
   }
 
-  void AllowCommits() {
-    allow_commits_ = true;
-    layer_tree_host()->SetDeferCommits(false);
-  }
-
   void AfterTest() override {
     EXPECT_GE(num_will_begin_impl_frame_, 5);
     EXPECT_EQ(1, num_send_begin_main_frame_);
   }
 
+  virtual void AllowCommits() {
+    allow_commits_ = true;
+    layer_tree_host()->SetDeferCommits(false);
+  }
+
+  virtual bool IsCommitAllowed() const { return allow_commits_; }
+
  private:
   bool allow_commits_ = false;
   int num_will_begin_impl_frame_ = 0;
@@ -3033,6 +3035,37 @@
 
 SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestDeferCommits);
 
+// This verifies that changing the size of a LayerTreeHost without providing a
+// LocalSurfaceId defers commits.
+class LayerTreeHostInvalidLocalSurfaceIdDefersCommit
+    : public LayerTreeHostTestDeferCommits {
+ public:
+  LayerTreeHostInvalidLocalSurfaceIdDefersCommit() = default;
+  void InitializeSettings(LayerTreeSettings* settings) override {
+    // With surface synchronization turned on, commits are deferred until a
+    // LocalSurfaceId has been assigned. The set up code sets the size of the
+    // LayerTreeHost (using SetViewportSize()), without providing a
+    // LocalSurfaceId. So, commits should be deferred until we set an id later
+    // during the test (in AllowCommits() override below).
+    settings->enable_surface_synchronization = true;
+  }
+
+  void BeginTest() override { PostSetNeedsCommitToMainThread(); }
+
+  void AllowCommits() override {
+    local_surface_id_ = allocator_.GenerateId();
+    PostSetLocalSurfaceIdToMainThread(local_surface_id_);
+  }
+
+  bool IsCommitAllowed() const override { return local_surface_id_.is_valid(); }
+
+ private:
+  viz::LocalSurfaceIdAllocator allocator_;
+  viz::LocalSurfaceId local_surface_id_;
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostInvalidLocalSurfaceIdDefersCommit);
+
 // This verifies that we can abort a commit inside the main frame, and
 // we don't leave any weird states around if we never allow the commit
 // to happen.
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn
index 48efa8e..e56a06b 100644
--- a/chrome/BUILD.gn
+++ b/chrome/BUILD.gn
@@ -1551,7 +1551,6 @@
     "//chrome/browser/resources:invalidations_resources",
     "//chrome/browser/resources:net_internals_resources",
     "//chrome/browser/resources:password_manager_internals_resources",
-    "//chrome/browser/resources:policy_resources",
     "//chrome/browser/resources:quota_internals_resources",
     "//chrome/browser/resources:task_scheduler_internals_resources",
     "//chrome/browser/resources:translate_internals_resources",
diff --git a/chrome/VERSION b/chrome/VERSION
index ba0767f..e1da9206 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=62
 MINOR=0
-BUILD=3181
+BUILD=3182
 PATCH=0
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 0844a81..f22f77e 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1963,10 +1963,6 @@
          switches::kEnableMessageCenterNewStyleNotification,
          switches::kDisableMessageCenterNewStyleNotification)},
 #endif  // !OS_ANDROID
-    {"enable-md-policy-page",
-     flag_descriptions::kEnableMaterialDesignPolicyPageName,
-     flag_descriptions::kEnableMaterialDesignPolicyPageDescription, kOsDesktop,
-     SINGLE_VALUE_TYPE(switches::kEnableMaterialDesignPolicyPage)},
 #if defined(OS_CHROMEOS)
     {"memory-pressure-thresholds",
      flag_descriptions::kMemoryPressureThresholdName,
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 8144ff5..82a50e4 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -384,6 +384,7 @@
 #endif
 
 #if BUILDFLAG(ENABLE_OOP_HEAP_PROFILING)
+#include "chrome/browser/profiling_host/chrome_browser_main_extra_parts_profiling.h"
 #include "chrome/browser/profiling_host/profiling_process_host.h"
 #include "chrome/common/profiling/constants.mojom.h"
 #endif
@@ -910,6 +911,10 @@
   main_parts->AddParts(new ChromeBrowserMainExtraPartsX11());
 #endif
 
+#if BUILDFLAG(ENABLE_OOP_HEAP_PROFILING)
+  main_parts->AddParts(new ChromeBrowserMainExtraPartsProfiling);
+#endif
+
   chrome::AddMetricsExtraParts(main_parts);
 
   return main_parts;
@@ -1567,17 +1572,11 @@
       *base::CommandLine::ForCurrentProcess();
 
   static const char* const kCommonSwitchNames[] = {
-#if BUILDFLAG(ENABLE_OOP_HEAP_PROFILING)
-    switches::kMemlogPipe,
-#endif
     switches::kUserAgent,
     switches::kUserDataDir,  // Make logs go to the right file.
   };
   command_line->CopySwitchesFrom(browser_command_line, kCommonSwitchNames,
                                  arraysize(kCommonSwitchNames));
-#if BUILDFLAG(ENABLE_OOP_HEAP_PROFILING)
-  profiling::ProfilingProcessHost::AddSwitchesToChildCmdLine(command_line);
-#endif
 
   static const char* const kDinosaurEasterEggSwitches[] = {
       error_page::switches::kDisableDinosaurEasterEgg,
diff --git a/chrome/browser/chrome_content_gpu_manifest_overlay.json b/chrome/browser/chrome_content_gpu_manifest_overlay.json
index a0e50fa2..b382337 100644
--- a/chrome/browser/chrome_content_gpu_manifest_overlay.json
+++ b/chrome/browser/chrome_content_gpu_manifest_overlay.json
@@ -8,12 +8,8 @@
           "arc::mojom::VideoDecodeClient",
           "arc::mojom::VideoEncodeAccelerator",
           "arc::mojom::VideoEncodeClient",
-          "chrome::mojom::ResourceUsageReporter"
-        ]
-      },
-      "requires": {
-        "profiling": [
-          "memlog"
+          "chrome::mojom::ResourceUsageReporter",
+          "profiling::mojom::MemlogClient"
         ]
       }
     }
diff --git a/chrome/browser/chrome_content_renderer_manifest_overlay.json b/chrome/browser/chrome_content_renderer_manifest_overlay.json
index eee27d1..9179941 100644
--- a/chrome/browser/chrome_content_renderer_manifest_overlay.json
+++ b/chrome/browser/chrome_content_renderer_manifest_overlay.json
@@ -6,12 +6,8 @@
         "browser": [
           "chrome::mojom::ResourceUsageReporter",
           "chrome::mojom::SearchBouncer",
-          "spellcheck::mojom::SpellChecker"
-        ]
-      },
-      "requires": {
-        "profiling": [
-          "memlog"
+          "spellcheck::mojom::SpellChecker",
+          "profiling::mojom::MemlogClient"
         ]
       }
     },
diff --git a/chrome/browser/chrome_content_utility_manifest_overlay.json b/chrome/browser/chrome_content_utility_manifest_overlay.json
index df19464..07008f85 100644
--- a/chrome/browser/chrome_content_utility_manifest_overlay.json
+++ b/chrome/browser/chrome_content_utility_manifest_overlay.json
@@ -18,6 +18,7 @@
           "extensions::mojom::WiFiCredentialsGetter",
           "net::interfaces::ProxyResolverFactory",
           "payments::mojom::PaymentManifestParser",
+          "profiling::mojom::MemlogClient",
           "safe_json::mojom::SafeJsonParser"
         ]
       }
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index d42e4550..08e5b09 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -1871,6 +1871,7 @@
     "policy/user_cloud_policy_store_chromeos_unittest.cc",
     "power/cpu_data_collector_unittest.cc",
     "power/extension_event_observer_unittest.cc",
+    "power/peripheral_battery_observer_unittest.cc",
     "power/power_data_collector_unittest.cc",
     "power/power_prefs_unittest.cc",
     "power/renderer_freezer_unittest.cc",
diff --git a/chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner.cc b/chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner.cc
index 23829b795..fc3c9bc 100644
--- a/chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner.cc
+++ b/chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner.cc
@@ -223,6 +223,27 @@
                                           callback);
 }
 
+void ArcFileSystemOperationRunner::GetRecentDocuments(
+    const std::string& authority,
+    const std::string& root_id,
+    const GetRecentDocumentsCallback& callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  if (should_defer_) {
+    deferred_operations_.emplace_back(base::Bind(
+        &ArcFileSystemOperationRunner::GetRecentDocuments,
+        weak_ptr_factory_.GetWeakPtr(), authority, root_id, callback));
+    return;
+  }
+  auto* file_system_instance = ARC_GET_INSTANCE_FOR_METHOD(
+      arc_bridge_service_->file_system(), GetRecentDocuments);
+  if (!file_system_instance) {
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::BindOnce(callback, base::nullopt));
+    return;
+  }
+  file_system_instance->GetRecentDocuments(authority, root_id, callback);
+}
+
 void ArcFileSystemOperationRunner::AddWatcher(
     const std::string& authority,
     const std::string& document_id,
diff --git a/chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner.h b/chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner.h
index 004408c..eadf57f7 100644
--- a/chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner.h
+++ b/chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner.h
@@ -70,6 +70,8 @@
   using GetDocumentCallback = mojom::FileSystemInstance::GetDocumentCallback;
   using GetChildDocumentsCallback =
       mojom::FileSystemInstance::GetChildDocumentsCallback;
+  using GetRecentDocumentsCallback =
+      mojom::FileSystemInstance::GetRecentDocumentsCallback;
   using AddWatcherCallback = base::Callback<void(int64_t watcher_id)>;
   using RemoveWatcherCallback = base::Callback<void(bool success)>;
   using ChangeType = storage::WatcherManager::ChangeType;
@@ -121,6 +123,9 @@
   void GetChildDocuments(const std::string& authority,
                          const std::string& parent_document_id,
                          const GetChildDocumentsCallback& callback);
+  void GetRecentDocuments(const std::string& authority,
+                          const std::string& root_id,
+                          const GetRecentDocumentsCallback& callback);
   void AddWatcher(const std::string& authority,
                   const std::string& document_id,
                   const WatcherCallback& watcher_callback,
diff --git a/chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner_unittest.cc b/chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner_unittest.cc
index b47b88a1..830793f 100644
--- a/chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner_unittest.cc
+++ b/chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner_unittest.cc
@@ -92,6 +92,14 @@
               ++*counter;
             },
             counter));
+    runner_->GetRecentDocuments(
+        kAuthority, kDocumentId,
+        base::Bind(
+            [](int* counter,
+               base::Optional<std::vector<mojom::DocumentPtr>> documents) {
+              ++*counter;
+            },
+            counter));
     runner_->OpenFileToRead(
         GURL(kUrl),
         base::Bind([](int* counter, mojo::ScopedHandle handle) { ++*counter; },
@@ -121,7 +129,7 @@
   CallSetShouldDefer(false);
   CallAllFunctions(&counter);
   base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(7, counter);
+  EXPECT_EQ(8, counter);
 }
 
 TEST_F(ArcFileSystemOperationRunnerTest, DeferAndRun) {
@@ -133,7 +141,7 @@
 
   CallSetShouldDefer(false);
   base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(7, counter);
+  EXPECT_EQ(8, counter);
 }
 
 // TODO(nya,hidehiko): Check if we should keep this test.
@@ -157,7 +165,7 @@
   CallSetShouldDefer(false);
   CallAllFunctions(&counter);
   base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(7, counter);
+  EXPECT_EQ(8, counter);
 }
 
 }  // namespace arc
diff --git a/chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner_util.cc b/chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner_util.cc
index 7a2d583..ccb9f56 100644
--- a/chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner_util.cc
+++ b/chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner_util.cc
@@ -88,6 +88,20 @@
   runner->GetChildDocuments(authority, parent_document_id, callback);
 }
 
+void GetRecentDocumentsOnUIThread(const std::string& authority,
+                                  const std::string& root_id,
+                                  const GetRecentDocumentsCallback& callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  auto* runner = GetArcFileSystemOperationRunner();
+  if (!runner) {
+    DLOG(ERROR) << "ArcFileSystemOperationRunner unavailable. "
+                << "File system operations are dropped.";
+    callback.Run(base::nullopt);
+    return;
+  }
+  runner->GetRecentDocuments(authority, root_id, callback);
+}
+
 void AddWatcherOnUIThread(const std::string& authority,
                           const std::string& document_id,
                           const WatcherCallback& watcher_callback,
@@ -209,6 +223,19 @@
               callback)));
 }
 
+void GetRecentDocumentsOnIOThread(const std::string& authority,
+                                  const std::string& root_id,
+                                  const GetRecentDocumentsCallback& callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  BrowserThread::PostTask(
+      BrowserThread::UI, FROM_HERE,
+      base::BindOnce(
+          &GetRecentDocumentsOnUIThread, authority, root_id,
+          base::Bind(
+              &PostToIOThread<base::Optional<std::vector<mojom::DocumentPtr>>>,
+              callback)));
+}
+
 void AddWatcherOnIOThread(const std::string& authority,
                           const std::string& document_id,
                           const WatcherCallback& watcher_callback,
diff --git a/chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner_util.h b/chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner_util.h
index b21c3da..4268c2d5 100644
--- a/chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner_util.h
+++ b/chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner_util.h
@@ -22,6 +22,8 @@
 using GetDocumentCallback = ArcFileSystemOperationRunner::GetDocumentCallback;
 using GetChildDocumentsCallback =
     ArcFileSystemOperationRunner::GetChildDocumentsCallback;
+using GetRecentDocumentsCallback =
+    ArcFileSystemOperationRunner::GetRecentDocumentsCallback;
 using AddWatcherCallback = ArcFileSystemOperationRunner::AddWatcherCallback;
 using RemoveWatcherCallback =
     ArcFileSystemOperationRunner::RemoveWatcherCallback;
@@ -73,6 +75,9 @@
 void GetChildDocumentsOnIOThread(const std::string& authority,
                                  const std::string& parent_document_id,
                                  const GetChildDocumentsCallback& callback);
+void GetRecentDocumentsOnIOThread(const std::string& authority,
+                                  const std::string& root_id,
+                                  const GetRecentDocumentsCallback& callback);
 void AddWatcherOnIOThread(const std::string& authority,
                           const std::string& document_id,
                           const WatcherCallback& watcher_callback,
diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
index ba2c11e..a4aa225 100644
--- a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
+++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
@@ -107,6 +107,7 @@
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/logging_chrome.h"
 #include "chrome/common/pref_names.h"
+#include "chrome/grit/chromium_strings.h"
 #include "chromeos/audio/audio_devices_pref_handler_impl.h"
 #include "chromeos/audio/cras_audio_handler.h"
 #include "chromeos/cert_loader.h"
@@ -169,10 +170,12 @@
 #include "ui/base/ime/chromeos/ime_keyboard.h"
 #include "ui/base/ime/chromeos/input_method_manager.h"
 #include "ui/base/ime/chromeos/input_method_util.h"
+#include "ui/base/l10n/l10n_util.h"
 #include "ui/base/touch/touch_device.h"
 #include "ui/chromeos/events/event_rewriter_chromeos.h"
 #include "ui/chromeos/events/pref_names.h"
 #include "ui/events/event_utils.h"
+#include "ui/message_center/message_center.h"
 
 #if BUILDFLAG(ENABLE_RLZ)
 #include "components/rlz/rlz_tracker.h"
@@ -787,6 +790,11 @@
     VLOG(1) << "Relaunching browser for user: " << account_id.Serialize()
             << " with hash: " << user_id_hash;
   }
+
+  // Set product name ("Chrome OS" or "Chromium OS") to be used in context
+  // header of new-style notification.
+  message_center::MessageCenter::Get()->SetProductOSName(
+      l10n_util::GetStringUTF16(IDS_PRODUCT_OS_NAME));
 }
 
 class GuestLanguageSetCallbackData {
diff --git a/chrome/browser/chromeos/lock_screen_apps/app_manager_impl.cc b/chrome/browser/chromeos/lock_screen_apps/app_manager_impl.cc
index 62afd988..54b6f10 100644
--- a/chrome/browser/chromeos/lock_screen_apps/app_manager_impl.cc
+++ b/chrome/browser/chromeos/lock_screen_apps/app_manager_impl.cc
@@ -23,6 +23,7 @@
 #include "chrome/browser/extensions/extension_assets_manager.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/common/pref_names.h"
+#include "extensions/browser/extension_file_task_runner.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_system.h"
 #include "extensions/browser/install_flag.h"
@@ -315,7 +316,7 @@
       extensions::ExtensionSystem::Get(lock_screen_profile_)
           ->extension_service();
 
-  lock_screen_service->GetFileTaskRunner()->PostTask(
+  extensions::GetExtensionFileTaskRunner()->PostTask(
       FROM_HERE,
       base::Bind(
           &InstallExtensionCopy, lock_profile_app, app->path(),
diff --git a/chrome/browser/chromeos/lock_screen_apps/app_manager_impl_unittest.cc b/chrome/browser/chromeos/lock_screen_apps/app_manager_impl_unittest.cc
index eb0f234..22a1eb3 100644
--- a/chrome/browser/chromeos/lock_screen_apps/app_manager_impl_unittest.cc
+++ b/chrome/browser/chromeos/lock_screen_apps/app_manager_impl_unittest.cc
@@ -32,6 +32,7 @@
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "extensions/browser/event_router.h"
 #include "extensions/browser/event_router_factory.h"
+#include "extensions/browser/extension_file_task_runner.h"
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/common/api/app_runtime.h"
@@ -358,11 +359,8 @@
   // file runner task finish,
   void RunExtensionServiceTaskRunner(Profile* profile) {
     base::RunLoop run_loop;
-    extensions::ExtensionSystem::Get(profile)
-        ->extension_service()
-        ->GetFileTaskRunner()
-        ->PostTaskAndReply(FROM_HERE, base::Bind(&base::DoNothing),
-                           run_loop.QuitClosure());
+    extensions::GetExtensionFileTaskRunner()->PostTaskAndReply(
+        FROM_HERE, base::Bind(&base::DoNothing), run_loop.QuitClosure());
     run_loop.Run();
   }
 
diff --git a/chrome/browser/chromeos/login/users/default_user_image/default_user_images.cc b/chrome/browser/chromeos/login/users/default_user_image/default_user_images.cc
index c2c06cb..ff776ec0 100644
--- a/chrome/browser/chromeos/login/users/default_user_image/default_user_images.cc
+++ b/chrome/browser/chromeos/login/users/default_user_image/default_user_images.cc
@@ -81,6 +81,16 @@
     IDR_LOGIN_DEFAULT_USER_45,
     IDR_LOGIN_DEFAULT_USER_46,
     IDR_LOGIN_DEFAULT_USER_47,
+    IDR_LOGIN_DEFAULT_USER_48,
+    IDR_LOGIN_DEFAULT_USER_49,
+    IDR_LOGIN_DEFAULT_USER_50,
+    IDR_LOGIN_DEFAULT_USER_51,
+    IDR_LOGIN_DEFAULT_USER_52,
+    IDR_LOGIN_DEFAULT_USER_53,
+    IDR_LOGIN_DEFAULT_USER_54,
+    IDR_LOGIN_DEFAULT_USER_55,
+    IDR_LOGIN_DEFAULT_USER_56,
+    IDR_LOGIN_DEFAULT_USER_57,
 };
 
 const int kDefaultImagesCount = arraysize(kDefaultImageResourceIDs);
@@ -230,6 +240,16 @@
     IDS_LOGIN_DEFAULT_USER_DESC_45,
     IDS_LOGIN_DEFAULT_USER_DESC_46,
     IDS_LOGIN_DEFAULT_USER_DESC_47,
+    IDS_LOGIN_DEFAULT_USER_DESC_48,
+    IDS_LOGIN_DEFAULT_USER_DESC_49,
+    IDS_LOGIN_DEFAULT_USER_DESC_50,
+    IDS_LOGIN_DEFAULT_USER_DESC_51,
+    IDS_LOGIN_DEFAULT_USER_DESC_52,
+    IDS_LOGIN_DEFAULT_USER_DESC_53,
+    IDS_LOGIN_DEFAULT_USER_DESC_54,
+    IDS_LOGIN_DEFAULT_USER_DESC_55,
+    IDS_LOGIN_DEFAULT_USER_DESC_56,
+    IDS_LOGIN_DEFAULT_USER_DESC_57,
 };
 
 const int kDefaultImageDescriptionsMaxID = arraysize(kDefaultImageDescriptions);
diff --git a/chrome/browser/chromeos/power/peripheral_battery_observer.cc b/chrome/browser/chromeos/power/peripheral_battery_observer.cc
index 2d447b81..56a3611 100644
--- a/chrome/browser/chromeos/power/peripheral_battery_observer.cc
+++ b/chrome/browser/chromeos/power/peripheral_battery_observer.cc
@@ -16,10 +16,6 @@
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/notifications/notification.h"
-#include "chrome/browser/notifications/notification_ui_manager.h"
-#include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/grit/theme_resources.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "content/public/browser/browser_thread.h"
@@ -41,9 +37,9 @@
 // it to be in low battery condition.
 const int kLowBatteryLevel = 15;
 
-// Don't show 2 low battery notification within |kNotificationIntervalSec|
-// seconds.
-const int kNotificationIntervalSec = 60;
+// Don't show 2 low battery notification within |kNotificationInterval|.
+constexpr base::TimeDelta kNotificationInterval =
+    base::TimeDelta::FromSeconds(60);
 
 // TODO(sammiequon): Add a notification url to chrome://settings/stylus once
 // battery related information is shown there.
@@ -93,22 +89,40 @@
   return false;
 }
 
-class PeripheralBatteryNotificationDelegate : public NotificationDelegate {
- public:
-  explicit PeripheralBatteryNotificationDelegate(const std::string& id)
-      : id_(id) {}
-
-  // Overridden from NotificationDelegate:
-  std::string id() const override { return id_; }
-
- private:
-  ~PeripheralBatteryNotificationDelegate() override {}
-
-  const std::string id_;
-
-  DISALLOW_COPY_AND_ASSIGN(PeripheralBatteryNotificationDelegate);
+// Struct containing parameters for the notification which vary between the
+// stylus notifications and the non stylus notifications.
+struct NotificationParams {
+  std::string id;
+  base::string16 title;
+  base::string16 message;
+  int image_id;
+  std::string notifier_name;
+  GURL url;
 };
 
+NotificationParams GetNonStylusNotificationParams(const std::string& address,
+                                                  const std::string& name,
+                                                  int battery_level) {
+  return NotificationParams{
+      address,
+      base::ASCIIToUTF16(name),
+      l10n_util::GetStringFUTF16Int(
+          IDS_ASH_LOW_PERIPHERAL_BATTERY_NOTIFICATION_TEXT, battery_level),
+      IDR_NOTIFICATION_PERIPHERAL_BATTERY_LOW,
+      kNotifierId,
+      GURL(kNotificationOriginUrl)};
+}
+
+NotificationParams GetStylusNotificationParams() {
+  return NotificationParams{
+      PeripheralBatteryObserver::kStylusNotificationId,
+      l10n_util::GetStringUTF16(IDS_ASH_LOW_STYLUS_BATTERY_NOTIFICATION_TITLE),
+      l10n_util::GetStringUTF16(IDS_ASH_LOW_STYLUS_BATTERY_NOTIFICATION_BODY),
+      IDR_NOTIFICATION_STYLUS_BATTERY_LOW,
+      ash::system_notifier::kNotifierStylusBattery,
+      GURL()};
+}
+
 }  // namespace
 
 const char PeripheralBatteryObserver::kStylusNotificationId[] =
@@ -116,7 +130,6 @@
 
 PeripheralBatteryObserver::PeripheralBatteryObserver()
     : testing_clock_(NULL),
-      notification_profile_(NULL),
       weakptr_factory_(
           new base::WeakPtrFactory<PeripheralBatteryObserver>(this)) {
   DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver(this);
@@ -167,11 +180,13 @@
   //    below kLowBatteryLevel.
   // 2. The battery level is in record and it drops below kLowBatteryLevel.
   if (batteries_.find(address) == batteries_.end()) {
-    BatteryInfo battery{name, level, base::TimeTicks()};
+    BatteryInfo battery{name, level, base::TimeTicks(),
+                        IsStylusDevice(address, name)};
     if (level <= kLowBatteryLevel) {
-      if (PostNotification(address, battery))
+      if (PostNotification(address, battery)) {
         battery.last_notification_timestamp = testing_clock_ ?
             testing_clock_->NowTicks() : base::TimeTicks::Now();
+      }
     }
     batteries_[address] = battery;
   } else {
@@ -180,9 +195,10 @@
     int old_level = battery->level;
     battery->level = level;
     if (old_level > kLowBatteryLevel && level <= kLowBatteryLevel) {
-      if (PostNotification(address, *battery))
+      if (PostNotification(address, *battery)) {
         battery->last_notification_timestamp = testing_clock_ ?
             testing_clock_->NowTicks() : base::TimeTicks::Now();
+      }
     }
   }
 }
@@ -211,8 +227,8 @@
   std::map<std::string, BatteryInfo>::iterator it =
       batteries_.find(address_lowercase);
   if (it != batteries_.end()) {
-    batteries_.erase(it);
     CancelNotification(address_lowercase);
+    batteries_.erase(it);
   }
 }
 
@@ -223,71 +239,37 @@
   // oscillates around the threshold level.
   base::TimeTicks now = testing_clock_ ? testing_clock_->NowTicks() :
       base::TimeTicks::Now();
-  if (now - battery.last_notification_timestamp <
-      base::TimeDelta::FromSeconds(kNotificationIntervalSec))
+  if (now - battery.last_notification_timestamp < kNotificationInterval)
     return false;
 
-  // Stylus battery notifications have a different icon and message. They are
-  // also system notifications.
-  // TODO(sammiequon): Change non-stylus notifications to also be system
-  // notifications.
-  if (IsStylusDevice(address, battery.name)) {
-    auto notification = base::MakeUnique<message_center::Notification>(
-        message_center::NOTIFICATION_TYPE_SIMPLE, kStylusNotificationId,
-        l10n_util::GetStringUTF16(
-            IDS_ASH_LOW_STYLUS_BATTERY_NOTIFICATION_TITLE),
-        l10n_util::GetStringUTF16(IDS_ASH_LOW_STYLUS_BATTERY_NOTIFICATION_BODY),
-        ui::ResourceBundle::GetSharedInstance().GetImageNamed(
-            IDR_NOTIFICATION_STYLUS_BATTERY_LOW),
-        base::string16(), GURL(),
-        message_center::NotifierId(
-            message_center::NotifierId::SYSTEM_COMPONENT,
-            ash::system_notifier::kNotifierStylusBattery),
-        message_center::RichNotificationData(), nullptr);
-    notification->SetSystemPriority();
+  // Stylus battery notifications differ slightly.
+  NotificationParams params = battery.is_stylus
+                                  ? GetStylusNotificationParams()
+                                  : GetNonStylusNotificationParams(
+                                        address, battery.name, battery.level);
 
-    message_center::MessageCenter::Get()->AddNotification(
-        std::move(notification));
-    return true;
-  }
-
-  NotificationUIManager* notification_manager =
-      g_browser_process->notification_ui_manager();
-
-  base::string16 string_text = l10n_util::GetStringFUTF16Int(
-      IDS_ASH_LOW_PERIPHERAL_BATTERY_NOTIFICATION_TEXT,
-      battery.level);
-
-  Notification notification(
-      message_center::NOTIFICATION_TYPE_SIMPLE, base::UTF8ToUTF16(battery.name),
-      string_text, ui::ResourceBundle::GetSharedInstance().GetImageNamed(
-                       IDR_NOTIFICATION_PERIPHERAL_BATTERY_LOW),
+  auto notification = base::MakeUnique<message_center::Notification>(
+      message_center::NOTIFICATION_TYPE_SIMPLE, params.id, params.title,
+      params.message,
+      ui::ResourceBundle::GetSharedInstance().GetImageNamed(params.image_id),
+      base::string16(), params.url,
       message_center::NotifierId(message_center::NotifierId::SYSTEM_COMPONENT,
-                                 kNotifierId),
-      base::string16(), GURL(kNotificationOriginUrl), address,
-      message_center::RichNotificationData(),
-      new PeripheralBatteryNotificationDelegate(address));
+                                 params.notifier_name),
+      message_center::RichNotificationData(), nullptr);
+  notification->SetSystemPriority();
 
-  notification.set_priority(message_center::SYSTEM_PRIORITY);
-
-  notification_profile_ = ProfileManager::GetPrimaryUserProfile();
-  notification_manager->Add(notification, notification_profile_);
-
+  message_center::MessageCenter::Get()->AddNotification(
+      std::move(notification));
   return true;
 }
 
 void PeripheralBatteryObserver::CancelNotification(const std::string& address) {
   const auto it = batteries_.find(address);
-  if (it != batteries_.end() && IsStylusDevice(address, it->second.name)) {
+  if (it != batteries_.end()) {
+    std::string notification_id =
+        it->second.is_stylus ? kStylusNotificationId : address;
     message_center::MessageCenter::Get()->RemoveNotification(
-        kStylusNotificationId, false /* by_user */);
-    return;
-  }
-
-  // If last_used_profile_ is NULL then no notification has been posted yet.
-  if (notification_profile_) {
-    g_browser_process->notification_ui_manager()->CancelById(
-        address, NotificationUIManager::GetProfileID(notification_profile_));
+        notification_id, false /* by_user */);
   }
 }
 
diff --git a/chrome/browser/chromeos/power/peripheral_battery_observer.h b/chrome/browser/chromeos/power/peripheral_battery_observer.h
index c3d43bc..07b5558 100644
--- a/chrome/browser/chromeos/power/peripheral_battery_observer.h
+++ b/chrome/browser/chromeos/power/peripheral_battery_observer.h
@@ -15,8 +15,6 @@
 #include "chromeos/dbus/power_manager_client.h"
 #include "device/bluetooth/bluetooth_adapter.h"
 
-class Profile;
-
 namespace chromeos {
 
 class BluetoothDevice;
@@ -24,7 +22,8 @@
 
 // This observer listens for peripheral device battery status and shows
 // notifications for low battery conditions.
-// TODO(sammiequon): Investigate whether we can move this class to //ash.
+// TODO(sammiequon): Investigate whether we can move this class to
+// //ash/system/power.
 class PeripheralBatteryObserver : public PowerManagerClient::Observer,
                                   public device::BluetoothAdapter::Observer {
  public:
@@ -36,12 +35,12 @@
 
   void set_testing_clock(base::TickClock* clock) { testing_clock_ = clock; }
 
-  // PowerManagerClient::Observer implementation.
+  // PowerManagerClient::Observer:
   void PeripheralBatteryStatusReceived(const std::string& path,
                                        const std::string& name,
                                        int level) override;
 
-  // device::BluetoothAdapter::Observer implementation.
+  // device::BluetoothAdapter::Observer:
   void DeviceChanged(device::BluetoothAdapter* adapter,
                      device::BluetoothDevice* device) override;
   void DeviceRemoved(device::BluetoothAdapter* adapter,
@@ -59,6 +58,7 @@
     // Battery level within range [0, 100], and -1 for unknown level.
     int level = -1;
     base::TimeTicks last_notification_timestamp;
+    bool is_stylus = false;
   };
 
   void InitializeOnBluetoothReady(
@@ -83,9 +83,6 @@
   // Used only for helping test. Not owned and can be NULL.
   base::TickClock* testing_clock_;
 
-  // Record the profile used when adding message center notifications.
-  Profile* notification_profile_;
-
   std::unique_ptr<base::WeakPtrFactory<PeripheralBatteryObserver>>
       weakptr_factory_;
 
diff --git a/chrome/browser/chromeos/power/peripheral_battery_observer_browsertest.cc b/chrome/browser/chromeos/power/peripheral_battery_observer_browsertest.cc
deleted file mode 100644
index 992e0fa..0000000
--- a/chrome/browser/chromeos/power/peripheral_battery_observer_browsertest.cc
+++ /dev/null
@@ -1,220 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/chromeos/power/peripheral_battery_observer.h"
-
-#include "base/command_line.h"
-#include "base/macros.h"
-#include "base/message_loop/message_loop.h"
-#include "base/test/simple_test_tick_clock.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/notifications/notification_ui_manager.h"
-#include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/test/base/in_process_browser_test.h"
-#include "chromeos/dbus/dbus_thread_manager.h"
-#include "content/public/test/test_utils.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/events/devices/touchscreen_device.h"
-#include "ui/events/test/device_data_manager_test_api.h"
-#include "ui/message_center/message_center.h"
-#include "ui/message_center/notification.h"
-
-using ::testing::_;
-using ::testing::InSequence;
-using ::testing::Return;
-using ::testing::SaveArg;
-
-namespace {
-
-const char kTestBatteryPath[] = "/sys/class/power_supply/hid-AA:BB:CC-battery";
-const char kTestBatteryAddress[] = "cc:bb:aa";
-const char kTestDeviceName[] = "test device";
-
-}  // namespace
-
-namespace chromeos {
-
-class PeripheralBatteryObserverTest : public InProcessBrowserTest {
- public:
-  PeripheralBatteryObserverTest() {}
-  ~PeripheralBatteryObserverTest() override {}
-
-  void SetUp() override {
-    InProcessBrowserTest::SetUp();
-    chromeos::DBusThreadManager::Initialize();
-  }
-
-  void SetUpOnMainThread() override {
-    observer_.reset(new PeripheralBatteryObserver());
-  }
-
-  void TearDownOnMainThread() override { observer_.reset(); }
-
- protected:
-  std::unique_ptr<PeripheralBatteryObserver> observer_;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(PeripheralBatteryObserverTest);
-};
-
-IN_PROC_BROWSER_TEST_F(PeripheralBatteryObserverTest, Basic) {
-  base::SimpleTestTickClock clock;
-  observer_->set_testing_clock(&clock);
-
-  NotificationUIManager* notification_manager =
-      g_browser_process->notification_ui_manager();
-
-  // Level 50 at time 100, no low-battery notification.
-  clock.Advance(base::TimeDelta::FromSeconds(100));
-  observer_->PeripheralBatteryStatusReceived(kTestBatteryPath,
-                                             kTestDeviceName, 50);
-  EXPECT_EQ(observer_->batteries_.count(kTestBatteryAddress), 1u);
-
-  const PeripheralBatteryObserver::BatteryInfo& info =
-      observer_->batteries_[kTestBatteryAddress];
-
-  EXPECT_EQ(info.name, kTestDeviceName);
-  EXPECT_EQ(info.level, 50);
-  EXPECT_EQ(info.last_notification_timestamp, base::TimeTicks());
-  EXPECT_FALSE(notification_manager->FindById(
-                   kTestBatteryAddress,
-                   NotificationUIManager::GetProfileID(
-                       ProfileManager::GetPrimaryUserProfile())) != NULL);
-
-  // Level 5 at time 110, low-battery notification.
-  clock.Advance(base::TimeDelta::FromSeconds(10));
-  observer_->PeripheralBatteryStatusReceived(kTestBatteryPath,
-                                             kTestDeviceName, 5);
-  EXPECT_EQ(info.level, 5);
-  EXPECT_EQ(info.last_notification_timestamp, clock.NowTicks());
-  EXPECT_TRUE(notification_manager->FindById(
-                  kTestBatteryAddress,
-                  NotificationUIManager::GetProfileID(
-                      ProfileManager::GetPrimaryUserProfile())) != NULL);
-
-  // Verify that the low-battery notification for stylus does not show up.
-  EXPECT_TRUE(message_center::MessageCenter::Get()->FindVisibleNotificationById(
-                  PeripheralBatteryObserver::kStylusNotificationId) == nullptr);
-
-  // Level -1 at time 115, cancel previous notification
-  clock.Advance(base::TimeDelta::FromSeconds(5));
-  observer_->PeripheralBatteryStatusReceived(kTestBatteryPath,
-                                             kTestDeviceName, -1);
-  EXPECT_EQ(info.level, 5);
-  EXPECT_EQ(info.last_notification_timestamp,
-            clock.NowTicks() - base::TimeDelta::FromSeconds(5));
-  EXPECT_FALSE(notification_manager->FindById(
-                   kTestBatteryAddress,
-                   NotificationUIManager::GetProfileID(
-                       ProfileManager::GetPrimaryUserProfile())) != NULL);
-
-  // Level 50 at time 120, no low-battery notification.
-  clock.Advance(base::TimeDelta::FromSeconds(5));
-  observer_->PeripheralBatteryStatusReceived(kTestBatteryPath,
-                                             kTestDeviceName, 50);
-  EXPECT_EQ(info.level, 50);
-  EXPECT_EQ(info.last_notification_timestamp,
-            clock.NowTicks() - base::TimeDelta::FromSeconds(10));
-  EXPECT_FALSE(notification_manager->FindById(
-                   kTestBatteryAddress,
-                   NotificationUIManager::GetProfileID(
-                       ProfileManager::GetPrimaryUserProfile())) != NULL);
-
-  // Level 5 at time 130, no low-battery notification (throttling).
-  clock.Advance(base::TimeDelta::FromSeconds(10));
-  observer_->PeripheralBatteryStatusReceived(kTestBatteryPath,
-                                             kTestDeviceName, 5);
-  EXPECT_EQ(info.level, 5);
-  EXPECT_EQ(info.last_notification_timestamp,
-            clock.NowTicks() - base::TimeDelta::FromSeconds(20));
-  EXPECT_FALSE(notification_manager->FindById(
-                   kTestBatteryAddress,
-                   NotificationUIManager::GetProfileID(
-                       ProfileManager::GetPrimaryUserProfile())) != NULL);
-}
-
-IN_PROC_BROWSER_TEST_F(PeripheralBatteryObserverTest, InvalidBatteryInfo) {
-  observer_->PeripheralBatteryStatusReceived("invalid-path", kTestDeviceName,
-                                             10);
-  EXPECT_TRUE(observer_->batteries_.empty());
-
-  observer_->PeripheralBatteryStatusReceived(
-      "/sys/class/power_supply/hid-battery", kTestDeviceName, 10);
-  EXPECT_TRUE(observer_->batteries_.empty());
-
-  observer_->PeripheralBatteryStatusReceived(kTestBatteryPath,
-                                             kTestDeviceName, -2);
-  EXPECT_TRUE(observer_->batteries_.empty());
-
-  observer_->PeripheralBatteryStatusReceived(kTestBatteryPath,
-                                             kTestDeviceName, 101);
-  EXPECT_TRUE(observer_->batteries_.empty());
-
-  observer_->PeripheralBatteryStatusReceived(kTestBatteryPath,
-                                             kTestDeviceName, -1);
-  EXPECT_TRUE(observer_->batteries_.empty());
-}
-
-IN_PROC_BROWSER_TEST_F(PeripheralBatteryObserverTest, DeviceRemove) {
-  NotificationUIManager* notification_manager =
-      g_browser_process->notification_ui_manager();
-
-  observer_->PeripheralBatteryStatusReceived(kTestBatteryPath,
-                                             kTestDeviceName, 5);
-  EXPECT_EQ(observer_->batteries_.count(kTestBatteryAddress), 1u);
-  EXPECT_TRUE(notification_manager->FindById(
-                  kTestBatteryAddress,
-                  NotificationUIManager::GetProfileID(
-                      ProfileManager::GetPrimaryUserProfile())) != NULL);
-
-  observer_->RemoveBattery(kTestBatteryAddress);
-  EXPECT_FALSE(notification_manager->FindById(
-                   kTestBatteryAddress,
-                   NotificationUIManager::GetProfileID(
-                       ProfileManager::GetPrimaryUserProfile())) != NULL);
-}
-
-IN_PROC_BROWSER_TEST_F(PeripheralBatteryObserverTest, StylusNotification) {
-  const std::string kTestStylusName = "test_stylus";
-
-  // Add an external stylus to our test device manager.
-  ui::TouchscreenDevice stylus(0 /* id */, ui::INPUT_DEVICE_EXTERNAL,
-                               kTestStylusName, gfx::Size(),
-                               1 /* touch_points */);
-  stylus.sys_path = base::FilePath(kTestBatteryPath);
-  stylus.is_stylus = true;
-
-  ui::test::DeviceDataManagerTestAPI test_api;
-  test_api.SetTouchscreenDevices({stylus});
-
-  message_center::MessageCenter* message_center =
-      message_center::MessageCenter::Get();
-
-  // Verify that when the battery level is 50, no stylus low battery
-  // notification is shown.
-  observer_->PeripheralBatteryStatusReceived(kTestBatteryPath, kTestStylusName,
-                                             50);
-  EXPECT_TRUE(message_center->FindVisibleNotificationById(
-                  PeripheralBatteryObserver::kStylusNotificationId) == nullptr);
-
-  // Verify that when the battery level is 5, a stylus low battery notification
-  // is shown. Also check that a non stylus device low battery notification will
-  // not show up.
-  observer_->PeripheralBatteryStatusReceived(kTestBatteryPath, kTestStylusName,
-                                             5);
-  EXPECT_TRUE(message_center->FindVisibleNotificationById(
-                  PeripheralBatteryObserver::kStylusNotificationId) != nullptr);
-  EXPECT_TRUE(message_center->FindVisibleNotificationById(
-                  kTestBatteryAddress) == nullptr);
-
-  // Verify that when the battery level is -1, the previous stylus low battery
-  // notification is cancelled.
-  observer_->PeripheralBatteryStatusReceived(kTestBatteryPath, kTestStylusName,
-                                             -1);
-  EXPECT_TRUE(message_center->FindVisibleNotificationById(
-                  PeripheralBatteryObserver::kStylusNotificationId) == nullptr);
-}
-
-}  // namespace chromeos
diff --git a/chrome/browser/chromeos/power/peripheral_battery_observer_unittest.cc b/chrome/browser/chromeos/power/peripheral_battery_observer_unittest.cc
new file mode 100644
index 0000000..83e79463
--- /dev/null
+++ b/chrome/browser/chromeos/power/peripheral_battery_observer_unittest.cc
@@ -0,0 +1,193 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/power/peripheral_battery_observer.h"
+
+#include "ash/test/ash_test_base.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/test/simple_test_tick_clock.h"
+#include "ui/events/devices/touchscreen_device.h"
+#include "ui/events/test/device_data_manager_test_api.h"
+#include "ui/message_center/message_center.h"
+#include "ui/message_center/notification.h"
+
+namespace {
+
+const char kTestBatteryPath[] = "/sys/class/power_supply/hid-AA:BB:CC-battery";
+const char kTestBatteryAddress[] = "cc:bb:aa";
+const char kTestDeviceName[] = "test device";
+
+}  // namespace
+
+namespace chromeos {
+
+class PeripheralBatteryObserverTest : public ash::AshTestBase {
+ public:
+  PeripheralBatteryObserverTest() {}
+  ~PeripheralBatteryObserverTest() override {}
+
+  void SetUp() override {
+    ash::AshTestBase::SetUp();
+    observer_ = base::MakeUnique<PeripheralBatteryObserver>();
+  }
+
+  void TearDown() override {
+    observer_.reset();
+    ash::AshTestBase::TearDown();
+  }
+
+ protected:
+  std::unique_ptr<PeripheralBatteryObserver> observer_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(PeripheralBatteryObserverTest);
+};
+
+TEST_F(PeripheralBatteryObserverTest, Basic) {
+  base::SimpleTestTickClock clock;
+  observer_->set_testing_clock(&clock);
+
+  message_center::MessageCenter* message_center =
+      message_center::MessageCenter::Get();
+
+  // Level 50 at time 100, no low-battery notification.
+  clock.Advance(base::TimeDelta::FromSeconds(100));
+  observer_->PeripheralBatteryStatusReceived(kTestBatteryPath, kTestDeviceName,
+                                             50);
+  EXPECT_EQ(1u, observer_->batteries_.count(kTestBatteryAddress));
+
+  const PeripheralBatteryObserver::BatteryInfo& info =
+      observer_->batteries_[kTestBatteryAddress];
+
+  EXPECT_EQ(kTestDeviceName, info.name);
+  EXPECT_EQ(50, info.level);
+  EXPECT_EQ(base::TimeTicks(), info.last_notification_timestamp);
+  EXPECT_TRUE(message_center->FindVisibleNotificationById(
+                  kTestBatteryAddress) == nullptr);
+
+  // Level 5 at time 110, low-battery notification.
+  clock.Advance(base::TimeDelta::FromSeconds(10));
+  observer_->PeripheralBatteryStatusReceived(kTestBatteryPath, kTestDeviceName,
+                                             5);
+  EXPECT_EQ(5, info.level);
+  EXPECT_EQ(clock.NowTicks(), info.last_notification_timestamp);
+  EXPECT_TRUE(message_center->FindVisibleNotificationById(
+                  kTestBatteryAddress) != nullptr);
+
+  // Verify that the low-battery notification for stylus does not show up.
+  EXPECT_FALSE(message_center->FindVisibleNotificationById(
+                   PeripheralBatteryObserver::kStylusNotificationId) !=
+               nullptr);
+
+  // Level -1 at time 115, cancel previous notification
+  clock.Advance(base::TimeDelta::FromSeconds(5));
+  observer_->PeripheralBatteryStatusReceived(kTestBatteryPath, kTestDeviceName,
+                                             -1);
+  EXPECT_EQ(5, info.level);
+  EXPECT_EQ(clock.NowTicks() - base::TimeDelta::FromSeconds(5),
+            info.last_notification_timestamp);
+  EXPECT_TRUE(message_center->FindVisibleNotificationById(
+                  kTestBatteryAddress) == nullptr);
+
+  // Level 50 at time 120, no low-battery notification.
+  clock.Advance(base::TimeDelta::FromSeconds(5));
+  observer_->PeripheralBatteryStatusReceived(kTestBatteryPath, kTestDeviceName,
+                                             50);
+  EXPECT_EQ(50, info.level);
+  EXPECT_EQ(clock.NowTicks() - base::TimeDelta::FromSeconds(10),
+            info.last_notification_timestamp);
+  EXPECT_TRUE(message_center->FindVisibleNotificationById(
+                  kTestBatteryAddress) == nullptr);
+
+  // Level 5 at time 130, no low-battery notification (throttling).
+  clock.Advance(base::TimeDelta::FromSeconds(10));
+  observer_->PeripheralBatteryStatusReceived(kTestBatteryPath, kTestDeviceName,
+                                             5);
+  EXPECT_EQ(5, info.level);
+  EXPECT_EQ(clock.NowTicks() - base::TimeDelta::FromSeconds(20),
+            info.last_notification_timestamp);
+  EXPECT_TRUE(message_center->FindVisibleNotificationById(
+                  kTestBatteryAddress) == nullptr);
+}
+
+TEST_F(PeripheralBatteryObserverTest, InvalidBatteryInfo) {
+  observer_->PeripheralBatteryStatusReceived("invalid-path", kTestDeviceName,
+                                             10);
+  EXPECT_TRUE(observer_->batteries_.empty());
+
+  observer_->PeripheralBatteryStatusReceived(
+      "/sys/class/power_supply/hid-battery", kTestDeviceName, 10);
+  EXPECT_TRUE(observer_->batteries_.empty());
+
+  observer_->PeripheralBatteryStatusReceived(kTestBatteryPath, kTestDeviceName,
+                                             -2);
+  EXPECT_TRUE(observer_->batteries_.empty());
+
+  observer_->PeripheralBatteryStatusReceived(kTestBatteryPath, kTestDeviceName,
+                                             101);
+  EXPECT_TRUE(observer_->batteries_.empty());
+
+  observer_->PeripheralBatteryStatusReceived(kTestBatteryPath, kTestDeviceName,
+                                             -1);
+  EXPECT_TRUE(observer_->batteries_.empty());
+}
+
+TEST_F(PeripheralBatteryObserverTest, DeviceRemove) {
+  message_center::MessageCenter* message_center =
+      message_center::MessageCenter::Get();
+
+  observer_->PeripheralBatteryStatusReceived(kTestBatteryPath, kTestDeviceName,
+                                             5);
+  EXPECT_EQ(1u, observer_->batteries_.count(kTestBatteryAddress));
+  EXPECT_TRUE(message_center->FindVisibleNotificationById(
+                  kTestBatteryAddress) != nullptr);
+
+  observer_->RemoveBattery(kTestBatteryAddress);
+  EXPECT_TRUE(message_center->FindVisibleNotificationById(
+                  kTestBatteryAddress) == nullptr);
+}
+
+TEST_F(PeripheralBatteryObserverTest, StylusNotification) {
+  const std::string kTestStylusName = "test_stylus";
+
+  // Add an external stylus to our test device manager.
+  ui::TouchscreenDevice stylus(0 /* id */, ui::INPUT_DEVICE_EXTERNAL,
+                               kTestStylusName, gfx::Size(),
+                               1 /* touch_points */);
+  stylus.sys_path = base::FilePath(kTestBatteryPath);
+  stylus.is_stylus = true;
+
+  ui::test::DeviceDataManagerTestAPI test_api;
+  test_api.SetTouchscreenDevices({stylus});
+
+  message_center::MessageCenter* message_center =
+      message_center::MessageCenter::Get();
+
+  // Verify that when the battery level is 50, no stylus low battery
+  // notification is shown.
+  observer_->PeripheralBatteryStatusReceived(kTestBatteryPath, kTestStylusName,
+                                             50);
+  EXPECT_TRUE(message_center->FindVisibleNotificationById(
+                  PeripheralBatteryObserver::kStylusNotificationId) == nullptr);
+
+  // Verify that when the battery level is 5, a stylus low battery notification
+  // is shown. Also check that a non stylus device low battery notification will
+  // not show up.
+  observer_->PeripheralBatteryStatusReceived(kTestBatteryPath, kTestStylusName,
+                                             5);
+  EXPECT_TRUE(message_center->FindVisibleNotificationById(
+                  PeripheralBatteryObserver::kStylusNotificationId) != nullptr);
+  EXPECT_TRUE(message_center->FindVisibleNotificationById(
+                  kTestBatteryAddress) == nullptr);
+
+  // Verify that when the battery level is -1, the previous stylus low battery
+  // notification is cancelled.
+  observer_->PeripheralBatteryStatusReceived(kTestBatteryPath, kTestStylusName,
+                                             -1);
+  EXPECT_TRUE(message_center->FindVisibleNotificationById(
+                  PeripheralBatteryObserver::kStylusNotificationId) == nullptr);
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/extensions/crx_installer.cc b/chrome/browser/extensions/crx_installer.cc
index a90431d..5789cf8 100644
--- a/chrome/browser/extensions/crx_installer.cc
+++ b/chrome/browser/extensions/crx_installer.cc
@@ -41,6 +41,7 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/resource_dispatcher_host.h"
+#include "extensions/browser/extension_file_task_runner.h"
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_system.h"
@@ -132,9 +133,9 @@
       off_store_install_allow_reason_(OffStoreInstallDisallowed),
       did_handle_successfully_(true),
       error_on_unsupported_requirements_(false),
+      installer_task_runner_(GetExtensionFileTaskRunner()),
       update_from_settings_page_(false),
       install_flags_(kInstallFlagNone) {
-  installer_task_runner_ = service_weak->GetFileTaskRunner();
   if (!approval)
     return;
 
diff --git a/chrome/browser/extensions/extension_assets_manager_chromeos.cc b/chrome/browser/extensions/extension_assets_manager_chromeos.cc
index ee709fd..916d3514 100644
--- a/chrome/browser/extensions/extension_assets_manager_chromeos.cc
+++ b/chrome/browser/extensions/extension_assets_manager_chromeos.cc
@@ -20,7 +20,6 @@
 #include "base/values.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
-#include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chromeos/chromeos_switches.h"
 #include "components/prefs/pref_registry_simple.h"
@@ -28,8 +27,8 @@
 #include "components/prefs/scoped_user_pref_update.h"
 #include "components/user_manager/user_manager.h"
 #include "content/public/browser/browser_thread.h"
+#include "extensions/browser/extension_file_task_runner.h"
 #include "extensions/browser/extension_prefs.h"
-#include "extensions/browser/extension_system.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_urls.h"
 #include "extensions/common/file_util.h"
@@ -247,15 +246,6 @@
 }
 
 // static
-base::SequencedTaskRunner* ExtensionAssetsManagerChromeOS::GetFileTaskRunner(
-    Profile* profile) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  ExtensionService* extension_service =
-      ExtensionSystem::Get(profile)->extension_service();
-  return extension_service->GetFileTaskRunner();
-}
-
-// static
 bool ExtensionAssetsManagerChromeOS::CanShareAssets(
     const Extension* extension,
     const base::FilePath& unpacked_extension_root) {
@@ -298,13 +288,10 @@
       !user_manager->IsLoggedInAsUserWithGaiaAccount()) {
     // Don't cache anything in shared location for ephemeral user or special
     // user types.
-    ExtensionAssetsManagerChromeOS::GetFileTaskRunner(profile)->PostTask(
+    GetExtensionFileTaskRunner()->PostTask(
         FROM_HERE,
-        base::Bind(&ExtensionAssetsManagerChromeOS::InstallLocalExtension,
-                   id,
-                   version,
-                   unpacked_extension_root,
-                   local_install_dir,
+        base::Bind(&ExtensionAssetsManagerChromeOS::InstallLocalExtension, id,
+                   version, unpacked_extension_root, local_install_dir,
                    callback));
     return;
   }
@@ -335,9 +322,8 @@
       users->AppendString(user_id);
 
     // unpacked_extension_root will be deleted by CrxInstaller.
-    ExtensionAssetsManagerChromeOS::GetFileTaskRunner(profile)->PostTask(
-        FROM_HERE,
-        base::Bind(callback, base::FilePath(shared_path)));
+    GetExtensionFileTaskRunner()->PostTask(
+        FROM_HERE, base::Bind(callback, base::FilePath(shared_path)));
   } else {
     // Desired version is not found in shared location.
     ExtensionAssetsManagerHelper* helper =
@@ -345,12 +331,10 @@
     if (helper->RecordSharedInstall(id, version, unpacked_extension_root,
                                     local_install_dir, profile, callback)) {
       // There is no install in progress for given <id, version> so run install.
-      ExtensionAssetsManagerChromeOS::GetFileTaskRunner(profile)->PostTask(
+      GetExtensionFileTaskRunner()->PostTask(
           FROM_HERE,
           base::Bind(&ExtensionAssetsManagerChromeOS::InstallSharedExtension,
-                     id,
-                     version,
-                     unpacked_extension_root));
+                     id, version, unpacked_extension_root));
     }
   }
 }
@@ -386,14 +370,11 @@
     for (size_t i = 0; i < pending_installs.size(); i++) {
       ExtensionAssetsManagerHelper::PendingInstallInfo& info =
           pending_installs[i];
-      ExtensionAssetsManagerChromeOS::GetFileTaskRunner(info.profile)->PostTask(
+      GetExtensionFileTaskRunner()->PostTask(
           FROM_HERE,
-          base::Bind(&ExtensionAssetsManagerChromeOS::InstallLocalExtension,
-                     id,
-                     version,
-                     info.unpacked_extension_root,
-                     info.local_install_dir,
-                     info.callback));
+          base::Bind(&ExtensionAssetsManagerChromeOS::InstallLocalExtension, id,
+                     version, info.unpacked_extension_root,
+                     info.local_install_dir, info.callback));
     }
     return;
   }
@@ -417,9 +398,8 @@
         pending_installs[i];
       users->AppendString(info.profile->GetProfileUserName());
 
-    ExtensionAssetsManagerChromeOS::GetFileTaskRunner(info.profile)->PostTask(
-        FROM_HERE,
-        base::Bind(info.callback, shared_version_dir));
+      GetExtensionFileTaskRunner()->PostTask(
+          FROM_HERE, base::Bind(info.callback, shared_version_dir));
   }
   version_info->Set(kSharedExtensionUsers, std::move(users));
   extension_info_weak->SetWithoutPathExpansion(version,
@@ -479,7 +459,7 @@
         NOTREACHED();
         continue;
       }
-      ExtensionAssetsManagerChromeOS::GetFileTaskRunner(profile)->PostTask(
+      GetExtensionFileTaskRunner()->PostTask(
           FROM_HERE,
           base::Bind(&ExtensionAssetsManagerChromeOS::DeleteSharedVersion,
                      base::FilePath(shared_path)));
diff --git a/chrome/browser/extensions/extension_assets_manager_chromeos.h b/chrome/browser/extensions/extension_assets_manager_chromeos.h
index 29212d3..1ef3090 100644
--- a/chrome/browser/extensions/extension_assets_manager_chromeos.h
+++ b/chrome/browser/extensions/extension_assets_manager_chromeos.h
@@ -18,7 +18,6 @@
 
 namespace base {
 class DictionaryValue;
-class SequencedTaskRunner;
 }
 
 namespace extensions {
@@ -73,10 +72,6 @@
   ExtensionAssetsManagerChromeOS();
   ~ExtensionAssetsManagerChromeOS() override;
 
-  // Should be called on UI thread to get associated file task runner for
-  // the profile.
-  static base::SequencedTaskRunner* GetFileTaskRunner(Profile* profile);
-
   // Return |true| if |extension| can be installed in a shared place for all
   // users on the device.
   static bool CanShareAssets(const Extension* extension,
diff --git a/chrome/browser/extensions/extension_garbage_collector.cc b/chrome/browser/extensions/extension_garbage_collector.cc
index 0ccf5b8..5b74ac1 100644
--- a/chrome/browser/extensions/extension_garbage_collector.cc
+++ b/chrome/browser/extensions/extension_garbage_collector.cc
@@ -28,6 +28,7 @@
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/storage_partition.h"
+#include "extensions/browser/extension_file_task_runner.h"
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_system.h"
@@ -201,7 +202,7 @@
 
   ExtensionService* service =
       ExtensionSystem::Get(context_)->extension_service();
-  if (!service->GetFileTaskRunner()->PostTask(
+  if (!GetExtensionFileTaskRunner()->PostTask(
           FROM_HERE,
           base::BindOnce(&GarbageCollectExtensionsOnFileThread,
                          service->install_directory(), extension_paths))) {
diff --git a/chrome/browser/extensions/extension_garbage_collector_chromeos.cc b/chrome/browser/extensions/extension_garbage_collector_chromeos.cc
index a2f7e65..99ba05e9 100644
--- a/chrome/browser/extensions/extension_garbage_collector_chromeos.cc
+++ b/chrome/browser/extensions/extension_garbage_collector_chromeos.cc
@@ -7,9 +7,8 @@
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/extensions/extension_assets_manager_chromeos.h"
 #include "chrome/browser/extensions/extension_garbage_collector_chromeos.h"
-#include "chrome/browser/extensions/extension_service.h"
 #include "components/user_manager/user_manager.h"
-#include "extensions/browser/extension_system.h"
+#include "extensions/browser/extension_file_task_runner.h"
 
 namespace extensions {
 
@@ -77,9 +76,7 @@
 void ExtensionGarbageCollectorChromeOS::GarbageCollectSharedExtensions() {
   std::multimap<std::string, base::FilePath> paths;
   if (ExtensionAssetsManagerChromeOS::CleanUpSharedExtensions(&paths)) {
-    ExtensionService* service =
-        ExtensionSystem::Get(context_)->extension_service();
-    if (!service->GetFileTaskRunner()->PostTask(
+    if (!GetExtensionFileTaskRunner()->PostTask(
             FROM_HERE,
             base::Bind(&GarbageCollectExtensionsOnFileThread,
                        ExtensionAssetsManagerChromeOS::GetSharedInstallDir(),
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc
index 95ca0f3..f46cbae 100644
--- a/chrome/browser/extensions/extension_service.cc
+++ b/chrome/browser/extensions/extension_service.cc
@@ -563,7 +563,7 @@
     // Delete extension_path since we're not creating a CrxInstaller
     // that would do it for us.
     if (file_ownership_passed &&
-        !GetFileTaskRunner()->PostTask(
+        !extensions::GetExtensionFileTaskRunner()->PostTask(
             FROM_HERE, base::BindOnce(&extensions::file_util::DeleteFile,
                                       file.path, false)))
       NOTREACHED();
@@ -821,7 +821,7 @@
 
   // Tell the backend to start deleting installed extensions on the file thread.
   if (!Manifest::IsUnpackedLocation(extension->location())) {
-    if (!GetFileTaskRunner()->PostTask(
+    if (!extensions::GetExtensionFileTaskRunner()->PostTask(
             FROM_HERE,
             base::BindOnce(&ExtensionService::UninstallExtensionOnFileThread,
                            extension->id(), profile_, install_directory_,
@@ -1772,7 +1772,7 @@
 
       // Delete the extension directory since we're not going to
       // load it.
-      if (!GetFileTaskRunner()->PostTask(
+      if (!extensions::GetExtensionFileTaskRunner()->PostTask(
               FROM_HERE, base::BindOnce(&extensions::file_util::DeleteFile,
                                         extension->path(), true))) {
         NOTREACHED();
@@ -2508,12 +2508,6 @@
   }
 }
 
-base::SequencedTaskRunner* ExtensionService::GetFileTaskRunner() {
-  // TODO(devlin): Update callers to use GetExtensionFileTaskRunner()
-  // directly.
-  return extensions::GetExtensionFileTaskRunner().get();
-}
-
 // Used only by test code.
 void ExtensionService::UnloadAllExtensionsInternal() {
   profile_->GetExtensionSpecialStoragePolicy()->RevokeRightsForAllExtensions();
diff --git a/chrome/browser/extensions/extension_service.h b/chrome/browser/extensions/extension_service.h
index 9c9e05b..f30a75ac 100644
--- a/chrome/browser/extensions/extension_service.h
+++ b/chrome/browser/extensions/extension_service.h
@@ -46,7 +46,6 @@
 
 namespace base {
 class CommandLine;
-class SequencedTaskRunner;
 }
 
 namespace content {
@@ -364,9 +363,6 @@
                            extensions::InstallGate* install_delayer);
   void UnregisterInstallGate(extensions::InstallGate* install_delayer);
 
-  // Returns task runner for crx installation file I/O operations.
-  base::SequencedTaskRunner* GetFileTaskRunner();
-
   //////////////////////////////////////////////////////////////////////////////
   // Simple Accessors
 
diff --git a/chrome/browser/extensions/process_manager_browsertest.cc b/chrome/browser/extensions/process_manager_browsertest.cc
index a94de9c..b8b6fd4 100644
--- a/chrome/browser/extensions/process_manager_browsertest.cc
+++ b/chrome/browser/extensions/process_manager_browsertest.cc
@@ -11,6 +11,7 @@
 #include "base/macros.h"
 #include "base/path_service.h"
 #include "base/run_loop.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/test/histogram_tester.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/extensions/browser_action_test_util.h"
@@ -24,6 +25,7 @@
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
+#include "components/app_modal/javascript_dialog_manager.h"
 #include "components/guest_view/browser/test_guest_view_manager.h"
 #include "content/public/browser/child_process_security_policy.h"
 #include "content/public/browser/navigation_entry.h"
@@ -1266,4 +1268,40 @@
   }
 }
 
+// Tests that we correctly account for vanilla web URLs that may be in the
+// same SiteInstance as a hosted app, and display alerts correctly.
+// https://crbug.com/746517.
+IN_PROC_BROWSER_TEST_F(ProcessManagerBrowserTest, HostedAppAlerts) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  scoped_refptr<const Extension> extension =
+      LoadExtension(test_data_dir_.AppendASCII("hosted_app"));
+  ASSERT_TRUE(extension);
+
+  content::WebContents* tab =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  GURL hosted_app_url("http://localhost/extensions/hosted_app/main.html");
+  NavigateToURL(hosted_app_url);
+  EXPECT_EQ(hosted_app_url, tab->GetLastCommittedURL());
+  ProcessManager* pm = ProcessManager::Get(profile());
+  EXPECT_EQ(extension, pm->GetExtensionForWebContents(tab));
+  app_modal::JavaScriptDialogManager* js_dialog_manager =
+      app_modal::JavaScriptDialogManager::GetInstance();
+  base::string16 hosted_app_title = base::ASCIIToUTF16("hosted_app");
+  EXPECT_EQ(hosted_app_title, js_dialog_manager->GetTitle(
+                                  tab, tab->GetLastCommittedURL().GetOrigin()));
+
+  GURL web_url = embedded_test_server()->GetURL("/title1.html");
+  ASSERT_TRUE(content::ExecuteScript(
+      tab, base::StringPrintf("window.open('%s');", web_url.spec().c_str())));
+  content::WebContents* new_tab =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  EXPECT_NE(new_tab, tab);
+  EXPECT_TRUE(content::WaitForLoadStop(new_tab));
+  EXPECT_EQ(web_url, new_tab->GetLastCommittedURL());
+  EXPECT_EQ(nullptr, pm->GetExtensionForWebContents(new_tab));
+  EXPECT_NE(hosted_app_title,
+            js_dialog_manager->GetTitle(
+                new_tab, new_tab->GetLastCommittedURL().GetOrigin()));
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 8a91c48..893a7e07 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -363,12 +363,6 @@
 const char kEnableMaterialDesignFeedbackDescription[] =
     "If enabled, reporting an issue will load the Material Design feedback UI.";
 
-const char kEnableMaterialDesignPolicyPageName[] =
-    "Enable Material Design policy page";
-const char kEnableMaterialDesignPolicyPageDescription[] =
-    "If enabled, the chrome://md-policy URL loads the Material Design policy "
-    "page.";
-
 const char kEnableMidiManagerDynamicInstantiationName[] =
     "MIDIManager dynamic instantiation for Web MIDI.";
 const char kEnableMidiManagerDynamicInstantiationDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index d5487e7..fa6363e 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -242,9 +242,6 @@
 extern const char kEnableMaterialDesignFeedbackName[];
 extern const char kEnableMaterialDesignFeedbackDescription[];
 
-extern const char kEnableMaterialDesignPolicyPageName[];
-extern const char kEnableMaterialDesignPolicyPageDescription[];
-
 extern const char kEnableMidiManagerDynamicInstantiationName[];
 extern const char kEnableMidiManagerDynamicInstantiationDescription[];
 
diff --git a/chrome/browser/offline_pages/android/offline_page_bridge.cc b/chrome/browser/offline_pages/android/offline_page_bridge.cc
index 2047073..f5f35b1c 100644
--- a/chrome/browser/offline_pages/android/offline_page_bridge.cc
+++ b/chrome/browser/offline_pages/android/offline_page_bridge.cc
@@ -30,6 +30,7 @@
 #include "components/offline_pages/core/offline_page_feature.h"
 #include "components/offline_pages/core/offline_page_item.h"
 #include "components/offline_pages/core/offline_page_model.h"
+#include "components/offline_pages/core/offline_page_types.h"
 #include "components/offline_pages/core/recent_tabs/recent_tabs_ui_adapter_delegate.h"
 #include "components/offline_pages/core/request_header/offline_page_header.h"
 #include "content/public/browser/browser_context.h"
@@ -470,10 +471,8 @@
   j_callback_ref.Reset(env, j_callback_obj);
 
   OfflinePageUtils::SelectPageForURL(
-      browser_context_,
-      GURL(ConvertJavaStringToUTF8(env, j_online_url)),
-      OfflinePageModel::URLSearchMode::SEARCH_BY_ALL_URLS,
-      tab_id,
+      browser_context_, GURL(ConvertJavaStringToUTF8(env, j_online_url)),
+      URLSearchMode::SEARCH_BY_ALL_URLS, tab_id,
       base::Bind(&SingleOfflinePageItemCallback, j_callback_ref));
 }
 
diff --git a/chrome/browser/offline_pages/offline_page_request_job.cc b/chrome/browser/offline_pages/offline_page_request_job.cc
index 229f559..0945ce5 100644
--- a/chrome/browser/offline_pages/offline_page_request_job.cc
+++ b/chrome/browser/offline_pages/offline_page_request_job.cc
@@ -416,16 +416,10 @@
   }
 
   OfflinePageUtils::SelectPageForURL(
-      web_contents->GetBrowserContext(),
-      url,
-      OfflinePageModel::URLSearchMode::SEARCH_BY_ALL_URLS,
+      web_contents->GetBrowserContext(), url, URLSearchMode::SEARCH_BY_ALL_URLS,
       tab_id,
-      base::Bind(&SucceededToFindOfflinePage,
-                 url,
-                 offline_header,
-                 network_state,
-                 job,
-                 web_contents_getter));
+      base::Bind(&SucceededToFindOfflinePage, url, offline_header,
+                 network_state, job, web_contents_getter));
 }
 
 void FindPageWithOfflineIDDone(
diff --git a/chrome/browser/offline_pages/offline_page_tab_helper.cc b/chrome/browser/offline_pages/offline_page_tab_helper.cc
index 06420a5e..cbd137e 100644
--- a/chrome/browser/offline_pages/offline_page_tab_helper.cc
+++ b/chrome/browser/offline_pages/offline_page_tab_helper.cc
@@ -167,10 +167,8 @@
   }
 
   OfflinePageUtils::SelectPageForURL(
-      web_contents()->GetBrowserContext(),
-      navigated_url,
-      OfflinePageModel::URLSearchMode::SEARCH_BY_ALL_URLS,
-      tab_id,
+      web_contents()->GetBrowserContext(), navigated_url,
+      URLSearchMode::SEARCH_BY_ALL_URLS, tab_id,
       base::Bind(&OfflinePageTabHelper::SelectPageForURLDone,
                  weak_ptr_factory_.GetWeakPtr()));
 }
diff --git a/chrome/browser/offline_pages/offline_page_utils.cc b/chrome/browser/offline_pages/offline_page_utils.cc
index c86158d..f017a45e 100644
--- a/chrome/browser/offline_pages/offline_page_utils.cc
+++ b/chrome/browser/offline_pages/offline_page_utils.cc
@@ -147,7 +147,7 @@
 void OfflinePageUtils::SelectPageForURL(
     content::BrowserContext* browser_context,
     const GURL& url,
-    OfflinePageModel::URLSearchMode url_search_mode,
+    URLSearchMode url_search_mode,
     int tab_id,
     const base::Callback<void(const OfflinePageItem*)>& callback) {
   OfflinePageModel* offline_page_model =
@@ -275,7 +275,7 @@
   };
 
   offline_page_model->GetPagesByURL(
-      url, OfflinePageModel::URLSearchMode::SEARCH_BY_ALL_URLS,
+      url, URLSearchMode::SEARCH_BY_ALL_URLS,
       base::Bind(continuation, browser_context, url, callback));
 }
 
diff --git a/chrome/browser/offline_pages/offline_page_utils.h b/chrome/browser/offline_pages/offline_page_utils.h
index 1d6dc7a..1c1ac36 100644
--- a/chrome/browser/offline_pages/offline_page_utils.h
+++ b/chrome/browser/offline_pages/offline_page_utils.h
@@ -62,7 +62,7 @@
   static void SelectPageForURL(
       content::BrowserContext* browser_context,
       const GURL& url,
-      OfflinePageModel::URLSearchMode url_search_mode,
+      URLSearchMode url_search_mode,
       int tab_id,
       const base::Callback<void(const OfflinePageItem*)>& callback);
 
diff --git a/chrome/browser/profiling_host/BUILD.gn b/chrome/browser/profiling_host/BUILD.gn
index 8d5fa33b..e3bcbd7e 100644
--- a/chrome/browser/profiling_host/BUILD.gn
+++ b/chrome/browser/profiling_host/BUILD.gn
@@ -7,6 +7,8 @@
 if (enable_oop_heap_profiling) {
   static_library("profiling_host") {
     sources = [
+      "chrome_browser_main_extra_parts_profiling.cc",
+      "chrome_browser_main_extra_parts_profiling.h",
       "profiling_process_host.cc",
       "profiling_process_host.h",
     ]
diff --git a/chrome/browser/profiling_host/chrome_browser_main_extra_parts_profiling.cc b/chrome/browser/profiling_host/chrome_browser_main_extra_parts_profiling.cc
new file mode 100644
index 0000000..0fa2fd4
--- /dev/null
+++ b/chrome/browser/profiling_host/chrome_browser_main_extra_parts_profiling.cc
@@ -0,0 +1,21 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/profiling_host/chrome_browser_main_extra_parts_profiling.h"
+
+#include "base/command_line.h"
+#include "chrome/browser/profiling_host/profiling_process_host.h"
+#include "chrome/common/chrome_switches.h"
+
+ChromeBrowserMainExtraPartsProfiling::ChromeBrowserMainExtraPartsProfiling() =
+    default;
+ChromeBrowserMainExtraPartsProfiling::~ChromeBrowserMainExtraPartsProfiling() =
+    default;
+
+void ChromeBrowserMainExtraPartsProfiling::ServiceManagerConnectionStarted(
+    content::ServiceManagerConnection* connection) {
+  const base::CommandLine& cmdline = *base::CommandLine::ForCurrentProcess();
+  if (cmdline.HasSwitch(switches::kMemlog))
+    profiling::ProfilingProcessHost::EnsureStarted(connection);
+}
diff --git a/chrome/browser/profiling_host/chrome_browser_main_extra_parts_profiling.h b/chrome/browser/profiling_host/chrome_browser_main_extra_parts_profiling.h
new file mode 100644
index 0000000..e3776d5a
--- /dev/null
+++ b/chrome/browser/profiling_host/chrome_browser_main_extra_parts_profiling.h
@@ -0,0 +1,26 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_PROFILING_HOST_CHROME_BROWSER_MAIN_EXTRA_PARTS_PROFILING_H_
+#define CHROME_BROWSER_PROFILING_HOST_CHROME_BROWSER_MAIN_EXTRA_PARTS_PROFILING_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "chrome/browser/chrome_browser_main_extra_parts.h"
+
+class ChromeBrowserMainExtraPartsProfiling
+    : public ChromeBrowserMainExtraParts {
+ public:
+  ChromeBrowserMainExtraPartsProfiling();
+  ~ChromeBrowserMainExtraPartsProfiling() override;
+
+ private:
+  // ChromeBrowserMainExtraParts overrides.
+  void ServiceManagerConnectionStarted(
+      content::ServiceManagerConnection* connection) override;
+
+  DISALLOW_COPY_AND_ASSIGN(ChromeBrowserMainExtraPartsProfiling);
+};
+
+#endif  // CHROME_BROWSER_PROFILING_HOST_CHROME_BROWSER_MAIN_EXTRA_PARTS_PROFILING_H_
diff --git a/chrome/browser/profiling_host/profiling_process_host.cc b/chrome/browser/profiling_host/profiling_process_host.cc
index df6875a..4b267eb 100644
--- a/chrome/browser/profiling_host/profiling_process_host.cc
+++ b/chrome/browser/profiling_host/profiling_process_host.cc
@@ -5,90 +5,142 @@
 #include "chrome/browser/profiling_host/profiling_process_host.h"
 
 #include "base/command_line.h"
+#include "base/path_service.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/task_scheduler/post_task.h"
 #include "build/build_config.h"
+#include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/profiling/constants.mojom.h"
-#include "chrome/common/profiling/memlog.mojom.h"
-#include "chrome/common/profiling/memlog_sender.h"
 #include "chrome/common/profiling/profiling_constants.h"
+#include "content/public/browser/browser_child_process_host.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_types.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/common/bind_interface_helpers.h"
+#include "content/public/common/child_process_host.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/service_manager_connection.h"
 #include "mojo/edk/embedder/platform_channel_pair.h"
 #include "mojo/edk/embedder/scoped_platform_handle.h"
 #include "mojo/public/cpp/system/platform_handle.h"
-#include "services/service_manager/public/cpp/connector.h"
 
 namespace profiling {
 
-namespace {
-
-ProfilingProcessHost* pph_singleton = nullptr;
-
-void BindToBrowserConnector(service_manager::mojom::ConnectorRequest request) {
-  if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
-    content::BrowserThread::PostTask(
-        content::BrowserThread::UI, FROM_HERE,
-        base::BindOnce(&BindToBrowserConnector, std::move(request)));
-    return;
-  }
-  content::ServiceManagerConnection::GetForProcess()
-      ->GetConnector()
-      ->BindConnectorRequest(std::move(request));
-}
-
-}  // namespace
-
 ProfilingProcessHost::ProfilingProcessHost() {
-  pph_singleton = this;
-  LaunchAsService();
+  Add(this);
+  registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CREATED,
+                 content::NotificationService::AllBrowserContextsAndSources());
 }
 
 ProfilingProcessHost::~ProfilingProcessHost() {
-  pph_singleton = nullptr;
+  Remove(this);
 }
 
-// static
-ProfilingProcessHost* ProfilingProcessHost::EnsureStarted() {
-  static ProfilingProcessHost host;
-  return &host;
+void ProfilingProcessHost::BrowserChildProcessLaunchedAndConnected(
+    const content::ChildProcessData& data) {
+  if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)) {
+    content::BrowserThread::GetTaskRunnerForThread(content::BrowserThread::IO)
+        ->PostTask(
+            FROM_HERE,
+            base::BindOnce(
+                &ProfilingProcessHost::BrowserChildProcessLaunchedAndConnected,
+                base::Unretained(this), data));
+    return;
+  }
+  content::BrowserChildProcessHost* host =
+      content::BrowserChildProcessHost::FromID(data.id);
+  if (!host)
+    return;
+
+  // Tell the child process to start profiling.
+  profiling::mojom::MemlogClientPtr memlog_client;
+  profiling::mojom::MemlogClientRequest request =
+      mojo::MakeRequest(&memlog_client);
+  BindInterface(host->GetHost(), std::move(request));
+  base::ProcessId pid = base::GetProcId(data.handle);
+  StartProfilingForClient(std::move(memlog_client), pid);
 }
 
-// static
-ProfilingProcessHost* ProfilingProcessHost::Get() {
-  return pph_singleton;
-}
+void ProfilingProcessHost::Observe(
+    int type,
+    const content::NotificationSource& source,
+    const content::NotificationDetails& details) {
+  if (type != content::NOTIFICATION_RENDERER_PROCESS_CREATED)
+    return;
 
-// static
-void ProfilingProcessHost::AddSwitchesToChildCmdLine(
-    base::CommandLine* child_cmd_line) {
-  // TODO(ajwong): Figure out how to trace the zygote process.
-  if (child_cmd_line->GetSwitchValueASCII(switches::kProcessType) ==
-      switches::kZygoteProcess) {
+  if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)) {
+    content::BrowserThread::GetTaskRunnerForThread(content::BrowserThread::IO)
+        ->PostTask(FROM_HERE, base::BindOnce(&ProfilingProcessHost::Observe,
+                                             base::Unretained(this), type,
+                                             source, details));
     return;
   }
 
-  // TODO(ajwong): Change this to just reuse the --memlog flag. There is no
-  // need for a separate pipe flag.
-  //
-  // Zero is browser which is specified in LaunchAsService.
-  static int sender_id = 1;
-  child_cmd_line->AppendSwitchASCII(switches::kMemlogPipe,
-                                    base::IntToString(sender_id++));
+  // Tell the child process to start profiling.
+  content::RenderProcessHost* host =
+      content::Source<content::RenderProcessHost>(source).ptr();
+  profiling::mojom::MemlogClientPtr memlog_client;
+  profiling::mojom::MemlogClientRequest request =
+      mojo::MakeRequest(&memlog_client);
+  content::BindInterface(host, std::move(request));
+  base::ProcessId pid = base::GetProcId(host->GetHandle());
+
+  StartProfilingForClient(std::move(memlog_client), pid);
+}
+
+void ProfilingProcessHost::StartProfilingForClient(
+    profiling::mojom::MemlogClientPtr memlog_client,
+    base::ProcessId pid) {
+  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+
+  mojo::edk::PlatformChannelPair data_channel;
+
+  memlog_->AddSender(
+      pid,
+      mojo::WrapPlatformFile(data_channel.PassServerHandle().release().handle));
+  memlog_client->StartProfiling(
+      mojo::WrapPlatformFile(data_channel.PassClientHandle().release().handle));
+}
+
+// static
+ProfilingProcessHost* ProfilingProcessHost::EnsureStarted(
+    content::ServiceManagerConnection* connection) {
+  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+  ProfilingProcessHost* host = GetInstance();
+  host->MakeConnector(connection);
+  host->LaunchAsService();
+  return host;
+}
+
+// static
+ProfilingProcessHost* ProfilingProcessHost::GetInstance() {
+  return base::Singleton<
+      ProfilingProcessHost,
+      base::LeakySingletonTraits<ProfilingProcessHost>>::get();
 }
 
 void ProfilingProcessHost::RequestProcessDump(base::ProcessId pid) {
-  // TODO(brettw) implement process dumping.
+  if (!connector_) {
+    LOG(ERROR)
+        << "Requesting process dump when profiling process hasn't started.";
+    return;
+  }
+  base::PostTaskWithTraits(
+      FROM_HERE, {base::TaskPriority::USER_VISIBLE, base::MayBlock()},
+      base::BindOnce(&ProfilingProcessHost::GetOutputFileOnBlockingThread,
+                     base::Unretained(this), pid));
+}
+
+void ProfilingProcessHost::MakeConnector(
+    content::ServiceManagerConnection* connection) {
+  connector_ = connection->GetConnector()->Clone();
 }
 
 void ProfilingProcessHost::LaunchAsService() {
   // May get called on different threads, we need to be on the IO thread to
   // work.
-  //
-  // TODO(ajwong): This thread bouncing logic is dumb. The
-  // BindToBrowserConnector() ends up jumping to the UI thread also so this is
-  // at least 2 bounces. Simplify.
   if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)) {
     content::BrowserThread::GetTaskRunnerForThread(content::BrowserThread::IO)
         ->PostTask(FROM_HERE,
@@ -97,25 +149,39 @@
     return;
   }
 
-  // TODO(ajwong): There's likely a cleaner preexisting connector sitting
-  // around. See if there's a way to reuse that rather than creating our own?
-  //
-  // TODO(ajwong): Dedupe with InitMemlogSenderIfNecessary().
-  service_manager::mojom::ConnectorRequest connector_request;
-  std::unique_ptr<service_manager::Connector> connector =
-      service_manager::Connector::Create(&connector_request);
-
-  BindToBrowserConnector(std::move(connector_request));
-
-  mojom::MemlogPtr memlog;
-  connector->BindInterface(mojom::kServiceName, &memlog);
+  // Ideally, we'd just call StartProfilingForClient, to interface with the
+  // memlog client in the current [browser] process, but ChromeContentClient is
+  // not correctly hooked up for the browser process. The MemlogClient there is
+  // never bound. Instead, we use the *second* MemlogClient instance in the
+  // process [a member variable of ProfilingProcessHost], which we also don't
+  // bind, but instead directly call StartProfiling.
+  connector_->BindInterface(mojom::kServiceName, &memlog_);
 
   mojo::edk::PlatformChannelPair data_channel;
-  memlog->AddSender(
-      mojo::WrapPlatformFile(data_channel.PassServerHandle().release().handle),
-      0);  // 0 is the browser.
-  StartMemlogSender(base::ScopedPlatformFile(
-      data_channel.PassClientHandle().release().handle));
+  memlog_->AddSender(
+      base::Process::Current().Pid(),
+      mojo::WrapPlatformFile(data_channel.PassServerHandle().release().handle));
+  memlog_client_.StartProfiling(
+      mojo::WrapPlatformFile(data_channel.PassClientHandle().release().handle));
+}
+
+void ProfilingProcessHost::GetOutputFileOnBlockingThread(base::ProcessId pid) {
+  base::FilePath user_data_dir;
+  PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
+  base::FilePath output_path = user_data_dir.AppendASCII("memlog_dump");
+  base::File file(output_path,
+                  base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
+
+  content::BrowserThread::PostTask(
+      content::BrowserThread::IO, FROM_HERE,
+      base::BindOnce(&ProfilingProcessHost::HandleDumpProcessOnIOThread,
+                     base::Unretained(this), pid, std::move(file)));
+}
+
+void ProfilingProcessHost::HandleDumpProcessOnIOThread(base::ProcessId pid,
+                                                       base::File file) {
+  mojo::ScopedHandle handle = mojo::WrapPlatformFile(file.TakePlatformFile());
+  memlog_->DumpProcess(pid, std::move(handle));
 }
 
 }  // namespace profiling
diff --git a/chrome/browser/profiling_host/profiling_process_host.h b/chrome/browser/profiling_host/profiling_process_host.h
index daa5b487..ecb7db1 100644
--- a/chrome/browser/profiling_host/profiling_process_host.h
+++ b/chrome/browser/profiling_host/profiling_process_host.h
@@ -6,9 +6,18 @@
 #define CHROME_BROWSER_PROFILING_HOST_PROFILING_PROCESS_HOST_H_
 
 #include "base/macros.h"
+#include "base/memory/singleton.h"
 #include "base/process/process.h"
 #include "build/build_config.h"
 #include "chrome/common/chrome_features.h"
+#include "chrome/common/profiling/memlog.mojom.h"
+#include "chrome/common/profiling/memlog_client.h"
+#include "chrome/common/profiling/memlog_client.mojom.h"
+#include "content/public/browser/browser_child_process_observer.h"
+#include "content/public/browser/child_process_data.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "services/service_manager/public/cpp/connector.h"
 
 // The .mojom include above may not be generated unless OOP heap profiling is
 // enabled.
@@ -16,10 +25,6 @@
 #error profiling_process_host.h should only be included with OOP heap profiling
 #endif
 
-namespace base {
-class CommandLine;
-}  // namespace base
-
 namespace profiling {
 
 // Represents the browser side of the profiling process (//chrome/profiling).
@@ -39,29 +44,53 @@
 //
 // TODO(ajwong): This host class seems over kill at this point. Can this be
 // fully subsumed by the ProfilingService class?
-class ProfilingProcessHost {
+class ProfilingProcessHost : public content::BrowserChildProcessObserver,
+                             content::NotificationObserver {
  public:
   // Launches the profiling process if necessary and returns a pointer to it.
-  static ProfilingProcessHost* EnsureStarted();
+  static ProfilingProcessHost* EnsureStarted(
+      content::ServiceManagerConnection* connection);
 
-  // Returns a pointer to the current global profiling process host or, if
-  // no profiling process is launched, nullptr.
-  static ProfilingProcessHost* Get();
-
-  // Appends necessary switches to a command line for a child process so it can
-  // be profiled. These switches will cause the child process to start in the
-  // same mode (either profiling or not) as the browser process.
-  static void AddSwitchesToChildCmdLine(base::CommandLine* child_cmd_line);
+  // Returns a pointer to the current global profiling process host.
+  static ProfilingProcessHost* GetInstance();
 
   // Sends a message to the profiling process that it dump the given process'
   // memory data.
   void RequestProcessDump(base::ProcessId pid);
 
  private:
+  friend struct base::DefaultSingletonTraits<ProfilingProcessHost>;
   ProfilingProcessHost();
-  ~ProfilingProcessHost();
+  ~ProfilingProcessHost() override;
 
+  // Make and store a connector from |connection|.
+  void MakeConnector(content::ServiceManagerConnection* connection);
+
+  // BrowserChildProcessObserver
+  // Observe connection of non-renderer child processes.
+  void BrowserChildProcessLaunchedAndConnected(
+      const content::ChildProcessData& data) override;
+
+  // NotificationObserver
+  // Observe connection of renderer child processes.
+  void Observe(int type,
+               const content::NotificationSource& source,
+               const content::NotificationDetails& details) override;
+
+  // Starts the profiling process.
   void LaunchAsService();
+  void StartProfilingForClient(profiling::mojom::MemlogClientPtr memlog_client,
+                               base::ProcessId pid);
+  void GetOutputFileOnBlockingThread(base::ProcessId pid);
+  void HandleDumpProcessOnIOThread(base::ProcessId pid, base::File file);
+
+  content::NotificationRegistrar registrar_;
+  std::unique_ptr<service_manager::Connector> connector_;
+  mojom::MemlogPtr memlog_;
+
+  // Handles profiling for the current process, without connecting to any
+  // service manager interfaces.
+  profiling::MemlogClient memlog_client_;
 
   DISALLOW_COPY_AND_ASSIGN(ProfilingProcessHost);
 };
diff --git a/chrome/browser/resources/BUILD.gn b/chrome/browser/resources/BUILD.gn
index 1360570..d95ce22 100644
--- a/chrome/browser/resources/BUILD.gn
+++ b/chrome/browser/resources/BUILD.gn
@@ -37,18 +37,6 @@
   output_dir = "$root_gen_dir/chrome"
 }
 
-grit("policy_resources") {
-  source = "md_policy/policy_resources.grd"
-  defines = chrome_grit_defines
-  outputs = [
-    "grit/policy_resources.h",
-    "grit/policy_resources_map.cc",
-    "grit/policy_resources_map.h",
-    "policy_resources.pak",
-  ]
-  output_dir = "$root_gen_dir/chrome"
-}
-
 grit("quota_internals_resources") {
   source = "quota_internals_resources.grd"
   defines = chrome_grit_defines
diff --git a/chrome/browser/resources/md_policy/md_policy.html b/chrome/browser/resources/md_policy/md_policy.html
deleted file mode 100644
index 0ed7819..0000000
--- a/chrome/browser/resources/md_policy/md_policy.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<!doctype html>
-<html>
-<head>
-  <meta charset="utf-8">
-  <title>Policies</title>
-</head>
-<body>
-  <h1>A Material Design policy page is under construction!</h1>
-</body>
-</html>
diff --git a/chrome/browser/resources/md_policy/policy_resources.grd b/chrome/browser/resources/md_policy/policy_resources.grd
deleted file mode 100644
index dc76f52..0000000
--- a/chrome/browser/resources/md_policy/policy_resources.grd
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<grit latest_public_release="0" current_release="1" output_all_resource_defines="false">
-  <outputs>
-    <output filename="grit/policy_resources.h" type="rc_header">
-      <emit emit_type='prepend'></emit>
-    </output>
-    <output filename="grit/policy_resources_map.cc"
-            type="resource_file_map_source" />
-    <output filename="grit/policy_resources_map.h"
-            type="resource_map_header" />
-    <output filename="policy_resources.pak" type="data_package" />
-  </outputs>
-  <release seq="1">
-    <structures>
-      <!-- TODO(fhorschig): Add upcoming polymer files here. -->
-      <structure name="IDR_MD_POLICY_HTML"
-                 file="md_policy.html"
-                 type="chrome_html"/>
-    </structures>
-  </release>
-</grit>
diff --git a/chrome/browser/themes/theme_service.cc b/chrome/browser/themes/theme_service.cc
index 420b7d6..a4408e4 100644
--- a/chrome/browser/themes/theme_service.cc
+++ b/chrome/browser/themes/theme_service.cc
@@ -36,6 +36,7 @@
 #include "components/grit/components_scaled_resources.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/notification_service.h"
+#include "extensions/browser/extension_file_task_runner.h"
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_system.h"
@@ -878,7 +879,7 @@
     return;
 
   // Write the packed file to disk.
-  service->GetFileTaskRunner()->PostTask(
+  extensions::GetExtensionFileTaskRunner()->PostTask(
       FROM_HERE, base::Bind(&WritePackToDiskCallback, base::RetainedRef(pack),
                             extension->path()));
 
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 98c274a..460e328 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -390,8 +390,6 @@
     "webui/password_manager_internals/password_manager_internals_ui.h",
     "webui/physical_web/physical_web_ui.cc",
     "webui/physical_web/physical_web_ui.h",
-    "webui/policy_material_design_ui.cc",
-    "webui/policy_material_design_ui.h",
     "webui/policy_ui.cc",
     "webui/policy_ui.h",
     "webui/policy_ui_handler.cc",
diff --git a/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.cc b/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.cc
index 90808ed..5cc66f30 100644
--- a/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.cc
+++ b/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.cc
@@ -445,7 +445,8 @@
   dialog_->DetachParent();
   dialog_ = nullptr;
 
-  DCHECK(!callback_.is_null());
+  if (callback_.is_null())
+    return;
 
   // Notify the |callback_| asynchronously because it may need to destroy
   // DesktopMediaPicker.
diff --git a/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views_browsertest.cc b/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views_browsertest.cc
new file mode 100644
index 0000000..d07f5aff5
--- /dev/null
+++ b/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views_browsertest.cc
@@ -0,0 +1,52 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.h"
+
+#include <string>
+
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/media/webrtc/desktop_media_list.h"
+#include "chrome/browser/media/webrtc/fake_desktop_media_list.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/test/test_browser_dialog.h"
+#include "content/public/browser/desktop_media_id.h"
+
+class DesktopMediaPickerViewsBrowserTest : public DialogBrowserTest {
+ public:
+  DesktopMediaPickerViewsBrowserTest() {}
+
+  // DialogBrowserTest:
+  void ShowDialog(const std::string& name) override {
+    picker_ = base::MakeUnique<DesktopMediaPickerViews>();
+    auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
+    gfx::NativeWindow native_window = browser()->window()->GetNativeWindow();
+
+    std::vector<std::unique_ptr<DesktopMediaList>> source_lists;
+    for (auto type : {content::DesktopMediaID::TYPE_SCREEN,
+                      content::DesktopMediaID::TYPE_WINDOW,
+                      content::DesktopMediaID::TYPE_WEB_CONTENTS}) {
+      source_lists.push_back(base::MakeUnique<FakeDesktopMediaList>(type));
+    }
+
+    picker_->Show(web_contents, native_window, nullptr,
+                  base::ASCIIToUTF16("app_name"),
+                  base::ASCIIToUTF16("target_name"), std::move(source_lists),
+                  true, DesktopMediaPicker::DoneCallback());
+  }
+
+ private:
+  std::unique_ptr<DesktopMediaPickerViews> picker_;
+
+  DISALLOW_COPY_AND_ASSIGN(DesktopMediaPickerViewsBrowserTest);
+};
+
+// Invokes a dialog that allows the user to select what view of their desktop
+// they would like to share. See test_browser_dialog.h.
+IN_PROC_BROWSER_TEST_F(DesktopMediaPickerViewsBrowserTest,
+                       InvokeDialog_default) {
+  RunDialog();
+}
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
index 3c68998..74a4316 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
@@ -34,6 +34,7 @@
 #include "chrome/test/base/in_process_browser_test.h"
 #include "components/signin/core/account_id/account_id.h"
 #include "ui/aura/client/aura_constants.h"
+#include "ui/aura/test/env_test_helper.h"
 #include "ui/base/hit_test.h"
 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
 #include "ui/views/widget/widget.h"
@@ -113,6 +114,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(BrowserNonClientFrameViewAshTest, ImmersiveFullscreen) {
+  aura::test::EnvTestHelper().SetAlwaysUseLastMouseLocation(true);
   BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
   content::WebContents* web_contents = browser_view->GetActiveWebContents();
   Widget* widget = browser_view->GetWidget();
@@ -342,6 +344,7 @@
 
   void PreRunTestOnMainThread() override {
     InProcessBrowserTest::PreRunTestOnMainThread();
+    aura::test::EnvTestHelper().SetAlwaysUseLastMouseLocation(true);
     auto* immersive_mode_controller =
         browser_view()->immersive_mode_controller();
     scoped_observer_.Add(immersive_mode_controller);
diff --git a/chrome/browser/ui/views/location_bar/zoom_bubble_view_browsertest.cc b/chrome/browser/ui/views/location_bar/zoom_bubble_view_browsertest.cc
index 42535dd..a72eb73 100644
--- a/chrome/browser/ui/views/location_bar/zoom_bubble_view_browsertest.cc
+++ b/chrome/browser/ui/views/location_bar/zoom_bubble_view_browsertest.cc
@@ -13,13 +13,14 @@
 #include "chrome/browser/ui/views/frame/immersive_mode_controller.h"
 #include "chrome/test/base/in_process_browser_test.h"
 
-typedef InProcessBrowserTest ZoomBubbleBrowserTest;
-
 #if defined(USE_ASH)
 #include "ash/public/cpp/immersive/immersive_fullscreen_controller_test_api.h"
 #include "chrome/browser/ui/views/frame/immersive_mode_controller_ash.h"
+#include "ui/aura/test/env_test_helper.h"
 #endif
 
+using ZoomBubbleBrowserTest = InProcessBrowserTest;
+
 // TODO(linux_aura) http://crbug.com/163931
 #if defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(USE_AURA)
 #define MAYBE_NonImmersiveFullscreen DISABLED_NonImmersiveFullscreen
@@ -72,11 +73,11 @@
   }
 }
 
-// TODO(zturner): Change this to USE_ASH after fixing the test on Windows.
 #if defined(OS_CHROMEOS)
 // Test whether the zoom bubble is anchored and whether it is visible when in
 // immersive fullscreen.
 IN_PROC_BROWSER_TEST_F(ZoomBubbleBrowserTest, ImmersiveFullscreen) {
+  aura::test::EnvTestHelper().SetAlwaysUseLastMouseLocation(true);
   BrowserView* browser_view = static_cast<BrowserView*>(browser()->window());
   content::WebContents* web_contents = browser_view->GetActiveWebContents();
 
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
index 7b94806..7028500 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -46,7 +46,6 @@
 #include "chrome/browser/ui/webui/omnibox/omnibox_ui.h"
 #include "chrome/browser/ui/webui/password_manager_internals/password_manager_internals_ui.h"
 #include "chrome/browser/ui/webui/physical_web/physical_web_ui.h"
-#include "chrome/browser/ui/webui/policy_material_design_ui.h"
 #include "chrome/browser/ui/webui/policy_ui.h"
 #include "chrome/browser/ui/webui/predictors/predictors_ui.h"
 #include "chrome/browser/ui/webui/profiler_ui.h"
@@ -549,10 +548,6 @@
 
   if (url.host_piece() == chrome::kChromeUIPolicyHost)
     return &NewWebUI<PolicyUI>;
-  if (url.host_piece() == chrome::kChromeUIMdPolicyHost &&
-      switches::MdPolicyPageEnabled()) {
-    return &NewWebUI<PolicyMaterialDesignUI>;
-  }
 
 #if BUILDFLAG(ENABLE_APP_LIST)
   if (url.host_piece() == chrome::kChromeUIAppListStartPageHost)
diff --git a/chrome/browser/ui/webui/memory_internals_ui.cc b/chrome/browser/ui/webui/memory_internals_ui.cc
index 2b0130bc..e03b93c 100644
--- a/chrome/browser/ui/webui/memory_internals_ui.cc
+++ b/chrome/browser/ui/webui/memory_internals_ui.cc
@@ -9,16 +9,11 @@
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/weak_ptr.h"
-#include "base/path_service.h"
 #include "base/process/process_handle.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/task_scheduler/post_task.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiling_host/profiling_process_host.h"
-#include "chrome/common/chrome_paths.h"
-#include "chrome/common/profiling/constants.mojom.h"
-#include "chrome/common/profiling/memlog.mojom.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/browser_resources.h"
 #include "content/public/browser/browser_child_process_host_iterator.h"
@@ -124,12 +119,8 @@
   if (!pid_value.is_int())
     return;
 
-  // TODO(ajwong): Convert from pid to sender_id. https://crbug.com/751283.
-
-  base::PostTaskWithTraits(
-      FROM_HERE, {base::TaskPriority::USER_VISIBLE, base::MayBlock()},
-      base::BindOnce(&MemoryInternalsDOMHandler::GetOutputFileOnFileThread,
-                     pid_value.GetInt()));
+  profiling::ProfilingProcessHost::GetInstance()->RequestProcessDump(
+      pid_value.GetInt());
 }
 
 void MemoryInternalsDOMHandler::GetChildProcessesOnIOThread(
@@ -191,33 +182,6 @@
   DisallowJavascript();
 }
 
-// static
-void MemoryInternalsDOMHandler::GetOutputFileOnFileThread(int32_t sender_id) {
-  base::FilePath user_data_dir;
-  PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
-  base::FilePath output_path = user_data_dir.AppendASCII("memlog_dump");
-  base::File f(output_path,
-               base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
-
-  content::BrowserThread::PostTask(
-      content::BrowserThread::UI, FROM_HERE,
-      base::BindOnce(&MemoryInternalsDOMHandler::HandleDumpProcessOnUIThread,
-                     sender_id, std::move(f)));
-}
-
-// static
-void MemoryInternalsDOMHandler::HandleDumpProcessOnUIThread(int32_t sender_id,
-                                                            base::File file) {
-  profiling::mojom::MemlogPtr memlog;
-  service_manager::Connector* connector =
-      content::ServiceManagerConnection::GetForProcess()->GetConnector();
-  connector->BindInterface(profiling::mojom::kServiceName,
-                           mojo::MakeRequest(&memlog));
-
-  mojo::ScopedHandle sh = mojo::WrapPlatformFile(file.TakePlatformFile());
-  memlog->DumpProcess(sender_id, std::move(sh));
-}
-
 }  // namespace
 
 MemoryInternalsUI::MemoryInternalsUI(content::WebUI* web_ui)
diff --git a/chrome/browser/ui/webui/policy_material_design_ui.cc b/chrome/browser/ui/webui/policy_material_design_ui.cc
deleted file mode 100644
index d6f0ccc..0000000
--- a/chrome/browser/ui/webui/policy_material_design_ui.cc
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright (c) 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/webui/policy_material_design_ui.h"
-
-#include <stddef.h>
-#include <utility>
-
-#include "base/macros.h"
-#include "base/memory/ptr_util.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/webui/policy_ui_handler.h"
-#include "chrome/common/url_constants.h"
-#include "chrome/grit/policy_resources.h"
-#include "chrome/grit/policy_resources_map.h"
-#include "components/policy/core/common/policy_types.h"
-#include "components/policy/policy_constants.h"
-#include "components/policy/risk_tag.h"
-#include "components/strings/grit/components_strings.h"
-
-namespace {
-
-// Strings that map from policy::RiskTag enum to i18n string keys and their IDs.
-// Their order has to follow the order of the policy::RiskTag enum.
-const PolicyStringMap kPolicyRiskTags[policy::RISK_TAG_COUNT] = {
-  {"fullAdminAccess", IDS_POLICY_RISK_TAG_FULL_ADMIN_ACCESS},
-  {"systemSecurity", IDS_POLICY_RISK_TAG_SYSTEM_SECURITY},
-  {"websiteSharing", IDS_POLICY_RISK_TAG_WEBSITE_SHARING},
-  {"adminSharing", IDS_POLICY_RISK_TAG_ADMIN_SHARING},
-  {"filtering", IDS_POLICY_RISK_TAG_FILTERING},
-  {"localDataAccess", IDS_POLICY_RISK_TAG_LOCAL_DATA_ACCESS},
-  {"googleSharing", IDS_POLICY_RISK_TAG_GOOGLE_SHARING},
-};
-
-content::WebUIDataSource* CreatePolicyMaterialDesignUIHtmlSource() {
-  content::WebUIDataSource* source =
-      content::WebUIDataSource::Create(chrome::kChromeUIMdPolicyHost);
-  PolicyUIHandler::AddCommonLocalizedStringsToSource(source);
-  PolicyUIHandler::AddLocalizedPolicyStrings(
-      source, kPolicyRiskTags, static_cast<size_t>(policy::RISK_TAG_COUNT));
-  for (size_t i = 0; i < kPolicyResourcesSize; ++i) {
-    source->AddResourcePath(kPolicyResources[i].name,
-                            kPolicyResources[i].value);
-  }
-  source->SetDefaultResource(IDR_MD_POLICY_HTML);
-  return source;
-}
-
-} // namespace
-
-// The JavaScript message handler for the chrome://md-policy page.
-class PolicyMaterialDesignUIHandler : public PolicyUIHandler {
- public:
-  PolicyMaterialDesignUIHandler();
-  ~PolicyMaterialDesignUIHandler() override;
-
- protected:
-  // PolicyUIHandler:
-  void AddPolicyName(const std::string& name,
-                     base::DictionaryValue* names) const override;
-  void SendPolicyNames() const override;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(PolicyMaterialDesignUIHandler);
-};
-
-PolicyMaterialDesignUIHandler::PolicyMaterialDesignUIHandler() {
-}
-
-PolicyMaterialDesignUIHandler::~PolicyMaterialDesignUIHandler() {
-}
-
-void PolicyMaterialDesignUIHandler::AddPolicyName(
-    const std::string& name, base::DictionaryValue* names) const {
-  std::unique_ptr<base::ListValue> list(new base::ListValue());
-  const policy::RiskTag* tags = policy::GetChromePolicyDetails(name)->risk_tags;
-  for (size_t i = 0; i < policy::kMaxRiskTagCount; ++i) {
-    if (tags[i] != policy::RISK_TAG_NONE)
-      list->AppendString(kPolicyRiskTags[tags[i]].key);
-  }
-  names->SetWithoutPathExpansion(name, std::move(list));
-}
-
-void PolicyMaterialDesignUIHandler::SendPolicyNames() const {
-  base::ListValue tags;
-  for (size_t tag = 0; tag < policy::RISK_TAG_COUNT; ++tag)
-    tags.AppendString(kPolicyRiskTags[tag].key);
-
-  web_ui()->CallJavascriptFunctionUnsafe("policy.Page.setPolicyGroups", tags);
-  PolicyUIHandler::SendPolicyNames();
-}
-
-PolicyMaterialDesignUI::PolicyMaterialDesignUI(content::WebUI* web_ui) :
-    WebUIController(web_ui) {
-  web_ui->AddMessageHandler(base::MakeUnique<PolicyMaterialDesignUIHandler>());
-  content::WebUIDataSource::Add(Profile::FromWebUI(web_ui),
-                                CreatePolicyMaterialDesignUIHtmlSource());
-}
-
-PolicyMaterialDesignUI::~PolicyMaterialDesignUI() {
-}
diff --git a/chrome/browser/ui/webui/policy_material_design_ui.h b/chrome/browser/ui/webui/policy_material_design_ui.h
deleted file mode 100644
index ecf7eb1a..0000000
--- a/chrome/browser/ui/webui/policy_material_design_ui.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (c) 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_WEBUI_POLICY_MATERIAL_DESIGN_UI_H_
-#define CHROME_BROWSER_UI_WEBUI_POLICY_MATERIAL_DESIGN_UI_H_
-
-#include "base/macros.h"
-#include "content/public/browser/web_ui_controller.h"
-
-namespace content {
-class WebUI;
-}
-
-// The Web UI controller for the chrome://md-policy page.
-class PolicyMaterialDesignUI : public content::WebUIController {
- public:
-  explicit PolicyMaterialDesignUI(content::WebUI* web_ui);
-  ~PolicyMaterialDesignUI() override;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(PolicyMaterialDesignUI);
-};
-
-#endif  // CHROME_BROWSER_UI_WEBUI_POLICY_MATERIAL_DESIGN_UI_H_
diff --git a/chrome/chrome_paks.gni b/chrome/chrome_paks.gni
index 6bbe0ff..04fc8273 100644
--- a/chrome/chrome_paks.gni
+++ b/chrome/chrome_paks.gni
@@ -95,7 +95,6 @@
       "$root_gen_dir/chrome/invalidations_resources.pak",
       "$root_gen_dir/chrome/net_internals_resources.pak",
       "$root_gen_dir/chrome/password_manager_internals_resources.pak",
-      "$root_gen_dir/chrome/policy_resources.pak",
       "$root_gen_dir/chrome/quota_internals_resources.pak",
       "$root_gen_dir/chrome/task_scheduler_internals_resources.pak",
       "$root_gen_dir/chrome/translate_internals_resources.pak",
@@ -111,7 +110,6 @@
       "//chrome/browser/resources:invalidations_resources",
       "//chrome/browser/resources:net_internals_resources",
       "//chrome/browser/resources:password_manager_internals_resources",
-      "//chrome/browser/resources:policy_resources",
       "//chrome/browser/resources:quota_internals_resources",
       "//chrome/browser/resources:task_scheduler_internals_resources",
       "//chrome/browser/resources:translate_internals_resources",
diff --git a/chrome/common/chrome_content_client.cc b/chrome/common/chrome_content_client.cc
index 689653c..a12d095 100644
--- a/chrome/common/chrome_content_client.cc
+++ b/chrome/common/chrome_content_client.cc
@@ -55,10 +55,6 @@
 #include "url/url_constants.h"
 #include "widevine_cdm_version.h"  // In SHARED_INTERMEDIATE_DIR.
 
-#if BUILDFLAG(ENABLE_OOP_HEAP_PROFILING)
-#include "chrome/common/profiling/memlog_sender.h"
-#endif
-
 #if defined(OS_LINUX)
 #include <fcntl.h>
 #include "chrome/common/component_flash_hint_file_linux.h"
@@ -718,8 +714,12 @@
 void ChromeContentClient::OnServiceManagerConnected(
     content::ServiceManagerConnection* connection) {
 #if BUILDFLAG(ENABLE_OOP_HEAP_PROFILING)
-  // MemlogSender depends on ServiceManager to spawn the utility process that
-  // receives the memory log.
-  profiling::InitMemlogSenderIfNecessary(connection);
+  // ChromeContentClient::OnServiceManagerConnected isn't called from the
+  // browser process or utility processes. This is confusing. :(
+  // For now, profiling in the the browser process is initialized by
+  // ChromeBrowserMainParts::ServiceManagerConnectionStarted, and we ignore
+  // utility processes.
+  // https://crbug.com/753106.
+  memlog_client_.OnServiceManagerConnected(connection);
 #endif  // ENABLE_OOP_HEAP_PROFILING
 }
diff --git a/chrome/common/chrome_content_client.h b/chrome/common/chrome_content_client.h
index df13b08..5e75016b 100644
--- a/chrome/common/chrome_content_client.h
+++ b/chrome/common/chrome_content_client.h
@@ -14,6 +14,7 @@
 #include "base/files/file_path.h"
 #include "base/synchronization/lock.h"
 #include "build/build_config.h"
+#include "chrome/common/features.h"
 #include "chrome/common/origin_trials/chrome_origin_trial_policy.h"
 #include "content/public/common/content_client.h"
 #include "ppapi/features/features.h"
@@ -22,7 +23,9 @@
 #include "content/public/common/pepper_plugin_info.h"
 #endif
 
-#include "url/url_util.h"
+#if BUILDFLAG(ENABLE_OOP_HEAP_PROFILING)
+#include "chrome/common/profiling/memlog_client.h"
+#endif
 
 // Returns the user agent of Chrome.
 std::string GetUserAgent();
@@ -113,6 +116,9 @@
   // Used to lock when |origin_trial_policy_| is initialized.
   base::Lock origin_trial_policy_lock_;
   std::unique_ptr<ChromeOriginTrialPolicy> origin_trial_policy_;
+#if BUILDFLAG(ENABLE_OOP_HEAP_PROFILING)
+  profiling::MemlogClient memlog_client_;
+#endif  // ENABLE_OOP_HEAP_PROFILING
 };
 
 #endif  // CHROME_COMMON_CHROME_CONTENT_CLIENT_H_
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index db78170..05860e00 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -336,9 +336,6 @@
 // Enables the Material Design feedback form.
 const char kEnableMaterialDesignFeedback[] = "enable-md-feedback";
 
-// Enables the Material Design policy page at chrome://md-policy.
-const char kEnableMaterialDesignPolicyPage[]  = "enable-md-policy-page";
-
 // Runs the Native Client inside the renderer process and enables GPU plugin
 // (internally adds lEnableGpuPlugin to the command line).
 const char kEnableNaCl[]                    = "enable-nacl";
@@ -1060,12 +1057,6 @@
 #if BUILDFLAG(ENABLE_OOP_HEAP_PROFILING)
 // Enables the out-of-process memory logging.
 const char kMemlog[] = "memlog";
-
-// Communicates the pipe name for out-of-process memory logging.
-const char kMemlogPipe[] = "memlog-pipe";
-
-// Value passed to kProcessType switch that indicates the profiling process.
-const char kProfiling[] = "profiling";
 #endif
 
 bool ExtensionsDisabled(const base::CommandLine& command_line) {
@@ -1082,11 +1073,6 @@
       ::switches::kEnableMaterialDesignFeedback);
 }
 
-bool MdPolicyPageEnabled() {
-  return base::CommandLine::ForCurrentProcess()->HasSwitch(
-      ::switches::kEnableMaterialDesignPolicyPage);
-}
-
 #if defined(OS_CHROMEOS)
 bool PowerOverlayEnabled() {
   return base::CommandLine::ForCurrentProcess()->HasSwitch(
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index 9a68d34c..ca79d202 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -110,7 +110,6 @@
 extern const char kEnableExtensionActivityLogTesting[];
 extern const char kEnableFastUnload[];
 extern const char kEnableMaterialDesignFeedback[];
-extern const char kEnableMaterialDesignPolicyPage[];
 extern const char kEnableNaCl[];
 extern const char kEnableNavigationTracing[];
 extern const char kEnableNetBenchmarking[];
@@ -341,14 +340,11 @@
 
 #if BUILDFLAG(ENABLE_OOP_HEAP_PROFILING)
 extern const char kMemlog[];
-extern const char kMemlogPipe[];
-extern const char kProfiling[];
 #endif
 
 bool ExtensionsDisabled(const base::CommandLine& command_line);
 bool ExtensionsDisabled();
 bool MdFeedbackEnabled();
-bool MdPolicyPageEnabled();
 
 #if defined(OS_CHROMEOS)
 bool PowerOverlayEnabled();
diff --git a/chrome/common/profiling/BUILD.gn b/chrome/common/profiling/BUILD.gn
index 421ef32..2cf75a95 100644
--- a/chrome/common/profiling/BUILD.gn
+++ b/chrome/common/profiling/BUILD.gn
@@ -10,8 +10,8 @@
     sources = [
       "memlog_allocator_shim.cc",
       "memlog_allocator_shim.h",
-      "memlog_sender.cc",
-      "memlog_sender.h",
+      "memlog_client.cc",
+      "memlog_client.h",
       "memlog_sender_pipe.h",
       "memlog_sender_pipe_posix.cc",
       "memlog_sender_pipe_posix.h",
@@ -35,6 +35,10 @@
     sources = [
       "constants.mojom",
       "memlog.mojom",
+      "memlog_client.mojom",
+    ]
+    deps = [
+      "//mojo/common:common_custom_types",
     ]
   }
 } else {
diff --git a/chrome/common/profiling/memlog.mojom b/chrome/common/profiling/memlog.mojom
index f7d48863..eeaeebe 100644
--- a/chrome/common/profiling/memlog.mojom
+++ b/chrome/common/profiling/memlog.mojom
@@ -5,19 +5,18 @@
 module profiling.mojom;
 
 import "mojo/common/file.mojom";
+import "mojo/common/process_id.mojom";
 
 // The profiling process is still in prototype stage, and is behind a
 // compile-time flag. There will be a security review before removing the
 // compile-time flag. https://crbug.com/751759.
 interface Memlog {
   // Adds a new platform-specific pipe to read memlog trace data from.
-  // In normal usage, each child process launch will have a corresponding call
-  // to this.
-  AddSender(handle sender_pipe, int32 sender_id);
+  // In normal usage, each child process will be given the other end of this
+  // pipe.
+  AddSender(mojo.common.mojom.ProcessId pid, handle sender_pipe);
 
-  // Dumps the memory log of the process with the given |sender_id| into
-  // |output_file|.  This currently dumps the memory log of an arbitrary
-  // process, since there does not yet a mechanism to identify specific
-  // processes. See https://crbug.com/751283.
-  DumpProcess(int32 sender_id, handle output_file);
+  // Dumps the memory log of the process with the given |pid| into
+  // |output_file|.
+  DumpProcess(mojo.common.mojom.ProcessId pid, handle output_file);
 };
diff --git a/chrome/common/profiling/memlog_client.cc b/chrome/common/profiling/memlog_client.cc
new file mode 100644
index 0000000..87a2d2e
--- /dev/null
+++ b/chrome/common/profiling/memlog_client.cc
@@ -0,0 +1,58 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/common/profiling/memlog_client.h"
+
+#include "base/files/platform_file.h"
+#include "chrome/common/profiling/memlog_allocator_shim.h"
+#include "chrome/common/profiling/memlog_sender_pipe.h"
+#include "chrome/common/profiling/memlog_stream.h"
+#include "content/public/common/service_manager_connection.h"
+#include "content/public/common/simple_connection_filter.h"
+#include "mojo/public/cpp/system/platform_handle.h"
+#include "services/service_manager/public/cpp/binder_registry.h"
+
+namespace profiling {
+
+MemlogClient::MemlogClient() = default;
+
+MemlogClient::~MemlogClient() {
+  if (connection_) {
+    connection_->RemoveConnectionFilter(connection_filter_id_);
+  }
+}
+
+void MemlogClient::OnServiceManagerConnected(
+    content::ServiceManagerConnection* connection) {
+  connection_ = connection;
+
+  std::unique_ptr<service_manager::BinderRegistry> registry(
+      new service_manager::BinderRegistry);
+  registry->AddInterface(base::Bind(&profiling::MemlogClient::BindToInterface,
+                                    base::Unretained(this)));
+  connection_filter_id_ = connection->AddConnectionFilter(
+      base::MakeUnique<content::SimpleConnectionFilter>(std::move(registry)));
+}
+
+void MemlogClient::BindToInterface(mojom::MemlogClientRequest request) {
+  binding_ = std::move(request);
+}
+
+void MemlogClient::StartProfiling(mojo::ScopedHandle sender_pipe) {
+  base::PlatformFile platform_file;
+  CHECK_EQ(MOJO_RESULT_OK,
+           mojo::UnwrapPlatformFile(std::move(sender_pipe), &platform_file));
+
+  base::ScopedPlatformFile scoped_platform_file(platform_file);
+  memlog_sender_pipe_.reset(
+      new MemlogSenderPipe(std::move(scoped_platform_file)));
+
+  StreamHeader header;
+  header.signature = kStreamSignature;
+  memlog_sender_pipe_->Send(&header, sizeof(header));
+
+  InitAllocatorShim(memlog_sender_pipe_.get());
+}
+
+}  // namespace profiling
diff --git a/chrome/common/profiling/memlog_client.h b/chrome/common/profiling/memlog_client.h
new file mode 100644
index 0000000..48271a1
--- /dev/null
+++ b/chrome/common/profiling/memlog_client.h
@@ -0,0 +1,50 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_COMMON_PROFILING_MEMLOG_CLIENT_H_
+#define CHROME_COMMON_PROFILING_MEMLOG_CLIENT_H_
+
+#include "chrome/common/profiling/memlog_client.mojom.h"
+#include "mojo/public/cpp/system/handle.h"
+
+namespace content {
+class ServiceManagerConnection;
+}  // namespace content
+
+namespace profiling {
+
+class MemlogSenderPipe;
+
+// The MemlogClient listens on the interface for a StartProfiling message. On
+// receiving the message, it begins profiling the current process.
+// It is also possible to use the MemlogClient to begin profiling the current
+// process without connecting to the service manager interface, if the caller
+// has a |sender_pipe| to pass to StartProfiling.
+class MemlogClient : public mojom::MemlogClient {
+ public:
+  MemlogClient();
+  ~MemlogClient() override;
+
+  // mojom::MemlogClient overrides:
+  void StartProfiling(mojo::ScopedHandle sender_pipe) override;
+
+  void OnServiceManagerConnected(content::ServiceManagerConnection* connection);
+  void BindToInterface(profiling::mojom::MemlogClientRequest request);
+
+ private:
+  // The most recent MemlogClientRequest is kept alive.
+  mojom::MemlogClientRequest binding_;
+
+  std::unique_ptr<MemlogSenderPipe> memlog_sender_pipe_;
+
+  // Used to remove the connection filter on destruction.
+  int connection_filter_id_ = 0;
+
+  // The ServiceManagerConnection must outlive this instance.
+  content::ServiceManagerConnection* connection_ = nullptr;
+};
+
+}  // namespace profiling
+
+#endif  // CHROME_COMMON_PROFILING_MEMLOG_CLIENT_H_
diff --git a/chrome/common/profiling/memlog_client.mojom b/chrome/common/profiling/memlog_client.mojom
new file mode 100644
index 0000000..f3eab396
--- /dev/null
+++ b/chrome/common/profiling/memlog_client.mojom
@@ -0,0 +1,15 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module profiling.mojom;
+
+// The profiling process is still in prototype stage, and is behind a
+// compile-time flag. There will be a security review before removing the
+// compile-time flag. https://crbug.com/751759.
+interface MemlogClient {
+  // Start recording allocations and sending them to the profiling process via
+  // |sender_pipe|. There is currently no mechanism to stop recording
+  // allocations.
+  StartProfiling(handle sender_pipe);
+};
diff --git a/chrome/common/profiling/memlog_sender.cc b/chrome/common/profiling/memlog_sender.cc
deleted file mode 100644
index 4d441fa..0000000
--- a/chrome/common/profiling/memlog_sender.cc
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/common/profiling/memlog_sender.h"
-
-#include "base/command_line.h"
-#include "base/strings/string_number_conversions.h"
-#include "build/build_config.h"
-#include "chrome/common/chrome_switches.h"
-#include "chrome/common/profiling/constants.mojom.h"
-#include "chrome/common/profiling/memlog.mojom.h"
-#include "chrome/common/profiling/memlog_allocator_shim.h"
-#include "chrome/common/profiling/memlog_sender_pipe.h"
-#include "chrome/common/profiling/memlog_stream.h"
-#include "content/public/common/service_manager_connection.h"
-#include "mojo/edk/embedder/platform_channel_pair.h"
-#include "services/service_manager/public/cpp/connector.h"
-
-namespace profiling {
-
-namespace {
-
-// TODO(brettw) this is a hack to allow StartProfilingMojo to work. Figure out
-// how to get the lifetime of this that allows that function call to work.
-MemlogSenderPipe* memlog_sender_pipe = nullptr;
-
-}  // namespace
-
-void InitMemlogSenderIfNecessary(
-    content::ServiceManagerConnection* connection) {
-  const base::CommandLine& cmdline = *base::CommandLine::ForCurrentProcess();
-  // TODO(ajwong): Rename to kMemlogId.
-  std::string pipe_id_str = cmdline.GetSwitchValueASCII(switches::kMemlogPipe);
-  if (pipe_id_str.empty()) {
-    return;
-  }
-  int sender_id;
-  CHECK(base::StringToInt(pipe_id_str, &sender_id));
-
-  // The |memlog| interface is used for a one-shot, no-response, publication
-  // of the filehandle for the data channel. As such, there is no need to have
-  // the MemlogPtr live beyond the AddSender() call.
-  mojom::MemlogPtr memlog;
-  connection->GetConnector()->BindInterface(profiling::mojom::kServiceName,
-                                            &memlog);
-
-  mojo::edk::PlatformChannelPair data_channel;
-  memlog->AddSender(
-      mojo::WrapPlatformFile(data_channel.PassServerHandle().release().handle),
-      sender_id);
-  StartMemlogSender(base::ScopedPlatformFile(
-      data_channel.PassClientHandle().release().handle));
-}
-
-void StartMemlogSender(base::ScopedPlatformFile file) {
-  static MemlogSenderPipe pipe(std::move(file));
-  memlog_sender_pipe = &pipe;
-
-  StreamHeader header;
-  header.signature = kStreamSignature;
-  pipe.Send(&header, sizeof(StreamHeader));
-
-  InitAllocatorShim(&pipe);
-}
-
-void StartProfilingMojo() {
-  static bool started_mojo = false;
-
-  if (!started_mojo) {
-    started_mojo = true;
-    StartMojoControlPacket start_mojo_message;
-    start_mojo_message.op = kStartMojoControlPacketType;
-    memlog_sender_pipe->Send(&start_mojo_message, sizeof(start_mojo_message));
-  }
-}
-
-}  // namespace profiling
diff --git a/chrome/common/profiling/memlog_sender.h b/chrome/common/profiling/memlog_sender.h
deleted file mode 100644
index fd276ee..0000000
--- a/chrome/common/profiling/memlog_sender.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_COMMON_PROFILING_MEMLOG_SENDER_H_
-#define CHROME_COMMON_PROFILING_MEMLOG_SENDER_H_
-
-#include <string>
-
-#include "base/files/platform_file.h"
-#include "mojo/public/cpp/system/platform_handle.h"
-
-namespace base {
-
-class CommandLine;
-
-}  // namespace base
-
-namespace content {
-
-class ServiceManagerConnection;
-
-}  // namespace content
-
-namespace profiling {
-
-// Starts the memlog sender pipe if the command line has requested it. The pipe
-// ID will be extracted from the CommandLine for the process.
-void InitMemlogSenderIfNecessary(content::ServiceManagerConnection* connection);
-
-// Starts the memlog sender pipe with the given ID.
-void StartMemlogSender(base::ScopedPlatformFile file);
-
-// Tells the profiling process to try to connect to the profiling control
-// channel. This must be done after the browser is ready to accept such a
-// connection.
-void StartProfilingMojo();
-
-}  // namespace profiling
-
-#endif  // CHROME_COMMON_PROFILING_MEMLOG_SENDER_H_
diff --git a/chrome/common/url_constants.cc b/chrome/common/url_constants.cc
index 7a5c988d..36db417 100644
--- a/chrome/common/url_constants.cc
+++ b/chrome/common/url_constants.cc
@@ -67,7 +67,6 @@
 const char kChromeUIInstantURL[] = "chrome://instant/";
 const char kChromeUIInterstitialURL[] = "chrome://interstitials/";
 const char kChromeUIInvalidationsURL[] = "chrome://invalidations/";
-const char kChromeUIMdPolicyURL[] = "chrome://md-policy/";
 const char kChromeUINaClURL[] = "chrome://nacl/";
 const char kChromeUINetInternalsURL[] = "chrome://net-internals/";
 const char kChromeUINewProfileURL[] = "chrome://newprofile/";
@@ -219,7 +218,6 @@
 const char kChromeUIKillHost[] = "kill";
 const char kChromeUILargeIconHost[] = "large-icon";
 const char kChromeUILocalStateHost[] = "local-state";
-const char kChromeUIMdPolicyHost[] = "md-policy";
 const char kChromeUIMediaEngagementHost[] = "media-engagement";
 const char kChromeUINaClHost[] = "nacl";
 const char kChromeUINetExportHost[] = "net-export";
diff --git a/chrome/common/url_constants.h b/chrome/common/url_constants.h
index dea320d..f8c25f3 100644
--- a/chrome/common/url_constants.h
+++ b/chrome/common/url_constants.h
@@ -61,7 +61,6 @@
 extern const char kChromeUIInstantURL[];
 extern const char kChromeUIInterstitialURL[];
 extern const char kChromeUIInvalidationsURL[];
-extern const char kChromeUIMdPolicyURL[];
 extern const char kChromeUIMediaEngagementHost[];
 extern const char kChromeUINaClURL[];
 extern const char kChromeUINetInternalsURL[];
@@ -208,7 +207,6 @@
 extern const char kChromeUIKillHost[];
 extern const char kChromeUILargeIconHost[];
 extern const char kChromeUILocalStateHost[];
-extern const char kChromeUIMdPolicyHost[];
 extern const char kChromeUINaClHost[];
 extern const char kChromeUINetExportHost[];
 extern const char kChromeUINetInternalsHost[];
diff --git a/chrome/installer/zucchini/BUILD.gn b/chrome/installer/zucchini/BUILD.gn
index 008406c3..cc00facf 100644
--- a/chrome/installer/zucchini/BUILD.gn
+++ b/chrome/installer/zucchini/BUILD.gn
@@ -41,6 +41,8 @@
     "type_win_pe.h",
     "typed_value.h",
     "zucchini.h",
+    "zucchini_apply.cc",
+    "zucchini_gen.cc",
   ]
 
   deps = [
diff --git a/chrome/installer/zucchini/buffer_view.h b/chrome/installer/zucchini/buffer_view.h
index 08ca499a..7c41ba6 100644
--- a/chrome/installer/zucchini/buffer_view.h
+++ b/chrome/installer/zucchini/buffer_view.h
@@ -51,6 +51,9 @@
       : first_(first), last_(first_ + size) {
     DCHECK_GE(last_, first_);
   }
+  template <class U>
+  explicit BufferViewBase(const BufferViewBase<U>& that)
+      : first_(that.begin()), last_(that.end()) {}
 
   BufferViewBase(const BufferViewBase&) = default;
   BufferViewBase& operator=(const BufferViewBase&) = default;
diff --git a/chrome/installer/zucchini/main_utils.cc b/chrome/installer/zucchini/main_utils.cc
index f11c0223..c80e74e 100644
--- a/chrome/installer/zucchini/main_utils.cc
+++ b/chrome/installer/zucchini/main_utils.cc
@@ -51,7 +51,7 @@
 /******** List of Zucchini commands ********/
 
 constexpr Command kCommands[] = {
-    {"gen", "-gen <old_file> <new_file> <patch_file>", 3, &MainGen},
+    {"gen", "-gen <old_file> <new_file> <patch_file> [-raw]", 3, &MainGen},
     {"apply", "-apply <old_file> <patch_file> <new_file>", 3, &MainApply},
     {"crc32", "-crc32 <file>", 1, &MainCrc32},
 };
diff --git a/chrome/installer/zucchini/zucchini.h b/chrome/installer/zucchini/zucchini.h
index 411180bc..a21b8095 100644
--- a/chrome/installer/zucchini/zucchini.h
+++ b/chrome/installer/zucchini/zucchini.h
@@ -5,6 +5,10 @@
 #ifndef CHROME_INSTALLER_ZUCCHINI_ZUCCHINI_H_
 #define CHROME_INSTALLER_ZUCCHINI_ZUCCHINI_H_
 
+#include "chrome/installer/zucchini/buffer_view.h"
+#include "chrome/installer/zucchini/patch_reader.h"
+#include "chrome/installer/zucchini/patch_writer.h"
+
 // Definitions, structures, and interfaces for the Zucchini library.
 
 namespace zucchini {
@@ -18,10 +22,32 @@
   kStatusInvalidParam = 1,
   kStatusFileReadError = 2,
   kStatusFileWriteError = 3,
+  kStatusPatchReadError = 4,
+  kStatusPatchWriteError = 5,
+  kStatusInvalidOldImage = 6,
+  kStatusInvalidNewImage = 7,
 };
 
 }  // namespace status
 
+// Generates ensemble patch from |old_image| to |new_image|, and writes it to
+// |patch_writer|.
+status::Code GenerateEnsemble(ConstBufferView old_image,
+                              ConstBufferView new_image,
+                              EnsemblePatchWriter* patch_writer);
+
+// Generates raw patch from |old_image| to |new_image|, and writes it to
+// |patch_writer|.
+status::Code GenerateRaw(ConstBufferView old_image,
+                         ConstBufferView new_image,
+                         EnsemblePatchWriter* patch_writer);
+
+// Applies |patch_reader| to |old_image| to build |new_image|, which refers to
+// preallocated memory of sufficient size.
+status::Code Apply(ConstBufferView old_image,
+                   const EnsemblePatchReader& patch_reader,
+                   MutableBufferView new_image);
+
 }  // namespace zucchini
 
 #endif  // CHROME_INSTALLER_ZUCCHINI_ZUCCHINI_H_
diff --git a/chrome/installer/zucchini/zucchini_apply.cc b/chrome/installer/zucchini/zucchini_apply.cc
new file mode 100644
index 0000000..95f8af8
--- /dev/null
+++ b/chrome/installer/zucchini/zucchini_apply.cc
@@ -0,0 +1,27 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/installer/zucchini/zucchini.h"
+
+namespace zucchini {
+
+status::Code Apply(ConstBufferView old_image,
+                   const EnsemblePatchReader& patch_reader,
+                   MutableBufferView new_image) {
+  if (!patch_reader.CheckOldFile(old_image)) {
+    LOG(ERROR) << "Invalid old_image.";
+    return zucchini::status::kStatusInvalidOldImage;
+  }
+
+  // TODO(etiennep): Implement.
+
+  // This will always fail for now, because of missing implementation.
+  if (!patch_reader.CheckNewFile(ConstBufferView(new_image))) {
+    LOG(ERROR) << "Invalid new_image.";
+    return zucchini::status::kStatusInvalidNewImage;
+  }
+  return zucchini::status::kStatusSuccess;
+}
+
+}  // namespace zucchini
diff --git a/chrome/installer/zucchini/zucchini_commands.cc b/chrome/installer/zucchini/zucchini_commands.cc
index be3bb34..1447f41b 100644
--- a/chrome/installer/zucchini/zucchini_commands.cc
+++ b/chrome/installer/zucchini/zucchini_commands.cc
@@ -16,6 +16,8 @@
 #include "chrome/installer/zucchini/buffer_view.h"
 #include "chrome/installer/zucchini/crc32.h"
 #include "chrome/installer/zucchini/io_utils.h"
+#include "chrome/installer/zucchini/patch_reader.h"
+#include "chrome/installer/zucchini/patch_writer.h"
 
 namespace {
 
@@ -68,6 +70,10 @@
   DISALLOW_COPY_AND_ASSIGN(MappedFileWriter);
 };
 
+/******** Command-line Switches ********/
+
+const char kSwitchRaw[] = "raw";
+
 }  // namespace
 
 zucchini::status::Code MainGen(MainParams params) {
@@ -79,13 +85,26 @@
   if (!new_image.is_ok())
     return zucchini::status::kStatusFileReadError;
 
-  // TODO(etiennep): Implement.
+  zucchini::EnsemblePatchWriter patch_writer(old_image.region(),
+                                             new_image.region());
 
-  // Dummy output as placeholder.
-  MappedFileWriter patch(params.file_paths[2], 256);
+  auto generate = params.command_line.HasSwitch(kSwitchRaw)
+                      ? zucchini::GenerateRaw
+                      : zucchini::GenerateEnsemble;
+  zucchini::status::Code status =
+      generate(old_image.region(), new_image.region(), &patch_writer);
+  if (status != zucchini::status::kStatusSuccess) {
+    params.out << "Fatal error encountered when generating patch." << std::endl;
+    return status;
+  }
+
+  MappedFileWriter patch(params.file_paths[2], patch_writer.SerializedSize());
   if (!patch.is_ok())
     return zucchini::status::kStatusFileWriteError;
 
+  if (!patch_writer.SerializeInto(patch.region()))
+    return zucchini::status::kStatusPatchWriteError;
+
   return zucchini::status::kStatusSuccess;
 }
 
@@ -98,13 +117,23 @@
   if (!patch.is_ok())
     return zucchini::status::kStatusFileReadError;
 
-  // TODO(etiennep): Implement.
+  auto patch_reader = zucchini::EnsemblePatchReader::Create(patch.region());
+  if (!patch_reader.has_value()) {
+    params.err << "Error reading patch header." << std::endl;
+    return zucchini::status::kStatusPatchReadError;
+  }
+  zucchini::PatchHeader header = patch_reader->header();
 
-  // Dummy output as placeholder.
-  MappedFileWriter new_image(params.file_paths[2], 256);
+  MappedFileWriter new_image(params.file_paths[2], header.new_size);
   if (!new_image.is_ok())
     return zucchini::status::kStatusFileWriteError;
 
+  zucchini::status::Code status =
+      zucchini::Apply(old_image.region(), *patch_reader, new_image.region());
+  if (status != zucchini::status::kStatusSuccess) {
+    params.err << "Fatal error encountered while applying patch." << std::endl;
+    return status;
+  }
   return zucchini::status::kStatusSuccess;
 }
 
diff --git a/chrome/installer/zucchini/zucchini_gen.cc b/chrome/installer/zucchini/zucchini_gen.cc
new file mode 100644
index 0000000..a3685f8
--- /dev/null
+++ b/chrome/installer/zucchini/zucchini_gen.cc
@@ -0,0 +1,46 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/installer/zucchini/image_utils.h"
+#include "chrome/installer/zucchini/zucchini.h"
+
+namespace zucchini {
+
+status::Code GenerateEnsemble(ConstBufferView old_image,
+                              ConstBufferView new_image,
+                              EnsemblePatchWriter* patch_writer) {
+  patch_writer->SetPatchType(PatchType::kEnsemblePatch);
+
+  // Dummy patch element to fill patch_writer.
+  PatchElementWriter patch_element(
+      {Element(old_image.region()), Element(new_image.region())});
+  patch_element.SetEquivalenceSink({});
+  patch_element.SetExtraDataSink({});
+  patch_element.SetRawDeltaSink({});
+  patch_element.SetReferenceDeltaSink({});
+  patch_writer->AddElement(std::move(patch_element));
+
+  // TODO(etiennep): Implement.
+  return zucchini::status::kStatusSuccess;
+}
+
+status::Code GenerateRaw(ConstBufferView old_image,
+                         ConstBufferView new_image,
+                         EnsemblePatchWriter* patch_writer) {
+  patch_writer->SetPatchType(PatchType::kRawPatch);
+
+  // Dummy patch element to fill patch_writer.
+  PatchElementWriter patch_element(
+      {Element(old_image.region()), Element(new_image.region())});
+  patch_element.SetEquivalenceSink({});
+  patch_element.SetExtraDataSink({});
+  patch_element.SetRawDeltaSink({});
+  patch_element.SetReferenceDeltaSink({});
+  patch_writer->AddElement(std::move(patch_element));
+
+  // TODO(etiennep): Implement.
+  return zucchini::status::kStatusSuccess;
+}
+
+}  // namespace zucchini
diff --git a/chrome/installer/zucchini/zucchini_main.cc b/chrome/installer/zucchini/zucchini_main.cc
index 3791890..170e814 100644
--- a/chrome/installer/zucchini/zucchini_main.cc
+++ b/chrome/installer/zucchini/zucchini_main.cc
@@ -44,6 +44,9 @@
       *base::CommandLine::ForCurrentProcess();
   InitLogging();
   InitErrorHandling(command_line);
-  return static_cast<int>(
-      RunZucchiniCommand(command_line, std::cout, std::cerr));
+  zucchini::status::Code status =
+      RunZucchiniCommand(command_line, std::cout, std::cerr);
+  if (status != zucchini::status::kStatusSuccess)
+    std::cerr << "Failed with code " << static_cast<int>(status) << std::endl;
+  return static_cast<int>(status);
 }
diff --git a/chrome/profiling/memlog_connection_manager.cc b/chrome/profiling/memlog_connection_manager.cc
index b801ccd..f1e5d70 100644
--- a/chrome/profiling/memlog_connection_manager.cc
+++ b/chrome/profiling/memlog_connection_manager.cc
@@ -18,9 +18,9 @@
 struct MemlogConnectionManager::Connection {
   Connection(AllocationTracker::CompleteCallback complete_cb,
              BacktraceStorage* backtrace_storage,
-             int sender_id,
+             base::ProcessId pid,
              scoped_refptr<MemlogReceiverPipe> p)
-      : thread(base::StringPrintf("Sender %d thread", sender_id)),
+      : thread(base::StringPrintf("Sender %d thread", pid)),
         pipe(p),
         tracker(std::move(complete_cb), backtrace_storage) {}
 
@@ -45,36 +45,35 @@
 MemlogConnectionManager::~MemlogConnectionManager() {}
 
 void MemlogConnectionManager::OnNewConnection(base::ScopedPlatformFile file,
-                                              int sender_id) {
+                                              base::ProcessId pid) {
   base::AutoLock l(connections_lock_);
-  DCHECK(connections_.find(sender_id) == connections_.end());
+  DCHECK(connections_.find(pid) == connections_.end());
 
   scoped_refptr<MemlogReceiverPipe> new_pipe =
       new MemlogReceiverPipe(std::move(file));
   // Task to post to clean up the connection. Don't need to retain |this| since
   // it wil be called by objects owned by the MemlogConnectionManager.
-  AllocationTracker::CompleteCallback complete_cb =
-      base::BindOnce(&MemlogConnectionManager::OnConnectionCompleteThunk,
-                     base::Unretained(this),
-                     base::MessageLoop::current()->task_runner(), sender_id);
+  AllocationTracker::CompleteCallback complete_cb = base::BindOnce(
+      &MemlogConnectionManager::OnConnectionCompleteThunk,
+      base::Unretained(this), base::MessageLoop::current()->task_runner(), pid);
 
   std::unique_ptr<Connection> connection = base::MakeUnique<Connection>(
-      std::move(complete_cb), backtrace_storage_, sender_id, new_pipe);
+      std::move(complete_cb), backtrace_storage_, pid, new_pipe);
   connection->thread.Start();
 
   connection->parser = new MemlogStreamParser(&connection->tracker);
   new_pipe->SetReceiver(connection->thread.task_runner(), connection->parser);
 
-  connections_[sender_id] = std::move(connection);
+  connections_[pid] = std::move(connection);
 
   io_runner_->PostTask(
       FROM_HERE,
       base::Bind(&MemlogReceiverPipe::StartReadingOnIOThread, new_pipe));
 }
 
-void MemlogConnectionManager::OnConnectionComplete(int sender_id) {
+void MemlogConnectionManager::OnConnectionComplete(base::ProcessId pid) {
   base::AutoLock l(connections_lock_);
-  auto found = connections_.find(sender_id);
+  auto found = connections_.find(pid);
   CHECK(found != connections_.end());
   found->second.release();
   connections_.erase(found);
@@ -83,24 +82,19 @@
 // Posts back to the given thread the connection complete message.
 void MemlogConnectionManager::OnConnectionCompleteThunk(
     scoped_refptr<base::SingleThreadTaskRunner> main_loop,
-    int sender_id) {
+    base::ProcessId pid) {
   // This code is called by the allocation tracker which is owned by the
   // connection manager. When we tell the connection manager a connection is
   // done, we know the conncetion manager will still be in scope.
   main_loop->PostTask(FROM_HERE,
                       base::Bind(&MemlogConnectionManager::OnConnectionComplete,
-                                 base::Unretained(this), sender_id));
+                                 base::Unretained(this), pid));
 }
 
-void MemlogConnectionManager::DumpProcess(int32_t sender_id,
+void MemlogConnectionManager::DumpProcess(base::ProcessId pid,
                                           base::File output_file) {
   base::AutoLock l(connections_lock_);
 
-  if (connections_.empty()) {
-    LOG(ERROR) << "No connections found for memory dump.";
-    return;
-  }
-
   // Lock all connections to prevent deallocations of atoms from
   // BacktraceStorage. This only works if no new connections are made, which
   // connections_lock_ guarantees.
@@ -111,13 +105,16 @@
         base::MakeUnique<base::AutoLock>(*connection->parser->GetLock()));
   }
 
-  // Pick the first connection, since there's no way to identify connections
-  // right now. https://crbug.com/751283.
-  Connection* connection = connections_.begin()->second.get();
+  auto it = connections_.find(pid);
+  if (it == connections_.end()) {
+    LOG(ERROR) << "No connections found for memory dump for pid:" << pid;
+    return;
+  }
+
+  Connection* connection = it->second.get();
 
   std::ostringstream oss;
-  ExportAllocationEventSetToJSON(sender_id, connection->tracker.live_allocs(),
-                                 oss);
+  ExportAllocationEventSetToJSON(pid, connection->tracker.live_allocs(), oss);
   std::string reply = oss.str();
   output_file.WriteAtCurrentPos(reply.c_str(), reply.size());
 }
diff --git a/chrome/profiling/memlog_connection_manager.h b/chrome/profiling/memlog_connection_manager.h
index 9c6afb3..4339d0f 100644
--- a/chrome/profiling/memlog_connection_manager.h
+++ b/chrome/profiling/memlog_connection_manager.h
@@ -11,6 +11,7 @@
 #include "base/files/file.h"
 #include "base/files/platform_file.h"
 #include "base/macros.h"
+#include "base/process/process_handle.h"
 #include "base/synchronization/lock.h"
 #include "build/build_config.h"
 #include "chrome/profiling/backtrace_storage.h"
@@ -36,9 +37,9 @@
   ~MemlogConnectionManager();
 
   // Dumps the memory log for the given process into |output_file|.
-  void DumpProcess(int32_t sender_id, base::File output_file);
+  void DumpProcess(base::ProcessId pid, base::File output_file);
 
-  void OnNewConnection(base::ScopedPlatformFile file, int sender_id);
+  void OnNewConnection(base::ScopedPlatformFile file, base::ProcessId pid);
 
  private:
   struct Connection;
@@ -47,17 +48,17 @@
   // is signaled by the pipe server, this is signaled by the allocation tracker
   // to ensure that the pipeline for this process has been flushed of all
   // messages.
-  void OnConnectionComplete(int sender_id);
+  void OnConnectionComplete(base::ProcessId pid);
 
   void OnConnectionCompleteThunk(
       scoped_refptr<base::SingleThreadTaskRunner> main_loop,
-      int process_id);
+      base::ProcessId process_id);
 
   scoped_refptr<base::SequencedTaskRunner> io_runner_;
   BacktraceStorage* backtrace_storage_;  // Not owned.
 
   // Maps process ID to the connection information for it.
-  base::flat_map<int, std::unique_ptr<Connection>> connections_;
+  base::flat_map<base::ProcessId, std::unique_ptr<Connection>> connections_;
   base::Lock connections_lock_;
 
   DISALLOW_COPY_AND_ASSIGN(MemlogConnectionManager);
diff --git a/chrome/profiling/memlog_impl.cc b/chrome/profiling/memlog_impl.cc
index 5d83a99..8b61c672 100644
--- a/chrome/profiling/memlog_impl.cc
+++ b/chrome/profiling/memlog_impl.cc
@@ -18,7 +18,8 @@
 
 MemlogImpl::~MemlogImpl() {}
 
-void MemlogImpl::AddSender(mojo::ScopedHandle sender_pipe, int32_t sender_id) {
+void MemlogImpl::AddSender(base::ProcessId pid,
+                           mojo::ScopedHandle sender_pipe) {
   base::PlatformFile platform_file;
   CHECK_EQ(MOJO_RESULT_OK,
            mojo::UnwrapPlatformFile(std::move(sender_pipe), &platform_file));
@@ -26,13 +27,12 @@
   // MemlogConnectionManager is deleted on the IOThread thus using
   // base::Unretained() is safe here.
   io_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(&MemlogConnectionManager::OnNewConnection,
-                     base::Unretained(connection_manager_.get()),
-                     base::ScopedPlatformFile(platform_file), sender_id));
+      FROM_HERE, base::BindOnce(&MemlogConnectionManager::OnNewConnection,
+                                base::Unretained(connection_manager_.get()),
+                                base::ScopedPlatformFile(platform_file), pid));
 }
 
-void MemlogImpl::DumpProcess(int32_t sender_id,
+void MemlogImpl::DumpProcess(base::ProcessId pid,
                              mojo::ScopedHandle output_file) {
   base::PlatformFile platform_file;
   MojoResult result =
@@ -45,7 +45,7 @@
   io_runner_->PostTask(
       FROM_HERE, base::BindOnce(&MemlogConnectionManager::DumpProcess,
                                 base::Unretained(connection_manager_.get()),
-                                sender_id, std::move(file)));
+                                pid, std::move(file)));
 }
 
 }  // namespace profiling
diff --git a/chrome/profiling/memlog_impl.h b/chrome/profiling/memlog_impl.h
index bd033c612..50cafa7 100644
--- a/chrome/profiling/memlog_impl.h
+++ b/chrome/profiling/memlog_impl.h
@@ -27,8 +27,9 @@
   MemlogImpl();
   ~MemlogImpl() override;
 
-  void AddSender(mojo::ScopedHandle sender_pipe, int32_t sender_id) override;
-  void DumpProcess(int32_t sender_id, mojo::ScopedHandle output_file) override;
+  void AddSender(base::ProcessId pid, mojo::ScopedHandle sender_pipe) override;
+  void DumpProcess(base::ProcessId pid,
+                   mojo::ScopedHandle output_file) override;
 
  private:
   // Helper for managing lifetime of MemlogConnectionManager.
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
index d9e3cea..eaf33c7d 100644
--- a/chrome/renderer/chrome_content_renderer_client.cc
+++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -122,6 +122,7 @@
 #include "third_party/WebKit/public/platform/WebURLError.h"
 #include "third_party/WebKit/public/platform/WebURLRequest.h"
 #include "third_party/WebKit/public/platform/WebURLResponse.h"
+#include "third_party/WebKit/public/platform/scheduler/renderer_process_type.h"
 #include "third_party/WebKit/public/web/WebDocument.h"
 #include "third_party/WebKit/public/web/WebElement.h"
 #include "third_party/WebKit/public/web/WebLocalFrame.h"
@@ -288,12 +289,14 @@
 SpellCheckReplacer::~SpellCheckReplacer() = default;
 #endif
 
-#if BUILDFLAG(ENABLE_EXTENSIONS)
 bool IsStandaloneExtensionProcess() {
+#if !BUILDFLAG(ENABLE_EXTENSIONS)
+  return false;
+#else
   return base::CommandLine::ForCurrentProcess()->HasSwitch(
       extensions::switches::kExtensionProcess);
-}
 #endif
+}
 
 // Defers media player loading in background pages until they're visible.
 // TODO(dalecurtis): Include an idle listener too.  http://crbug.com/509135
@@ -387,6 +390,11 @@
 void ChromeContentRendererClient::RenderThreadStarted() {
   RenderThread* thread = RenderThread::Get();
 
+  thread->SetRendererProcessType(
+      IsStandaloneExtensionProcess()
+          ? blink::scheduler::RendererProcessType::kExtensionRenderer
+          : blink::scheduler::RendererProcessType::kRenderer);
+
   {
     startup_metric_utils::mojom::StartupMetricHostPtr startup_metric_host;
     thread->GetConnector()->BindInterface(content::mojom::kBrowserServiceName,
@@ -1183,11 +1191,7 @@
 }
 
 bool ChromeContentRendererClient::RunIdleHandlerWhenWidgetsHidden() {
-#if BUILDFLAG(ENABLE_EXTENSIONS)
   return !IsStandaloneExtensionProcess();
-#else
-  return true;
-#endif
 }
 
 bool ChromeContentRendererClient::AllowStoppingTimersWhenProcessBackgrounded() {
@@ -1417,11 +1421,7 @@
   // TODO(nick): https://crbug.com/268640 Gather stats for extension processes
   // too; we would need to check the extension's manifest to know which sites
   // it's allowed to access.
-#if BUILDFLAG(ENABLE_EXTENSIONS)
   return !IsStandaloneExtensionProcess();
-#else
-  return true;
-#endif
 }
 
 std::unique_ptr<blink::WebContentSettingsClient>
@@ -1584,11 +1584,7 @@
 // information. Also, the enforcement of sending and binding UDP is already done
 // by chrome extension permission model.
 bool ChromeContentRendererClient::ShouldEnforceWebRTCRoutingPreferences() {
-#if BUILDFLAG(ENABLE_EXTENSIONS)
   return !IsStandaloneExtensionProcess();
-#else
-  return true;
-#endif
 }
 
 GURL ChromeContentRendererClient::OverrideFlashEmbedWithHTML(const GURL& url) {
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 4429362..94fd01a 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -2310,7 +2310,6 @@
         "../browser/chromeos/policy/user_policy_test_helper.cc",
         "../browser/chromeos/policy/user_policy_test_helper.h",
         "../browser/chromeos/policy/variations_service_policy_browsertest.cc",
-        "../browser/chromeos/power/peripheral_battery_observer_browsertest.cc",
         "../browser/chromeos/preferences_chromeos_browsertest.cc",
         "../browser/chromeos/profiles/profile_helper_browsertest.cc",
         "../browser/chromeos/shutdown_policy_browsertest.cc",
@@ -2662,6 +2661,7 @@
     }
 
     if (use_aura) {
+      sources += [ "../browser/ui/views/desktop_capture/desktop_media_picker_views_browsertest.cc" ]
       if (enable_wifi_display) {
         sources += [
           "../../extensions/browser/api/display_source/display_source_apitestbase.cc",
@@ -5014,7 +5014,7 @@
     inputs = [
       script,
       shell_script,
-      "data/safe_browsing/dmg/make_hfs.sh",
+      "data/safe_browsing/dmg/hfs_raw_images.tar.bz2",
       "data/safe_browsing/mach_o/executablefat",
       "data/safe_browsing/mach_o/lib64.dylib",
     ]
diff --git a/chrome/test/data/safe_browsing/dmg/.gitignore b/chrome/test/data/safe_browsing/dmg/.gitignore
new file mode 100644
index 0000000..d7b4bd6
--- /dev/null
+++ b/chrome/test/data/safe_browsing/dmg/.gitignore
@@ -0,0 +1,2 @@
+hfs_plus.img
+hfsx_case_sensitive.img
diff --git a/chrome/test/data/safe_browsing/dmg/Makefile b/chrome/test/data/safe_browsing/dmg/Makefile
new file mode 100644
index 0000000..935b64e
--- /dev/null
+++ b/chrome/test/data/safe_browsing/dmg/Makefile
@@ -0,0 +1,17 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+hfs_raw_images.tar.bz2: hfs_plus.img hfsx_case_sensitive.img
+	tar cjf $@ $^
+
+MAKE_HFS=./make_hfs.sh
+
+hfs_plus.img: $(MAKE_HFS)
+	rm -f $@
+	$(MAKE_HFS) HFS+ 1024 $@
+
+hfsx_case_sensitive.img: $(MAKE_HFS)
+	rm -f $@
+	# 8*1024 = 8192
+	$(MAKE_HFS) hfsx 8192 $@
diff --git a/chrome/test/data/safe_browsing/dmg/README.md b/chrome/test/data/safe_browsing/dmg/README.md
new file mode 100644
index 0000000..6d5e8ad3
--- /dev/null
+++ b/chrome/test/data/safe_browsing/dmg/README.md
@@ -0,0 +1,13 @@
+# Safe Browsing DMG Test Data
+
+This directory contains scripts to generate test DMG and HFS files for
+unit-testing the Safe Browsing archive scanner. The contents of this directory
+are primarily used by `//chrome/test:mac_safe_browsing_test_data`.
+
+Most of the data are generated at build-time using the `generate_test_data.sh`
+script. However, due to a [macOS issue](https://crbug.com/696529) the outputs
+from the `make_hfs.sh` script are generated independently and checked-in.
+
+These independently generated data are stored in `hfs_raw_images.tar.bz2` and
+can be regenerated using the `Makefile` in this directory. The build system
+will extract the archive into the build output directory.
diff --git a/chrome/test/data/safe_browsing/dmg/generate_test_data.sh b/chrome/test/data/safe_browsing/dmg/generate_test_data.sh
index cbb266fa..df85c7b 100755
--- a/chrome/test/data/safe_browsing/dmg/generate_test_data.sh
+++ b/chrome/test/data/safe_browsing/dmg/generate_test_data.sh
@@ -30,9 +30,9 @@
 
   # HFS Raw Images #############################################################
 
-  MAKE_HFS="${THIS_DIR}/make_hfs.sh"
-  "${MAKE_HFS}" HFS+ 1024 "${OUT_DIR}/hfs_plus.img"
-  "${MAKE_HFS}" hfsx $((8 * 1024)) "${OUT_DIR}/hfsx_case_sensitive.img"
+  # Extract the checked-in testdata to the OUT_DIR, ignoring the archived
+  # modification times.
+  tar x -m -C "${OUT_DIR}" -f "${THIS_DIR}/hfs_raw_images.tar.bz2"
 
   # DMG Files ##################################################################
 
@@ -93,7 +93,7 @@
   # Overwrites 'koly' with '????'.
   printf '\xa1\xa1\xa1\xa1' | dd conv=notrunc \
       of="${OUT_DIR}/mach_o_in_dmg_no_koly_signature.dmg" \
-      bs=1 seek=$(($SIZE - 512))
+      bs=1 seek=$(($SIZE - 512)) &> /dev/null
 
   # Copy of Mach-O DMG with extension changed to .txt.
   cp "${OUT_DIR}/mach_o_in_dmg.dmg" "${OUT_DIR}/mach_o_in_dmg.txt"
diff --git a/chrome/test/data/safe_browsing/dmg/hfs_raw_images.tar.bz2 b/chrome/test/data/safe_browsing/dmg/hfs_raw_images.tar.bz2
new file mode 100644
index 0000000..85ed0b1
--- /dev/null
+++ b/chrome/test/data/safe_browsing/dmg/hfs_raw_images.tar.bz2
Binary files differ
diff --git a/chrome/test/data/safe_browsing/dmg/make_hfs.sh b/chrome/test/data/safe_browsing/dmg/make_hfs.sh
index 0de6c360..4287ee4 100755
--- a/chrome/test/data/safe_browsing/dmg/make_hfs.sh
+++ b/chrome/test/data/safe_browsing/dmg/make_hfs.sh
@@ -33,10 +33,6 @@
 
 RAMDISK_VOLUME=$(hdiutil attach -nomount ram://$RAMDISK_SIZE)
 diskutil erasevolume "${FILESYSTEM_TYPE}" "${VOLUME_NAME}" ${RAMDISK_VOLUME}
->&2 echo "Temporary debugging for crbug.com/696529."
->&2 diskutil list
->&2 hdiutil pmap -complete -debug ${RAMDISK_VOLUME}
->&2 echo "RAMDISK_VOLUME: " + ${RAMDISK_VOLUME}
 diskutil mount ${RAMDISK_VOLUME}
 
 pushd "/Volumes/${VOLUME_NAME}"
diff --git a/chromeos/components/tether/host_scanner.cc b/chromeos/components/tether/host_scanner.cc
index b4736dae..570b6ba 100644
--- a/chromeos/components/tether/host_scanner.cc
+++ b/chromeos/components/tether/host_scanner.cc
@@ -39,7 +39,6 @@
       device_id_tether_network_guid_map_(device_id_tether_network_guid_map),
       host_scan_cache_(host_scan_cache),
       clock_(clock),
-      is_fetching_hosts_(false),
       weak_ptr_factory_(this) {}
 
 HostScanner::~HostScanner() {}
@@ -99,6 +98,8 @@
       // notification.
       notification_presenter_->NotifyMultiplePotentialHotspotsNearby();
     }
+
+    was_available_hotspot_notification_shown_ = true;
   }
 
   if (is_final_scan_result) {
@@ -167,7 +168,10 @@
   }
 
   if (final_scan_results.empty()) {
-    RecordHostScanResult(HostScanResultEventType::NOTIFICATION_NOT_SHOWN);
+    RecordHostScanResult(HostScanResultEventType::NO_HOSTS_FOUND);
+  } else if (!was_available_hotspot_notification_shown_) {
+    RecordHostScanResult(
+        HostScanResultEventType::HOSTS_FOUND_BUT_NO_NOTIFICATION_SHOWN);
   } else if (final_scan_results.size() == 1u) {
     RecordHostScanResult(
         HostScanResultEventType::NOTIFICATION_SHOWN_SINGLE_HOST);
@@ -175,6 +179,7 @@
     RecordHostScanResult(
         HostScanResultEventType::NOTIFICATION_SHOWN_MULTIPLE_HOSTS);
   }
+  was_available_hotspot_notification_shown_ = false;
 
   // If the final scan result has been received, the operation is finished.
   // Delete it.
diff --git a/chromeos/components/tether/host_scanner.h b/chromeos/components/tether/host_scanner.h
index a162f35..d2ef16f7 100644
--- a/chromeos/components/tether/host_scanner.h
+++ b/chromeos/components/tether/host_scanner.h
@@ -77,9 +77,10 @@
   FRIEND_TEST_ALL_PREFIXES(HostScannerTest, TestScan_ResultsFromNoDevices);
 
   enum HostScanResultEventType {
-    NOTIFICATION_NOT_SHOWN = 0,
+    NO_HOSTS_FOUND = 0,
     NOTIFICATION_SHOWN_SINGLE_HOST = 1,
     NOTIFICATION_SHOWN_MULTIPLE_HOSTS = 2,
+    HOSTS_FOUND_BUT_NO_NOTIFICATION_SHOWN = 3,
     HOST_SCAN_RESULT_MAX
   };
 
@@ -100,7 +101,8 @@
   HostScanCache* host_scan_cache_;
   base::Clock* clock_;
 
-  bool is_fetching_hosts_;
+  bool is_fetching_hosts_ = false;
+  bool was_available_hotspot_notification_shown_ = false;
   std::unique_ptr<HostScannerOperation> host_scanner_operation_;
   std::unordered_set<std::string> tether_guids_in_cache_before_scan_;
 
diff --git a/chromeos/components/tether/host_scanner_unittest.cc b/chromeos/components/tether/host_scanner_unittest.cc
index 4254a028..98f47776 100644
--- a/chromeos/components/tether/host_scanner_unittest.cc
+++ b/chromeos/components/tether/host_scanner_unittest.cc
@@ -283,8 +283,12 @@
 
     if (is_final_scan_result) {
       HostScanner::HostScanResultEventType expected_event_type =
-          HostScanner::HostScanResultEventType::NOTIFICATION_NOT_SHOWN;
-      if (scanned_device_infos_from_current_scan_.size() == 1) {
+          HostScanner::HostScanResultEventType::NO_HOSTS_FOUND;
+      if (!scanned_device_infos_from_current_scan_.empty() &&
+          is_connected_to_internet) {
+        expected_event_type = HostScanner::HostScanResultEventType::
+            HOSTS_FOUND_BUT_NO_NOTIFICATION_SHOWN;
+      } else if (scanned_device_infos_from_current_scan_.size() == 1) {
         expected_event_type = HostScanner::HostScanResultEventType::
             NOTIFICATION_SHOWN_SINGLE_HOST;
       } else if (scanned_device_infos_from_current_scan_.size() > 1) {
@@ -495,7 +499,7 @@
 
   histogram_tester_.ExpectUniqueSample(
       "InstantTethering.HostScanResult",
-      HostScanner::HostScanResultEventType::NOTIFICATION_NOT_SHOWN, 1);
+      HostScanner::HostScanResultEventType::NO_HOSTS_FOUND, 1);
 }
 
 TEST_F(HostScannerTest, TestScan_ResultsFromSomeDevices) {
diff --git a/components/arc/common/file_system.mojom b/components/arc/common/file_system.mojom
index aa45972..698cf6f5 100644
--- a/components/arc/common/file_system.mojom
+++ b/components/arc/common/file_system.mojom
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
-// Next MinVersion: 5
+// Next MinVersion: 6
 
 module arc.mojom;
 
@@ -27,6 +27,12 @@
   // TODO(crbug.com/672737): Use mojo.common.mojom.Time once the type is
   // converted to a non-native type so that it can be used from Java.
   uint64 last_modified;
+
+  // Path to a real file on the Android VFS corresponding to this document,
+  // e.g. "/storage/emulated/0/DCIM/kitten.jpg".
+  // This value is available in limited DocumentsProviders only. If the
+  // provider does not expose real VFS paths, this field is always set to null.
+  [MinVersion=5] string? android_file_system_path;
 };
 
 // Describes the type of a change made to a document.
@@ -51,7 +57,7 @@
   [MinVersion=3] OnDocumentChanged@0(int64 watcher_id, ChangeType type);
 };
 
-// Next method ID: 9
+// Next method ID: 10
 interface FileSystemInstance {
   // Notes about Android Documents Provider:
   //
@@ -62,8 +68,11 @@
   //   It is the origin part of a content:// URI used to access the Documents
   //   Provider via Content Resolver protocol.
   //   Example: "com.android.providers.media.documents"
+  // - A documents provider may provide one or more roots. Each root is identified
+  //   by a root ID.
   // - A document ID is an opaque string that specifies a particular document
-  //   in a documents provider. Its format varies by providers.
+  //   in a documents provider. Its format varies by providers. Roots also have
+  //   associated document IDs.
   //
   // See the following documents for details about Documents Provider:
   // https://developer.android.com/guide/topics/providers/document-provider.html
@@ -109,6 +118,12 @@
   // URL. When an error occurs, returns null value.
   [MinVersion=4] GetMimeType@8(string url) => (string? mime_type);
 
+  // Queries recent documents of a root specified by |authority| and |root_id|.
+  // If the root exists and it supports recent document queries, a (possibly
+  // empty) list of documents is returned. Otherwise, null is returned.
+  [MinVersion=5] GetRecentDocuments@9(string authority, string root_id) =>
+      (array<Document>? documents);
+
   // Establishes full-duplex communication with the host.
   [MinVersion=3] Init@5(FileSystemHost host_ptr);
 
diff --git a/components/arc/test/fake_file_system_instance.cc b/components/arc/test/fake_file_system_instance.cc
index fdb4e78..1ada10b 100644
--- a/components/arc/test/fake_file_system_instance.cc
+++ b/components/arc/test/fake_file_system_instance.cc
@@ -250,6 +250,15 @@
                  base::Passed(base::make_optional(std::move(children)))));
 }
 
+void FakeFileSystemInstance::GetRecentDocuments(
+    const std::string& authority,
+    const std::string& root_id,
+    const GetRecentDocumentsCallback& callback) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::Bind(callback, base::nullopt));
+}
+
 void FakeFileSystemInstance::Init(mojom::FileSystemHostPtr host) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(host);
diff --git a/components/arc/test/fake_file_system_instance.h b/components/arc/test/fake_file_system_instance.h
index 8e0d4a5..9192d9e 100644
--- a/components/arc/test/fake_file_system_instance.h
+++ b/components/arc/test/fake_file_system_instance.h
@@ -36,6 +36,7 @@
 // Documents provider based functions are:
 // - GetDocument()
 // - GetChildDocuments()
+// - GetRecentDocuments()
 // Fake documents for those functions can be set up by AddDocument().
 //
 // Notes:
@@ -139,6 +140,9 @@
                    const GetFileSizeCallback& callback) override;
   void GetMimeType(const std::string& url,
                    const GetMimeTypeCallback& callback) override;
+  void GetRecentDocuments(const std::string& authority,
+                          const std::string& root_id,
+                          const GetRecentDocumentsCallback& callback) override;
   void Init(mojom::FileSystemHostPtr host) override;
   void OpenFileToRead(const std::string& url,
                       const OpenFileToReadCallback& callback) override;
diff --git a/components/autofill/android/java/src/org/chromium/components/autofill/AutofillProvider.java b/components/autofill/android/java/src/org/chromium/components/autofill/AutofillProvider.java
index ece02e1..9f87219 100644
--- a/components/autofill/android/java/src/org/chromium/components/autofill/AutofillProvider.java
+++ b/components/autofill/android/java/src/org/chromium/components/autofill/AutofillProvider.java
@@ -55,13 +55,9 @@
     /**
      * @return whether query autofill suggestion.
      */
-    // TODO(michaelbai): Change it to abstract after DEP roll.
-    public boolean shouldQueryAutofillSuggestion() {
-        return false;
-    }
+    public abstract boolean shouldQueryAutofillSuggestion();
 
-    // TODO(michaelbai): Change it to abstract after DEP roll.
-    public void queryAutofillSuggestion() {}
+    public abstract void queryAutofillSuggestion();
 
     /**
      * Invoked when filling form is need. AutofillProvider shall ask autofill
diff --git a/components/browser_sync/profile_sync_components_factory_impl.cc b/components/browser_sync/profile_sync_components_factory_impl.cc
index 03f7473..99214549 100644
--- a/components/browser_sync/profile_sync_components_factory_impl.cc
+++ b/components/browser_sync/profile_sync_components_factory_impl.cc
@@ -45,6 +45,7 @@
 #include "components/sync_bookmarks/bookmark_change_processor.h"
 #include "components/sync_bookmarks/bookmark_data_type_controller.h"
 #include "components/sync_bookmarks/bookmark_model_associator.h"
+#include "components/sync_bookmarks/bookmark_model_type_controller.h"
 #include "components/sync_sessions/session_data_type_controller.h"
 #include "google_apis/gaia/oauth2_token_service.h"
 #include "google_apis/gaia/oauth2_token_service_request.h"
@@ -204,9 +205,14 @@
   // Bookmark sync is enabled by default.  Register unless explicitly
   // disabled.
   if (!disabled_types.Has(syncer::BOOKMARKS)) {
-    sync_service->RegisterDataTypeController(
-        base::MakeUnique<BookmarkDataTypeController>(error_callback,
-                                                     sync_client_));
+    if (FeatureList::IsEnabled(switches::kSyncUSSBookmarks)) {
+      sync_service->RegisterDataTypeController(
+          base::MakeUnique<sync_bookmarks::BookmarkModelTypeController>());
+    } else {
+      sync_service->RegisterDataTypeController(
+          base::MakeUnique<BookmarkDataTypeController>(error_callback,
+                                                       sync_client_));
+    }
   }
 
   // These features are enabled only if history is not disabled.
diff --git a/components/offline_pages/core/offline_page_model.h b/components/offline_pages/core/offline_page_model.h
index ee5fba6..c7b89426 100644
--- a/components/offline_pages/core/offline_page_model.h
+++ b/components/offline_pages/core/offline_page_model.h
@@ -44,15 +44,6 @@
 // * how to cancel requests and what to expect
 class OfflinePageModel : public base::SupportsUserData {
  public:
-  // Controls how to search on differnt URLs for pages.
-  enum class URLSearchMode {
-    // Match against the last committed URL only.
-    SEARCH_BY_FINAL_URL_ONLY,
-    // Match against all stored URLs, including the last committed URL and
-    // the original request URL.
-    SEARCH_BY_ALL_URLS
-  };
-
   // Describes the parameters to control how to save a page.
   struct SavePageParams {
     SavePageParams();
diff --git a/components/offline_pages/core/offline_page_model_impl.cc b/components/offline_pages/core/offline_page_model_impl.cc
index c6ee5cab..5a973ee 100644
--- a/components/offline_pages/core/offline_page_model_impl.cc
+++ b/components/offline_pages/core/offline_page_model_impl.cc
@@ -561,7 +561,9 @@
   OfflinePageModelQueryBuilder builder;
   builder
       .SetUrls(OfflinePageModelQuery::Requirement::INCLUDE_MATCHING,
-               std::vector<GURL>(urls.begin(), urls.end()))
+               std::vector<GURL>(urls.begin(), urls.end()),
+               URLSearchMode::SEARCH_BY_FINAL_URL_ONLY,
+               false /* strip_fragment */)
       .RequireRestrictedToOriginalTab(
           OfflinePageModelQueryBuilder::Requirement::EXCLUDE_MATCHING);
   auto pages_to_urls = base::Bind(
@@ -648,43 +650,14 @@
     const GURL& url,
     URLSearchMode url_search_mode,
     const MultipleOfflinePageItemCallback& callback) {
+  OfflinePageModelQueryBuilder builder;
+  builder.SetUrls(OfflinePageModelQuery::Requirement::INCLUDE_MATCHING,
+                  std::vector<GURL>({url}), url_search_mode,
+                  true /* strip_fragment */);
   RunWhenLoaded(
-      base::Bind(&OfflinePageModelImpl::GetPagesByURLWhenLoadDone,
-                 weak_ptr_factory_.GetWeakPtr(), url,
-                 url_search_mode, callback));
-}
-
-void OfflinePageModelImpl::GetPagesByURLWhenLoadDone(
-    const GURL& url,
-    URLSearchMode url_search_mode,
-    const MultipleOfflinePageItemCallback& callback) const {
-  DCHECK(is_loaded_);
-  std::vector<OfflinePageItem> result;
-
-  GURL::Replacements remove_params;
-  remove_params.ClearRef();
-
-  GURL url_without_fragment =
-      url.ReplaceComponents(remove_params);
-
-  for (const auto& id_page_pair : offline_pages_) {
-    // First, search by last committed URL with fragment stripped.
-    if (url_without_fragment ==
-            id_page_pair.second.url.ReplaceComponents(remove_params)) {
-      result.push_back(id_page_pair.second);
-      continue;
-    }
-    // Then, search by original request URL if |url_search_mode| wants it.
-    // Note that we want to do the exact match with fragment included. This is
-    // because original URL is used for redirect purpose and it is always safer
-    // to support the exact redirect.
-    if (url_search_mode == URLSearchMode::SEARCH_BY_ALL_URLS &&
-        url == id_page_pair.second.original_url) {
-      result.push_back(id_page_pair.second);
-    }
-  }
-
-  callback.Run(result);
+      base::Bind(&OfflinePageModelImpl::GetPagesMatchingQueryWhenLoadDone,
+                 weak_ptr_factory_.GetWeakPtr(),
+                 base::Passed(builder.Build(GetPolicyController())), callback));
 }
 
 void OfflinePageModelImpl::CheckMetadataConsistency() {
diff --git a/components/offline_pages/core/offline_page_model_impl_unittest.cc b/components/offline_pages/core/offline_page_model_impl_unittest.cc
index 89b7432c..bc78dd5 100644
--- a/components/offline_pages/core/offline_page_model_impl_unittest.cc
+++ b/components/offline_pages/core/offline_page_model_impl_unittest.cc
@@ -527,8 +527,7 @@
     const GURL& url) {
   MultipleOfflinePageItemResult result;
   model()->GetPagesByURL(
-      url,
-      OfflinePageModel::URLSearchMode::SEARCH_BY_FINAL_URL_ONLY,
+      url, URLSearchMode::SEARCH_BY_FINAL_URL_ONLY,
       base::Bind(&OfflinePageModelImplTest::OnGetMultipleOfflinePageItemsResult,
                  AsWeakPtr(), base::Unretained(&result)));
   PumpLoop();
@@ -539,8 +538,7 @@
     const GURL& url) {
   MultipleOfflinePageItemResult result;
   model()->GetPagesByURL(
-      url,
-      OfflinePageModel::URLSearchMode::SEARCH_BY_ALL_URLS,
+      url, URLSearchMode::SEARCH_BY_ALL_URLS,
       base::Bind(&OfflinePageModelImplTest::OnGetMultipleOfflinePageItemsResult,
                  AsWeakPtr(), base::Unretained(&result)));
   PumpLoop();
diff --git a/components/offline_pages/core/offline_page_model_query.cc b/components/offline_pages/core/offline_page_model_query.cc
index 382e20b..2f87756 100644
--- a/components/offline_pages/core/offline_page_model_query.cc
+++ b/components/offline_pages/core/offline_page_model_query.cc
@@ -11,8 +11,45 @@
 
 namespace offline_pages {
 
+namespace {
+
+int CountMatchingUrls(const GURL& url_pattern,
+                      const std::set<GURL>& urls,
+                      bool strip_fragment) {
+  int count = 0;
+
+  // If |strip_fragment| is true, all urls will be compared after fragments
+  // stripped. Otherwise just do exact matching.
+  if (strip_fragment) {
+    for (const auto& url : urls) {
+      GURL::Replacements remove_params;
+      remove_params.ClearRef();
+      GURL url_without_fragment = url.ReplaceComponents(remove_params);
+      if (url_without_fragment == url_pattern.ReplaceComponents(remove_params))
+        count++;
+    }
+  } else {
+    count = urls.count(url_pattern);
+  }
+  return count;
+}
+
+}  // namespace
+
 using Requirement = OfflinePageModelQuery::Requirement;
 
+OfflinePageModelQuery::URLSearchParams::URLSearchParams() = default;
+
+OfflinePageModelQuery::URLSearchParams::URLSearchParams(
+    std::set<GURL> url_set,
+    URLSearchMode search_mode,
+    bool strip_frag)
+    : urls(url_set), mode(search_mode), strip_fragment(strip_frag) {}
+
+OfflinePageModelQuery::URLSearchParams::URLSearchParams(
+    const URLSearchParams& params) = default;
+OfflinePageModelQuery::URLSearchParams::~URLSearchParams() = default;
+
 OfflinePageModelQueryBuilder::OfflinePageModelQueryBuilder()
     : offline_ids_(std::make_pair(Requirement::UNSET, std::vector<int64_t>())) {
 }
@@ -35,8 +72,13 @@
 
 OfflinePageModelQueryBuilder& OfflinePageModelQueryBuilder::SetUrls(
     Requirement requirement,
-    const std::vector<GURL>& urls) {
-  urls_ = std::make_pair(requirement, urls);
+    const std::vector<GURL>& urls,
+    URLSearchMode search_mode,
+    bool defrag) {
+  urls_ = std::make_pair(
+      requirement,
+      OfflinePageModelQuery::URLSearchParams(
+          std::set<GURL>(urls.begin(), urls.end()), search_mode, defrag));
   return *this;
 }
 
@@ -80,9 +122,11 @@
 
   auto query = base::MakeUnique<OfflinePageModelQuery>();
 
-  query->urls_ = std::make_pair(
-      urls_.first, std::set<GURL>(urls_.second.begin(), urls_.second.end()));
-  urls_ = std::make_pair(Requirement::UNSET, std::vector<GURL>());
+  query->urls_ = urls_;
+  urls_ = std::make_pair(
+      Requirement::UNSET,
+      OfflinePageModelQuery::URLSearchParams(
+          std::set<GURL>(), URLSearchMode::SEARCH_BY_FINAL_URL_ONLY, false));
   query->offline_ids_ = std::make_pair(
       offline_ids_.first, std::set<int64_t>(offline_ids_.second.begin(),
                                             offline_ids_.second.end()));
@@ -176,11 +220,15 @@
   return client_ids_;
 }
 
-std::pair<Requirement, std::set<GURL>>
+std::pair<Requirement, OfflinePageModelQuery::URLSearchParams>
 OfflinePageModelQuery::GetRestrictedToUrls() const {
-  if (urls_.first == Requirement::UNSET)
-    return std::make_pair(Requirement::UNSET, std::set<GURL>());
-
+  if (std::get<0>(urls_) == Requirement::UNSET) {
+    URLSearchParams unset_params;
+    unset_params.urls = std::set<GURL>();
+    unset_params.mode = URLSearchMode::SEARCH_BY_FINAL_URL_ONLY;
+    unset_params.strip_fragment = false;
+    return std::make_pair(Requirement::UNSET, unset_params);
+  }
   return urls_;
 }
 
@@ -198,17 +246,17 @@
       break;
   }
 
-  switch (urls_.first) {
-    case Requirement::UNSET:
-      break;
-    case Requirement::INCLUDE_MATCHING:
-      if (urls_.second.count(item.url) == 0)
-        return false;
-      break;
-    case Requirement::EXCLUDE_MATCHING:
-      if (urls_.second.count(item.url) > 0)
-        return false;
-      break;
+  Requirement url_requirement = urls_.first;
+  URLSearchParams params = urls_.second;
+  if (url_requirement != Requirement::UNSET) {
+    int count = CountMatchingUrls(item.url, params.urls, params.strip_fragment);
+    if (params.mode == URLSearchMode::SEARCH_BY_ALL_URLS)
+      count += CountMatchingUrls(item.original_url, params.urls,
+                                 false /* strip_fragment */);
+    if ((url_requirement == Requirement::INCLUDE_MATCHING && count == 0) ||
+        (url_requirement == Requirement::EXCLUDE_MATCHING && count > 0)) {
+      return false;
+    }
   }
 
   const ClientId& client_id = item.client_id;
diff --git a/components/offline_pages/core/offline_page_model_query.h b/components/offline_pages/core/offline_page_model_query.h
index 4107b39..8378b0ef 100644
--- a/components/offline_pages/core/offline_page_model_query.h
+++ b/components/offline_pages/core/offline_page_model_query.h
@@ -32,13 +32,33 @@
     EXCLUDE_MATCHING,
   };
 
+  struct URLSearchParams {
+    URLSearchParams();
+    URLSearchParams(const URLSearchParams& params);
+    URLSearchParams(std::set<GURL> url_set,
+                    URLSearchMode search_mode,
+                    bool strip_frag);
+    ~URLSearchParams();
+
+    // The set of urls for matching.
+    std::set<GURL> urls;
+    // The mode for searching. By final url only or both final and original
+    // urls.
+    URLSearchMode mode;
+    // Whether fragments should be stripped. It will *not* work on
+    // *|original_url|*.
+    // TODO(crbug.com/753609): Try to make this also available when matching for
+    // |original_url|.
+    bool strip_fragment;
+  };
+
   OfflinePageModelQuery();
   virtual ~OfflinePageModelQuery();
 
   std::pair<bool, std::set<std::string>> GetRestrictedToNamespaces() const;
   std::pair<Requirement, std::set<int64_t>> GetRestrictedToOfflineIds() const;
   std::pair<Requirement, std::set<ClientId>> GetRestrictedToClientIds() const;
-  std::pair<Requirement, std::set<GURL>> GetRestrictedToUrls() const;
+  std::pair<Requirement, URLSearchParams> GetRestrictedToUrls() const;
 
   // This is the workhorse function that is used by the in-memory offline page
   // model, given a page it will find out whether that page matches the query.
@@ -51,7 +71,7 @@
 
   std::pair<Requirement, std::set<int64_t>> offline_ids_;
   std::pair<Requirement, std::set<ClientId>> client_ids_;
-  std::pair<Requirement, std::set<GURL>> urls_;
+  std::pair<Requirement, URLSearchParams> urls_;
 
   DISALLOW_COPY_AND_ASSIGN(OfflinePageModelQuery);
 };
@@ -82,8 +102,16 @@
 
   // Sets the URLs that are valid for this request.  If called multiple times,
   // overwrites previous URL restrictions.
+  // |search_mode| is used to control if the URL will be matched with final
+  // URL only or both final URL and original URL.
+  // If |strip_fragment| is true, *only final* urls will be matched without
+  // fragment.
+  // TODO(crbug.com/753609): Try to unify fragment handling for original and
+  // final urls.
   OfflinePageModelQueryBuilder& SetUrls(Requirement requirement,
-                                        const std::vector<GURL>& urls);
+                                        const std::vector<GURL>& urls,
+                                        URLSearchMode search_mode,
+                                        bool strip_fragment);
 
   // Only include pages whose namespaces satisfy
   // ClientPolicyController::IsRemovedOnCacheReset(|namespace|) ==
@@ -131,7 +159,7 @@
 
   std::pair<Requirement, std::vector<int64_t>> offline_ids_;
   std::pair<Requirement, std::vector<ClientId>> client_ids_;
-  std::pair<Requirement, std::vector<GURL>> urls_;
+  std::pair<Requirement, OfflinePageModelQuery::URLSearchParams> urls_;
 
   Requirement removed_on_cache_reset_ = Requirement::UNSET;
   Requirement supported_by_download_ = Requirement::UNSET;
diff --git a/components/offline_pages/core/offline_page_model_query_unittest.cc b/components/offline_pages/core/offline_page_model_query_unittest.cc
index 01eb27b6a..d3d90b5 100644
--- a/components/offline_pages/core/offline_page_model_query_unittest.cc
+++ b/components/offline_pages/core/offline_page_model_query_unittest.cc
@@ -14,6 +14,7 @@
 namespace offline_pages {
 
 using Requirement = OfflinePageModelQueryBuilder::Requirement;
+using URLSearchParams = OfflinePageModelQuery::URLSearchParams;
 
 namespace {
 
@@ -26,6 +27,9 @@
 const OfflinePageItem kTestItem2(kUrl2, 2, kClientId2, base::FilePath(), 2);
 
 const char kTestNamespace[] = "test_namespace";
+const GURL kTempUrl = GURL("https://temp.temp");
+const GURL kTempFragUrl = GURL("https://temp.temp#frag1");
+const GURL kFragUrl1 = GURL("https://ktestitem1.com#frag");
 }  // namespace
 
 class OfflinePageModelQueryTest : public testing::Test {
@@ -60,6 +64,14 @@
     return OfflinePageItem(GURL("https://download.com"), 7,
                            {kLastNNamespace, "id1"}, base::FilePath(), 7);
   }
+
+  const OfflinePageItem CreatePageWithUrls(const GURL& url,
+                                           const GURL& original_url) {
+    OfflinePageItem page = kTestItem1;
+    page.url = url;
+    page.original_url = original_url;
+    return page;
+  }
 };
 
 OfflinePageModelQueryTest::OfflinePageModelQueryTest() {
@@ -215,67 +227,85 @@
 
 TEST_F(OfflinePageModelQueryTest, UrlsSet) {
   std::vector<GURL> urls = {kUrl1, GURL("https://abc.def")};
-  builder_.SetUrls(Requirement::INCLUDE_MATCHING, urls);
+  builder_.SetUrls(Requirement::INCLUDE_MATCHING, urls,
+                   URLSearchMode::SEARCH_BY_FINAL_URL_ONLY, false);
 
   std::unique_ptr<OfflinePageModelQuery> query = builder_.Build(&policy_);
 
   auto restriction = query->GetRestrictedToUrls();
   const Requirement& requirement = restriction.first;
-  const std::set<GURL>& urls_out = restriction.second;
+  const URLSearchParams& params = restriction.second;
 
   EXPECT_EQ(Requirement::INCLUDE_MATCHING, requirement);
+  EXPECT_EQ(URLSearchMode::SEARCH_BY_FINAL_URL_ONLY, params.mode);
+  EXPECT_FALSE(params.strip_fragment);
 
-  ASSERT_EQ(urls.size(), urls_out.size());
+  ASSERT_EQ(urls.size(), params.urls.size());
   for (auto url : urls) {
-    EXPECT_EQ(1U, urls_out.count(url)) << "Did not find " << url
-                                       << "in query restrictions.";
+    EXPECT_EQ(1U, params.urls.count(url))
+        << "Did not find " << url << "in query restrictions.";
   }
 
   EXPECT_TRUE(query->Matches(kTestItem1));
   EXPECT_FALSE(query->Matches(kTestItem2));
+  EXPECT_TRUE(query->Matches(CreatePageWithUrls(kUrl1, kTempUrl)));
+  EXPECT_FALSE(query->Matches(CreatePageWithUrls(kTempUrl, kUrl1)));
+  EXPECT_FALSE(
+      query->Matches(CreatePageWithUrls(GURL(""), GURL("https://abc.def"))));
 }
 
 TEST_F(OfflinePageModelQueryTest, UrlsSet_Exclude) {
   std::vector<GURL> urls = {kUrl1, GURL("https://abc.def")};
-  builder_.SetUrls(Requirement::EXCLUDE_MATCHING, urls);
+  builder_.SetUrls(Requirement::EXCLUDE_MATCHING, urls,
+                   URLSearchMode::SEARCH_BY_FINAL_URL_ONLY, false);
 
   std::unique_ptr<OfflinePageModelQuery> query = builder_.Build(&policy_);
 
   auto restriction = query->GetRestrictedToUrls();
   const Requirement& requirement = restriction.first;
-  const std::set<GURL>& urls_out = restriction.second;
+  const URLSearchParams& params = restriction.second;
 
   EXPECT_EQ(Requirement::EXCLUDE_MATCHING, requirement);
+  EXPECT_EQ(URLSearchMode::SEARCH_BY_FINAL_URL_ONLY, params.mode);
+  EXPECT_FALSE(params.strip_fragment);
 
-  ASSERT_EQ(urls.size(), urls_out.size());
+  ASSERT_EQ(urls.size(), params.urls.size());
   for (auto url : urls) {
-    EXPECT_EQ(1U, urls_out.count(url)) << "Did not find " << url
-                                       << "in query restrictions.";
+    EXPECT_EQ(1U, params.urls.count(url))
+        << "Did not find " << url << "in query restrictions.";
   }
 
   EXPECT_FALSE(query->Matches(kTestItem1));
   EXPECT_TRUE(query->Matches(kTestItem2));
+  EXPECT_FALSE(query->Matches(CreatePageWithUrls(kUrl1, kTempUrl)));
+  EXPECT_TRUE(query->Matches(CreatePageWithUrls(kTempUrl, kUrl1)));
+  EXPECT_TRUE(
+      query->Matches(CreatePageWithUrls(GURL(""), GURL("https://abc.def"))));
 }
 
 TEST_F(OfflinePageModelQueryTest, UrlsReplace) {
   std::vector<GURL> urls = {kUrl1, GURL("https://abc.def")};
   std::vector<GURL> urls2 = {kUrl2, GURL("https://abc.def")};
 
-  builder_.SetUrls(Requirement::INCLUDE_MATCHING, urls);
-  builder_.SetUrls(Requirement::INCLUDE_MATCHING, urls2);
+  builder_.SetUrls(Requirement::INCLUDE_MATCHING, urls,
+                   URLSearchMode::SEARCH_BY_FINAL_URL_ONLY, false);
+  builder_.SetUrls(Requirement::INCLUDE_MATCHING, urls2,
+                   URLSearchMode::SEARCH_BY_FINAL_URL_ONLY, false);
 
   std::unique_ptr<OfflinePageModelQuery> query = builder_.Build(&policy_);
 
   auto restriction = query->GetRestrictedToUrls();
   const Requirement& requirement = restriction.first;
-  const std::set<GURL>& urls_out = restriction.second;
+  const URLSearchParams& params = restriction.second;
 
   EXPECT_EQ(Requirement::INCLUDE_MATCHING, requirement);
+  EXPECT_EQ(URLSearchMode::SEARCH_BY_FINAL_URL_ONLY, params.mode);
+  EXPECT_FALSE(params.strip_fragment);
 
-  ASSERT_EQ(urls2.size(), urls_out.size());
+  ASSERT_EQ(urls2.size(), params.urls.size());
   for (auto url : urls2) {
-    EXPECT_EQ(1U, urls_out.count(url)) << "Did not find " << url
-                                       << "in query restrictions.";
+    EXPECT_EQ(1U, params.urls.count(url))
+        << "Did not find " << url << "in query restrictions.";
   }
 
   EXPECT_FALSE(query->Matches(kTestItem1));
@@ -452,4 +482,210 @@
   EXPECT_FALSE(query->Matches(test_namespace_page()));
 }
 
+TEST_F(OfflinePageModelQueryTest, UrlsSet_SearchByAll) {
+  std::vector<GURL> urls = {kUrl1, GURL("https://abc.def")};
+  builder_.SetUrls(Requirement::INCLUDE_MATCHING, urls,
+                   URLSearchMode::SEARCH_BY_ALL_URLS, false);
+
+  std::unique_ptr<OfflinePageModelQuery> query = builder_.Build(&policy_);
+
+  auto restriction = query->GetRestrictedToUrls();
+  const Requirement& requirement = restriction.first;
+  const URLSearchParams& params = restriction.second;
+
+  EXPECT_EQ(Requirement::INCLUDE_MATCHING, requirement);
+  EXPECT_EQ(URLSearchMode::SEARCH_BY_ALL_URLS, params.mode);
+  EXPECT_FALSE(params.strip_fragment);
+
+  ASSERT_EQ(urls.size(), params.urls.size());
+  for (auto url : urls) {
+    EXPECT_EQ(1U, params.urls.count(url))
+        << "Did not find " << url << "in query restrictions.";
+  }
+
+  EXPECT_TRUE(query->Matches(kTestItem1));
+  EXPECT_FALSE(query->Matches(kTestItem2));
+  EXPECT_TRUE(query->Matches(CreatePageWithUrls(kUrl1, kTempUrl)));
+  EXPECT_TRUE(query->Matches(CreatePageWithUrls(kTempUrl, kUrl1)));
+  EXPECT_TRUE(
+      query->Matches(CreatePageWithUrls(kUrl1, GURL("https://abc.def"))));
+  EXPECT_TRUE(
+      query->Matches(CreatePageWithUrls(kTempUrl, GURL("https://abc.def"))));
+  EXPECT_TRUE(
+      query->Matches(CreatePageWithUrls(GURL("https://abc.def"), GURL(""))));
+  EXPECT_FALSE(query->Matches(CreatePageWithUrls(kTempUrl, GURL(""))));
+}
+
+TEST_F(OfflinePageModelQueryTest, UrlsSet_Exclude_SearchByAll) {
+  std::vector<GURL> urls = {kUrl1, GURL("https://abc.def")};
+  builder_.SetUrls(Requirement::EXCLUDE_MATCHING, urls,
+                   URLSearchMode::SEARCH_BY_ALL_URLS, false);
+
+  std::unique_ptr<OfflinePageModelQuery> query = builder_.Build(&policy_);
+
+  auto restriction = query->GetRestrictedToUrls();
+  const Requirement& requirement = restriction.first;
+  const URLSearchParams& params = restriction.second;
+
+  EXPECT_EQ(Requirement::EXCLUDE_MATCHING, requirement);
+  EXPECT_EQ(URLSearchMode::SEARCH_BY_ALL_URLS, params.mode);
+  EXPECT_FALSE(params.strip_fragment);
+
+  ASSERT_EQ(urls.size(), params.urls.size());
+  for (auto url : urls) {
+    EXPECT_EQ(1U, params.urls.count(url))
+        << "Did not find " << url << "in query restrictions.";
+  }
+
+  EXPECT_FALSE(query->Matches(kTestItem1));
+  EXPECT_TRUE(query->Matches(kTestItem2));
+  EXPECT_FALSE(query->Matches(CreatePageWithUrls(kUrl1, kTempUrl)));
+  EXPECT_FALSE(query->Matches(CreatePageWithUrls(kTempUrl, kUrl1)));
+  EXPECT_FALSE(
+      query->Matches(CreatePageWithUrls(kUrl1, GURL("https://abc.def"))));
+  EXPECT_FALSE(
+      query->Matches(CreatePageWithUrls(kTempUrl, GURL("https://abc.def"))));
+  EXPECT_FALSE(
+      query->Matches(CreatePageWithUrls(GURL("https://abc.def"), GURL(""))));
+  EXPECT_TRUE(query->Matches(CreatePageWithUrls(kTempUrl, GURL(""))));
+}
+
+TEST_F(OfflinePageModelQueryTest, UrlsSet_Defrag) {
+  std::vector<GURL> urls = {kUrl1, GURL("https://abc.def")};
+  builder_.SetUrls(Requirement::INCLUDE_MATCHING, urls,
+                   URLSearchMode::SEARCH_BY_FINAL_URL_ONLY, true);
+
+  std::unique_ptr<OfflinePageModelQuery> query = builder_.Build(&policy_);
+
+  auto restriction = query->GetRestrictedToUrls();
+  const Requirement& requirement = restriction.first;
+  const URLSearchParams& params = restriction.second;
+
+  EXPECT_EQ(Requirement::INCLUDE_MATCHING, requirement);
+  EXPECT_EQ(URLSearchMode::SEARCH_BY_FINAL_URL_ONLY, params.mode);
+  EXPECT_TRUE(params.strip_fragment);
+
+  ASSERT_EQ(urls.size(), params.urls.size());
+  for (auto url : urls) {
+    EXPECT_EQ(1U, params.urls.count(url))
+        << "Did not find " << url << "in query restrictions.";
+  }
+
+  EXPECT_TRUE(query->Matches(kTestItem1));
+  EXPECT_FALSE(query->Matches(kTestItem2));
+  EXPECT_TRUE(query->Matches(CreatePageWithUrls(kFragUrl1, kTempUrl)));
+  EXPECT_FALSE(query->Matches(CreatePageWithUrls(kTempUrl, kUrl1)));
+  EXPECT_TRUE(
+      query->Matches(CreatePageWithUrls(kFragUrl1, GURL("https://abc.def"))));
+  EXPECT_FALSE(
+      query->Matches(CreatePageWithUrls(kTempUrl, GURL("https://abc.def"))));
+  EXPECT_TRUE(
+      query->Matches(CreatePageWithUrls(GURL("https://abc.def"), GURL(""))));
+  EXPECT_TRUE(query->Matches(
+      CreatePageWithUrls(GURL("https://abc.def#frag2"), GURL(""))));
+}
+
+TEST_F(OfflinePageModelQueryTest, UrlsSet_Exclude_Defrag) {
+  std::vector<GURL> urls = {kUrl1, GURL("https://abc.def")};
+  builder_.SetUrls(Requirement::EXCLUDE_MATCHING, urls,
+                   URLSearchMode::SEARCH_BY_FINAL_URL_ONLY, true);
+
+  std::unique_ptr<OfflinePageModelQuery> query = builder_.Build(&policy_);
+
+  auto restriction = query->GetRestrictedToUrls();
+  const Requirement& requirement = restriction.first;
+  const URLSearchParams& params = restriction.second;
+
+  EXPECT_EQ(Requirement::EXCLUDE_MATCHING, requirement);
+  EXPECT_EQ(URLSearchMode::SEARCH_BY_FINAL_URL_ONLY, params.mode);
+  EXPECT_TRUE(params.strip_fragment);
+
+  ASSERT_EQ(urls.size(), params.urls.size());
+  for (auto url : urls) {
+    EXPECT_EQ(1U, params.urls.count(url))
+        << "Did not find " << url << "in query restrictions.";
+  }
+
+  EXPECT_FALSE(query->Matches(kTestItem1));
+  EXPECT_TRUE(query->Matches(kTestItem2));
+  EXPECT_FALSE(query->Matches(CreatePageWithUrls(kFragUrl1, kTempUrl)));
+  EXPECT_TRUE(query->Matches(CreatePageWithUrls(kTempUrl, kUrl1)));
+  EXPECT_FALSE(
+      query->Matches(CreatePageWithUrls(kFragUrl1, GURL("https://abc.def"))));
+  EXPECT_TRUE(
+      query->Matches(CreatePageWithUrls(kTempUrl, GURL("https://abc.def"))));
+  EXPECT_FALSE(
+      query->Matches(CreatePageWithUrls(GURL("https://abc.def"), GURL(""))));
+  EXPECT_FALSE(query->Matches(
+      CreatePageWithUrls(GURL("https://abc.def#frag2"), GURL(""))));
+}
+
+TEST_F(OfflinePageModelQueryTest, UrlsSet_SearchByAll_Defrag) {
+  std::vector<GURL> urls = {kUrl1, GURL("https://abc.def")};
+  builder_.SetUrls(Requirement::INCLUDE_MATCHING, urls,
+                   URLSearchMode::SEARCH_BY_ALL_URLS, true);
+
+  std::unique_ptr<OfflinePageModelQuery> query = builder_.Build(&policy_);
+
+  auto restriction = query->GetRestrictedToUrls();
+  const Requirement& requirement = restriction.first;
+  const URLSearchParams& params = restriction.second;
+
+  EXPECT_EQ(Requirement::INCLUDE_MATCHING, requirement);
+  EXPECT_EQ(URLSearchMode::SEARCH_BY_ALL_URLS, params.mode);
+  EXPECT_TRUE(params.strip_fragment);
+
+  ASSERT_EQ(urls.size(), params.urls.size());
+  for (auto url : urls) {
+    EXPECT_EQ(1U, params.urls.count(url))
+        << "Did not find " << url << "in query restrictions.";
+  }
+
+  EXPECT_TRUE(query->Matches(kTestItem1));
+  EXPECT_FALSE(query->Matches(kTestItem2));
+  EXPECT_TRUE(query->Matches(CreatePageWithUrls(kFragUrl1, kTempUrl)));
+  EXPECT_FALSE(query->Matches(CreatePageWithUrls(kTempUrl, kFragUrl1)));
+  EXPECT_TRUE(
+      query->Matches(CreatePageWithUrls(kFragUrl1, GURL("https://abc.def"))));
+  EXPECT_FALSE(query->Matches(
+      CreatePageWithUrls(kTempUrl, GURL("https://abc.def#frag2"))));
+  EXPECT_TRUE(
+      query->Matches(CreatePageWithUrls(GURL("https://abc.def"), GURL(""))));
+  EXPECT_FALSE(query->Matches(CreatePageWithUrls(kTempFragUrl, GURL(""))));
+}
+
+TEST_F(OfflinePageModelQueryTest, UrlsSet_Exclude_SearchByAll_Defrag) {
+  std::vector<GURL> urls = {kUrl1, GURL("https://abc.def")};
+  builder_.SetUrls(Requirement::EXCLUDE_MATCHING, urls,
+                   URLSearchMode::SEARCH_BY_ALL_URLS, true);
+
+  std::unique_ptr<OfflinePageModelQuery> query = builder_.Build(&policy_);
+
+  auto restriction = query->GetRestrictedToUrls();
+  const Requirement& requirement = restriction.first;
+  const URLSearchParams& params = restriction.second;
+
+  EXPECT_EQ(Requirement::EXCLUDE_MATCHING, requirement);
+  EXPECT_EQ(URLSearchMode::SEARCH_BY_ALL_URLS, params.mode);
+  EXPECT_TRUE(params.strip_fragment);
+
+  ASSERT_EQ(urls.size(), params.urls.size());
+  for (auto url : urls) {
+    EXPECT_EQ(1U, params.urls.count(url))
+        << "Did not find " << url << "in query restrictions.";
+  }
+
+  EXPECT_FALSE(query->Matches(kTestItem1));
+  EXPECT_TRUE(query->Matches(kTestItem2));
+  EXPECT_FALSE(query->Matches(CreatePageWithUrls(kFragUrl1, kTempFragUrl)));
+  EXPECT_TRUE(query->Matches(CreatePageWithUrls(kTempUrl, kFragUrl1)));
+  EXPECT_FALSE(
+      query->Matches(CreatePageWithUrls(kUrl1, GURL("https://abc.def#frag2"))));
+  EXPECT_TRUE(query->Matches(
+      CreatePageWithUrls(kTempUrl, GURL("https://abc.def#frag2"))));
+  EXPECT_FALSE(
+      query->Matches(CreatePageWithUrls(GURL("https://abc.def"), GURL(""))));
+  EXPECT_TRUE(query->Matches(CreatePageWithUrls(kTempFragUrl, GURL(""))));
+}
+
 }  // namespace offline_pages
diff --git a/components/offline_pages/core/offline_page_types.h b/components/offline_pages/core/offline_page_types.h
index 552b32a..34dfc84 100644
--- a/components/offline_pages/core/offline_page_types.h
+++ b/components/offline_pages/core/offline_page_types.h
@@ -72,6 +72,15 @@
   RESULT_COUNT,
 };
 
+// Controls how to search on differnt URLs for pages.
+enum class URLSearchMode {
+  // Match against the last committed URL only.
+  SEARCH_BY_FINAL_URL_ONLY,
+  // Match against all stored URLs, including the last committed URL and
+  // the original request URL.
+  SEARCH_BY_ALL_URLS,
+};
+
 typedef std::set<GURL> CheckPagesExistOfflineResult;
 typedef std::vector<int64_t> MultipleOfflineIdResult;
 typedef std::vector<OfflinePageItem> MultipleOfflinePageItemResult;
diff --git a/components/sync/driver/async_directory_type_controller.cc b/components/sync/driver/async_directory_type_controller.cc
index 51576a6..af25444 100644
--- a/components/sync/driver/async_directory_type_controller.cc
+++ b/components/sync/driver/async_directory_type_controller.cc
@@ -144,11 +144,6 @@
   state_ = NOT_RUNNING;
 }
 
-std::string AsyncDirectoryTypeController::name() const {
-  // For logging only.
-  return ModelTypeToString(type());
-}
-
 DataTypeController::State AsyncDirectoryTypeController::state() const {
   return state_;
 }
diff --git a/components/sync/driver/async_directory_type_controller.h b/components/sync/driver/async_directory_type_controller.h
index 90e2975..1ac3f5b 100644
--- a/components/sync/driver/async_directory_type_controller.h
+++ b/components/sync/driver/async_directory_type_controller.h
@@ -39,7 +39,6 @@
   void StartAssociating(const StartCallback& start_callback) override;
   void Stop() override;
   ChangeProcessor* GetChangeProcessor() const override;
-  std::string name() const override;
   State state() const override;
 
   // Used by tests to override the factory used to create
diff --git a/components/sync/driver/data_type_controller.h b/components/sync/driver/data_type_controller.h
index 8fd7bd8..473f7827 100644
--- a/components/sync/driver/data_type_controller.h
+++ b/components/sync/driver/data_type_controller.h
@@ -137,7 +137,7 @@
   virtual void Stop() = 0;
 
   // Name of this data type.  For logging purposes only.
-  virtual std::string name() const = 0;
+  std::string name() const { return ModelTypeToString(type()); }
 
   // Current state of the data type controller.
   virtual State state() const = 0;
diff --git a/components/sync/driver/fake_data_type_controller.cc b/components/sync/driver/fake_data_type_controller.cc
index de640d19..660f2e3e 100644
--- a/components/sync/driver/fake_data_type_controller.cc
+++ b/components/sync/driver/fake_data_type_controller.cc
@@ -112,10 +112,6 @@
   state_ = NOT_RUNNING;
 }
 
-std::string FakeDataTypeController::name() const {
-  return ModelTypeToString(type());
-}
-
 ChangeProcessor* FakeDataTypeController::GetChangeProcessor() const {
   return nullptr;
 }
diff --git a/components/sync/driver/fake_data_type_controller.h b/components/sync/driver/fake_data_type_controller.h
index 5add35c6..43de65a 100644
--- a/components/sync/driver/fake_data_type_controller.h
+++ b/components/sync/driver/fake_data_type_controller.h
@@ -34,7 +34,6 @@
                            ModelTypeConfigurer* configurer) override;
   void StartAssociating(const StartCallback& start_callback) override;
   void Stop() override;
-  std::string name() const override;
   ChangeProcessor* GetChangeProcessor() const override;
   State state() const override;
   bool ReadyForStart() const override;
diff --git a/components/sync/driver/frontend_data_type_controller.cc b/components/sync/driver/frontend_data_type_controller.cc
index 1bf2c18..c68abb2 100644
--- a/components/sync/driver/frontend_data_type_controller.cc
+++ b/components/sync/driver/frontend_data_type_controller.cc
@@ -106,11 +106,6 @@
   state_ = NOT_RUNNING;
 }
 
-std::string FrontendDataTypeController::name() const {
-  // For logging only.
-  return ModelTypeToString(type());
-}
-
 DataTypeController::State FrontendDataTypeController::state() const {
   return state_;
 }
diff --git a/components/sync/driver/frontend_data_type_controller.h b/components/sync/driver/frontend_data_type_controller.h
index a873b2c..69b61aa 100644
--- a/components/sync/driver/frontend_data_type_controller.h
+++ b/components/sync/driver/frontend_data_type_controller.h
@@ -46,7 +46,6 @@
   void LoadModels(const ModelLoadCallback& model_load_callback) override;
   void StartAssociating(const StartCallback& start_callback) override;
   void Stop() override;
-  std::string name() const override;
   State state() const override;
 
  protected:
diff --git a/components/sync/driver/model_type_controller.cc b/components/sync/driver/model_type_controller.cc
index 69f56b9..a55b5977 100644
--- a/components/sync/driver/model_type_controller.cc
+++ b/components/sync/driver/model_type_controller.cc
@@ -217,11 +217,6 @@
   state_ = NOT_RUNNING;
 }
 
-std::string ModelTypeController::name() const {
-  // For logging only.
-  return ModelTypeToString(type());
-}
-
 DataTypeController::State ModelTypeController::state() const {
   return state_;
 }
diff --git a/components/sync/driver/model_type_controller.h b/components/sync/driver/model_type_controller.h
index 496c418..203f3bc 100644
--- a/components/sync/driver/model_type_controller.h
+++ b/components/sync/driver/model_type_controller.h
@@ -46,7 +46,6 @@
   void ActivateDataType(ModelTypeConfigurer* configurer) override;
   void DeactivateDataType(ModelTypeConfigurer* configurer) override;
   void Stop() override;
-  std::string name() const override;
   State state() const override;
   void GetAllNodes(const AllNodesCallback& callback) override;
   void GetStatusCounters(const StatusCountersCallback& callback) override;
diff --git a/components/sync/driver/proxy_data_type_controller.cc b/components/sync/driver/proxy_data_type_controller.cc
index 837145b..2a0e29d 100644
--- a/components/sync/driver/proxy_data_type_controller.cc
+++ b/components/sync/driver/proxy_data_type_controller.cc
@@ -58,11 +58,6 @@
   state_ = NOT_RUNNING;
 }
 
-std::string ProxyDataTypeController::name() const {
-  // For logging only.
-  return ModelTypeToString(type());
-}
-
 DataTypeController::State ProxyDataTypeController::state() const {
   return state_;
 }
diff --git a/components/sync/driver/proxy_data_type_controller.h b/components/sync/driver/proxy_data_type_controller.h
index 93b3c13..661c093 100644
--- a/components/sync/driver/proxy_data_type_controller.h
+++ b/components/sync/driver/proxy_data_type_controller.h
@@ -29,7 +29,6 @@
                            ModelTypeConfigurer* configurer) override;
   void StartAssociating(const StartCallback& start_callback) override;
   void Stop() override;
-  std::string name() const override;
   State state() const override;
   void ActivateDataType(ModelTypeConfigurer* configurer) override;
   void DeactivateDataType(ModelTypeConfigurer* configurer) override;
diff --git a/components/sync/driver/sync_driver_switches.cc b/components/sync/driver/sync_driver_switches.cc
index 5e745ac..a0e74ef5 100644
--- a/components/sync/driver/sync_driver_switches.cc
+++ b/components/sync/driver/sync_driver_switches.cc
@@ -53,6 +53,10 @@
 const base::Feature kSyncUSSAutocomplete{"SyncUSSAutocomplete",
                                          base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enable USS implementation of Bookmarks datatype.
+const base::Feature kSyncUSSBookmarks{"SyncUSSBookmarks",
+                                      base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Enables USS implementation of DeviceInfo datatype. This flag controls whether
 // SyncableService based or ModelTypeSyncBridge based implementation is used for
 // DeviceInfo type.
diff --git a/components/sync/driver/sync_driver_switches.h b/components/sync/driver/sync_driver_switches.h
index bce16be..8b2f121 100644
--- a/components/sync/driver/sync_driver_switches.h
+++ b/components/sync/driver/sync_driver_switches.h
@@ -24,8 +24,9 @@
 extern const base::Feature kSyncUserLanguageDetectionEvents;
 extern const base::Feature kSyncUserTranslationEvents;
 extern const base::Feature kSyncUSSAutocomplete;
-extern const base::Feature kSyncUSSTypedURL;
+extern const base::Feature kSyncUSSBookmarks;
 extern const base::Feature kSyncUSSDeviceInfo;
+extern const base::Feature kSyncUSSTypedURL;
 
 }  // namespace switches
 
diff --git a/components/sync_bookmarks/BUILD.gn b/components/sync_bookmarks/BUILD.gn
index e283f687..2cc8268 100644
--- a/components/sync_bookmarks/BUILD.gn
+++ b/components/sync_bookmarks/BUILD.gn
@@ -12,6 +12,10 @@
     "bookmark_data_type_controller.h",
     "bookmark_model_associator.cc",
     "bookmark_model_associator.h",
+    "bookmark_model_type_controller.cc",
+    "bookmark_model_type_controller.h",
+    "bookmark_model_type_processor.cc",
+    "bookmark_model_type_processor.h",
   ]
 
   deps = [
@@ -30,6 +34,7 @@
 
   sources = [
     "bookmark_data_type_controller_unittest.cc",
+    "bookmark_model_type_processor_unittest.cc",
   ]
 
   deps = [
diff --git a/components/sync_bookmarks/bookmark_data_type_controller.h b/components/sync_bookmarks/bookmark_data_type_controller.h
index 62c27415..781333d 100644
--- a/components/sync_bookmarks/bookmark_data_type_controller.h
+++ b/components/sync_bookmarks/bookmark_data_type_controller.h
@@ -7,7 +7,6 @@
 
 #include <string>
 
-#include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/scoped_observer.h"
 #include "components/bookmarks/browser/base_bookmark_model_observer.h"
diff --git a/components/sync_bookmarks/bookmark_model_type_controller.cc b/components/sync_bookmarks/bookmark_model_type_controller.cc
new file mode 100644
index 0000000..832fe9a
--- /dev/null
+++ b/components/sync_bookmarks/bookmark_model_type_controller.cc
@@ -0,0 +1,71 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/sync_bookmarks/bookmark_model_type_controller.h"
+
+namespace sync_bookmarks {
+
+BookmarkModelTypeController::BookmarkModelTypeController()
+    : DataTypeController(syncer::BOOKMARKS) {}
+
+bool BookmarkModelTypeController::ShouldLoadModelBeforeConfigure() const {
+  NOTIMPLEMENTED();
+  return false;
+}
+
+void BookmarkModelTypeController::BeforeLoadModels(
+    syncer::ModelTypeConfigurer* configurer) {
+  NOTIMPLEMENTED();
+}
+
+void BookmarkModelTypeController::LoadModels(
+    const ModelLoadCallback& model_load_callback) {
+  NOTIMPLEMENTED();
+}
+
+void BookmarkModelTypeController::RegisterWithBackend(
+    base::Callback<void(bool)> set_downloaded,
+    syncer::ModelTypeConfigurer* configurer) {
+  NOTIMPLEMENTED();
+}
+
+void BookmarkModelTypeController::StartAssociating(
+    const StartCallback& start_callback) {
+  NOTIMPLEMENTED();
+}
+
+void BookmarkModelTypeController::ActivateDataType(
+    syncer::ModelTypeConfigurer* configurer) {
+  NOTIMPLEMENTED();
+}
+
+void BookmarkModelTypeController::DeactivateDataType(
+    syncer::ModelTypeConfigurer* configurer) {
+  NOTIMPLEMENTED();
+}
+
+void BookmarkModelTypeController::Stop() {
+  NOTIMPLEMENTED();
+}
+
+syncer::DataTypeController::State BookmarkModelTypeController::state() const {
+  NOTIMPLEMENTED();
+  return NOT_RUNNING;
+}
+
+void BookmarkModelTypeController::GetAllNodes(
+    const AllNodesCallback& callback) {
+  NOTIMPLEMENTED();
+}
+
+void BookmarkModelTypeController::GetStatusCounters(
+    const StatusCountersCallback& callback) {
+  NOTIMPLEMENTED();
+}
+
+void BookmarkModelTypeController::RecordMemoryUsageHistogram() {
+  NOTIMPLEMENTED();
+}
+
+}  // namespace sync_bookmarks
diff --git a/components/sync_bookmarks/bookmark_model_type_controller.h b/components/sync_bookmarks/bookmark_model_type_controller.h
new file mode 100644
index 0000000..49f69745
--- /dev/null
+++ b/components/sync_bookmarks/bookmark_model_type_controller.h
@@ -0,0 +1,40 @@
+// 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_SYNC_BOOKMARKS_BOOKMARK_MODEL_TYPE_CONTROLLER_H_
+#define COMPONENTS_SYNC_BOOKMARKS_BOOKMARK_MODEL_TYPE_CONTROLLER_H_
+
+#include "base/macros.h"
+#include "components/sync/driver/data_type_controller.h"
+
+namespace sync_bookmarks {
+
+// A class that manages the startup and shutdown of bookmark sync implemented
+// through USS APIs.
+class BookmarkModelTypeController : public syncer::DataTypeController {
+ public:
+  BookmarkModelTypeController();
+
+  // syncer::DataTypeController implementation.
+  bool ShouldLoadModelBeforeConfigure() const override;
+  void BeforeLoadModels(syncer::ModelTypeConfigurer* configurer) override;
+  void LoadModels(const ModelLoadCallback& model_load_callback) override;
+  void RegisterWithBackend(base::Callback<void(bool)> set_downloaded,
+                           syncer::ModelTypeConfigurer* configurer) override;
+  void StartAssociating(const StartCallback& start_callback) override;
+  void ActivateDataType(syncer::ModelTypeConfigurer* configurer) override;
+  void DeactivateDataType(syncer::ModelTypeConfigurer* configurer) override;
+  void Stop() override;
+  State state() const override;
+  void GetAllNodes(const AllNodesCallback& callback) override;
+  void GetStatusCounters(const StatusCountersCallback& callback) override;
+  void RecordMemoryUsageHistogram() override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(BookmarkModelTypeController);
+};
+
+}  // namespace sync_bookmarks
+
+#endif  // COMPONENTS_SYNC_BOOKMARKS_BOOKMARK_MODEL_TYPE_CONTROLLER_H_
diff --git a/components/sync_bookmarks/bookmark_model_type_processor.cc b/components/sync_bookmarks/bookmark_model_type_processor.cc
new file mode 100644
index 0000000..db7ecbe
--- /dev/null
+++ b/components/sync_bookmarks/bookmark_model_type_processor.cc
@@ -0,0 +1,43 @@
+// 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/sync_bookmarks/bookmark_model_type_processor.h"
+
+#include "components/sync/base/model_type.h"
+#include "components/sync/engine/commit_queue.h"
+
+namespace sync_bookmarks {
+
+BookmarkModelTypeProcessor::BookmarkModelTypeProcessor() = default;
+
+BookmarkModelTypeProcessor::~BookmarkModelTypeProcessor() = default;
+
+void BookmarkModelTypeProcessor::ConnectSync(
+    std::unique_ptr<syncer::CommitQueue> worker) {
+  NOTIMPLEMENTED();
+}
+
+void BookmarkModelTypeProcessor::DisconnectSync() {
+  NOTIMPLEMENTED();
+}
+
+void BookmarkModelTypeProcessor::GetLocalChanges(
+    size_t max_entries,
+    const GetLocalChangesCallback& callback) {
+  NOTIMPLEMENTED();
+}
+
+void BookmarkModelTypeProcessor::OnCommitCompleted(
+    const sync_pb::ModelTypeState& type_state,
+    const syncer::CommitResponseDataList& response_list) {
+  NOTIMPLEMENTED();
+}
+
+void BookmarkModelTypeProcessor::OnUpdateReceived(
+    const sync_pb::ModelTypeState& model_type_state,
+    const syncer::UpdateResponseDataList& updates) {
+  NOTIMPLEMENTED();
+}
+
+}  // namespace sync_bookmarks
diff --git a/components/sync_bookmarks/bookmark_model_type_processor.h b/components/sync_bookmarks/bookmark_model_type_processor.h
new file mode 100644
index 0000000..5ced433
--- /dev/null
+++ b/components/sync_bookmarks/bookmark_model_type_processor.h
@@ -0,0 +1,37 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SYNC_BOOKMARKS_BOOKMARK_MODEL_TYPE_PROCESSOR_H_
+#define COMPONENTS_SYNC_BOOKMARKS_BOOKMARK_MODEL_TYPE_PROCESSOR_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "components/sync/engine/model_type_processor.h"
+
+namespace sync_bookmarks {
+
+class BookmarkModelTypeProcessor : public syncer::ModelTypeProcessor {
+ public:
+  BookmarkModelTypeProcessor();
+  ~BookmarkModelTypeProcessor() override;
+
+  // ModelTypeProcessor implementation.
+  void ConnectSync(std::unique_ptr<syncer::CommitQueue> worker) override;
+  void DisconnectSync() override;
+  void GetLocalChanges(size_t max_entries,
+                       const GetLocalChangesCallback& callback) override;
+  void OnCommitCompleted(
+      const sync_pb::ModelTypeState& type_state,
+      const syncer::CommitResponseDataList& response_list) override;
+  void OnUpdateReceived(const sync_pb::ModelTypeState& type_state,
+                        const syncer::UpdateResponseDataList& updates) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(BookmarkModelTypeProcessor);
+};
+
+}  // namespace sync_bookmarks
+
+#endif  // COMPONENTS_SYNC_BOOKMARKS_BOOKMARK_MODEL_TYPE_PROCESSOR_H_
diff --git a/components/sync_bookmarks/bookmark_model_type_processor_unittest.cc b/components/sync_bookmarks/bookmark_model_type_processor_unittest.cc
new file mode 100644
index 0000000..b3d40e3
--- /dev/null
+++ b/components/sync_bookmarks/bookmark_model_type_processor_unittest.cc
@@ -0,0 +1,21 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/sync_bookmarks/bookmark_model_type_processor.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sync_bookmarks {
+
+namespace {
+
+class BookmarkModelTypeProcessorTest : public testing::Test {};
+
+TEST_F(BookmarkModelTypeProcessorTest, CreateInstance) {
+  BookmarkModelTypeProcessor processor;
+}
+
+}  // namespace
+
+}  // namespace sync_bookmarks
diff --git a/components/variations/service/variations_field_trial_creator.cc b/components/variations/service/variations_field_trial_creator.cc
index 9bc296fc..af4eae1f 100644
--- a/components/variations/service/variations_field_trial_creator.cc
+++ b/components/variations/service/variations_field_trial_creator.cc
@@ -126,7 +126,9 @@
     : client_(client),
       ui_string_overrider_(ui_string_overrider),
       seed_store_(local_state),
-      create_trials_from_seed_called_(false) {}
+      create_trials_from_seed_called_(false),
+      has_platform_override_(false),
+      platform_override_(Study::PLATFORM_WINDOWS) {}
 
 VariationsFieldTrialCreator::~VariationsFieldTrialCreator() {}
 
@@ -218,7 +220,9 @@
   state->version = version;
   state->channel = GetChannelForVariations(client_->GetChannel());
   state->form_factor = GetCurrentFormFactor();
-  state->platform = ClientFilterableState::GetCurrentPlatform();
+  state->platform = (has_platform_override_)
+                        ? platform_override_
+                        : ClientFilterableState::GetCurrentPlatform();
   state->hardware_class = GetHardwareClass();
 #if defined(OS_ANDROID)
   // This is set on Android only currently, because the IsLowEndDevice() API
@@ -329,6 +333,12 @@
   return seed_store_.LoadSeed(seed);
 }
 
+void VariationsFieldTrialCreator::OverrideVariationsPlatform(
+    Study::Platform platform_override) {
+  has_platform_override_ = true;
+  platform_override_ = platform_override;
+}
+
 bool VariationsFieldTrialCreator::SetupFieldTrials(
     const char* kEnableGpuBenchmarking,
     const char* kEnableFeatures,
diff --git a/components/variations/service/variations_field_trial_creator.h b/components/variations/service/variations_field_trial_creator.h
index a8ef3f9..548267355 100644
--- a/components/variations/service/variations_field_trial_creator.h
+++ b/components/variations/service/variations_field_trial_creator.h
@@ -101,6 +101,10 @@
   // so that it can be overridden by tests.
   virtual bool LoadSeed(VariationsSeed* seed);
 
+  // Allow the platform that is used to filter the set of active trials
+  // to be overridden.
+  void OverrideVariationsPlatform(Study::Platform platform_override);
+
  private:
   PrefService* local_state() { return seed_store_.local_state(); }
 
@@ -133,6 +137,14 @@
   // it gets called prior to |StartRepeatedVariationsSeedFetch|.
   bool create_trials_from_seed_called_;
 
+  // Indiciate if OverrideVariationsPlatform has been used to set
+  // |platform_override_|.
+  bool has_platform_override_;
+
+  // Platform to be used for variations filtering, overridding the current
+  // platform.
+  Study::Platform platform_override_;
+
   SEQUENCE_CHECKER(sequence_checker_);
 
   DISALLOW_COPY_AND_ASSIGN(VariationsFieldTrialCreator);
diff --git a/content/browser/renderer_host/input/legacy_input_router_impl.cc b/content/browser/renderer_host/input/legacy_input_router_impl.cc
index b214d96..253f419 100644
--- a/content/browser/renderer_host/input/legacy_input_router_impl.cc
+++ b/content/browser/renderer_host/input/legacy_input_router_impl.cc
@@ -420,12 +420,6 @@
     const WebInputEvent& input_event,
     const ui::LatencyInfo& latency_info,
     InputEventDispatchType dispatch_type) {
-  DCHECK(input_event.GetType() != blink::WebInputEvent::kGestureFlingStart ||
-         static_cast<const blink::WebGestureEvent&>(input_event)
-                 .data.fling_start.velocity_x != 0.0 ||
-         static_cast<const blink::WebGestureEvent&>(input_event)
-                 .data.fling_start.velocity_y != 0.0);
-
   // This conversion is temporary. WebInputEvent should be generated
   // directly from ui::Event with the viewport coordinates. See
   // crbug.com/563730.
diff --git a/content/public/android/java/src/org/chromium/content/browser/SmartSelectionClient.java b/content/public/android/java/src/org/chromium/content/browser/SmartSelectionClient.java
index ac1a2af..1990bca 100644
--- a/content/public/android/java/src/org/chromium/content/browser/SmartSelectionClient.java
+++ b/content/public/android/java/src/org/chromium/content/browser/SmartSelectionClient.java
@@ -130,7 +130,7 @@
     @CalledByNative
     private void onSurroundingTextReceived(
             @RequestType int callbackData, String text, int start, int end) {
-        if (TextUtils.isEmpty(text)) {
+        if (!textHasValidSelection(text, start, end)) {
             mCallback.onClassified(new SmartSelectionProvider.Result());
             return;
         }
@@ -150,6 +150,10 @@
         }
     }
 
+    private boolean textHasValidSelection(String text, int start, int end) {
+        return !TextUtils.isEmpty(text) && 0 <= start && start < end && end <= text.length();
+    }
+
     private native long nativeInit(WebContents webContents);
     private native void nativeRequestSurroundingText(
             long nativeSmartSelectionClient, int numExtraCharacters, int callbackData);
diff --git a/content/public/renderer/render_thread.h b/content/public/renderer/render_thread.h
index 6a02634..dcda3b1 100644
--- a/content/public/renderer/render_thread.h
+++ b/content/public/renderer/render_thread.h
@@ -22,6 +22,12 @@
 class WaitableEvent;
 }
 
+namespace blink {
+namespace scheduler {
+enum class RendererProcessType;
+}
+}  // namespace blink
+
 namespace IPC {
 class MessageFilter;
 class SyncChannel;
@@ -111,6 +117,10 @@
   virtual scoped_refptr<base::SingleThreadTaskRunner> GetTimerTaskRunner() = 0;
   virtual scoped_refptr<base::SingleThreadTaskRunner>
   GetLoadingTaskRunner() = 0;
+
+  // Set the renderer process type.
+  virtual void SetRendererProcessType(
+      blink::scheduler::RendererProcessType type) = 0;
 };
 
 }  // namespace content
diff --git a/content/public/test/mock_render_thread.cc b/content/public/test/mock_render_thread.cc
index 2bc7194..3da45ce 100644
--- a/content/public/test/mock_render_thread.cc
+++ b/content/public/test/mock_render_thread.cc
@@ -235,6 +235,9 @@
   return base::ThreadTaskRunnerHandle::Get();
 }
 
+void MockRenderThread::SetRendererProcessType(
+    blink::scheduler::RendererProcessType type) {}
+
 #if defined(OS_WIN)
 void MockRenderThread::PreCacheFont(const LOGFONT& log_font) {
 }
diff --git a/content/public/test/mock_render_thread.h b/content/public/test/mock_render_thread.h
index 076e6fe..5350017 100644
--- a/content/public/test/mock_render_thread.h
+++ b/content/public/test/mock_render_thread.h
@@ -84,6 +84,8 @@
   int32_t GetClientId() override;
   scoped_refptr<base::SingleThreadTaskRunner> GetTimerTaskRunner() override;
   scoped_refptr<base::SingleThreadTaskRunner> GetLoadingTaskRunner() override;
+  void SetRendererProcessType(
+      blink::scheduler::RendererProcessType type) override;
 #if defined(OS_WIN)
   void PreCacheFont(const LOGFONT& log_font) override;
   void ReleaseCachedFonts() override;
diff --git a/content/renderer/gpu/render_widget_compositor.cc b/content/renderer/gpu/render_widget_compositor.cc
index 4055636..3b28891 100644
--- a/content/renderer/gpu/render_widget_compositor.cc
+++ b/content/renderer/gpu/render_widget_compositor.cc
@@ -745,8 +745,9 @@
 }
 
 void RenderWidgetCompositor::SetViewportSize(
-    const gfx::Size& device_viewport_size) {
-  layer_tree_host_->SetViewportSize(device_viewport_size);
+    const gfx::Size& device_viewport_size,
+    const viz::LocalSurfaceId& local_surface_id) {
+  layer_tree_host_->SetViewportSize(device_viewport_size, local_surface_id);
 }
 
 viz::FrameSinkId RenderWidgetCompositor::GetFrameSinkId() {
@@ -1286,11 +1287,6 @@
   layer_tree_host_->SetContentSourceId(id);
 }
 
-void RenderWidgetCompositor::SetLocalSurfaceId(
-    const viz::LocalSurfaceId& local_surface_id) {
-  layer_tree_host_->SetLocalSurfaceId(local_surface_id);
-}
-
 void RenderWidgetCompositor::NotifySwapTime(ReportTimeCallback callback) {
   QueueSwapPromise(base::MakeUnique<ReportTimeSwapPromise>(
       std::move(callback), base::ThreadTaskRunnerHandle::Get()));
diff --git a/content/renderer/gpu/render_widget_compositor.h b/content/renderer/gpu/render_widget_compositor.h
index cfaa25e..cdcd003 100644
--- a/content/renderer/gpu/render_widget_compositor.h
+++ b/content/renderer/gpu/render_widget_compositor.h
@@ -118,8 +118,8 @@
   void SetRasterColorSpace(const gfx::ColorSpace& color_space);
   void SetIsForOopif(bool is_for_oopif);
   void SetContentSourceId(uint32_t source_id);
-  void SetLocalSurfaceId(const viz::LocalSurfaceId& local_surface_id);
-  void SetViewportSize(const gfx::Size& device_viewport_size);
+  void SetViewportSize(const gfx::Size& device_viewport_size,
+                       const viz::LocalSurfaceId& local_surface_id);
 
   // WebLayerTreeView implementation.
   viz::FrameSinkId GetFrameSinkId() override;
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index 0c23197..c20fac2c 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -1550,6 +1550,11 @@
   return renderer_scheduler_->LoadingTaskRunner();
 }
 
+void RenderThreadImpl::SetRendererProcessType(
+    blink::scheduler::RendererProcessType type) {
+  renderer_scheduler_->SetRendererProcessType(type);
+}
+
 void RenderThreadImpl::OnAssociatedInterfaceRequest(
     const std::string& name,
     mojo::ScopedInterfaceEndpointHandle handle) {
diff --git a/content/renderer/render_thread_impl.h b/content/renderer/render_thread_impl.h
index 2649e89c..30489e0 100644
--- a/content/renderer/render_thread_impl.h
+++ b/content/renderer/render_thread_impl.h
@@ -227,6 +227,8 @@
   int32_t GetClientId() override;
   scoped_refptr<base::SingleThreadTaskRunner> GetTimerTaskRunner() override;
   scoped_refptr<base::SingleThreadTaskRunner> GetLoadingTaskRunner() override;
+  void SetRendererProcessType(
+      blink::scheduler::RendererProcessType type) override;
 
   // IPC::Listener implementation via ChildThreadImpl:
   void OnAssociatedInterfaceRequest(
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index f5a8d1b..8b007888 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -1256,18 +1256,18 @@
     local_surface_id_ = *params.local_surface_id;
 
   if (compositor_) {
-    compositor_->SetViewportSize(params.physical_backing_size);
-    compositor_->SetBrowserControlsHeight(
-        params.top_controls_height, params.bottom_controls_height,
-        params.browser_controls_shrink_blink_size);
-    compositor_->SetRasterColorSpace(
-        screen_info_.color_space.GetRasterColorSpace());
     // If surface synchronization is enable, then this will use the provided
     // |local_surface_id_| to submit the next generated CompositorFrame.
     // If the ID is not valid, then the compositor will defer commits until
     // it receives a valid surface ID. This is a no-op if surface
     // synchronization is disabled.
-    compositor_->SetLocalSurfaceId(local_surface_id_);
+    compositor_->SetViewportSize(params.physical_backing_size,
+                                 local_surface_id_);
+    compositor_->SetBrowserControlsHeight(
+        params.top_controls_height, params.bottom_controls_height,
+        params.browser_controls_shrink_blink_size);
+    compositor_->SetRasterColorSpace(
+        screen_info_.color_space.GetRasterColorSpace());
   }
 
   visible_viewport_size_ = params.visible_viewport_size;
@@ -1338,8 +1338,11 @@
 
 void RenderWidget::AutoResizeCompositor()  {
   physical_backing_size_ = gfx::ScaleToCeiledSize(size_, device_scale_factor_);
+  // A new LocalSurfaceId will need to be allocated by the browser for the new
+  // size.
+  local_surface_id_ = viz::LocalSurfaceId();
   if (compositor_)
-    compositor_->SetViewportSize(physical_backing_size_);
+    compositor_->SetViewportSize(physical_backing_size_, local_surface_id_);
 }
 
 blink::WebLayerTreeView* RenderWidget::InitializeLayerTreeView() {
@@ -1356,12 +1359,11 @@
   compositor_->Initialize(std::move(layer_tree_host),
                           std::move(animation_host));
 
-  compositor_->SetViewportSize(physical_backing_size_);
+  compositor_->SetViewportSize(physical_backing_size_, local_surface_id_);
   OnDeviceScaleFactorChanged();
   compositor_->SetRasterColorSpace(
       screen_info_.color_space.GetRasterColorSpace());
   compositor_->SetContentSourceId(current_content_source_id_);
-  compositor_->SetLocalSurfaceId(local_surface_id_);
   // For background pages and certain tests, we don't want to trigger
   // LayerTreeFrameSink creation.
   bool should_generate_frame_sink =
diff --git a/content/shell/test_runner/mock_web_theme_engine.cc b/content/shell/test_runner/mock_web_theme_engine.cc
index 59009cc..93a4c1a 100644
--- a/content/shell/test_runner/mock_web_theme_engine.cc
+++ b/content/shell/test_runner/mock_web_theme_engine.cc
@@ -210,11 +210,15 @@
 
   flags.setColor(color);
   flags.setStyle(cc::PaintFlags::kFill_Style);
-  canvas->drawCircle(cx, cy, radius, flags);
+  canvas->drawOval(
+      SkRect::MakeLTRB(cx - radius, cy - radius, cx + radius, cy + radius),
+      flags);
 
   flags.setColor(edgeColor);
   flags.setStyle(cc::PaintFlags::kStroke_Style);
-  canvas->drawCircle(cx, cy, radius, flags);
+  canvas->drawOval(
+      SkRect::MakeLTRB(cx - radius, cy - radius, cx + radius, cy + radius),
+      flags);
 }
 
 void nestedBoxes(cc::PaintCanvas* canvas,
diff --git a/extensions/browser/process_manager.cc b/extensions/browser/process_manager.cc
index cb63e501..073efff20 100644
--- a/extensions/browser/process_manager.cc
+++ b/extensions/browser/process_manager.cc
@@ -16,6 +16,8 @@
 #include "base/time/time.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/devtools_agent_host.h"
+#include "content/public/browser/navigation_controller.h"
+#include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
@@ -442,8 +444,30 @@
     const content::WebContents* web_contents) {
   if (!web_contents->GetSiteInstance())
     return nullptr;
-  return extension_registry_->enabled_extensions().GetByID(
-      GetExtensionIdForSiteInstance(web_contents->GetSiteInstance()));
+  const Extension* extension =
+      extension_registry_->enabled_extensions().GetByID(
+          GetExtensionIdForSiteInstance(web_contents->GetSiteInstance()));
+  if (extension && extension->is_hosted_app()) {
+    // For hosted apps, be sure to exclude URLs outside of the app that might
+    // be loaded in the same SiteInstance (extensions guarantee that only
+    // extension urls are loaded in that SiteInstance).
+    const content::NavigationController& controller =
+        web_contents->GetController();
+    content::NavigationEntry* entry = controller.GetLastCommittedEntry();
+    // If there is no last committed entry, check the pending entry. This can
+    // happen in cases where we query this before any entry is fully committed,
+    // such as when attributing a WebContents for the TaskManager. If there is
+    // a committed navigation, use that instead.
+    if (!entry)
+      entry = controller.GetPendingEntry();
+    if (!entry ||
+        extension_registry_->enabled_extensions().GetExtensionOrAppByURL(
+            entry->GetURL()) != extension) {
+      return nullptr;
+    }
+  }
+
+  return extension;
 }
 
 int ProcessManager::GetLazyKeepaliveCount(const Extension* extension) {
diff --git a/extensions/browser/process_manager.h b/extensions/browser/process_manager.h
index e4843160..2afd31f1 100644
--- a/extensions/browser/process_manager.h
+++ b/extensions/browser/process_manager.h
@@ -108,10 +108,13 @@
   // already been sent the unload event and is shutting down.
   bool IsBackgroundHostClosing(const std::string& extension_id);
 
-  // Returns the extension associated with the specified RenderFrameHost/
-  // WebContents, or null.
+  // Returns the extension associated with the specified RenderFrameHost,
+  // or null.
   const Extension* GetExtensionForRenderFrameHost(
       content::RenderFrameHost* render_frame_host);
+
+  // Returns the extension associated with the main frame of the given
+  // |web_contents|, or null if there isn't one.
   const Extension* GetExtensionForWebContents(
       const content::WebContents* web_contents);
 
diff --git a/extensions/browser/sandboxed_unpacker.cc b/extensions/browser/sandboxed_unpacker.cc
index f49e9292..7bcc6d3 100644
--- a/extensions/browser/sandboxed_unpacker.cc
+++ b/extensions/browser/sandboxed_unpacker.cc
@@ -577,8 +577,6 @@
       return ASCIIToUTF16("ERROR_RE_ENCODING_THEME_IMAGE");
     case ERROR_SAVING_THEME_IMAGE:
       return ASCIIToUTF16("ERROR_SAVING_THEME_IMAGE");
-    case ABORTED_DUE_TO_SHUTDOWN:
-      return ASCIIToUTF16("ABORTED_DUE_TO_SHUTDOWN");
 
     case COULD_NOT_READ_CATALOG_DATA_FROM_DISK:
       return ASCIIToUTF16("COULD_NOT_READ_CATALOG_DATA_FROM_DISK");
@@ -599,6 +597,7 @@
     case DIRECTORY_MOVE_FAILED:
       return ASCIIToUTF16("DIRECTORY_MOVE_FAILED");
 
+    case DEPRECATED_ABORTED_DUE_TO_SHUTDOWN:
     case NUM_FAILURE_REASONS:
       NOTREACHED();
       return base::string16();
@@ -796,15 +795,6 @@
 
   // Write our parsed images back to disk as well.
   for (size_t i = 0; i < images.size(); ++i) {
-    if (BrowserThread::GetBlockingPool()->IsShutdownInProgress()) {
-      // Abort package installation if shutdown was initiated, crbug.com/235525
-      ReportFailure(
-          ABORTED_DUE_TO_SHUTDOWN,
-          l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
-                                     ASCIIToUTF16("ABORTED_DUE_TO_SHUTDOWN")));
-      return false;
-    }
-
     const SkBitmap& image = std::get<0>(images[i]);
     base::FilePath path_suffix = std::get<1>(images[i]);
     if (path_suffix.MaybeAsASCII() == install_icon_path)
diff --git a/extensions/browser/sandboxed_unpacker.h b/extensions/browser/sandboxed_unpacker.h
index c8bb78b..10ed871c 100644
--- a/extensions/browser/sandboxed_unpacker.h
+++ b/extensions/browser/sandboxed_unpacker.h
@@ -94,6 +94,14 @@
   // passing the |location| and |creation_flags| to Extension::Create. The
   // |extensions_dir| parameter should specify the directory under which we'll
   // create a subdirectory to write the unpacked extension contents.
+  // Note: Because this requires disk I/O, the task runner passed should use
+  // TaskShutdownBehavior::SKIP_ON_SHUTDOWN to ensure that either the task is
+  // fully run (if initiated before shutdown) or not run at all (if shutdown is
+  // initiated first). See crbug.com/235525.
+  // TODO(devlin): We should probably just have SandboxedUnpacker use the common
+  // ExtensionFileTaskRunner, and not pass in a separate one.
+  // TODO(devlin): SKIP_ON_SHUTDOWN is also not quite sufficient for this. We
+  // should probably instead be using base::ImportantFileWriter or similar.
   SandboxedUnpacker(
       Manifest::Location location,
       int creation_flags,
@@ -160,7 +168,7 @@
     INVALID_PATH_FOR_BITMAP_IMAGE,
     ERROR_RE_ENCODING_THEME_IMAGE,
     ERROR_SAVING_THEME_IMAGE,
-    ABORTED_DUE_TO_SHUTDOWN,
+    DEPRECATED_ABORTED_DUE_TO_SHUTDOWN,  // No longer used; kept for UMA.
 
     // SandboxedUnpacker::RewriteCatalogFiles()
     COULD_NOT_READ_CATALOG_DATA_FROM_DISK,
diff --git a/gpu/ipc/service/stream_texture_android.cc b/gpu/ipc/service/stream_texture_android.cc
index dea1f36a..3003f14 100644
--- a/gpu/ipc/service/stream_texture_android.cc
+++ b/gpu/ipc/service/stream_texture_android.cc
@@ -145,7 +145,7 @@
     return false;
 
   if (!owner_stub_ || !surface_texture_.get())
-    return true;
+    return false;
 
   GLint texture_id;
   glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &texture_id);
diff --git a/ios/chrome/browser/metrics/tab_usage_recorder_egtest.mm b/ios/chrome/browser/metrics/tab_usage_recorder_egtest.mm
index 989be9a..a4553c4e 100644
--- a/ios/chrome/browser/metrics/tab_usage_recorder_egtest.mm
+++ b/ios/chrome/browser/metrics/tab_usage_recorder_egtest.mm
@@ -12,6 +12,9 @@
 #include "components/strings/grit/components_strings.h"
 #import "ios/chrome/browser/metrics/tab_usage_recorder.h"
 #import "ios/chrome/browser/metrics/tab_usage_recorder_test_util.h"
+#import "ios/chrome/browser/ui/settings/privacy_collection_view_controller.h"
+#import "ios/chrome/browser/ui/settings/settings_collection_view_controller.h"
+#import "ios/chrome/browser/ui/toolbar/toolbar_controller.h"
 #include "ios/chrome/browser/ui/tools_menu/tools_menu_constants.h"
 #include "ios/chrome/browser/ui/ui_util.h"
 #import "ios/chrome/browser/ui/uikit_ui_util.h"
@@ -109,7 +112,9 @@
 // synchronization.
 void OpenNewMainTabUsingUIUnsynced() {
   int nb_main_tab = chrome_test_util::GetMainTabCount();
-  WaitAndTap(chrome_test_util::ToolsMenuButton(), @"Tool menu");
+  id<GREYMatcher> tool_menu_matcher =
+      grey_accessibilityID(kToolbarToolsMenuButtonIdentifier);
+  WaitAndTap(tool_menu_matcher, @"Tool menu");
   id<GREYMatcher> new_main_tab_button_matcher =
       grey_accessibilityID(kToolsMenuNewTabId);
   WaitAndTap(new_main_tab_button_matcher, @"New tab button");
@@ -522,7 +527,9 @@
   [[GREYConfiguration sharedInstance]
           setValue:@(NO)
       forConfigKey:kGREYConfigKeySynchronizationEnabled];
-  Wait(chrome_test_util::ToolsMenuButton(), @"Tool Menu");
+  id<GREYMatcher> toolMenuMatcher =
+      grey_accessibilityID(kToolbarToolsMenuButtonIdentifier);
+  Wait(toolMenuMatcher, @"Tool Menu");
 
   GREYAssertTrue(chrome_test_util::SimulateTabsBackgrounding(),
                  @"Failed to simulate tab backgrounding.");
diff --git a/ios/chrome/browser/ui/BUILD.gn b/ios/chrome/browser/ui/BUILD.gn
index b17ee78a3fa..a841f75d 100644
--- a/ios/chrome/browser/ui/BUILD.gn
+++ b/ios/chrome/browser/ui/BUILD.gn
@@ -266,6 +266,7 @@
     "//ios/chrome/browser/ui/find_bar",
     "//ios/chrome/browser/ui/first_run",
     "//ios/chrome/browser/ui/history",
+    "//ios/chrome/browser/ui/history_popup/requirements",
     "//ios/chrome/browser/ui/keyboard",
     "//ios/chrome/browser/ui/ntp",
     "//ios/chrome/browser/ui/ntp/recent_tabs",
@@ -390,6 +391,8 @@
     "//ios/chrome/browser/ui/elements:elements_internal",
     "//ios/chrome/browser/ui/find_bar",
     "//ios/chrome/browser/ui/first_run",
+    "//ios/chrome/browser/ui/history_popup:coordinator",
+    "//ios/chrome/browser/ui/history_popup/requirements",
     "//ios/chrome/browser/ui/keyboard",
     "//ios/chrome/browser/ui/ntp",
     "//ios/chrome/browser/ui/ntp:ntp_controller",
diff --git a/ios/chrome/browser/ui/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view_controller.mm
index 2338cae..1a17eb0 100644
--- a/ios/chrome/browser/ui/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view_controller.mm
@@ -121,6 +121,8 @@
 #import "ios/chrome/browser/ui/find_bar/find_bar_controller_ios.h"
 #import "ios/chrome/browser/ui/first_run/welcome_to_chrome_view_controller.h"
 #import "ios/chrome/browser/ui/fullscreen_controller.h"
+#import "ios/chrome/browser/ui/history_popup/requirements/tab_history_presentation.h"
+#import "ios/chrome/browser/ui/history_popup/tab_history_coordinator.h"
 #import "ios/chrome/browser/ui/key_commands_provider.h"
 #import "ios/chrome/browser/ui/ntp/new_tab_page_controller.h"
 #import "ios/chrome/browser/ui/ntp/recent_tabs/recent_tabs_panel_view_controller.h"
@@ -361,6 +363,7 @@
                                     StoreKitLauncher,
                                     TabDialogDelegate,
                                     TabHeadersDelegate,
+                                    TabHistoryPresentation,
                                     TabModelObserver,
                                     TabSnapshottingDelegate,
                                     UIGestureRecognizerDelegate,
@@ -524,6 +527,9 @@
 
   // Coordinator for displaying alerts.
   AlertCoordinator* _alertCoordinator;
+
+  // Coordinator for Tab History Popup.
+  TabHistoryCoordinator* _tabHistoryCoordinator;
 }
 
 // The browser's side swipe controller.  Lazily instantiated on the first call.
@@ -1742,6 +1748,10 @@
   [_model browserStateDestroyed];
   [_preloadController browserStateDestroyed];
   _preloadController = nil;
+
+  // Disconnect child coordinators.
+  [_tabHistoryCoordinator disconnect];
+
   // The file remover needs the browser state, so needs to be destroyed now.
   _externalFileRemover = nil;
   _browserState = nullptr;
@@ -1830,6 +1840,15 @@
       [[_model currentTab] infoBarManager];
   _infoBarContainer->ChangeInfoBarManager(infoBarManager);
 
+  // Create child coordinators.
+  _tabHistoryCoordinator =
+      [[TabHistoryCoordinator alloc] initWithBaseViewController:self];
+  _tabHistoryCoordinator.dispatcher = _dispatcher;
+  _tabHistoryCoordinator.positionProvider = _toolbarController;
+  _tabHistoryCoordinator.tabModel = _model;
+  _tabHistoryCoordinator.presentationProvider = self;
+  _tabHistoryCoordinator.tabHistoryUIUpdater = _toolbarController;
+
   if (base::FeatureList::IsEnabled(payments::features::kWebPayments)) {
     _paymentRequestManager = [[PaymentRequestManager alloc]
         initWithBaseViewController:self
@@ -1994,7 +2013,6 @@
 - (void)dismissPopups {
   [_toolbarController dismissToolsMenuPopup];
   [self hidePageInfoPopupForView:nil];
-  [_toolbarController dismissTabHistoryPopup];
   [self.bubbleViewControllerPresenter dismissAnimated:YES];
 }
 
@@ -4140,42 +4158,6 @@
                    completion:nil];
 }
 
-- (void)showTabHistoryPopupForBackwardHistory {
-  DCHECK(self.visible || self.dismissingModal);
-
-  // Dismiss the omnibox (if open).
-  [_toolbarController cancelOmniboxEdit];
-  // Dismiss the soft keyboard (if open).
-  Tab* tab = [_model currentTab];
-  [tab.webController dismissKeyboard];
-  web::NavigationItemList backwardItems =
-      [tab navigationManager]->GetBackwardItems();
-  [_toolbarController showTabHistoryPopupInView:[self view]
-                                      withItems:backwardItems
-                                 forBackHistory:YES];
-}
-
-- (void)showTabHistoryPopupForForwardHistory {
-  DCHECK(self.visible || self.dismissingModal);
-
-  // Dismiss the omnibox (if open).
-  [_toolbarController cancelOmniboxEdit];
-  // Dismiss the soft keyboard (if open).
-  Tab* tab = [_model currentTab];
-  [tab.webController dismissKeyboard];
-
-  web::NavigationItemList forwardItems =
-      [tab navigationManager]->GetForwardItems();
-  [_toolbarController showTabHistoryPopupInView:[self view]
-                                      withItems:forwardItems
-                                 forBackHistory:NO];
-}
-
-- (void)navigateToHistoryItem:(const web::NavigationItem*)item {
-  [[_model currentTab] goToItem:item];
-  [_toolbarController dismissTabHistoryPopup];
-}
-
 - (void)showReadingList {
   _readingListCoordinator = [[ReadingListCoordinator alloc]
       initWithBaseViewController:self
@@ -5123,4 +5105,12 @@
   return [self currentLogoAnimationControllerOwner];
 }
 
+#pragma mark - TabHistoryPresenter
+
+- (void)prepareForTabHistoryPresentation {
+  DCHECK(self.visible || self.dismissingModal);
+  [[self.tabModel currentTab].webController dismissKeyboard];
+  [_toolbarController cancelOmniboxEdit];
+}
+
 @end
diff --git a/ios/chrome/browser/ui/commands/BUILD.gn b/ios/chrome/browser/ui/commands/BUILD.gn
index fb70574..6807233 100644
--- a/ios/chrome/browser/ui/commands/BUILD.gn
+++ b/ios/chrome/browser/ui/commands/BUILD.gn
@@ -13,6 +13,7 @@
     "clear_browsing_data_command.mm",
     "generic_chrome_command.h",
     "generic_chrome_command.mm",
+    "history_popup_commands.h",
     "ios_command_ids.h",
     "open_new_tab_command.h",
     "open_new_tab_command.mm",
diff --git a/ios/chrome/browser/ui/commands/browser_commands.h b/ios/chrome/browser/ui/commands/browser_commands.h
index e0b50d64..de94444 100644
--- a/ios/chrome/browser/ui/commands/browser_commands.h
+++ b/ios/chrome/browser/ui/commands/browser_commands.h
@@ -7,15 +7,14 @@
 
 #import <Foundation/Foundation.h>
 
+#import "ios/chrome/browser/ui/commands/history_popup_commands.h"
+
 @class OpenNewTabCommand;
 @class ReadingListAddCommand;
-namespace web {
-class NavigationItem;
-}
 
 // Protocol for commands that will generally be handled by the "current tab",
 // which in practice is the BrowserViewController instance displaying the tab.
-@protocol BrowserCommands<NSObject>
+@protocol BrowserCommands<NSObject, TabHistoryPopupCommands>
 
 // Closes the current tab.
 - (void)closeCurrentTab;
@@ -53,15 +52,6 @@
 // Shows the QR scanner UI.
 - (void)showQRScanner;
 
-// Shows the tab history popup containing the tab's backward history.
-- (void)showTabHistoryPopupForBackwardHistory;
-
-// Shows the tab history popup containing the tab's forward history.
-- (void)showTabHistoryPopupForForwardHistory;
-
-// Navigate back/forward to the selected entry in the tab's history.
-- (void)navigateToHistoryItem:(const web::NavigationItem*)item;
-
 // Shows the Reading List UI.
 - (void)showReadingList;
 
diff --git a/ios/chrome/browser/ui/commands/history_popup_commands.h b/ios/chrome/browser/ui/commands/history_popup_commands.h
new file mode 100644
index 0000000..7d8e820
--- /dev/null
+++ b/ios/chrome/browser/ui/commands/history_popup_commands.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 IOS_CHROME_BROWSER_UI_COMMANDS_HISTORY_POPUP_COMMANDS_H_
+#define IOS_CHROME_BROWSER_UI_COMMANDS_HISTORY_POPUP_COMMANDS_H_
+
+#import <Foundation/Foundation.h>
+
+namespace web {
+class NavigationItem;
+}
+
+@protocol TabHistoryPopupCommands
+// Shows the tab history popup containing the tab's backward history.
+- (void)showTabHistoryPopupForBackwardHistory;
+
+// Shows the tab history popup containing the tab's forward history.
+- (void)showTabHistoryPopupForForwardHistory;
+
+// Navigate back/forward to the selected entry in the tab's history.
+- (void)navigateToHistoryItem:(const web::NavigationItem*)item;
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_COMMANDS_HISTORY_POPUP_COMMANDS_H_
diff --git a/ios/chrome/browser/ui/fullscreen_controller.mm b/ios/chrome/browser/ui/fullscreen_controller.mm
index e2eae0ce..aee4c268 100644
--- a/ios/chrome/browser/ui/fullscreen_controller.mm
+++ b/ios/chrome/browser/ui/fullscreen_controller.mm
@@ -9,10 +9,10 @@
 #include "base/logging.h"
 
 #import "ios/chrome/browser/ui/browser_view_controller.h"
+#import "ios/chrome/browser/ui/history_popup/requirements/tab_history_constants.h"
 #import "ios/chrome/browser/ui/overscroll_actions/overscroll_actions_controller.h"
 #import "ios/chrome/browser/ui/tabs/tab_strip_controller.h"
 #import "ios/chrome/browser/ui/toolbar/toolbar_controller.h"
-#import "ios/chrome/browser/ui/toolbar/web_toolbar_controller.h"
 #import "ios/chrome/browser/ui/voice/voice_search_notification_names.h"
 #include "ios/web/public/navigation_item.h"
 #import "ios/web/public/navigation_manager.h"
diff --git a/ios/chrome/browser/ui/history_popup/BUILD.gn b/ios/chrome/browser/ui/history_popup/BUILD.gn
index 7bf1ad6..9b5f15e 100644
--- a/ios/chrome/browser/ui/history_popup/BUILD.gn
+++ b/ios/chrome/browser/ui/history_popup/BUILD.gn
@@ -26,6 +26,23 @@
   ]
 }
 
+source_set("coordinator") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  sources = [
+    "tab_history_coordinator.h",
+    "tab_history_coordinator.mm",
+  ]
+  deps = [
+    ":history_popup",
+    "//base",
+    "//ios/chrome/browser",
+    "//ios/chrome/browser/tabs",
+    "//ios/chrome/browser/ui/commands",
+    "//ios/chrome/browser/ui/history_popup/requirements",
+    "//ios/shared/chrome/browser/ui/commands",
+  ]
+}
+
 source_set("unit_tests") {
   configs += [ "//build/config/compiler:enable_arc" ]
   testonly = true
diff --git a/ios/chrome/browser/ui/history_popup/requirements/BUILD.gn b/ios/chrome/browser/ui/history_popup/requirements/BUILD.gn
new file mode 100644
index 0000000..f5dc3974
--- /dev/null
+++ b/ios/chrome/browser/ui/history_popup/requirements/BUILD.gn
@@ -0,0 +1,14 @@
+# 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.
+
+source_set("requirements") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  sources = [
+    "tab_history_constants.h",
+    "tab_history_constants.mm",
+    "tab_history_positioner.h",
+    "tab_history_presentation.h",
+    "tab_history_ui_updater.h",
+  ]
+}
diff --git a/ios/chrome/browser/ui/history_popup/requirements/tab_history_constants.h b/ios/chrome/browser/ui/history_popup/requirements/tab_history_constants.h
new file mode 100644
index 0000000..fb8c5d7f
--- /dev/null
+++ b/ios/chrome/browser/ui/history_popup/requirements/tab_history_constants.h
@@ -0,0 +1,22 @@
+// 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 IOS_CHROME_BROWSER_UI_HISTORY_POPUP_CONSTANTS_H_
+#define IOS_CHROME_BROWSER_UI_HISTORY_POPUP_CONSTANTS_H_
+
+#import <Foundation/Foundation.h>
+
+// Notification center names.
+extern NSString* const kTabHistoryPopupWillShowNotification;
+extern NSString* const kTabHistoryPopupWillHideNotification;
+
+// Toolbar buttons that can display the HistoryPopup.
+typedef NS_OPTIONS(NSUInteger, ToolbarButton) {
+  // Back Toolbar Button.
+  ToolbarButtonForward = 0,
+  // Forward Toolbar Button.
+  ToolbarButtonBack = 1,
+};
+
+#endif  // IOS_CHROME_BROWSER_UI_HISTORY_POPUP_CONSTANTS_H_
diff --git a/ios/chrome/browser/ui/history_popup/requirements/tab_history_constants.mm b/ios/chrome/browser/ui/history_popup/requirements/tab_history_constants.mm
new file mode 100644
index 0000000..2b11d975
--- /dev/null
+++ b/ios/chrome/browser/ui/history_popup/requirements/tab_history_constants.mm
@@ -0,0 +1,10 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/history_popup/requirements/tab_history_constants.h"
+
+NSString* const kTabHistoryPopupWillShowNotification =
+    @"kTabHistoryPopupWillShowNotification";
+NSString* const kTabHistoryPopupWillHideNotification =
+    @"kTabHistoryPopupWillHideNotification";
diff --git a/ios/chrome/browser/ui/history_popup/requirements/tab_history_positioner.h b/ios/chrome/browser/ui/history_popup/requirements/tab_history_positioner.h
new file mode 100644
index 0000000..5d7100b
--- /dev/null
+++ b/ios/chrome/browser/ui/history_popup/requirements/tab_history_positioner.h
@@ -0,0 +1,15 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_HISTORY_POPUP_REQUIREMENTS_TAB_HISTORY_POSITIONER_H_
+#define IOS_CHROME_BROWSER_UI_HISTORY_POPUP_REQUIREMENTS_TAB_HISTORY_POSITIONER_H_
+
+#import "ios/chrome/browser/ui/history_popup/requirements/tab_history_constants.h"
+
+@protocol TabHistoryPositioner
+// CGPoint which the Tab History Popup will be presented from.
+- (CGPoint)originPointForToolbarButton:(ToolbarButton)toolbarButton;
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_HISTORY_POPUP_REQUIREMENTS_TAB_HISTORY_POSITIONER_H_
diff --git a/ios/chrome/browser/ui/history_popup/requirements/tab_history_presentation.h b/ios/chrome/browser/ui/history_popup/requirements/tab_history_presentation.h
new file mode 100644
index 0000000..e565e171
--- /dev/null
+++ b/ios/chrome/browser/ui/history_popup/requirements/tab_history_presentation.h
@@ -0,0 +1,14 @@
+// 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 IOS_CHROME_BROWSER_UI_HISTORY_POPUP_REQUIREMENTS_TAB_HISTORY_PRESENTATION_H_
+#define IOS_CHROME_BROWSER_UI_HISTORY_POPUP_REQUIREMENTS_TAB_HISTORY_PRESENTATION_H_
+
+@protocol TabHistoryPresentation
+@optional
+// Tells the receiver the Tab History popup will be presented.
+- (void)prepareForTabHistoryPresentation;
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_HISTORY_POPUP_REQUIREMENTS_TAB_HISTORY_PRESENTATION_H_
diff --git a/ios/chrome/browser/ui/history_popup/requirements/tab_history_ui_updater.h b/ios/chrome/browser/ui/history_popup/requirements/tab_history_ui_updater.h
new file mode 100644
index 0000000..e73bbec
--- /dev/null
+++ b/ios/chrome/browser/ui/history_popup/requirements/tab_history_ui_updater.h
@@ -0,0 +1,20 @@
+// 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 IOS_CHROME_BROWSER_UI_HISTORY_POPUP_REQUIREMENTS_TAB_HISTORY_UI_UPDATER_H_
+#define IOS_CHROME_BROWSER_UI_HISTORY_POPUP_REQUIREMENTS_TAB_HISTORY_UI_UPDATER_H_
+
+#import "ios/chrome/browser/ui/history_popup/requirements/tab_history_constants.h"
+
+@protocol TabHistoryUIUpdater
+@optional
+// Tells the receiver to update its UI now that TabHistory popup will be
+// presented.
+- (void)updateUIForTabHistoryPresentationFrom:(ToolbarButton)button;
+
+// Tells the receiver to update its UI now that TabHistory popup was dismissed.
+- (void)updateUIForTabHistoryWasDismissed;
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_HISTORY_POPUP_REQUIREMENTS_TAB_HISTORY_UI_UPDATER_H_
diff --git a/ios/chrome/browser/ui/history_popup/tab_history_coordinator.h b/ios/chrome/browser/ui/history_popup/tab_history_coordinator.h
new file mode 100644
index 0000000..6f9a1d47
--- /dev/null
+++ b/ios/chrome/browser/ui/history_popup/tab_history_coordinator.h
@@ -0,0 +1,43 @@
+// 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 IOS_CHROME_BROWSER_UI_HISTORY_POPUP_TAB_HISTORY_COORDINATOR_H_
+#define IOS_CHROME_BROWSER_UI_HISTORY_POPUP_TAB_HISTORY_COORDINATOR_H_
+
+#import "ios/chrome/browser/chrome_coordinator.h"
+
+@class CommandDispatcher;
+@protocol PopupMenuDelegate;
+@protocol TabHistoryPresentation;
+@protocol TabHistoryPositioner;
+@protocol TabHistoryUIUpdater;
+@class TabModel;
+
+// The coordinator in charge of displaying and dismissing the TabHistoryPopup.
+// The TabHistoryPopup is presented when the user long presses the back or
+// forward Toolbar button.
+@interface TabHistoryCoordinator : ChromeCoordinator
+
+// The dispatcher for this Coordinator.
+@property(nonatomic, weak) CommandDispatcher* dispatcher;
+// |positionProvider| provides the presentation origin for the TabHistoryPopup.
+@property(nonatomic, weak) id<TabHistoryPositioner> positionProvider;
+// |presentationProvider| runs tasks for before and after presenting the
+// TabHistoryPopup.
+@property(nonatomic, weak) id<TabHistoryPresentation> presentationProvider;
+// |tabHistoryUIUpdater| updates the relevant UI before and after presenting
+// the TabHistoryPopup.
+@property(nonatomic, weak) id<TabHistoryUIUpdater> tabHistoryUIUpdater;
+// The current TabModel being used by BVC.
+@property(nonatomic, weak) TabModel* tabModel;
+// Dissmisses the currently presented TabHistoryPopup, if none is being
+// presented it will no-op.
+
+- (void)dismissHistoryPopup;
+// Stops listening for dispatcher calls.
+- (void)disconnect;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_HISTORY_POPUP_TAB_HISTORY_COORDINATOR_H_
diff --git a/ios/chrome/browser/ui/history_popup/tab_history_coordinator.mm b/ios/chrome/browser/ui/history_popup/tab_history_coordinator.mm
new file mode 100644
index 0000000..d90504e
--- /dev/null
+++ b/ios/chrome/browser/ui/history_popup/tab_history_coordinator.mm
@@ -0,0 +1,175 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/history_popup/tab_history_coordinator.h"
+
+#include "base/metrics/user_metrics.h"
+#include "base/metrics/user_metrics_action.h"
+#import "ios/chrome/browser/tabs/tab.h"
+#import "ios/chrome/browser/tabs/tab_model.h"
+#import "ios/chrome/browser/ui/commands/history_popup_commands.h"
+#import "ios/chrome/browser/ui/history_popup/requirements/tab_history_constants.h"
+#import "ios/chrome/browser/ui/history_popup/requirements/tab_history_positioner.h"
+#import "ios/chrome/browser/ui/history_popup/requirements/tab_history_presentation.h"
+#import "ios/chrome/browser/ui/history_popup/requirements/tab_history_ui_updater.h"
+#import "ios/chrome/browser/ui/history_popup/tab_history_popup_controller.h"
+#import "ios/shared/chrome/browser/ui/commands/command_dispatcher.h"
+#include "ios/web/public/navigation_item.h"
+#import "ios/web/public/navigation_manager.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+using base::UserMetricsAction;
+
+@interface TabHistoryCoordinator ()<PopupMenuDelegate>
+
+// The TabHistoryPopupController instance that this coordinator will be
+// presenting.
+@property(nonatomic, strong)
+    TabHistoryPopupController* tabHistoryPopupController;
+
+@end
+
+@implementation TabHistoryCoordinator
+
+@synthesize dispatcher = _dispatcher;
+@synthesize positionProvider = _positionProvider;
+@synthesize presentationProvider = _presentationProvider;
+@synthesize tabHistoryPopupController = _tabHistoryPopupController;
+@synthesize tabHistoryUIUpdater = _tabHistoryUIUpdater;
+@synthesize tabModel = _tabModel;
+
+- (void)disconnect {
+  self.dispatcher = nil;
+}
+
+- (void)setDispatcher:(CommandDispatcher*)dispatcher {
+  if (dispatcher == self.dispatcher) {
+    return;
+  }
+  if (self.dispatcher) {
+    [self.dispatcher stopDispatchingToTarget:self];
+  }
+  [dispatcher startDispatchingToTarget:self
+                           forProtocol:@protocol(TabHistoryPopupCommands)];
+  _dispatcher = dispatcher;
+}
+
+#pragma mark - TabHistoryPopupCommands
+
+- (void)showTabHistoryPopupForBackwardHistory {
+  Tab* tab = [self.tabModel currentTab];
+  web::NavigationItemList backwardItems =
+      [tab navigationManager]->GetBackwardItems();
+  CGPoint origin = [self.baseViewController.view.window
+      convertPoint:[self.positionProvider
+                       originPointForToolbarButton:ToolbarButtonBack]
+            toView:self.baseViewController.view];
+
+  [self.tabHistoryUIUpdater
+      updateUIForTabHistoryPresentationFrom:ToolbarButtonBack];
+  [self presentTabHistoryPopupWithItems:backwardItems origin:origin];
+}
+
+- (void)showTabHistoryPopupForForwardHistory {
+  Tab* tab = [self.tabModel currentTab];
+  web::NavigationItemList forwardItems =
+      [tab navigationManager]->GetForwardItems();
+  CGPoint origin = [self.baseViewController.view.window
+      convertPoint:[self.positionProvider
+                       originPointForToolbarButton:ToolbarButtonForward]
+            toView:self.baseViewController.view];
+
+  [self.tabHistoryUIUpdater
+      updateUIForTabHistoryPresentationFrom:ToolbarButtonForward];
+  [self presentTabHistoryPopupWithItems:forwardItems origin:origin];
+}
+
+- (void)navigateToHistoryItem:(const web::NavigationItem*)item {
+  [[self.tabModel currentTab] goToItem:item];
+  [self dismissPopupMenu:nil];
+}
+
+#pragma mark - Helper Methods
+
+// Present a Tab History Popup that displays |items|, and its view is presented
+// from |origin|.
+- (void)presentTabHistoryPopupWithItems:(const web::NavigationItemList&)items
+                                 origin:(CGPoint)historyPopupOrigin {
+  if (self.tabHistoryPopupController)
+    return;
+  base::RecordAction(UserMetricsAction("MobileToolbarShowTabHistoryMenu"));
+
+  [self.presentationProvider prepareForTabHistoryPresentation];
+
+  // Initializing also displays the Tab History Popup VC.
+  self.tabHistoryPopupController = [[TabHistoryPopupController alloc]
+      initWithOrigin:historyPopupOrigin
+          parentView:self.baseViewController.view
+               items:items
+          dispatcher:static_cast<id<TabHistoryPopupCommands>>(self.dispatcher)];
+
+  [self.tabHistoryPopupController setDelegate:self];
+
+  // Notify observers that TabHistoryPopup will show.
+  [[NSNotificationCenter defaultCenter]
+      postNotificationName:kTabHistoryPopupWillShowNotification
+                    object:nil];
+
+  // Register to receive notification for when the App is backgrounded so we can
+  // dismiss the TabHistoryPopup.
+  NSNotificationCenter* defaultCenter = [NSNotificationCenter defaultCenter];
+  [defaultCenter addObserver:self
+                    selector:@selector(applicationDidEnterBackground:)
+                        name:UIApplicationDidEnterBackgroundNotification
+                      object:nil];
+}
+
+// Dismiss the presented Tab History Popup.
+- (void)dismissHistoryPopup {
+  if (!self.tabHistoryPopupController)
+    return;
+  __block TabHistoryPopupController* tempController =
+      self.tabHistoryPopupController;
+  [tempController containerView].userInteractionEnabled = NO;
+  [tempController dismissAnimatedWithCompletion:^{
+    [self.tabHistoryUIUpdater updateUIForTabHistoryWasDismissed];
+    // Reference tempTHPC so the block retains it.
+    tempController = nil;
+  }];
+  // Reset _tabHistoryPopupController to prevent -applicationDidEnterBackground
+  // from posting another kTabHistoryPopupWillHideNotification.
+  self.tabHistoryPopupController = nil;
+
+  // Stop listening for notifications since these are only used to dismiss the
+  // Tab History Popup.
+  [[NSNotificationCenter defaultCenter] removeObserver:self];
+
+  [[NSNotificationCenter defaultCenter]
+      postNotificationName:kTabHistoryPopupWillHideNotification
+                    object:nil];
+}
+
+#pragma mark - PopupMenuDelegate
+
+- (void)dismissPopupMenu:(PopupMenuController*)controller {
+  [self dismissHistoryPopup];
+}
+
+#pragma mark - Observer Methods
+
+- (void)applicationDidEnterBackground:(NSNotification*)notify {
+  if (self.tabHistoryPopupController) {
+    // Dismiss the tab history popup without animation.
+    [self.tabHistoryUIUpdater updateUIForTabHistoryWasDismissed];
+    self.tabHistoryPopupController = nil;
+    [[NSNotificationCenter defaultCenter]
+        postNotificationName:kTabHistoryPopupWillHideNotification
+                      object:nil];
+  }
+}
+
+@end
diff --git a/ios/chrome/browser/ui/history_popup/tab_history_popup_controller.h b/ios/chrome/browser/ui/history_popup/tab_history_popup_controller.h
index cc17f0c..73a6ee6 100644
--- a/ios/chrome/browser/ui/history_popup/tab_history_popup_controller.h
+++ b/ios/chrome/browser/ui/history_popup/tab_history_popup_controller.h
@@ -9,7 +9,7 @@
 
 #include "ios/web/public/navigation_item_list.h"
 
-@protocol BrowserCommands;
+@protocol TabHistoryPopupCommands;
 
 // The view controller for the tab history menu that appears when the user long
 // presses the back or forward button.
@@ -17,11 +17,11 @@
 
 // Initializes the popup to display |items| with the given |origin| that is
 // relevant to the |parent|'s coordinate system.
-// |entries| is an array of CRWSessionEntries.
 - (id)initWithOrigin:(CGPoint)origin
           parentView:(UIView*)parent
                items:(const web::NavigationItemList&)items
-          dispatcher:(id<BrowserCommands>)dispatcher NS_DESIGNATED_INITIALIZER;
+          dispatcher:(id<TabHistoryPopupCommands>)dispatcher
+    NS_DESIGNATED_INITIALIZER;
 
 @end
 
diff --git a/ios/chrome/browser/ui/history_popup/tab_history_popup_controller.mm b/ios/chrome/browser/ui/history_popup/tab_history_popup_controller.mm
index 2622557..c5be1138 100644
--- a/ios/chrome/browser/ui/history_popup/tab_history_popup_controller.mm
+++ b/ios/chrome/browser/ui/history_popup/tab_history_popup_controller.mm
@@ -65,7 +65,7 @@
 - (id)initWithOrigin:(CGPoint)origin
           parentView:(UIView*)parent
                items:(const web::NavigationItemList&)items
-          dispatcher:(id<BrowserCommands>)dispatcher {
+          dispatcher:(id<TabHistoryPopupCommands>)dispatcher {
   DCHECK(parent);
   if ((self = [super initWithParentView:parent])) {
     // Create the table view controller.
@@ -92,6 +92,12 @@
     CGFloat popupWidth = [[self class] popupWidthForItems:items];
     [self setOptimalSize:CGSizeMake(popupWidth, optimalHeight)
                 atOrigin:newOrigin];
+
+    // Fade in the popup.
+    CGRect containerFrame = [[self popupContainer] frame];
+    CGPoint destination = CGPointMake(CGRectGetLeadingEdge(containerFrame),
+                                      CGRectGetMinY(containerFrame));
+    [self fadeInPopupFromSource:origin toDestination:destination];
   }
   return self;
 }
diff --git a/ios/chrome/browser/ui/history_popup/tab_history_view_controller.h b/ios/chrome/browser/ui/history_popup/tab_history_view_controller.h
index 5529d57..72664a8 100644
--- a/ios/chrome/browser/ui/history_popup/tab_history_view_controller.h
+++ b/ios/chrome/browser/ui/history_popup/tab_history_view_controller.h
@@ -9,14 +9,14 @@
 
 #include "ios/web/public/navigation_item_list.h"
 
-@protocol BrowserCommands;
+@protocol TabHistoryPopupCommands;
 
 // View controller for displaying a list of NavigationItems in a table.
 @interface TabHistoryViewController : UICollectionViewController
 
 // Designated initializer that takes a NavigationItemList.
 - (instancetype)initWithItems:(const web::NavigationItemList&)items
-                   dispatcher:(id<BrowserCommands>)dispatcher
+                   dispatcher:(id<TabHistoryPopupCommands>)dispatcher
     NS_DESIGNATED_INITIALIZER;
 
 // TabHistoryViewControllers must be initialized with |-initWithItems:|.
diff --git a/ios/chrome/browser/ui/history_popup/tab_history_view_controller.mm b/ios/chrome/browser/ui/history_popup/tab_history_view_controller.mm
index 765e31d..e7f958c 100644
--- a/ios/chrome/browser/ui/history_popup/tab_history_view_controller.mm
+++ b/ios/chrome/browser/ui/history_popup/tab_history_view_controller.mm
@@ -7,9 +7,7 @@
 #include "base/logging.h"
 #include "base/mac/foundation_util.h"
 #include "base/strings/sys_string_conversions.h"
-#import "ios/chrome/browser/ui/commands/UIKit+ChromeExecuteCommand.h"
-#include "ios/chrome/browser/ui/commands/browser_commands.h"
-#include "ios/chrome/browser/ui/commands/ios_command_ids.h"
+#include "ios/chrome/browser/ui/commands/history_popup_commands.h"
 #import "ios/chrome/browser/ui/history_popup/tab_history_cell.h"
 #include "ios/chrome/browser/ui/rtl_geometry.h"
 #import "ios/third_party/material_components_ios/src/components/Ink/src/MaterialInk.h"
@@ -266,7 +264,7 @@
 - (void)clearNavigationItems;
 
 // The dispatcher used by this ViewController.
-@property(nonatomic, readonly, weak) id<BrowserCommands> dispatcher;
+@property(nonatomic, readonly, weak) id<TabHistoryPopupCommands> dispatcher;
 
 @end
 
@@ -275,7 +273,7 @@
 @synthesize dispatcher = _dispatcher;
 
 - (instancetype)initWithItems:(const web::NavigationItemList&)items
-                   dispatcher:(id<BrowserCommands>)dispatcher {
+                   dispatcher:(id<TabHistoryPopupCommands>)dispatcher {
   TabHistoryViewControllerLayout* layout =
       [[TabHistoryViewControllerLayout alloc] init];
   if ((self = [super initWithCollectionViewLayout:layout])) {
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_egtest.mm b/ios/chrome/browser/ui/ntp/new_tab_page_egtest.mm
index a706f20..5e9afa3 100644
--- a/ios/chrome/browser/ui/ntp/new_tab_page_egtest.mm
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_egtest.mm
@@ -283,10 +283,12 @@
     EARL_GREY_TEST_SKIPPED(@"Skipped for iPad (no hidden toolbar in tablet)");
   }
 
+  NSString* toolsMenuLabel = l10n_util::GetNSString(IDS_IOS_TOOLBAR_SETTINGS);
+
   // Check that the toolbar's tab switcher and tools menu buttons are visible.
   [[EarlGrey selectElementWithMatcher:chrome_test_util::ShowTabsButton()]
       assertWithMatcher:grey_sufficientlyVisible()];
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::ToolsMenuButton()]
+  [[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(toolsMenuLabel)]
       assertWithMatcher:grey_sufficientlyVisible()];
   AssertNTPScrolledToTop(NO);
 
@@ -299,7 +301,7 @@
   // Check that tab switcher and tools menu buttons are not on screen.
   [[EarlGrey selectElementWithMatcher:chrome_test_util::ShowTabsButton()]
       assertWithMatcher:grey_notVisible()];
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::ToolsMenuButton()]
+  [[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(toolsMenuLabel)]
       assertWithMatcher:grey_notVisible()];
 }
 
diff --git a/ios/chrome/browser/ui/overscroll_actions/BUILD.gn b/ios/chrome/browser/ui/overscroll_actions/BUILD.gn
index 13b54b5..bd98076a 100644
--- a/ios/chrome/browser/ui/overscroll_actions/BUILD.gn
+++ b/ios/chrome/browser/ui/overscroll_actions/BUILD.gn
@@ -22,6 +22,7 @@
     "//base",
     "//ios/chrome/app/theme",
     "//ios/chrome/browser/ui",
+    "//ios/chrome/browser/ui/history_popup/requirements",
     "//ios/chrome/browser/ui/static_content",
     "//ios/chrome/browser/ui/toolbar",
     "//ios/chrome/browser/ui/util",
diff --git a/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_controller.mm b/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_controller.mm
index c331fdf..6cefbc79 100644
--- a/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_controller.mm
+++ b/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_controller.mm
@@ -10,11 +10,11 @@
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
 #import "ios/chrome/browser/ui/browser_view_controller.h"
+#import "ios/chrome/browser/ui/history_popup/requirements/tab_history_constants.h"
 #import "ios/chrome/browser/ui/overscroll_actions/overscroll_actions_gesture_recognizer.h"
 #import "ios/chrome/browser/ui/overscroll_actions/overscroll_actions_view.h"
 #include "ios/chrome/browser/ui/rtl_geometry.h"
 #import "ios/chrome/browser/ui/toolbar/toolbar_controller.h"
-#import "ios/chrome/browser/ui/toolbar/web_toolbar_controller.h"
 #include "ios/chrome/browser/ui/uikit_ui_util.h"
 #import "ios/chrome/browser/ui/voice/voice_search_notification_names.h"
 #import "ios/web/public/web_state/ui/crw_web_view_proxy.h"
diff --git a/ios/chrome/browser/ui/toolbar/BUILD.gn b/ios/chrome/browser/ui/toolbar/BUILD.gn
index 011f19fe..74976925 100644
--- a/ios/chrome/browser/ui/toolbar/BUILD.gn
+++ b/ios/chrome/browser/ui/toolbar/BUILD.gn
@@ -65,7 +65,7 @@
     "//ios/chrome/browser/ui/commands",
     "//ios/chrome/browser/ui/fancy_ui",
     "//ios/chrome/browser/ui/history",
-    "//ios/chrome/browser/ui/history_popup",
+    "//ios/chrome/browser/ui/history_popup/requirements",
     "//ios/chrome/browser/ui/keyboard",
     "//ios/chrome/browser/ui/popup_menu",
     "//ios/chrome/browser/ui/qr_scanner",
diff --git a/ios/chrome/browser/ui/toolbar/web_toolbar_controller.h b/ios/chrome/browser/ui/toolbar/web_toolbar_controller.h
index a4e3039..36d2a142 100644
--- a/ios/chrome/browser/ui/toolbar/web_toolbar_controller.h
+++ b/ios/chrome/browser/ui/toolbar/web_toolbar_controller.h
@@ -7,6 +7,8 @@
 
 #import <UIKit/UIKit.h>
 
+#import "ios/chrome/browser/ui/history_popup/requirements/tab_history_positioner.h"
+#import "ios/chrome/browser/ui/history_popup/requirements/tab_history_ui_updater.h"
 #include "ios/chrome/browser/ui/omnibox/omnibox_popup_positioner.h"
 #include "ios/chrome/browser/ui/qr_scanner/qr_scanner_view_controller.h"
 #import "ios/chrome/browser/ui/toolbar/toolbar_controller.h"
@@ -84,6 +86,8 @@
 @interface WebToolbarController
     : ToolbarController<OmniboxFocuser,
                         QRScannerViewControllerDelegate,
+                        TabHistoryPositioner,
+                        TabHistoryUIUpdater,
                         VoiceSearchControllerDelegate>
 
 @property(nonatomic, weak) id<WebToolbarDelegate> delegate;
@@ -150,14 +154,6 @@
 // if it is up to date.
 - (UIImage*)snapshotWithWidth:(CGFloat)width;
 
-// Shows the tab history popup inside |view|.
-- (void)showTabHistoryPopupInView:(UIView*)view
-                        withItems:(const web::NavigationItemList&)items
-                   forBackHistory:(BOOL)isBackHistory;
-
-// Dismisses the tab history popup.
-- (void)dismissTabHistoryPopup;
-
 // Returns whether omnibox is a first responder.
 - (BOOL)isOmniboxFirstResponder;
 
diff --git a/ios/chrome/browser/ui/toolbar/web_toolbar_controller.mm b/ios/chrome/browser/ui/toolbar/web_toolbar_controller.mm
index d636742..a042e047 100644
--- a/ios/chrome/browser/ui/toolbar/web_toolbar_controller.mm
+++ b/ios/chrome/browser/ui/toolbar/web_toolbar_controller.mm
@@ -45,7 +45,6 @@
 #import "ios/chrome/browser/ui/commands/generic_chrome_command.h"
 #include "ios/chrome/browser/ui/commands/ios_command_ids.h"
 #include "ios/chrome/browser/ui/commands/start_voice_search_command.h"
-#import "ios/chrome/browser/ui/history_popup/tab_history_popup_controller.h"
 #import "ios/chrome/browser/ui/image_util.h"
 #include "ios/chrome/browser/ui/omnibox/location_bar_controller_impl.h"
 #include "ios/chrome/browser/ui/omnibox/omnibox_view_ios.h"
@@ -87,10 +86,6 @@
 using base::UserMetricsAction;
 using ios::material::TimingFunction;
 
-NSString* const kTabHistoryPopupWillShowNotification =
-    @"kTabHistoryPopupWillShowNotification";
-NSString* const kTabHistoryPopupWillHideNotification =
-    @"kTabHistoryPopupWillHideNotification";
 const CGFloat kiPhoneOmniboxPlaceholderColorBrightness = 150 / 255.0;
 
 // The histogram recording CLAuthorizationStatus for omnibox queries.
@@ -285,10 +280,6 @@
   // A hash of the state of the toolbar when the snapshot was taken.
   uint32_t _snapshotHash;
 
-  // View controller for displaying tab history when the user long presses the
-  // back or forward button. nil if not visible.
-  TabHistoryPopupController* _tabHistoryPopupController;
-
 #if defined(__IPHONE_11_0) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_11_0)
   API_AVAILABLE(ios(11.0)) DropAndNavigateInteraction* _dropInteraction;
 #endif
@@ -702,7 +693,6 @@
 
 - (void)dealloc {
   [[NSNotificationCenter defaultCenter] removeObserver:self];
-  [_tabHistoryPopupController setDelegate:nil];
 }
 
 #pragma mark -
@@ -837,65 +827,6 @@
   return _snapshot;
 }
 
-- (void)showTabHistoryPopupInView:(UIView*)view
-                        withItems:(const web::NavigationItemList&)items
-                   forBackHistory:(BOOL)isBackHistory {
-  if (_tabHistoryPopupController)
-    return;
-
-  base::RecordAction(UserMetricsAction("MobileToolbarShowTabHistoryMenu"));
-
-  UIButton* historyButton = isBackHistory ? _backButton : _forwardButton;
-  // Keep the button pressed by swapping the normal and highlighted images.
-  [self setImagesForNavButton:historyButton withTabHistoryVisible:YES];
-
-  // Set the origin for the tools popup to the leading side of the bottom of the
-  // pressed buttons.
-  CGRect buttonBounds = [historyButton.imageView bounds];
-  CGPoint origin = CGPointMake(CGRectGetLeadingEdge(buttonBounds),
-                               CGRectGetMaxY(buttonBounds));
-  CGPoint convertedOrigin =
-      [view convertPoint:origin fromView:historyButton.imageView];
-  _tabHistoryPopupController =
-      [[TabHistoryPopupController alloc] initWithOrigin:convertedOrigin
-                                             parentView:view
-                                                  items:items
-                                             dispatcher:self.dispatcher];
-  [_tabHistoryPopupController setDelegate:self];
-
-  // Fade in the popup and notify observers.
-  CGRect containerFrame = [[_tabHistoryPopupController popupContainer] frame];
-  CGPoint destination = CGPointMake(CGRectGetLeadingEdge(containerFrame),
-                                    CGRectGetMinY(containerFrame));
-  [_tabHistoryPopupController fadeInPopupFromSource:convertedOrigin
-                                      toDestination:destination];
-  [[NSNotificationCenter defaultCenter]
-      postNotificationName:kTabHistoryPopupWillShowNotification
-                    object:nil];
-}
-
-- (void)dismissTabHistoryPopup {
-  if (!_tabHistoryPopupController)
-    return;
-  __block TabHistoryPopupController* tempTHPC = _tabHistoryPopupController;
-  [tempTHPC containerView].userInteractionEnabled = NO;
-  [tempTHPC dismissAnimatedWithCompletion:^{
-    // Unpress the back/forward button by restoring the normal and
-    // highlighted images to their usual state.
-    [self setImagesForNavButton:_backButton withTabHistoryVisible:NO];
-    [self setImagesForNavButton:_forwardButton withTabHistoryVisible:NO];
-    // Reference tempTHPC so the block retains it.
-    tempTHPC = nil;
-  }];
-  // reset _tabHistoryPopupController to prevent -applicationDidEnterBackground
-  // from posting another kTabHistoryPopupWillHideNotification.
-  _tabHistoryPopupController = nil;
-
-  [[NSNotificationCenter defaultCenter]
-      postNotificationName:kTabHistoryPopupWillHideNotification
-                    object:nil];
-}
-
 - (BOOL)isOmniboxFirstResponder {
   return [_omniBox isFirstResponder];
 }
@@ -932,19 +863,6 @@
 #pragma mark -
 #pragma mark Overridden superclass methods.
 
-- (void)applicationDidEnterBackground:(NSNotification*)notify {
-  if (_tabHistoryPopupController) {
-    // Dismiss the tab history popup without animation.
-    [self setImagesForNavButton:_backButton withTabHistoryVisible:NO];
-    [self setImagesForNavButton:_forwardButton withTabHistoryVisible:NO];
-    _tabHistoryPopupController = nil;
-    [[NSNotificationCenter defaultCenter]
-        postNotificationName:kTabHistoryPopupWillHideNotification
-                      object:nil];
-  }
-  [super applicationDidEnterBackground:notify];
-}
-
 - (void)setUpButton:(UIButton*)button
        withImageEnum:(int)imageEnum
      forInitialState:(UIControlState)initialState
@@ -1426,17 +1344,6 @@
 }
 
 #pragma mark -
-#pragma mark PopupMenuDelegate methods.
-
-- (void)dismissPopupMenu:(PopupMenuController*)controller {
-  if ([controller isKindOfClass:[TabHistoryPopupController class]] &&
-      (TabHistoryPopupController*)controller == _tabHistoryPopupController)
-    [self dismissTabHistoryPopup];
-  else
-    [super dismissPopupMenu:controller];
-}
-
-#pragma mark -
 #pragma mark ToolbarFrameDelegate methods.
 
 - (void)frameDidChangeFrame:(CGRect)newFrame fromFrame:(CGRect)oldFrame {
@@ -1508,6 +1415,32 @@
   [_omniBox insertTextWhileEditing:text];
 }
 
+#pragma mark - TabHistory Requirements
+
+- (CGPoint)originPointForToolbarButton:(ToolbarButton)toolbarButton {
+  UIButton* historyButton = toolbarButton ? _backButton : _forwardButton;
+
+  // Set the origin for the tools popup to the leading side of the bottom of the
+  // pressed buttons.
+  CGRect buttonBounds = [historyButton.imageView bounds];
+  CGPoint leadingBottomCorner = CGPointMake(CGRectGetLeadingEdge(buttonBounds),
+                                            CGRectGetMaxY(buttonBounds));
+  CGPoint origin = [historyButton.imageView convertPoint:leadingBottomCorner
+                                                  toView:historyButton.window];
+  return origin;
+}
+
+- (void)updateUIForTabHistoryPresentationFrom:(ToolbarButton)button {
+  UIButton* historyButton = button ? _backButton : _forwardButton;
+  // Keep the button pressed by swapping the normal and highlighted images.
+  [self setImagesForNavButton:historyButton withTabHistoryVisible:YES];
+}
+
+- (void)updateUIForTabHistoryWasDismissed {
+  [self setImagesForNavButton:_backButton withTabHistoryVisible:NO];
+  [self setImagesForNavButton:_forwardButton withTabHistoryVisible:NO];
+}
+
 #pragma mark -
 #pragma mark DropAndNavigateDelegate
 
diff --git a/ios/chrome/test/earl_grey/chrome_matchers.mm b/ios/chrome/test/earl_grey/chrome_matchers.mm
index 99d95529..f1669b5 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers.mm
+++ b/ios/chrome/test/earl_grey/chrome_matchers.mm
@@ -139,7 +139,8 @@
 }
 
 id<GREYMatcher> ToolsMenuButton() {
-  return ButtonWithAccessibilityLabelId(IDS_IOS_TOOLBAR_SETTINGS);
+  return grey_allOf(grey_accessibilityID(kToolbarToolsMenuButtonIdentifier),
+                    grey_sufficientlyVisible(), nil);
 }
 
 id<GREYMatcher> ShareButton() {
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 1288ecc..016344d 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -1280,8 +1280,6 @@
       "quic/core/quic_client_promised_info.h",
       "quic/core/quic_client_push_promise_index.cc",
       "quic/core/quic_client_push_promise_index.h",
-      "quic/core/quic_client_session_base.cc",
-      "quic/core/quic_client_session_base.h",
       "quic/core/quic_config.cc",
       "quic/core/quic_config.h",
       "quic/core/quic_connection.cc",
@@ -1342,6 +1340,8 @@
       "quic/core/quic_simple_buffer_allocator.h",
       "quic/core/quic_socket_address_coder.cc",
       "quic/core/quic_socket_address_coder.h",
+      "quic/core/quic_spdy_client_session_base.cc",
+      "quic/core/quic_spdy_client_session_base.h",
       "quic/core/quic_spdy_session.cc",
       "quic/core/quic_spdy_session.h",
       "quic/core/quic_spdy_stream.cc",
@@ -3010,6 +3010,8 @@
       "tools/quic/platform/impl/quic_socket_utils.h",
       "tools/quic/quic_client.cc",
       "tools/quic/quic_client.h",
+      "tools/quic/quic_client_epoll_network_helper.cc",
+      "tools/quic/quic_client_epoll_network_helper.h",
       "tools/quic/quic_default_packet_writer.cc",
       "tools/quic/quic_default_packet_writer.h",
       "tools/quic/quic_epoll_alarm_factory.cc",
@@ -3257,8 +3259,8 @@
     "tools/quic/chlo_extractor.h",
     "tools/quic/quic_client_base.cc",
     "tools/quic/quic_client_base.h",
-    "tools/quic/quic_client_session.cc",
-    "tools/quic/quic_client_session.h",
+    "tools/quic/quic_client_message_loop_network_helper.cc",
+    "tools/quic/quic_client_message_loop_network_helper.h",
     "tools/quic/quic_dispatcher.cc",
     "tools/quic/quic_dispatcher.h",
     "tools/quic/quic_http_response_cache.cc",
@@ -3284,6 +3286,10 @@
     "tools/quic/quic_simple_server_session_helper.h",
     "tools/quic/quic_simple_server_stream.cc",
     "tools/quic/quic_simple_server_stream.h",
+    "tools/quic/quic_spdy_client_base.cc",
+    "tools/quic/quic_spdy_client_base.h",
+    "tools/quic/quic_spdy_client_session.cc",
+    "tools/quic/quic_spdy_client_session.h",
     "tools/quic/quic_spdy_client_stream.cc",
     "tools/quic/quic_spdy_client_stream.h",
     "tools/quic/quic_spdy_server_stream_base.cc",
@@ -5297,7 +5303,6 @@
       "tools/quic/end_to_end_test.cc",
       "tools/quic/platform/impl/quic_epoll_clock_test.cc",
       "tools/quic/platform/impl/quic_socket_utils_test.cc",
-      "tools/quic/quic_client_session_test.cc",
       "tools/quic/quic_client_test.cc",
       "tools/quic/quic_dispatcher_test.cc",
       "tools/quic/quic_epoll_alarm_factory_test.cc",
@@ -5308,6 +5313,7 @@
       "tools/quic/quic_simple_server_session_test.cc",
       "tools/quic/quic_simple_server_stream_test.cc",
       "tools/quic/quic_simple_server_test.cc",
+      "tools/quic/quic_spdy_client_session_test.cc",
       "tools/quic/quic_spdy_client_stream_test.cc",
       "tools/quic/quic_spdy_server_stream_base_test.cc",
       "tools/quic/quic_time_wait_list_manager_test.cc",
diff --git a/net/quic/chromium/quic_chromium_client_session.cc b/net/quic/chromium/quic_chromium_client_session.cc
index 904f837f..d2a17ba 100644
--- a/net/quic/chromium/quic_chromium_client_session.cc
+++ b/net/quic/chromium/quic_chromium_client_session.cc
@@ -592,7 +592,7 @@
     base::TaskRunner* task_runner,
     std::unique_ptr<SocketPerformanceWatcher> socket_performance_watcher,
     NetLog* net_log)
-    : QuicClientSessionBase(connection, push_promise_index, config),
+    : QuicSpdyClientSessionBase(connection, push_promise_index, config),
       server_id_(server_id),
       require_confirmation_(require_confirmation),
       stream_factory_(stream_factory),
@@ -758,7 +758,7 @@
 }
 
 void QuicChromiumClientSession::Initialize() {
-  QuicClientSessionBase::Initialize();
+  QuicSpdyClientSessionBase::Initialize();
   SetHpackEncoderDebugVisitor(
       base::MakeUnique<HpackEncoderDebugVisitor>());
   SetHpackDecoderDebugVisitor(
@@ -1149,7 +1149,7 @@
 }
 
 void QuicChromiumClientSession::OnConfigNegotiated() {
-  QuicClientSessionBase::OnConfigNegotiated();
+  QuicSpdyClientSessionBase::OnConfigNegotiated();
   if (!stream_factory_ || !config()->HasReceivedAlternateServerAddress())
     return;
 
@@ -1778,7 +1778,8 @@
 bool QuicChromiumClientSession::HandlePromised(QuicStreamId id,
                                                QuicStreamId promised_id,
                                                const SpdyHeaderBlock& headers) {
-  bool result = QuicClientSessionBase::HandlePromised(id, promised_id, headers);
+  bool result =
+      QuicSpdyClientSessionBase::HandlePromised(id, promised_id, headers);
   if (result) {
     // The push promise is accepted, notify the push_delegate that a push
     // promise has been received.
@@ -1799,7 +1800,7 @@
     QuicClientPromisedInfo* promised) {
   if (IsOpenStream(promised->id()))
     streams_pushed_and_claimed_count_++;
-  QuicClientSessionBase::DeletePromised(promised);
+  QuicSpdyClientSessionBase::DeletePromised(promised);
 }
 
 void QuicChromiumClientSession::OnPushStreamTimedOut(QuicStreamId stream_id) {
@@ -1810,7 +1811,7 @@
 
 void QuicChromiumClientSession::CancelPush(const GURL& url) {
   QuicClientPromisedInfo* promised_info =
-      QuicClientSessionBase::GetPromisedByUrl(url.spec());
+      QuicSpdyClientSessionBase::GetPromisedByUrl(url.spec());
   if (!promised_info || promised_info->is_validating()) {
     // Push stream has already been claimed or is pending matched to a request.
     return;
@@ -1824,7 +1825,7 @@
     bytes_pushed_and_unclaimed_count_ += stream->stream_bytes_read();
 
   // Send the reset and remove the promised info from the promise index.
-  QuicClientSessionBase::ResetPromised(stream_id, QUIC_STREAM_CANCELLED);
+  QuicSpdyClientSessionBase::ResetPromised(stream_id, QUIC_STREAM_CANCELLED);
   DeletePromised(promised_info);
 }
 
diff --git a/net/quic/chromium/quic_chromium_client_session.h b/net/quic/chromium/quic_chromium_client_session.h
index 3b2102c..9a888bb 100644
--- a/net/quic/chromium/quic_chromium_client_session.h
+++ b/net/quic/chromium/quic_chromium_client_session.h
@@ -33,10 +33,10 @@
 #include "net/quic/chromium/quic_chromium_packet_writer.h"
 #include "net/quic/chromium/quic_connection_logger.h"
 #include "net/quic/core/quic_client_push_promise_index.h"
-#include "net/quic/core/quic_client_session_base.h"
 #include "net/quic/core/quic_crypto_client_stream.h"
 #include "net/quic/core/quic_packets.h"
 #include "net/quic/core/quic_server_id.h"
+#include "net/quic/core/quic_spdy_client_session_base.h"
 #include "net/quic/core/quic_time.h"
 #include "net/socket/socket_performance_watcher.h"
 #include "net/spdy/chromium/multiplexed_session.h"
@@ -62,7 +62,7 @@
 }  // namespace test
 
 class NET_EXPORT_PRIVATE QuicChromiumClientSession
-    : public QuicClientSessionBase,
+    : public QuicSpdyClientSessionBase,
       public MultiplexedSession,
       public QuicChromiumPacketReader::Visitor,
       public QuicChromiumPacketWriter::Delegate {
diff --git a/net/quic/chromium/quic_chromium_client_stream.cc b/net/quic/chromium/quic_chromium_client_stream.cc
index 029cd4d8..f765b19 100644
--- a/net/quic/chromium/quic_chromium_client_stream.cc
+++ b/net/quic/chromium/quic_chromium_client_stream.cc
@@ -389,7 +389,7 @@
 
 QuicChromiumClientStream::QuicChromiumClientStream(
     QuicStreamId id,
-    QuicClientSessionBase* session,
+    QuicSpdyClientSessionBase* session,
     const NetLogWithSource& net_log)
     : QuicSpdyStream(id, session),
       net_log_(net_log),
diff --git a/net/quic/chromium/quic_chromium_client_stream.h b/net/quic/chromium/quic_chromium_client_stream.h
index 80a84b17..92dfa30 100644
--- a/net/quic/chromium/quic_chromium_client_stream.h
+++ b/net/quic/chromium/quic_chromium_client_stream.h
@@ -26,7 +26,7 @@
 
 namespace net {
 
-class QuicClientSessionBase;
+class QuicSpdyClientSessionBase;
 
 // A client-initiated ReliableQuicStream.  Instances of this class
 // are owned by the QuicClientSession which created them.
@@ -190,7 +190,7 @@
   };
 
   QuicChromiumClientStream(QuicStreamId id,
-                           QuicClientSessionBase* session,
+                           QuicSpdyClientSessionBase* session,
                            const NetLogWithSource& net_log);
 
   ~QuicChromiumClientStream() override;
@@ -273,7 +273,7 @@
   // True when initial headers have been sent.
   bool initial_headers_sent_;
 
-  QuicClientSessionBase* session_;
+  QuicSpdyClientSessionBase* session_;
 
   // Set to false if this stream to not be migrated during connection migration.
   bool can_migrate_;
diff --git a/net/quic/chromium/quic_chromium_client_stream_test.cc b/net/quic/chromium/quic_chromium_client_stream_test.cc
index dab3390..62431ae1 100644
--- a/net/quic/chromium/quic_chromium_client_stream_test.cc
+++ b/net/quic/chromium/quic_chromium_client_stream_test.cc
@@ -13,7 +13,7 @@
 #include "net/base/net_errors.h"
 #include "net/base/test_completion_callback.h"
 #include "net/quic/chromium/quic_chromium_client_session.h"
-#include "net/quic/core/quic_client_session_base.h"
+#include "net/quic/core/quic_spdy_client_session_base.h"
 #include "net/quic/core/quic_utils.h"
 #include "net/quic/core/spdy_utils.h"
 #include "net/quic/platform/api/quic_ptr_util.h"
@@ -38,7 +38,7 @@
 
 const QuicStreamId kTestStreamId = 5u;
 
-class MockQuicClientSessionBase : public QuicClientSessionBase {
+class MockQuicClientSessionBase : public QuicSpdyClientSessionBase {
  public:
   explicit MockQuicClientSessionBase(QuicConnection* connection,
                                      QuicClientPushPromiseIndex* index);
@@ -142,9 +142,9 @@
 MockQuicClientSessionBase::MockQuicClientSessionBase(
     QuicConnection* connection,
     QuicClientPushPromiseIndex* push_promise_index)
-    : QuicClientSessionBase(connection,
-                            push_promise_index,
-                            DefaultQuicConfig()) {
+    : QuicSpdyClientSessionBase(connection,
+                                push_promise_index,
+                                DefaultQuicConfig()) {
   crypto_stream_.reset(new MockQuicCryptoStream(this));
   Initialize();
   ON_CALL(*this, WritevData(_, _, _, _, _, _))
diff --git a/net/quic/chromium/quic_test_packet_maker.cc b/net/quic/chromium/quic_test_packet_maker.cc
index 6a692ec..23a40b5 100644
--- a/net/quic/chromium/quic_test_packet_maker.cc
+++ b/net/quic/chromium/quic_test_packet_maker.cc
@@ -99,7 +99,7 @@
     ack.received_packet_times.push_back(std::make_pair(i, clock_->Now()));
   }
   if (largest_received > 0) {
-    ack.packets.Add(1, largest_received + 1);
+    ack.packets.AddRange(1, largest_received + 1);
   }
   QuicFrames frames;
   frames.push_back(QuicFrame(&ack));
@@ -148,7 +148,7 @@
     ack.received_packet_times.push_back(std::make_pair(i, clock_->Now()));
   }
   if (largest_received > 0) {
-    ack.packets.Add(1, largest_received + 1);
+    ack.packets.AddRange(1, largest_received + 1);
   }
   QuicFrames frames;
   frames.push_back(QuicFrame(&ack));
@@ -242,7 +242,7 @@
     ack.received_packet_times.push_back(std::make_pair(i, clock_->Now()));
   }
   if (largest_received > 0) {
-    ack.packets.Add(1, largest_received + 1);
+    ack.packets.AddRange(1, largest_received + 1);
   }
 
   QuicFramer framer(SupportedVersions(version_), clock_->Now(), perspective_);
@@ -324,7 +324,7 @@
     ack.received_packet_times.push_back(std::make_pair(i, clock_->Now()));
   }
   if (largest_received > 0) {
-    ack.packets.Add(1, largest_received + 1);
+    ack.packets.AddRange(1, largest_received + 1);
   }
   QuicFrames frames;
   frames.push_back(QuicFrame(&ack));
diff --git a/net/quic/core/crypto/crypto_framer_test.cc b/net/quic/core/crypto/crypto_framer_test.cc
index 846f91f4..9d61aa4e 100644
--- a/net/quic/core/crypto/crypto_framer_test.cc
+++ b/net/quic/core/crypto/crypto_framer_test.cc
@@ -47,6 +47,11 @@
   std::vector<CryptoHandshakeMessage> messages_;
 };
 
+INSTANTIATE_TEST_CASE_P(Tests,
+                        CryptoFramerTest,
+                        ::testing::ValuesIn({Perspective::IS_CLIENT,
+                                             Perspective::IS_SERVER}));
+
 TEST_P(CryptoFramerTest, ConstructHandshakeMessage) {
   CryptoHandshakeMessage message;
   message.set_tag(0xFFAA7733);
diff --git a/net/quic/core/crypto/crypto_handshake_message_test.cc b/net/quic/core/crypto/crypto_handshake_message_test.cc
index a44e64a..1f5660b9 100644
--- a/net/quic/core/crypto/crypto_handshake_message_test.cc
+++ b/net/quic/core/crypto/crypto_handshake_message_test.cc
@@ -6,6 +6,7 @@
 
 #include "net/quic/core/crypto/crypto_handshake.h"
 #include "net/quic/core/crypto/crypto_protocol.h"
+#include "net/quic/platform/api/quic_endian.h"
 #include "net/quic/platform/api/quic_test.h"
 
 namespace net {
@@ -14,6 +15,11 @@
 
 class CryptoHandshakeMessageTest : public QuicTestWithParam<Perspective> {};
 
+INSTANTIATE_TEST_CASE_P(Perspective,
+                        CryptoHandshakeMessageTest,
+                        ::testing::ValuesIn({Perspective::IS_CLIENT,
+                                             Perspective::IS_SERVER}));
+
 TEST_P(CryptoHandshakeMessageTest, DebugString) {
   const char* str = "SHLO<\n>";
 
@@ -99,7 +105,8 @@
 
   CryptoHandshakeMessage message;
   message.set_tag(kSREJ);
-  message.SetValue(kRCID, UINT64_C(18364758544493064720));
+  message.SetValue(kRCID,
+                   QuicEndian::NetToHost64(UINT64_C(18364758544493064720)));
   EXPECT_EQ(str, message.DebugString(GetParam()));
 
   // Test copy
diff --git a/net/quic/core/crypto/crypto_protocol.h b/net/quic/core/crypto/crypto_protocol.h
index e681283..c408070 100644
--- a/net/quic/core/crypto/crypto_protocol.h
+++ b/net/quic/core/crypto/crypto_protocol.h
@@ -115,6 +115,8 @@
                                                  // with 1/8 RTT acks.
 const QuicTag kAKD4 = TAG('A', 'K', 'D', '4');   // Ack decimation with 1/8 RTT
                                                  // tolerating out of order.
+const QuicTag kAKDU = TAG('A', 'K', 'D', 'U');   // Unlimited number of packets
+                                                 // receieved before acking
 const QuicTag kSSLR = TAG('S', 'S', 'L', 'R');   // Slow Start Large Reduction.
 const QuicTag kNPRR = TAG('N', 'P', 'R', 'R');   // Pace at unity instead of PRR
 const QuicTag k5RTO = TAG('5', 'R', 'T', 'O');   // Close connection on 5 RTOs
diff --git a/net/quic/core/frames/quic_ack_frame.cc b/net/quic/core/frames/quic_ack_frame.cc
index a86f6d0..385500a7 100644
--- a/net/quic/core/frames/quic_ack_frame.cc
+++ b/net/quic/core/frames/quic_ack_frame.cc
@@ -90,27 +90,30 @@
           Interval<QuicPacketNumber>(packet_number, packet_number + 1));
       return;
     }
+    Interval<QuicPacketNumber> back = packet_number_deque_.back();
 
     // Check for the typical case,
     // when the next packet in order is acked
-    if ((packet_number_deque_.back()).max() == packet_number) {
-      (packet_number_deque_.back()).SetMax(packet_number + 1);
+    if (back.max() == packet_number) {
+      packet_number_deque_.back().SetMax(packet_number + 1);
       return;
     }
     // Check if the next packet in order is skipped
-    if ((packet_number_deque_.back()).max() < packet_number) {
+    if (back.max() < packet_number) {
       packet_number_deque_.push_back(
           Interval<QuicPacketNumber>(packet_number, packet_number + 1));
       return;
     }
+
+    Interval<QuicPacketNumber> front = packet_number_deque_.front();
     // Check if the packet can be  popped on the front
-    if ((packet_number_deque_.front()).min() > packet_number + 1) {
+    if (front.min() > packet_number + 1) {
       packet_number_deque_.push_front(
           Interval<QuicPacketNumber>(packet_number, packet_number + 1));
       return;
     }
-    if ((packet_number_deque_.front()).min() == packet_number + 1) {
-      (packet_number_deque_.front()).SetMin(packet_number);
+    if (front.min() == packet_number + 1) {
+      packet_number_deque_.front().SetMin(packet_number);
       return;
     }
 
@@ -118,36 +121,35 @@
     // Iterating through the queue backwards
     // to find a proper place for the packet
     while (i >= 0) {
+      Interval<QuicPacketNumber> packet_interval = packet_number_deque_[i];
       // Check if the packet is contained in an interval already
-      if (packet_number_deque_[i].max() > packet_number &&
-          packet_number_deque_[i].min() <= packet_number) {
+      if (packet_interval.max() > packet_number &&
+          packet_interval.min() <= packet_number) {
         return;
       }
 
       // Check if the packet can extend an interval
       // and merges two intervals if needed
-      if (packet_number_deque_[i].max() == packet_number) {
+      if (packet_interval.max() == packet_number) {
         packet_number_deque_[i].SetMax(packet_number + 1);
         if (static_cast<size_t>(i) < packet_number_deque_.size() - 1 &&
-            packet_number_deque_[i].max() ==
-                packet_number_deque_[i + 1].min()) {
+            packet_number + 1 == packet_number_deque_[i + 1].min()) {
           packet_number_deque_[i].SetMax(packet_number_deque_[i + 1].max());
           packet_number_deque_.erase(packet_number_deque_.begin() + i + 1);
         }
         return;
       }
-      if (packet_number_deque_[i].min() == packet_number + 1) {
+      if (packet_interval.min() == packet_number + 1) {
         packet_number_deque_[i].SetMin(packet_number);
-        if (i > 0 && packet_number_deque_[i].min() ==
-                         packet_number_deque_[i - 1].max()) {
-          packet_number_deque_[i - 1].SetMax(packet_number_deque_[i].max());
+        if (i > 0 && packet_number == packet_number_deque_[i - 1].max()) {
+          packet_number_deque_[i - 1].SetMax(packet_interval.max());
           packet_number_deque_.erase(packet_number_deque_.begin() + i);
         }
         return;
       }
 
       // Check if we need to make a new interval for the packet
-      if (packet_number_deque_[i].max() < packet_number + 1) {
+      if (packet_interval.max() < packet_number + 1) {
         packet_number_deque_.insert(
             packet_number_deque_.begin() + i + 1,
             Interval<QuicPacketNumber>(packet_number, packet_number + 1));
@@ -160,7 +162,8 @@
   }
 }
 
-void PacketNumberQueue::Add(QuicPacketNumber lower, QuicPacketNumber higher) {
+void PacketNumberQueue::AddRange(QuicPacketNumber lower,
+                                 QuicPacketNumber higher) {
   if (lower >= higher) {
     return;
   }
@@ -168,26 +171,47 @@
     if (packet_number_deque_.empty()) {
       packet_number_deque_.push_front(
           Interval<QuicPacketNumber>(lower, higher));
+      return;
+    }
+    Interval<QuicPacketNumber> back = packet_number_deque_.back();
 
-    } else if ((packet_number_deque_.back()).max() == lower) {
+    if (back.max() == lower) {
       // Check for the typical case,
       // when the next packet in order is acked
-      (packet_number_deque_.back()).SetMax(higher);
-
-    } else if ((packet_number_deque_.back()).max() < lower) {
+      packet_number_deque_.back().SetMax(higher);
+      return;
+    }
+    if (back.max() < lower) {
       // Check if the next packet in order is skipped
       packet_number_deque_.push_back(Interval<QuicPacketNumber>(lower, higher));
-
-      // Check if the packets are being added in reverse order
-    } else if ((packet_number_deque_.front()).min() == higher) {
-      (packet_number_deque_.front()).SetMax(lower);
-    } else if ((packet_number_deque_.front()).min() > higher) {
+      return;
+    }
+    Interval<QuicPacketNumber> front = packet_number_deque_.front();
+    // Check if the packets are being added in reverse order
+    if (front.min() == higher) {
+      packet_number_deque_.front().SetMax(lower);
+    } else if (front.min() > higher) {
       packet_number_deque_.push_front(
           Interval<QuicPacketNumber>(lower, higher));
 
     } else {
       // Iterating through the interval and adding packets one by one
-      for (size_t i = lower; i != higher; i++) {
+      QUIC_BUG << "In the slowpath of AddRange. Adding [" << lower << ", "
+               << higher << "), in a deque of size "
+               << packet_number_deque_.size() << ", whose largest element is "
+               << back.max() << " and smallest " << front.min() << ".\n";
+      // Check if the first and/or the last interval of the deque can be
+      // extended, which would reduce the compexity of the following for loop.
+      if (higher >= back.max()) {
+        packet_number_deque_.back().SetMax(higher);
+        higher = back.min();
+      }
+      if (lower < front.min()) {
+        packet_number_deque_.front().SetMin(lower);
+        lower = front.max() - 1;
+      }
+
+      for (size_t i = lower; i < higher; i++) {
         PacketNumberQueue::Add(i);
       }
     }
@@ -203,13 +227,12 @@
   const QuicPacketNumber old_min = Min();
   if (use_deque_) {
     while (!packet_number_deque_.empty()) {
-      if (packet_number_deque_.front().max() < higher) {
+      Interval<QuicPacketNumber> front = packet_number_deque_.front();
+      if (front.max() < higher) {
         packet_number_deque_.pop_front();
-      } else if (packet_number_deque_.front().min() < higher &&
-                 packet_number_deque_.front().max() >= higher) {
+      } else if (front.min() < higher && front.max() >= higher) {
         packet_number_deque_.front().SetMin(higher);
-        if (packet_number_deque_.front().max() ==
-            packet_number_deque_.front().min()) {
+        if (front.max() == higher) {
           packet_number_deque_.pop_front();
         }
         break;
@@ -243,22 +266,14 @@
     if (packet_number_deque_.empty()) {
       return false;
     }
-    int low = 0;
-    int high = packet_number_deque_.size() - 1;
-
-    while (low <= high) {
-      int mid = (low + high) / 2;
-      if (packet_number_deque_[mid].min() > packet_number) {
-        high = mid - 1;
-        continue;
+    if (packet_number_deque_.front().min() > packet_number ||
+        packet_number_deque_.back().max() <= packet_number) {
+      return false;
+    }
+    for (Interval<QuicPacketNumber> interval : packet_number_deque_) {
+      if (interval.Contains(packet_number)) {
+        return true;
       }
-      if (packet_number_deque_[mid].max() <= packet_number) {
-        low = mid + 1;
-        continue;
-      }
-      DCHECK(packet_number_deque_[mid].max() > packet_number);
-      DCHECK(packet_number_deque_[mid].min() <= packet_number);
-      return true;
     }
     return false;
   } else {
@@ -294,9 +309,9 @@
 
 size_t PacketNumberQueue::NumPacketsSlow() const {
   if (use_deque_) {
-    size_t n_packets = 0;
-    for (size_t i = 0; i < packet_number_deque_.size(); i++) {
-      n_packets += packet_number_deque_[i].Length();
+    int n_packets = 0;
+    for (Interval<QuicPacketNumber> interval : packet_number_deque_) {
+      n_packets += interval.Length();
     }
     return n_packets;
   } else {
@@ -351,7 +366,7 @@
 QuicPacketNumber PacketNumberQueue::LastIntervalLength() const {
   DCHECK(!Empty());
   if (use_deque_) {
-    return packet_number_deque_[packet_number_deque_.size() - 1].Length();
+    return packet_number_deque_.back().Length();
   } else {
     return packet_number_intervals_.rbegin()->Length();
   }
diff --git a/net/quic/core/frames/quic_ack_frame.h b/net/quic/core/frames/quic_ack_frame.h
index c7584c1..f8b9ef0 100644
--- a/net/quic/core/frames/quic_ack_frame.h
+++ b/net/quic/core/frames/quic_ack_frame.h
@@ -198,7 +198,7 @@
 
   // Adds packets between [lower, higher) to the set of packets in the queue. It
   // is undefined behavior to call this with |higher| < |lower|.
-  void Add(QuicPacketNumber lower, QuicPacketNumber higher);
+  void AddRange(QuicPacketNumber lower, QuicPacketNumber higher);
 
   // Removes packets with values less than |higher| from the set of packets in
   // the queue. Returns true if packets were removed.
diff --git a/net/quic/core/frames/quic_frames_test.cc b/net/quic/core/frames/quic_frames_test.cc
index 70cbfcb3..9312e58 100644
--- a/net/quic/core/frames/quic_frames_test.cc
+++ b/net/quic/core/frames/quic_frames_test.cc
@@ -15,6 +15,7 @@
 #include "net/quic/core/frames/quic_stream_frame.h"
 #include "net/quic/core/frames/quic_window_update_frame.h"
 #include "net/quic/platform/api/quic_test.h"
+#include "net/quic/test_tools/quic_test_utils.h"
 
 namespace net {
 namespace test {
@@ -117,7 +118,7 @@
 TEST_F(QuicFramesTest, IsAwaitingPacket) {
   QuicAckFrame ack_frame1;
   ack_frame1.largest_observed = 10u;
-  ack_frame1.packets.Add(1, 11);
+  ack_frame1.packets.AddRange(1, 11);
   EXPECT_TRUE(IsAwaitingPacket(ack_frame1, 11u, 0u));
   EXPECT_FALSE(IsAwaitingPacket(ack_frame1, 1u, 0u));
 
@@ -126,12 +127,12 @@
 
   QuicAckFrame ack_frame2;
   ack_frame2.largest_observed = 100u;
-  ack_frame2.packets.Add(21, 100);
+  ack_frame2.packets.AddRange(21, 100);
   EXPECT_FALSE(IsAwaitingPacket(ack_frame2, 11u, 20u));
   EXPECT_FALSE(IsAwaitingPacket(ack_frame2, 80u, 20u));
   EXPECT_TRUE(IsAwaitingPacket(ack_frame2, 101u, 20u));
 
-  ack_frame2.packets.Add(102, 200);
+  ack_frame2.packets.AddRange(102, 200);
   EXPECT_TRUE(IsAwaitingPacket(ack_frame2, 101u, 20u));
 }
 
@@ -207,8 +208,8 @@
 
 TEST_F(QuicFramesTest, AddInterval) {
   QuicAckFrame ack_frame1;
-  ack_frame1.packets.Add(1, 10);
-  ack_frame1.packets.Add(50, 100);
+  ack_frame1.packets.AddRange(1, 10);
+  ack_frame1.packets.AddRange(50, 100);
 
   EXPECT_EQ(2u, ack_frame1.packets.NumIntervals());
   EXPECT_EQ(1u, ack_frame1.packets.Min());
@@ -223,7 +224,12 @@
 
   EXPECT_EQ(expected_intervals, actual_intervals);
 
-  ack_frame1.packets.Add(20, 30);
+  if (FLAGS_quic_reloadable_flag_quic_frames_deque) {
+    EXPECT_QUIC_BUG(ack_frame1.packets.AddRange(20, 30), "");
+  } else {
+    ack_frame1.packets.AddRange(20, 30);
+  }
+
   const std::vector<Interval<QuicPacketNumber>> actual_intervals2(
       ack_frame1.packets.begin(), ack_frame1.packets.end());
 
@@ -235,8 +241,13 @@
   EXPECT_EQ(3u, ack_frame1.packets.NumIntervals());
   EXPECT_EQ(expected_intervals2, actual_intervals2);
 
-  ack_frame1.packets.Add(15, 20);
-  ack_frame1.packets.Add(30, 35);
+  if (FLAGS_quic_reloadable_flag_quic_frames_deque) {
+    EXPECT_QUIC_BUG(ack_frame1.packets.AddRange(15, 20), "");
+    EXPECT_QUIC_BUG(ack_frame1.packets.AddRange(30, 35), "");
+  } else {
+    ack_frame1.packets.AddRange(15, 20);
+    ack_frame1.packets.AddRange(30, 35);
+  }
 
   const std::vector<Interval<QuicPacketNumber>> actual_intervals3(
       ack_frame1.packets.begin(), ack_frame1.packets.end());
@@ -248,15 +259,23 @@
 
   EXPECT_EQ(expected_intervals3, actual_intervals3);
 
-  ack_frame1.packets.Add(20, 35);
+  if (FLAGS_quic_reloadable_flag_quic_frames_deque) {
+    EXPECT_QUIC_BUG(ack_frame1.packets.AddRange(20, 35), "");
+  } else {
+    ack_frame1.packets.AddRange(20, 35);
+  }
 
   const std::vector<Interval<QuicPacketNumber>> actual_intervals4(
       ack_frame1.packets.begin(), ack_frame1.packets.end());
 
   EXPECT_EQ(expected_intervals3, actual_intervals4);
-
-  ack_frame1.packets.Add(12, 20);
-  ack_frame1.packets.Add(30, 38);
+  if (FLAGS_quic_reloadable_flag_quic_frames_deque) {
+    EXPECT_QUIC_BUG(ack_frame1.packets.AddRange(12, 20), "");
+    EXPECT_QUIC_BUG(ack_frame1.packets.AddRange(30, 38), "");
+  } else {
+    ack_frame1.packets.AddRange(12, 20);
+    ack_frame1.packets.AddRange(30, 38);
+  }
 
   const std::vector<Interval<QuicPacketNumber>> actual_intervals5(
       ack_frame1.packets.begin(), ack_frame1.packets.end());
@@ -267,8 +286,11 @@
   expected_intervals5.push_back(Interval<QuicPacketNumber>(50, 100));
 
   EXPECT_EQ(expected_intervals5, actual_intervals5);
-
-  ack_frame1.packets.Add(8, 55);
+  if (FLAGS_quic_reloadable_flag_quic_frames_deque) {
+    EXPECT_QUIC_BUG(ack_frame1.packets.AddRange(8, 55), "");
+  } else {
+    ack_frame1.packets.AddRange(8, 55);
+  }
 
   const std::vector<Interval<QuicPacketNumber>> actual_intervals6(
       ack_frame1.packets.begin(), ack_frame1.packets.end());
@@ -278,7 +300,11 @@
 
   EXPECT_EQ(expected_intervals6, actual_intervals6);
 
-  ack_frame1.packets.Add(0, 200);
+  if (FLAGS_quic_reloadable_flag_quic_frames_deque) {
+    EXPECT_QUIC_BUG(ack_frame1.packets.AddRange(0, 200), "");
+  } else {
+    ack_frame1.packets.AddRange(0, 200);
+  }
 
   const std::vector<Interval<QuicPacketNumber>> actual_intervals7(
       ack_frame1.packets.begin(), ack_frame1.packets.end());
@@ -289,11 +315,11 @@
   EXPECT_EQ(expected_intervals7, actual_intervals7);
 
   QuicAckFrame ack_frame2;
-  ack_frame2.packets.Add(20, 25);
-  ack_frame2.packets.Add(40, 45);
-  ack_frame2.packets.Add(60, 65);
-  ack_frame2.packets.Add(10, 15);
-  ack_frame2.packets.Add(80, 85);
+  ack_frame2.packets.AddRange(20, 25);
+  ack_frame2.packets.AddRange(40, 45);
+  ack_frame2.packets.AddRange(60, 65);
+  ack_frame2.packets.AddRange(10, 15);
+  ack_frame2.packets.AddRange(80, 85);
 
   const std::vector<Interval<QuicPacketNumber>> actual_intervals8(
       ack_frame2.packets.begin(), ack_frame2.packets.end());
@@ -308,12 +334,85 @@
   EXPECT_EQ(expected_intervals8, actual_intervals8);
 }
 
+TEST_F(QuicFramesTest, AddIntervalBig) {
+  QuicAckFrame ack_frame1;
+  ack_frame1.packets.AddRange(20, 30);
+  ack_frame1.packets.AddRange(70, 100);
+  if (FLAGS_quic_reloadable_flag_quic_frames_deque) {
+    EXPECT_QUIC_BUG(ack_frame1.packets.AddRange(56, 58), "");
+    EXPECT_QUIC_BUG(ack_frame1.packets.AddRange(65, 69), "");
+    EXPECT_QUIC_BUG(ack_frame1.packets.AddRange(59, 64), "");
+    EXPECT_QUIC_BUG(ack_frame1.packets.AddRange(50, 55), "");
+  } else {
+    ack_frame1.packets.AddRange(56, 58);
+    ack_frame1.packets.AddRange(65, 69);
+    ack_frame1.packets.AddRange(59, 64);
+    ack_frame1.packets.AddRange(50, 55);
+  }
+
+  std::vector<Interval<QuicPacketNumber>> expected_intervals;
+  expected_intervals.push_back(Interval<QuicPacketNumber>(20, 30));
+  expected_intervals.push_back(Interval<QuicPacketNumber>(50, 55));
+  expected_intervals.push_back(Interval<QuicPacketNumber>(56, 58));
+  expected_intervals.push_back(Interval<QuicPacketNumber>(59, 64));
+  expected_intervals.push_back(Interval<QuicPacketNumber>(65, 69));
+  expected_intervals.push_back(Interval<QuicPacketNumber>(70, 100));
+
+  const std::vector<Interval<QuicPacketNumber>> actual_intervals(
+      ack_frame1.packets.begin(), ack_frame1.packets.end());
+
+  EXPECT_EQ(expected_intervals, actual_intervals);
+  if (FLAGS_quic_reloadable_flag_quic_frames_deque) {
+    EXPECT_QUIC_BUG(ack_frame1.packets.AddRange(10, 60), "");
+  } else {
+    ack_frame1.packets.AddRange(10, 60);
+  }
+
+  std::vector<Interval<QuicPacketNumber>> expected_intervals2;
+  expected_intervals2.push_back(Interval<QuicPacketNumber>(10, 64));
+  expected_intervals2.push_back(Interval<QuicPacketNumber>(65, 69));
+  expected_intervals2.push_back(Interval<QuicPacketNumber>(70, 100));
+
+  const std::vector<Interval<QuicPacketNumber>> actual_intervals2(
+      ack_frame1.packets.begin(), ack_frame1.packets.end());
+
+  EXPECT_EQ(expected_intervals2, actual_intervals2);
+
+  if (FLAGS_quic_reloadable_flag_quic_frames_deque) {
+    EXPECT_QUIC_BUG(ack_frame1.packets.AddRange(68, 1000), "");
+  } else {
+    ack_frame1.packets.AddRange(68, 1000);
+  }
+
+  std::vector<Interval<QuicPacketNumber>> expected_intervals3;
+  expected_intervals3.push_back(Interval<QuicPacketNumber>(10, 64));
+  expected_intervals3.push_back(Interval<QuicPacketNumber>(65, 1000));
+
+  const std::vector<Interval<QuicPacketNumber>> actual_intervals3(
+      ack_frame1.packets.begin(), ack_frame1.packets.end());
+
+  EXPECT_EQ(expected_intervals3, actual_intervals3);
+  if (FLAGS_quic_reloadable_flag_quic_frames_deque) {
+    EXPECT_QUIC_BUG(ack_frame1.packets.AddRange(0, 10000), "");
+  } else {
+    ack_frame1.packets.AddRange(0, 10000);
+  }
+
+  std::vector<Interval<QuicPacketNumber>> expected_intervals4;
+  expected_intervals4.push_back(Interval<QuicPacketNumber>(0, 10000));
+
+  const std::vector<Interval<QuicPacketNumber>> actual_intervals4(
+      ack_frame1.packets.begin(), ack_frame1.packets.end());
+
+  EXPECT_EQ(expected_intervals4, actual_intervals4);
+}
+
 TEST_F(QuicFramesTest, RemoveSmallestInterval) {
   QuicAckFrame ack_frame1;
   ack_frame1.largest_observed = 100u;
-  ack_frame1.packets.Add(51, 60);
-  ack_frame1.packets.Add(71, 80);
-  ack_frame1.packets.Add(91, 100);
+  ack_frame1.packets.AddRange(51, 60);
+  ack_frame1.packets.AddRange(71, 80);
+  ack_frame1.packets.AddRange(91, 100);
   ack_frame1.packets.RemoveSmallestInterval();
   EXPECT_EQ(2u, ack_frame1.packets.NumIntervals());
   EXPECT_EQ(71u, ack_frame1.packets.Min());
@@ -330,7 +429,7 @@
 // Tests that a queue contains the expected data after calls to Add().
 TEST_F(PacketNumberQueueTest, AddRange) {
   PacketNumberQueue queue;
-  queue.Add(1, 51);
+  queue.AddRange(1, 51);
   queue.Add(53);
 
   EXPECT_FALSE(queue.Contains(0));
@@ -353,7 +452,7 @@
 TEST_F(PacketNumberQueueTest, Contains) {
   PacketNumberQueue queue;
   EXPECT_FALSE(queue.Contains(0));
-  queue.Add(5, 10);
+  queue.AddRange(5, 10);
   queue.Add(20);
 
   for (int i = 1; i < 5; ++i) {
@@ -389,7 +488,7 @@
 TEST_F(PacketNumberQueueTest, Removal) {
   PacketNumberQueue queue;
   EXPECT_FALSE(queue.Contains(51));
-  queue.Add(0, 100);
+  queue.AddRange(0, 100);
 
   EXPECT_TRUE(queue.RemoveUpTo(51));
   EXPECT_FALSE(queue.RemoveUpTo(51));
@@ -406,7 +505,7 @@
   EXPECT_EQ(99u, queue.Max());
 
   PacketNumberQueue queue2;
-  queue2.Add(0, 5);
+  queue2.AddRange(0, 5);
   EXPECT_TRUE(queue2.RemoveUpTo(3));
   EXPECT_TRUE(queue2.RemoveUpTo(50));
   EXPECT_TRUE(queue2.Empty());
@@ -418,7 +517,7 @@
   EXPECT_TRUE(queue.Empty());
   EXPECT_EQ(0u, queue.NumPacketsSlow());
 
-  queue.Add(1, 100);
+  queue.AddRange(1, 100);
   EXPECT_TRUE(queue.RemoveUpTo(100));
   EXPECT_TRUE(queue.Empty());
   EXPECT_EQ(0u, queue.NumPacketsSlow());
@@ -431,21 +530,21 @@
   oss << queue;
 
   queue.Add(1);
-  queue.Add(50, 100);
+  queue.AddRange(50, 100);
   oss << queue;
 }
 
 // Tests that the iterators returned from a packet queue iterate over the queue.
 TEST_F(PacketNumberQueueTest, Iterators) {
   PacketNumberQueue queue;
-  queue.Add(1, 100);
+  queue.AddRange(1, 100);
 
   const std::vector<Interval<QuicPacketNumber>> actual_intervals(queue.begin(),
                                                                  queue.end());
 
   PacketNumberQueue queue2;
   for (int i = 1; i < 100; i++) {
-    queue2.Add(i, i + 1);
+    queue2.AddRange(i, i + 1);
   }
 
   const std::vector<Interval<QuicPacketNumber>> actual_intervals2(
@@ -460,10 +559,10 @@
 
 TEST_F(PacketNumberQueueTest, ReversedIterators) {
   PacketNumberQueue queue;
-  queue.Add(1, 100);
+  queue.AddRange(1, 100);
   PacketNumberQueue queue2;
   for (int i = 1; i < 100; i++) {
-    queue2.Add(i, i + 1);
+    queue2.AddRange(i, i + 1);
   }
   const std::vector<Interval<QuicPacketNumber>> actual_intervals(queue.rbegin(),
                                                                  queue.rend());
@@ -495,9 +594,9 @@
 
 TEST_F(PacketNumberQueueTest, IntervalLengthAndRemoveInterval) {
   PacketNumberQueue queue;
-  queue.Add(1, 10);
-  queue.Add(20, 30);
-  queue.Add(40, 50);
+  queue.AddRange(1, 10);
+  queue.AddRange(20, 30);
+  queue.AddRange(40, 50);
   EXPECT_EQ(3u, queue.NumIntervals());
   EXPECT_EQ(10u, queue.LastIntervalLength());
 
diff --git a/net/quic/core/quic_client_promised_info.cc b/net/quic/core/quic_client_promised_info.cc
index ebc6d18..a8241ec 100644
--- a/net/quic/core/quic_client_promised_info.cc
+++ b/net/quic/core/quic_client_promised_info.cc
@@ -11,9 +11,10 @@
 
 namespace net {
 
-QuicClientPromisedInfo::QuicClientPromisedInfo(QuicClientSessionBase* session,
-                                               QuicStreamId id,
-                                               string url)
+QuicClientPromisedInfo::QuicClientPromisedInfo(
+    QuicSpdyClientSessionBase* session,
+    QuicStreamId id,
+    string url)
     : session_(session),
       id_(id),
       url_(std::move(url)),
diff --git a/net/quic/core/quic_client_promised_info.h b/net/quic/core/quic_client_promised_info.h
index 1f755fe..1946956e 100644
--- a/net/quic/core/quic_client_promised_info.h
+++ b/net/quic/core/quic_client_promised_info.h
@@ -10,16 +10,14 @@
 
 #include "net/quic/core/quic_alarm.h"
 #include "net/quic/core/quic_client_push_promise_index.h"
-#include "net/quic/core/quic_client_session_base.h"
 #include "net/quic/core/quic_packets.h"
+#include "net/quic/core/quic_spdy_client_session_base.h"
 #include "net/quic/core/quic_spdy_stream.h"
 #include "net/quic/platform/api/quic_export.h"
 #include "net/spdy/core/spdy_framer.h"
 
 namespace net {
 
-class QuicClientSessionBase;
-
 namespace test {
 class QuicClientPromisedInfoPeer;
 }  // namespace test
@@ -32,7 +30,7 @@
     : public QuicClientPushPromiseIndex::TryHandle {
  public:
   // Interface to QuicSpdyClientStream
-  QuicClientPromisedInfo(QuicClientSessionBase* session,
+  QuicClientPromisedInfo(QuicSpdyClientSessionBase* session,
                          QuicStreamId id,
                          std::string url);
   virtual ~QuicClientPromisedInfo();
@@ -60,7 +58,7 @@
   // uing the |promised_by_url| map.  The push can be cross-origin, so
   // the client should validate that the session is authoritative for
   // the promised URL.  If not, it should call |RejectUnauthorized|.
-  QuicClientSessionBase* session() { return session_; }
+  QuicSpdyClientSessionBase* session() { return session_; }
 
   // If the promised response contains Vary header, then the fields
   // specified by Vary must match between the client request header
@@ -94,7 +92,7 @@
 
   QuicAsyncStatus FinalValidation();
 
-  QuicClientSessionBase* session_;
+  QuicSpdyClientSessionBase* session_;
   QuicStreamId id_;
   std::string url_;
   std::unique_ptr<SpdyHeaderBlock> request_headers_;
diff --git a/net/quic/core/quic_client_promised_info_test.cc b/net/quic/core/quic_client_promised_info_test.cc
index 954f21b..70d0ac6 100644
--- a/net/quic/core/quic_client_promised_info_test.cc
+++ b/net/quic/core/quic_client_promised_info_test.cc
@@ -15,7 +15,7 @@
 #include "net/quic/test_tools/quic_client_promised_info_peer.h"
 #include "net/quic/test_tools/quic_spdy_session_peer.h"
 #include "net/test/gtest_util.h"
-#include "net/tools/quic/quic_client_session.h"
+#include "net/tools/quic/quic_spdy_client_session.h"
 
 using std::string;
 using testing::StrictMock;
@@ -24,11 +24,12 @@
 namespace test {
 namespace {
 
-class MockQuicClientSession : public QuicClientSession {
+class MockQuicSpdyClientSession : public QuicSpdyClientSession {
  public:
-  explicit MockQuicClientSession(QuicConnection* connection,
-                                 QuicClientPushPromiseIndex* push_promise_index)
-      : QuicClientSession(
+  explicit MockQuicSpdyClientSession(
+      QuicConnection* connection,
+      QuicClientPushPromiseIndex* push_promise_index)
+      : QuicSpdyClientSession(
             DefaultQuicConfig(),
             connection,
             QuicServerId("example.com", 443, PRIVACY_MODE_DISABLED),
@@ -36,7 +37,7 @@
             push_promise_index),
         crypto_config_(crypto_test_utils::ProofVerifierForTesting()),
         authorized_(true) {}
-  ~MockQuicClientSession() override {}
+  ~MockQuicSpdyClientSession() override {}
 
   bool IsAuthorized(const string& authority) override { return authorized_; }
 
@@ -49,7 +50,7 @@
 
   bool authorized_;
 
-  DISALLOW_COPY_AND_ASSIGN(MockQuicClientSession);
+  DISALLOW_COPY_AND_ASSIGN(MockQuicSpdyClientSession);
 };
 
 class QuicClientPromisedInfoTest : public QuicTest {
@@ -104,7 +105,7 @@
   StrictMock<MockQuicConnection>* connection_;
   QuicClientPushPromiseIndex push_promise_index_;
 
-  MockQuicClientSession session_;
+  MockQuicSpdyClientSession session_;
   std::unique_ptr<QuicSpdyClientStream> stream_;
   std::unique_ptr<StreamVisitor> stream_visitor_;
   std::unique_ptr<QuicSpdyClientStream> promised_stream_;
diff --git a/net/quic/core/quic_client_push_promise_index.h b/net/quic/core/quic_client_push_promise_index.h
index 38dc1fc8..2609ae1 100644
--- a/net/quic/core/quic_client_push_promise_index.h
+++ b/net/quic/core/quic_client_push_promise_index.h
@@ -7,7 +7,7 @@
 
 #include <string>
 
-#include "net/quic/core/quic_client_session_base.h"
+#include "net/quic/core/quic_spdy_client_session_base.h"
 #include "net/quic/core/quic_types.h"
 #include "net/quic/platform/api/quic_export.h"
 
diff --git a/net/quic/core/quic_client_push_promise_index_test.cc b/net/quic/core/quic_client_push_promise_index_test.cc
index 0a93cf1..9f980f14 100644
--- a/net/quic/core/quic_client_push_promise_index_test.cc
+++ b/net/quic/core/quic_client_push_promise_index_test.cc
@@ -12,36 +12,37 @@
 #include "net/quic/test_tools/mock_quic_client_promised_info.h"
 #include "net/quic/test_tools/quic_spdy_session_peer.h"
 #include "net/quic/test_tools/quic_test_utils.h"
-#include "net/tools/quic/quic_client_session.h"
+#include "net/tools/quic/quic_spdy_client_session.h"
 
-using testing::_;
 using testing::Return;
 using testing::StrictMock;
+using testing::_;
 using std::string;
 
 namespace net {
 namespace test {
 namespace {
 
-class MockQuicClientSession : public QuicClientSession {
+class MockQuicSpdyClientSession : public QuicSpdyClientSession {
  public:
-  explicit MockQuicClientSession(QuicConnection* connection,
-                                 QuicClientPushPromiseIndex* push_promise_index)
-      : QuicClientSession(
+  explicit MockQuicSpdyClientSession(
+      QuicConnection* connection,
+      QuicClientPushPromiseIndex* push_promise_index)
+      : QuicSpdyClientSession(
             DefaultQuicConfig(),
             connection,
             QuicServerId("example.com", 443, PRIVACY_MODE_DISABLED),
             &crypto_config_,
             push_promise_index),
         crypto_config_(crypto_test_utils::ProofVerifierForTesting()) {}
-  ~MockQuicClientSession() override {}
+  ~MockQuicSpdyClientSession() override {}
 
   MOCK_METHOD1(CloseStream, void(QuicStreamId stream_id));
 
  private:
   QuicCryptoClientConfig crypto_config_;
 
-  DISALLOW_COPY_AND_ASSIGN(MockQuicClientSession);
+  DISALLOW_COPY_AND_ASSIGN(MockQuicSpdyClientSession);
 };
 
 class QuicClientPushPromiseIndexTest : public QuicTest {
@@ -66,7 +67,7 @@
   MockQuicConnectionHelper helper_;
   MockAlarmFactory alarm_factory_;
   StrictMock<MockQuicConnection>* connection_;
-  MockQuicClientSession session_;
+  MockQuicSpdyClientSession session_;
   QuicClientPushPromiseIndex index_;
   SpdyHeaderBlock request_;
   string url_;
diff --git a/net/quic/core/quic_connection.cc b/net/quic/core/quic_connection.cc
index 019d15a..e1f2cbbd 100644
--- a/net/quic/core/quic_connection.cc
+++ b/net/quic/core/quic_connection.cc
@@ -216,6 +216,7 @@
       stop_waiting_count_(0),
       ack_mode_(TCP_ACKING),
       ack_decimation_delay_(kAckDecimationDelay),
+      unlimited_ack_decimation_(false),
       delay_setting_retransmission_alarm_(false),
       pending_retransmission_alarm_(false),
       defer_send_in_response_to_packets_(false),
@@ -270,7 +271,7 @@
       largest_received_packet_size_(0),
       goaway_sent_(false),
       goaway_received_(false),
-      write_error_occured_(false),
+      write_error_occurred_(false),
       no_stop_waiting_frames_(false),
       consecutive_num_packets_with_no_retransmittable_frames_(0) {
   QUIC_DLOG(INFO) << ENDPOINT
@@ -354,6 +355,12 @@
     ack_mode_ = ACK_DECIMATION_WITH_REORDERING;
     ack_decimation_delay_ = kShortAckDecimationDelay;
   }
+  if (FLAGS_quic_reloadable_flag_quic_ack_decimation) {
+    QUIC_FLAG_COUNT(quic_reloadable_flag_quic_ack_decimation);
+    if (config.HasClientSentConnectionOption(kAKDU, perspective_)) {
+      unlimited_ack_decimation_ = true;
+    }
+  }
   if (config.HasClientSentConnectionOption(k5RTO, perspective_)) {
     close_connection_after_five_rtos_ = true;
   }
@@ -974,9 +981,10 @@
     ++num_retransmittable_packets_received_since_last_ack_sent_;
     if (ack_mode_ != TCP_ACKING &&
         last_header_.packet_number > kMinReceivedBeforeAckDecimation) {
-      // Ack up to 10 packets at once.
-      if (num_retransmittable_packets_received_since_last_ack_sent_ >=
-          kMaxRetransmittablePacketsBeforeAck) {
+      // Ack up to 10 packets at once unless ack decimation is unlimited.
+      if (!unlimited_ack_decimation_ &&
+          num_retransmittable_packets_received_since_last_ack_sent_ >=
+              kMaxRetransmittablePacketsBeforeAck) {
         ack_queued_ = true;
       } else if (!ack_alarm_->IsSet()) {
         // Wait the minimum of a quarter min_rtt and the delayed ack time.
@@ -1639,11 +1647,11 @@
 }
 
 void QuicConnection::OnWriteError(int error_code) {
-  if (write_error_occured_) {
+  if (write_error_occurred_) {
     // A write error already occurred. The connection is being closed.
     return;
   }
-  write_error_occured_ = true;
+  write_error_occurred_ = true;
 
   const string error_details = QuicStrCat(
       "Write failed with error: ", error_code, " (", strerror(error_code), ")");
diff --git a/net/quic/core/quic_connection.h b/net/quic/core/quic_connection.h
index 30c58fa..80c76bb 100644
--- a/net/quic/core/quic_connection.h
+++ b/net/quic/core/quic_connection.h
@@ -960,6 +960,9 @@
   AckMode ack_mode_;
   // The max delay in fraction of min_rtt to use when sending decimated acks.
   float ack_decimation_delay_;
+  // When true, removes ack decimation's max number of packets(10) before
+  // sending an ack.
+  bool unlimited_ack_decimation_;
 
   // Indicates the retransmit alarm is going to be set by the
   // ScopedRetransmitAlarmDelayer
@@ -1095,7 +1098,7 @@
 
   // Indicates whether a write error is encountered currently. This is used to
   // avoid infinite write errors.
-  bool write_error_occured_;
+  bool write_error_occurred_;
 
   // Indicates not to send or process stop waiting frames.
   bool no_stop_waiting_frames_;
diff --git a/net/quic/core/quic_connection_test.cc b/net/quic/core/quic_connection_test.cc
index 7fa3a95f..0ee071c 100644
--- a/net/quic/core/quic_connection_test.cc
+++ b/net/quic/core/quic_connection_test.cc
@@ -999,7 +999,7 @@
   const QuicAckFrame InitAckFrame(QuicPacketNumber largest_observed) {
     QuicAckFrame frame(MakeAckFrame(largest_observed));
     if (largest_observed > 0) {
-      frame.packets.Add(1, largest_observed + 1);
+      frame.packets.AddRange(1, largest_observed + 1);
     }
     return frame;
   }
@@ -1017,12 +1017,12 @@
                                  QuicPacketNumber missing) {
     QuicAckFrame ack_frame;
     if (largest_acked > missing) {
-      ack_frame.packets.Add(1, missing);
-      ack_frame.packets.Add(missing + 1, largest_acked + 1);
+      ack_frame.packets.AddRange(1, missing);
+      ack_frame.packets.AddRange(missing + 1, largest_acked + 1);
       ack_frame.largest_observed = largest_acked;
     }
     if (largest_acked == missing) {
-      ack_frame.packets.Add(1, missing);
+      ack_frame.packets.AddRange(1, missing);
       ack_frame.largest_observed = largest_acked;
     }
     return ack_frame;
@@ -3151,12 +3151,12 @@
                                                  mtu_discovery_packets.end());
       QuicPacketNumber max_packet = *max_element(mtu_discovery_packets.begin(),
                                                  mtu_discovery_packets.end());
-      ack.packets.Add(1, min_packet);
-      ack.packets.Add(max_packet + 1, creator_->packet_number() + 1);
+      ack.packets.AddRange(1, min_packet);
+      ack.packets.AddRange(max_packet + 1, creator_->packet_number() + 1);
       ack.largest_observed = creator_->packet_number();
 
     } else {
-      ack.packets.Add(1, creator_->packet_number() + 1);
+      ack.packets.AddRange(1, creator_->packet_number() + 1);
       ack.largest_observed = creator_->packet_number();
     }
 
@@ -3846,6 +3846,64 @@
   EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
 }
 
+TEST_P(QuicConnectionTest, SendDelayedAckDecimationUnlimitedAggregation) {
+  FLAGS_quic_reloadable_flag_quic_ack_decimation = true;
+  EXPECT_CALL(visitor_, OnAckNeedsRetransmittableFrame()).Times(AnyNumber());
+  EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+  QuicConfig config;
+  QuicTagVector connection_options;
+  connection_options.push_back(kACKD);
+  // No limit on the number of packets received before sending an ack.
+  connection_options.push_back(kAKDU);
+  config.SetConnectionOptionsToSend(connection_options);
+  connection_.SetFromConfig(config);
+
+  const size_t kMinRttMs = 40;
+  RttStats* rtt_stats = const_cast<RttStats*>(manager_->GetRttStats());
+  rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(kMinRttMs),
+                       QuicTime::Delta::Zero(), QuicTime::Zero());
+  // The ack time should be based on min_rtt/4, since it's less than the
+  // default delayed ack time.
+  QuicTime ack_time = clock_.ApproximateNow() +
+                      QuicTime::Delta::FromMilliseconds(kMinRttMs / 4);
+  EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+  EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+  const uint8_t tag = 0x07;
+  connection_.SetDecrypter(ENCRYPTION_INITIAL, new StrictTaggingDecrypter(tag));
+  peer_framer_.SetEncrypter(ENCRYPTION_INITIAL, new TaggingEncrypter(tag));
+  // Process a packet from the non-crypto stream.
+  frame1_.stream_id = 3;
+
+  // Process all the initial packets in order so there aren't missing packets.
+  QuicPacketNumber kFirstDecimatedPacket = 101;
+  for (unsigned int i = 0; i < kFirstDecimatedPacket - 1; ++i) {
+    EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+    ProcessDataPacketAtLevel(1 + i, !kHasStopWaiting, ENCRYPTION_INITIAL);
+  }
+  EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+  // The same as ProcessPacket(1) except that ENCRYPTION_INITIAL is used
+  // instead of ENCRYPTION_NONE.
+  EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+  ProcessDataPacketAtLevel(kFirstDecimatedPacket, !kHasStopWaiting,
+                           ENCRYPTION_INITIAL);
+
+  // Check if delayed ack timer is running for the expected interval.
+  EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+  EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline());
+
+  // 18 packets will not cause an ack to be sent.  19 will because when
+  // stop waiting frames are in use, we ack every 20 packets no matter what.
+  for (int i = 0; i < 18; ++i) {
+    EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+    EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+    ProcessDataPacketAtLevel(kFirstDecimatedPacket + 1 + i, !kHasStopWaiting,
+                             ENCRYPTION_INITIAL);
+  }
+  // The delayed ack timer should still be set to the expected deadline.
+  EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+  EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline());
+}
+
 TEST_P(QuicConnectionTest, SendDelayedAckDecimationEighthRtt) {
   EXPECT_CALL(visitor_, OnAckNeedsRetransmittableFrame()).Times(AnyNumber());
   QuicConnectionPeer::SetAckMode(&connection_, QuicConnection::ACK_DECIMATION);
diff --git a/net/quic/core/quic_flags_list.h b/net/quic/core/quic_flags_list.h
index ee70910..958f5041 100644
--- a/net/quic/core/quic_flags_list.h
+++ b/net/quic/core/quic_flags_list.h
@@ -117,12 +117,6 @@
 // If enabled, use refactored stream creation methods.
 QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_refactor_stream_creation, false)
 
-// If true, GFEs generate and validate source address token using the actual
-// client IP for proxied session.
-QUIC_FLAG(bool,
-          FLAGS_quic_reloadable_flag_quic_use_client_address_for_stk_in_proxy,
-          true)
-
 // If true, export a varz mapping QUIC non 0-rtt handshake with corresponding
 // frontend service.
 QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_account_handshake, false)
@@ -137,9 +131,6 @@
 // If true, enable QUIC v40.
 QUIC_FLAG(bool, FLAGS_quic_enable_version_40, false)
 
-// If true, use the more CPU efficient bandwidth sampler datastructure.
-QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_faster_bandwidth_sampler, true)
-
 // In QUIC, QuicSession gets notified when stream frames are acked, discarded or
 // retransmitted.
 QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_use_stream_notifier2, false)
@@ -206,3 +197,7 @@
 QUIC_FLAG(bool,
           FLAGS_quic_reloadable_flag_quic_bbr_ack_aggregation_bytes4,
           false)
+
+// Add 4 new ack decimation modes to QUIC that are entirely time based at 1/4
+// or 1/8 RTT.
+QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_ack_decimation, false)
diff --git a/net/quic/core/quic_framer.cc b/net/quic/core/quic_framer.cc
index 09dd259..56b3bd0 100644
--- a/net/quic/core/quic_framer.cc
+++ b/net/quic/core/quic_framer.cc
@@ -73,31 +73,37 @@
 // Stream frame relative shifts and masks for interpreting the stream flags.
 // StreamID may be 1, 2, 3, or 4 bytes.
 const uint8_t kQuicStreamIdShift_Pre40 = 2;
-const uint8_t kQuicStreamIDLengthMask = 0x03;
+const uint8_t kQuicStreamIDLengthMask_Pre40 = 0x03;
 const uint8_t kQuicStreamIDLengthOffsetShift = 3;
+const uint8_t kQuicStreamIDLengthNumBits = 2;
 
 // Offset may be 0, 2, 4, or 8 bytes.
 const uint8_t kQuicStreamOffsetShift_Pre40 = 3;
 const uint8_t kQuicStreamOffsetMask_Pre40 = 0x07;
-const uint8_t kQuicStreamOffsetMask = 0x03;
+const uint8_t kQuicStreamOffsetNumBits = 2;
 const uint8_t kQuicStreamOffsetOffsetShift = 1;
 
 // Data length may be 0 or 2 bytes.
 const uint8_t kQuicStreamDataLengthShift_Pre40 = 1;
-const uint8_t kQuicStreamDataLengthMask = 0x01;
+const uint8_t kQuicStreamDataLengthMask_Pre40 = 0x01;
+const uint8_t kQuicStreamDataLengthNumBits = 1;
 const uint8_t kQuicStreamDataLengthOffsetShift = 0;
 
 // Fin bit may be set or not.
 const uint8_t kQuicStreamFinShift_Pre40 = 1;
-const uint8_t kQuicStreamFinMask = 0x01;
+const uint8_t kQuicStreamFinMask_Pre40 = 0x01;
+const uint8_t kQuicStreamFinNumBits = 0x01;
 const uint8_t kQuicStreamFinOffsetShift = 5;
 
 // packet number size shift used in AckFrames.
-const uint8_t kQuicSequenceNumberLengthShift = 2;
+const uint8_t kQuicSequenceNumberLengthNumBits = 2;
+const uint8_t kActBlockLengthOffset = 0;
+const uint8_t kLargestAckedOffset = 2;
 
 // Acks may have only one ack block.
-const uint8_t kQuicHasMultipleAckBlocksMask = 0x01;
-const uint8_t kQuicHasMultipleAckBlocksShift_Pre40 = 1;
+const uint8_t kQuicHasMultipleAckBlocksOffset_Pre40 = 5;
+const uint8_t kQuicHasMultipleAckBlocksOffset = 4;
+const uint8_t kBooleanNumBits = 1;
 
 // Returns the absolute value of the difference between |a| and |b|.
 QuicPacketNumber Delta(QuicPacketNumber a, QuicPacketNumber b) {
@@ -1150,10 +1156,24 @@
   return true;
 }
 
-uint8_t ExtractBits(uint8_t flags, uint8_t num_bits, uint8_t offset) {
-  return ((flags >> offset) & ~(0xFF << num_bits));
+namespace {
+// Create a mask that sets the last |num_bits| to 1 and the rest to 0.
+inline uint8_t GetMaskFromNumBits(uint8_t num_bits) {
+  return (1u << num_bits) - 1;
 }
 
+// Extract |num_bits| from |flags| offset by |offset|.
+uint8_t ExtractBits(uint8_t flags, uint8_t num_bits, uint8_t offset) {
+  return (flags >> offset) & GetMaskFromNumBits(num_bits);
+}
+
+// Set |num_bits|, offset by |offset| to |val| in |flags|.
+void SetBits(uint8_t* flags, uint8_t val, uint8_t num_bits, uint8_t offset) {
+  DCHECK_LE(val, GetMaskFromNumBits(num_bits));
+  *flags |= val << offset;
+}
+}  // namespace
+
 bool QuicFramer::ProcessStreamFrame(QuicDataReader* reader,
                                     uint8_t frame_type,
                                     QuicStreamFrame* frame) {
@@ -1166,7 +1186,7 @@
     stream_flags &= ~kQuicFrameTypeStreamMask_Pre40;
 
     // Read from right to left: StreamID, Offset, Data Length, Fin.
-    stream_id_length = (stream_flags & kQuicStreamIDLengthMask) + 1;
+    stream_id_length = (stream_flags & kQuicStreamIDLengthMask_Pre40) + 1;
     stream_flags >>= kQuicStreamIdShift_Pre40;
 
     offset_length = (stream_flags & kQuicStreamOffsetMask_Pre40);
@@ -1176,31 +1196,31 @@
     }
     stream_flags >>= kQuicStreamOffsetShift_Pre40;
 
-    has_data_length =
-        (stream_flags & kQuicStreamDataLengthMask) == kQuicStreamDataLengthMask;
+    has_data_length = (stream_flags & kQuicStreamDataLengthMask_Pre40) ==
+                      kQuicStreamDataLengthMask_Pre40;
     stream_flags >>= kQuicStreamDataLengthShift_Pre40;
 
     frame->fin =
-        (stream_flags & kQuicStreamFinMask) == kQuicStreamFinShift_Pre40;
+        (stream_flags & kQuicStreamFinMask_Pre40) == kQuicStreamFinShift_Pre40;
 
   } else {
     stream_flags &= ~kQuicFrameTypeStreamMask;
 
-    stream_id_length = 1 + ((stream_flags >> kQuicStreamIDLengthOffsetShift) &
-                            kQuicStreamIDLengthMask);
+    stream_id_length = 1 + ExtractBits(stream_flags, kQuicStreamIDLengthNumBits,
+                                       kQuicStreamIDLengthOffsetShift);
 
-    offset_length = 1 << ((stream_flags >> kQuicStreamOffsetOffsetShift) &
-                          kQuicStreamOffsetMask);
+    offset_length = 1 << ExtractBits(stream_flags, kQuicStreamOffsetNumBits,
+                                     kQuicStreamOffsetOffsetShift);
 
     if (offset_length == 1) {
       offset_length = 0;
     }
 
-    has_data_length = stream_flags & (kQuicStreamDataLengthMask
-                                      << kQuicStreamDataLengthOffsetShift);
+    has_data_length = ExtractBits(stream_flags, kQuicStreamDataLengthNumBits,
+                                  kQuicStreamDataLengthOffsetShift);
 
-    frame->fin = ((stream_flags >> kQuicStreamFinOffsetShift) &
-                  kQuicStreamFinMask) == kQuicStreamFinMask;
+    frame->fin = ExtractBits(stream_flags, kQuicStreamFinNumBits,
+                             kQuicStreamFinOffsetShift);
   }
 
   uint16_t data_len = 0;
@@ -1254,17 +1274,16 @@
                                  QuicAckFrame* ack_frame) {
   // Determine the two lengths from the frame type: largest acked length,
   // ack block length.
-  uint8_t offset = 0u;
   const QuicPacketNumberLength ack_block_length =
-      ReadSequenceNumberLength(ExtractBits(frame_type, 2, offset));
-  offset += kQuicSequenceNumberLengthShift;
+      ReadSequenceNumberLength(ExtractBits(
+          frame_type, kQuicSequenceNumberLengthNumBits, kActBlockLengthOffset));
   const QuicPacketNumberLength largest_acked_length =
-      ReadSequenceNumberLength(ExtractBits(frame_type, 2, offset));
-  offset += kQuicSequenceNumberLengthShift;
-  if (quic_version_ < QUIC_VERSION_40) {
-    offset += kQuicHasMultipleAckBlocksShift_Pre40;
-  }
-  bool has_ack_blocks = ExtractBits(frame_type, 1, offset) != 0;
+      ReadSequenceNumberLength(ExtractBits(
+          frame_type, kQuicSequenceNumberLengthNumBits, kLargestAckedOffset));
+  bool has_ack_blocks = ExtractBits(frame_type, kBooleanNumBits,
+                                    quic_version_ < QUIC_VERSION_40
+                                        ? kQuicHasMultipleAckBlocksOffset_Pre40
+                                        : kQuicHasMultipleAckBlocksOffset);
 
   if (!reader->ReadBytesToUInt64(largest_acked_length,
                                  &ack_frame->largest_observed)) {
@@ -1300,7 +1319,7 @@
   }
   QuicPacketNumber first_received =
       ack_frame->largest_observed + 1 - first_block_length;
-  ack_frame->packets.Add(first_received, ack_frame->largest_observed + 1);
+  ack_frame->packets.AddRange(first_received, ack_frame->largest_observed + 1);
 
   if (num_ack_blocks > 0) {
     for (size_t i = 0; i < num_ack_blocks; ++i) {
@@ -1316,8 +1335,8 @@
       }
       first_received -= (gap + current_block_length);
       if (current_block_length > 0) {
-        ack_frame->packets.Add(first_received,
-                               first_received + current_block_length);
+        ack_frame->packets.AddRange(first_received,
+                                    first_received + current_block_length);
       }
     }
   }
@@ -1801,11 +1820,12 @@
       }
       if (quic_version_ < QUIC_VERSION_40) {
         // Fin bit.
-        type_byte |= frame.stream_frame->fin ? kQuicStreamFinMask : 0;
+        type_byte |= frame.stream_frame->fin ? kQuicStreamFinMask_Pre40 : 0;
 
         // Data Length bit.
         type_byte <<= kQuicStreamDataLengthShift_Pre40;
-        type_byte |= no_stream_frame_length ? 0 : kQuicStreamDataLengthMask;
+        type_byte |=
+            no_stream_frame_length ? 0 : kQuicStreamDataLengthMask_Pre40;
 
         // Offset 3 bits.
         type_byte <<= kQuicStreamOffsetShift_Pre40;
@@ -1822,12 +1842,12 @@
             kQuicFrameTypeStreamMask_Pre40;  // Set Stream Frame Type to 1.
       } else {
         // Fin bit.
-        type_byte |= ((frame.stream_frame->fin ? kQuicStreamFinMask : 0)
-                      << kQuicStreamFinOffsetShift);
+        SetBits(&type_byte, frame.stream_frame->fin ? 1 : 0,
+                kQuicStreamFinNumBits, kQuicStreamFinOffsetShift);
 
         // Data Length bit.
-        type_byte |= ((no_stream_frame_length ? 0 : kQuicStreamDataLengthMask)
-                      << kQuicStreamDataLengthOffsetShift);
+        SetBits(&type_byte, no_stream_frame_length ? 0 : 1,
+                kQuicStreamDataLengthNumBits, kQuicStreamDataLengthOffsetShift);
 
         // Offset 2 bits.
         uint8_t offset_len_encode = 3;
@@ -1848,11 +1868,12 @@
           default:
             QUIC_BUG << "Invalid offset_length.";
         }
-        type_byte |= (offset_len_encode) << kQuicStreamOffsetOffsetShift;
+        SetBits(&type_byte, offset_len_encode, kQuicStreamOffsetNumBits,
+                kQuicStreamOffsetOffsetShift);
 
         // stream id 2 bits.
-        type_byte |= ((GetStreamIdSize(frame.stream_frame->stream_id) - 1)
-                      << kQuicStreamIDLengthOffsetShift);
+        SetBits(&type_byte, GetStreamIdSize(frame.stream_frame->stream_id) - 1,
+                kQuicStreamIDLengthNumBits, kQuicStreamIDLengthOffsetShift);
         type_byte |= kQuicFrameTypeStreamMask;  // Set Stream Frame Type to 1.
       }
       break;
@@ -1985,18 +2006,17 @@
   // Write out the type byte by setting the low order bits and doing shifts
   // to make room for the next bit flags to be set.
   // Whether there are multiple ack blocks.
-  uint8_t type_byte =
-      new_ack_info.num_ack_blocks == 0 ? 0 : kQuicHasMultipleAckBlocksMask;
-  if (quic_version_ < QUIC_VERSION_40) {
-    type_byte <<= kQuicHasMultipleAckBlocksShift_Pre40;
-  }
-  // Largest acked length.
-  type_byte <<= kQuicSequenceNumberLengthShift;
-  type_byte |= GetPacketNumberFlags(largest_acked_length);
+  uint8_t type_byte = 0;
+  SetBits(&type_byte, new_ack_info.num_ack_blocks == 0 ? 0 : 1, kBooleanNumBits,
+          quic_version_ < QUIC_VERSION_40
+              ? kQuicHasMultipleAckBlocksOffset_Pre40
+              : kQuicHasMultipleAckBlocksOffset);
 
-  // Ack block length.
-  type_byte <<= kQuicSequenceNumberLengthShift;
-  type_byte |= GetPacketNumberFlags(ack_block_length);
+  SetBits(&type_byte, GetPacketNumberFlags(largest_acked_length),
+          kQuicSequenceNumberLengthNumBits, kLargestAckedOffset);
+
+  SetBits(&type_byte, GetPacketNumberFlags(ack_block_length),
+          kQuicSequenceNumberLengthNumBits, kActBlockLengthOffset);
 
   if (quic_version_ < QUIC_VERSION_40) {
     type_byte |= kQuicFrameTypeAckMask_Pre40;
diff --git a/net/quic/core/quic_framer_test.cc b/net/quic/core/quic_framer_test.cc
index 8486999..976d1917 100644
--- a/net/quic/core/quic_framer_test.cc
+++ b/net/quic/core/quic_framer_test.cc
@@ -3741,7 +3741,7 @@
   QuicAckFrame ack_frame;
   ack_frame.largest_observed = kSmallLargestObserved;
   ack_frame.ack_delay_time = QuicTime::Delta::Zero();
-  ack_frame.packets.Add(1, kSmallLargestObserved + 1);
+  ack_frame.packets.AddRange(1, kSmallLargestObserved + 1);
 
   QuicFrames frames = {QuicFrame(&ack_frame)};
 
@@ -3834,10 +3834,11 @@
   QuicAckFrame ack_frame;
   ack_frame.largest_observed = kSmallLargestObserved;
   ack_frame.ack_delay_time = QuicTime::Delta::Zero();
-  ack_frame.packets.Add(1, 5);
-  ack_frame.packets.Add(10, 500);
-  ack_frame.packets.Add(900, kSmallMissingPacket);
-  ack_frame.packets.Add(kSmallMissingPacket + 1, kSmallLargestObserved + 1);
+  ack_frame.packets.AddRange(1, 5);
+  ack_frame.packets.AddRange(10, 500);
+  ack_frame.packets.AddRange(900, kSmallMissingPacket);
+  ack_frame.packets.AddRange(kSmallMissingPacket + 1,
+                             kSmallLargestObserved + 1);
 
   QuicFrames frames = {QuicFrame(&ack_frame)};
 
@@ -3989,7 +3990,7 @@
   for (size_t i = 2; i < 2 * 300; i += 2) {
     ack_frame.packets.Add(i);
   }
-  ack_frame.packets.Add(600, kSmallLargestObserved + 1);
+  ack_frame.packets.AddRange(600, kSmallLargestObserved + 1);
 
   QuicFrames frames = {QuicFrame(&ack_frame)};
 
@@ -5074,7 +5075,7 @@
 
   QuicAckFrame ack_frame;
   ack_frame.largest_observed = 201;
-  ack_frame.packets.Add(1, ack_frame.largest_observed);
+  ack_frame.packets.AddRange(1, ack_frame.largest_observed);
 
   // Create a packet with just the ack.
   QuicFrames frames = {QuicFrame(&ack_frame)};
diff --git a/net/quic/core/quic_sent_packet_manager.cc b/net/quic/core/quic_sent_packet_manager.cc
index 139c023..3aece47 100644
--- a/net/quic/core/quic_sent_packet_manager.cc
+++ b/net/quic/core/quic_sent_packet_manager.cc
@@ -309,6 +309,7 @@
       break;
     }
     if (skip_unackable_packets_early && it->is_unackable) {
+      QUIC_FLAG_COUNT(quic_reloadable_flag_quic_handle_acks);
       continue;
     }
     if (!ack_frame.packets.Contains(packet_number)) {
diff --git a/net/quic/core/quic_sent_packet_manager_test.cc b/net/quic/core/quic_sent_packet_manager_test.cc
index 031b7e8..4de9065 100644
--- a/net/quic/core/quic_sent_packet_manager_test.cc
+++ b/net/quic/core/quic_sent_packet_manager_test.cc
@@ -257,7 +257,7 @@
   const QuicAckFrame InitAckFrame(QuicPacketNumber largest_observed) {
     QuicAckFrame frame(MakeAckFrame(largest_observed));
     if (largest_observed > 0) {
-      frame.packets.Add(1, largest_observed + 1);
+      frame.packets.AddRange(1, largest_observed + 1);
     }
     return frame;
   }
@@ -269,10 +269,10 @@
                                  QuicPacketNumber range2_end) {
     QuicAckFrame ack_frame;
     if (range1_start < range1_end) {
-      ack_frame.packets.Add(range1_start, range1_end);
+      ack_frame.packets.AddRange(range1_start, range1_end);
     }
     if (range2_start <= range2_end) {
-      ack_frame.packets.Add(range2_start, range2_end + 1);
+      ack_frame.packets.AddRange(range2_start, range2_end + 1);
     }
     ack_frame.largest_observed = range2_end;
     return ack_frame;
@@ -766,8 +766,8 @@
   QuicPacketNumber acked[] = {3, 4, 5, 8, 9};
   ExpectAcksAndLosses(true, acked, arraysize(acked), nullptr, 0);
   QuicAckFrame ack_frame;
-  ack_frame.packets.Add(3, 6);
-  ack_frame.packets.Add(8, 10);
+  ack_frame.packets.AddRange(3, 6);
+  ack_frame.packets.AddRange(8, 10);
   ack_frame.largest_observed = 9;
   manager_.OnIncomingAck(ack_frame, clock_.ApproximateNow());
 
diff --git a/net/quic/core/quic_client_session_base.cc b/net/quic/core/quic_spdy_client_session_base.cc
similarity index 79%
rename from net/quic/core/quic_client_session_base.cc
rename to net/quic/core/quic_spdy_client_session_base.cc
index b13496d..a1fd538 100644
--- a/net/quic/core/quic_client_session_base.cc
+++ b/net/quic/core/quic_spdy_client_session_base.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 "net/quic/core/quic_client_session_base.h"
+#include "net/quic/core/quic_spdy_client_session_base.h"
 
 #include "net/quic/core/quic_client_promised_info.h"
 #include "net/quic/core/spdy_utils.h"
@@ -13,7 +13,7 @@
 
 namespace net {
 
-QuicClientSessionBase::QuicClientSessionBase(
+QuicSpdyClientSessionBase::QuicSpdyClientSessionBase(
     QuicConnection* connection,
     QuicClientPushPromiseIndex* push_promise_index,
     const QuicConfig& config)
@@ -21,7 +21,7 @@
       push_promise_index_(push_promise_index),
       largest_promised_stream_id_(kInvalidStreamId) {}
 
-QuicClientSessionBase::~QuicClientSessionBase() {
+QuicSpdyClientSessionBase::~QuicSpdyClientSessionBase() {
   //  all promised streams for this session
   for (auto& it : promised_by_id_) {
     QUIC_DVLOG(1) << "erase stream " << it.first << " url " << it.second->url();
@@ -30,15 +30,16 @@
   delete connection();
 }
 
-void QuicClientSessionBase::OnConfigNegotiated() {
+void QuicSpdyClientSessionBase::OnConfigNegotiated() {
   QuicSpdySession::OnConfigNegotiated();
 }
 
-void QuicClientSessionBase::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) {
+void QuicSpdyClientSessionBase::OnCryptoHandshakeEvent(
+    CryptoHandshakeEvent event) {
   QuicSpdySession::OnCryptoHandshakeEvent(event);
 }
 
-void QuicClientSessionBase::OnInitialHeadersComplete(
+void QuicSpdyClientSessionBase::OnInitialHeadersComplete(
     QuicStreamId stream_id,
     const SpdyHeaderBlock& response_headers) {
   // Note that the strong ordering of the headers stream means that
@@ -53,7 +54,7 @@
   promised->OnResponseHeaders(response_headers);
 }
 
-void QuicClientSessionBase::OnPromiseHeaderList(
+void QuicSpdyClientSessionBase::OnPromiseHeaderList(
     QuicStreamId stream_id,
     QuicStreamId promised_stream_id,
     size_t frame_len,
@@ -77,9 +78,9 @@
   stream->OnPromiseHeaderList(promised_stream_id, frame_len, header_list);
 }
 
-bool QuicClientSessionBase::HandlePromised(QuicStreamId /* associated_id */,
-                                           QuicStreamId promised_id,
-                                           const SpdyHeaderBlock& headers) {
+bool QuicSpdyClientSessionBase::HandlePromised(QuicStreamId /* associated_id */,
+                                               QuicStreamId promised_id,
+                                               const SpdyHeaderBlock& headers) {
   // Due to pathalogical packet re-ordering, it is possible that
   // frames for the promised stream have already arrived, and the
   // promised stream could be active or closed.
@@ -126,7 +127,7 @@
   return true;
 }
 
-QuicClientPromisedInfo* QuicClientSessionBase::GetPromisedByUrl(
+QuicClientPromisedInfo* QuicSpdyClientSessionBase::GetPromisedByUrl(
     const string& url) {
   QuicPromisedByUrlMap::iterator it =
       push_promise_index_->promised_by_url()->find(url);
@@ -136,7 +137,7 @@
   return nullptr;
 }
 
-QuicClientPromisedInfo* QuicClientSessionBase::GetPromisedById(
+QuicClientPromisedInfo* QuicSpdyClientSessionBase::GetPromisedById(
     const QuicStreamId id) {
   QuicPromisedByIdMap::iterator it = promised_by_id_.find(id);
   if (it != promised_by_id_.end()) {
@@ -145,7 +146,7 @@
   return nullptr;
 }
 
-QuicSpdyStream* QuicClientSessionBase::GetPromisedStream(
+QuicSpdyStream* QuicSpdyClientSessionBase::GetPromisedStream(
     const QuicStreamId id) {
   DynamicStreamMap::iterator it = dynamic_streams().find(id);
   if (it != dynamic_streams().end()) {
@@ -154,7 +155,8 @@
   return nullptr;
 }
 
-void QuicClientSessionBase::DeletePromised(QuicClientPromisedInfo* promised) {
+void QuicSpdyClientSessionBase::DeletePromised(
+    QuicClientPromisedInfo* promised) {
   push_promise_index_->promised_by_url()->erase(promised->url());
   // Since promised_by_id_ contains the unique_ptr, this will destroy
   // promised.
@@ -162,23 +164,24 @@
   headers_stream()->MaybeReleaseSequencerBuffer();
 }
 
-void QuicClientSessionBase::OnPushStreamTimedOut(QuicStreamId stream_id) {}
+void QuicSpdyClientSessionBase::OnPushStreamTimedOut(QuicStreamId stream_id) {}
 
-void QuicClientSessionBase::ResetPromised(QuicStreamId id,
-                                          QuicRstStreamErrorCode error_code) {
+void QuicSpdyClientSessionBase::ResetPromised(
+    QuicStreamId id,
+    QuicRstStreamErrorCode error_code) {
   SendRstStream(id, error_code, 0);
   if (!IsOpenStream(id)) {
     MaybeIncreaseLargestPeerStreamId(id);
   }
 }
 
-void QuicClientSessionBase::CloseStreamInner(QuicStreamId stream_id,
-                                             bool locally_reset) {
+void QuicSpdyClientSessionBase::CloseStreamInner(QuicStreamId stream_id,
+                                                 bool locally_reset) {
   QuicSpdySession::CloseStreamInner(stream_id, locally_reset);
   headers_stream()->MaybeReleaseSequencerBuffer();
 }
 
-bool QuicClientSessionBase::ShouldReleaseHeadersStreamSequencerBuffer() {
+bool QuicSpdyClientSessionBase::ShouldReleaseHeadersStreamSequencerBuffer() {
   return num_active_requests() == 0 && promised_by_id_.empty();
 }
 
diff --git a/net/quic/core/quic_client_session_base.h b/net/quic/core/quic_spdy_client_session_base.h
similarity index 90%
rename from net/quic/core/quic_client_session_base.h
rename to net/quic/core/quic_spdy_client_session_base.h
index 8e6c1a6d..696be5e 100644
--- a/net/quic/core/quic_client_session_base.h
+++ b/net/quic/core/quic_spdy_client_session_base.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 NET_QUIC_CORE_QUIC_CLIENT_SESSION_BASE_H_
-#define NET_QUIC_CORE_QUIC_CLIENT_SESSION_BASE_H_
+#ifndef NET_QUIC_CORE_QUIC_SPDY_CLIENT_SESSION_BASE_H_
+#define NET_QUIC_CORE_QUIC_SPDY_CLIENT_SESSION_BASE_H_
 
 #include <string>
 
@@ -33,17 +33,17 @@
 const int64_t kPushPromiseTimeoutSecs = 60;
 
 // Base class for all client-specific QuicSession subclasses.
-class QUIC_EXPORT_PRIVATE QuicClientSessionBase
+class QUIC_EXPORT_PRIVATE QuicSpdyClientSessionBase
     : public QuicSpdySession,
       public QuicCryptoClientStream::ProofHandler {
  public:
   // Takes ownership of |connection|. Caller retains ownership of
   // |promised_by_url|.
-  QuicClientSessionBase(QuicConnection* connection,
-                        QuicClientPushPromiseIndex* push_promise_index,
-                        const QuicConfig& config);
+  QuicSpdyClientSessionBase(QuicConnection* connection,
+                            QuicClientPushPromiseIndex* push_promise_index,
+                            const QuicConfig& config);
 
-  ~QuicClientSessionBase() override;
+  ~QuicSpdyClientSessionBase() override;
 
   void OnConfigNegotiated() override;
 
@@ -131,9 +131,9 @@
   QuicPromisedByIdMap promised_by_id_;
   QuicStreamId largest_promised_stream_id_;
 
-  DISALLOW_COPY_AND_ASSIGN(QuicClientSessionBase);
+  DISALLOW_COPY_AND_ASSIGN(QuicSpdyClientSessionBase);
 };
 
 }  // namespace net
 
-#endif  // NET_QUIC_CORE_QUIC_CLIENT_SESSION_BASE_H_
+#endif  // NET_QUIC_CORE_QUIC_SPDY_CLIENT_SESSION_BASE_H_
diff --git a/net/quic/platform/api/quic_text_utils_test.cc b/net/quic/platform/api/quic_text_utils_test.cc
index 8bbbbad..23e3c46 100644
--- a/net/quic/platform/api/quic_text_utils_test.cc
+++ b/net/quic/platform/api/quic_text_utils_test.cc
@@ -13,9 +13,9 @@
 namespace net {
 namespace test {
 
-class QuicTextUtilsText : public QuicTest {};
+class QuicTextUtilsTest : public QuicTest {};
 
-TEST_F(QuicTextUtilsText, StartsWith) {
+TEST_F(QuicTextUtilsTest, StartsWith) {
   EXPECT_TRUE(QuicTextUtils::StartsWith("hello world", "hello"));
   EXPECT_TRUE(QuicTextUtils::StartsWith("hello world", "hello world"));
   EXPECT_TRUE(QuicTextUtils::StartsWith("hello world", ""));
@@ -24,7 +24,7 @@
   EXPECT_FALSE(QuicTextUtils::StartsWith("hello world", "bar"));
 }
 
-TEST_F(QuicTextUtilsText, EndsWithIgnoreCase) {
+TEST_F(QuicTextUtilsTest, EndsWithIgnoreCase) {
   EXPECT_TRUE(QuicTextUtils::EndsWithIgnoreCase("hello world", "world"));
   EXPECT_TRUE(QuicTextUtils::EndsWithIgnoreCase("hello world", "hello world"));
   EXPECT_TRUE(QuicTextUtils::EndsWithIgnoreCase("hello world", ""));
@@ -32,7 +32,7 @@
   EXPECT_FALSE(QuicTextUtils::EndsWithIgnoreCase("hello world", "hello"));
 }
 
-TEST_F(QuicTextUtilsText, ToLower) {
+TEST_F(QuicTextUtilsTest, ToLower) {
   EXPECT_EQ("lower", QuicTextUtils::ToLower("LOWER"));
   EXPECT_EQ("lower", QuicTextUtils::ToLower("lower"));
   EXPECT_EQ("lower", QuicTextUtils::ToLower("lOwEr"));
@@ -40,7 +40,7 @@
   EXPECT_EQ("", QuicTextUtils::ToLower(""));
 }
 
-TEST_F(QuicTextUtilsText, RemoveLeadingAndTrailingWhitespace) {
+TEST_F(QuicTextUtilsTest, RemoveLeadingAndTrailingWhitespace) {
   string input;
 
   for (auto* input : {"text", " text", "  text", "text ", "text  ", " text ",
@@ -51,7 +51,7 @@
   }
 }
 
-TEST_F(QuicTextUtilsText, StringToNumbers) {
+TEST_F(QuicTextUtilsTest, StringToNumbers) {
   const string kMaxInt32Plus1 = "2147483648";
   const string kMinInt32Minus1 = "-2147483649";
   const string kMaxUint32Plus1 = "4294967296";
@@ -124,25 +124,25 @@
   }
 }
 
-TEST_F(QuicTextUtilsText, Uint64ToString) {
+TEST_F(QuicTextUtilsTest, Uint64ToString) {
   EXPECT_EQ("123", QuicTextUtils::Uint64ToString(123));
   EXPECT_EQ("1234", QuicTextUtils::Uint64ToString(1234));
 }
 
-TEST_F(QuicTextUtilsText, HexEncode) {
+TEST_F(QuicTextUtilsTest, HexEncode) {
   EXPECT_EQ("48656c6c6f", QuicTextUtils::HexEncode("Hello", 5));
   EXPECT_EQ("48656c6c6f", QuicTextUtils::HexEncode("Hello World", 5));
   EXPECT_EQ("48656c6c6f", QuicTextUtils::HexEncode("Hello"));
   EXPECT_EQ("0102779cfa", QuicTextUtils::HexEncode("\x01\x02\x77\x9c\xfa"));
 }
 
-TEST_F(QuicTextUtilsText, HexDecode) {
+TEST_F(QuicTextUtilsTest, HexDecode) {
   EXPECT_EQ("Hello", QuicTextUtils::HexDecode("48656c6c6f"));
   EXPECT_EQ("", QuicTextUtils::HexDecode(""));
   EXPECT_EQ("\x01\x02\x77\x9c\xfa", QuicTextUtils::HexDecode("0102779cfa"));
 }
 
-TEST_F(QuicTextUtilsText, HexDump) {
+TEST_F(QuicTextUtilsTest, HexDump) {
   // Verify output of the HexDump method is as expected.
   char packet[] = {
       0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x51, 0x55, 0x49, 0x43, 0x21,
@@ -161,9 +161,16 @@
       "0x0030:  6f20 7370 616e 206d 756c 7469 706c 6520  o.span.multiple.\n"
       "0x0040:  6c69 6e65 7320 6f66 206f 7574 7075 742e  lines.of.output.\n"
       "0x0050:  0102 03                                  ...\n");
+  // Verify that 0x21 and 0x7e are printable, 0x20 and 0x7f are not.
+  EXPECT_EQ("0x0000:  2021 7e7f                                .!~.\n",
+            QuicTextUtils::HexDump(QuicTextUtils::HexDecode("20217e7f")));
+  // Verify that values above numeric_limits<unsigned char>::max() are formatted
+  // properly on platforms where char is unsigned.
+  EXPECT_EQ("0x0000:  90aa ff                                  ...\n",
+            QuicTextUtils::HexDump(QuicTextUtils::HexDecode("90aaff")));
 }
 
-TEST_F(QuicTextUtilsText, Base64Encode) {
+TEST_F(QuicTextUtilsTest, Base64Encode) {
   string output;
   string input = "Hello";
   QuicTextUtils::Base64Encode(reinterpret_cast<const uint8_t*>(input.data()),
@@ -181,7 +188,7 @@
       output);
 }
 
-TEST_F(QuicTextUtilsText, ContainsUpperCase) {
+TEST_F(QuicTextUtilsTest, ContainsUpperCase) {
   EXPECT_FALSE(QuicTextUtils::ContainsUpperCase("abc"));
   EXPECT_FALSE(QuicTextUtils::ContainsUpperCase(""));
   EXPECT_FALSE(QuicTextUtils::ContainsUpperCase("123"));
@@ -189,7 +196,7 @@
   EXPECT_TRUE(QuicTextUtils::ContainsUpperCase("aBc"));
 }
 
-TEST_F(QuicTextUtilsText, Split) {
+TEST_F(QuicTextUtilsTest, Split) {
   EXPECT_EQ(std::vector<QuicStringPiece>({"a", "b", "c"}),
             QuicTextUtils::Split("a,b,c", ','));
   EXPECT_EQ(std::vector<QuicStringPiece>({"a", "b", "c"}),
diff --git a/net/quic/test_tools/mock_crypto_client_stream.cc b/net/quic/test_tools/mock_crypto_client_stream.cc
index ba12953..397fc00f 100644
--- a/net/quic/test_tools/mock_crypto_client_stream.cc
+++ b/net/quic/test_tools/mock_crypto_client_stream.cc
@@ -8,7 +8,7 @@
 #include "net/quic/core/crypto/null_encrypter.h"
 #include "net/quic/core/crypto/quic_decrypter.h"
 #include "net/quic/core/crypto/quic_encrypter.h"
-#include "net/quic/core/quic_client_session_base.h"
+#include "net/quic/core/quic_spdy_client_session_base.h"
 #include "net/quic/test_tools/quic_config_peer.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -18,7 +18,7 @@
 
 MockCryptoClientStream::MockCryptoClientStream(
     const QuicServerId& server_id,
-    QuicClientSessionBase* session,
+    QuicSpdyClientSessionBase* session,
     ProofVerifyContext* verify_context,
     const QuicConfig& config,
     QuicCryptoClientConfig* crypto_config,
@@ -66,7 +66,7 @@
       crypto_negotiated_params_->key_exchange = kC255;
       crypto_negotiated_params_->aead = kAESG;
       if (proof_verify_details_) {
-        reinterpret_cast<QuicClientSessionBase*>(session())
+        reinterpret_cast<QuicSpdyClientSessionBase*>(session())
             ->OnProofVerifyDetailsAvailable(*proof_verify_details_);
       }
       session()->connection()->SetDecrypter(
@@ -85,7 +85,7 @@
       crypto_negotiated_params_->key_exchange = kC255;
       crypto_negotiated_params_->aead = kAESG;
       if (proof_verify_details_) {
-        reinterpret_cast<QuicClientSessionBase*>(session())
+        reinterpret_cast<QuicSpdyClientSessionBase*>(session())
             ->OnProofVerifyDetailsAvailable(*proof_verify_details_);
       }
       SetConfigNegotiated();
diff --git a/net/quic/test_tools/mock_crypto_client_stream.h b/net/quic/test_tools/mock_crypto_client_stream.h
index 30e1f9b..b31c6e6 100644
--- a/net/quic/test_tools/mock_crypto_client_stream.h
+++ b/net/quic/test_tools/mock_crypto_client_stream.h
@@ -11,10 +11,10 @@
 #include "net/quic/chromium/crypto/proof_verifier_chromium.h"
 #include "net/quic/core/crypto/crypto_handshake.h"
 #include "net/quic/core/crypto/crypto_protocol.h"
-#include "net/quic/core/quic_client_session_base.h"
 #include "net/quic/core/quic_crypto_client_stream.h"
 #include "net/quic/core/quic_server_id.h"
 #include "net/quic/core/quic_session.h"
+#include "net/quic/core/quic_spdy_client_session_base.h"
 
 namespace net {
 
@@ -47,7 +47,7 @@
 
   MockCryptoClientStream(
       const QuicServerId& server_id,
-      QuicClientSessionBase* session,
+      QuicSpdyClientSessionBase* session,
       ProofVerifyContext* verify_context,
       const QuicConfig& config,
       QuicCryptoClientConfig* crypto_config,
diff --git a/net/quic/test_tools/mock_quic_client_promised_info.cc b/net/quic/test_tools/mock_quic_client_promised_info.cc
index 793ed3fd..7518ac2 100644
--- a/net/quic/test_tools/mock_quic_client_promised_info.cc
+++ b/net/quic/test_tools/mock_quic_client_promised_info.cc
@@ -10,7 +10,7 @@
 namespace test {
 
 MockQuicClientPromisedInfo::MockQuicClientPromisedInfo(
-    QuicClientSessionBase* session,
+    QuicSpdyClientSessionBase* session,
     QuicStreamId id,
     string url)
     : QuicClientPromisedInfo(session, id, url) {}
diff --git a/net/quic/test_tools/mock_quic_client_promised_info.h b/net/quic/test_tools/mock_quic_client_promised_info.h
index 4137b56..ab10cb5 100644
--- a/net/quic/test_tools/mock_quic_client_promised_info.h
+++ b/net/quic/test_tools/mock_quic_client_promised_info.h
@@ -17,7 +17,7 @@
 
 class MockQuicClientPromisedInfo : public QuicClientPromisedInfo {
  public:
-  MockQuicClientPromisedInfo(QuicClientSessionBase* session,
+  MockQuicClientPromisedInfo(QuicSpdyClientSessionBase* session,
                              QuicStreamId id,
                              std::string url);
   ~MockQuicClientPromisedInfo() override;
diff --git a/net/quic/test_tools/mock_quic_spdy_client_stream.cc b/net/quic/test_tools/mock_quic_spdy_client_stream.cc
index 6da6fd0..9eb1451 100644
--- a/net/quic/test_tools/mock_quic_spdy_client_stream.cc
+++ b/net/quic/test_tools/mock_quic_spdy_client_stream.cc
@@ -7,8 +7,9 @@
 namespace net {
 namespace test {
 
-MockQuicSpdyClientStream::MockQuicSpdyClientStream(QuicStreamId id,
-                                                   QuicClientSession* session)
+MockQuicSpdyClientStream::MockQuicSpdyClientStream(
+    QuicStreamId id,
+    QuicSpdyClientSession* session)
     : QuicSpdyClientStream(id, session) {}
 
 MockQuicSpdyClientStream::~MockQuicSpdyClientStream() {}
diff --git a/net/quic/test_tools/mock_quic_spdy_client_stream.h b/net/quic/test_tools/mock_quic_spdy_client_stream.h
index e4ca6007..dcaeb7ab9 100644
--- a/net/quic/test_tools/mock_quic_spdy_client_stream.h
+++ b/net/quic/test_tools/mock_quic_spdy_client_stream.h
@@ -16,7 +16,7 @@
 
 class MockQuicSpdyClientStream : public QuicSpdyClientStream {
  public:
-  MockQuicSpdyClientStream(QuicStreamId id, QuicClientSession* session);
+  MockQuicSpdyClientStream(QuicStreamId id, QuicSpdyClientSession* session);
   ~MockQuicSpdyClientStream() override;
 
   MOCK_METHOD1(OnStreamFrame, void(const QuicStreamFrame& frame));
diff --git a/net/quic/test_tools/quic_test_utils.cc b/net/quic/test_tools/quic_test_utils.cc
index ab117b81..9f22b2f 100644
--- a/net/quic/test_tools/quic_test_utils.cc
+++ b/net/quic/test_tools/quic_test_utils.cc
@@ -484,7 +484,7 @@
     const QuicConfig& config,
     const QuicServerId& server_id,
     QuicCryptoClientConfig* crypto_config)
-    : QuicClientSessionBase(connection, &push_promise_index_, config) {
+    : QuicSpdyClientSessionBase(connection, &push_promise_index_, config) {
   crypto_stream_.reset(new QuicCryptoClientStream(
       server_id, this, crypto_test_utils::ProofVerifyContextForTesting(),
       crypto_config, this));
diff --git a/net/quic/test_tools/quic_test_utils.h b/net/quic/test_tools/quic_test_utils.h
index 5cb218d..1f721c8 100644
--- a/net/quic/test_tools/quic_test_utils.h
+++ b/net/quic/test_tools/quic_test_utils.h
@@ -694,7 +694,7 @@
   QuicSpdyStream* rendezvous_stream_;
 };
 
-class TestQuicSpdyClientSession : public QuicClientSessionBase {
+class TestQuicSpdyClientSession : public QuicSpdyClientSessionBase {
  public:
   TestQuicSpdyClientSession(QuicConnection* connection,
                             const QuicConfig& config,
@@ -704,7 +704,7 @@
 
   bool IsAuthorized(const std::string& authority) override;
 
-  // QuicClientSessionBase
+  // QuicSpdyClientSessionBase
   MOCK_METHOD1(OnProofValid,
                void(const QuicCryptoClientConfig::CachedState& cached));
   MOCK_METHOD1(OnProofVerifyDetailsAvailable,
diff --git a/net/tools/quic/end_to_end_test.cc b/net/tools/quic/end_to_end_test.cc
index c9ef6ab..9409dae 100644
--- a/net/tools/quic/end_to_end_test.cc
+++ b/net/tools/quic/end_to_end_test.cc
@@ -21,12 +21,12 @@
 #include "net/base/ip_endpoint.h"
 #include "net/quic/core/crypto/aes_128_gcm_12_encrypter.h"
 #include "net/quic/core/crypto/null_encrypter.h"
-#include "net/quic/core/quic_client_session_base.h"
 #include "net/quic/core/quic_framer.h"
 #include "net/quic/core/quic_packet_creator.h"
 #include "net/quic/core/quic_packets.h"
 #include "net/quic/core/quic_server_id.h"
 #include "net/quic/core/quic_session.h"
+#include "net/quic/core/quic_spdy_client_session_base.h"
 #include "net/quic/core/quic_utils.h"
 #include "net/quic/platform/api/quic_flags.h"
 #include "net/quic/platform/api/quic_logging.h"
@@ -263,7 +263,7 @@
   ~ClientDelegate() override {}
   void OnCanWrite() override {
     EpollEvent event(EPOLLOUT);
-    client_->OnEvent(client_->GetLatestFD(), &event);
+    client_->epoll_network_helper()->OnEvent(client_->GetLatestFD(), &event);
   }
 
  private:
@@ -421,9 +421,9 @@
     if (client_writer_ != nullptr) {
       client_writer_->Initialize(
           QuicConnectionPeer::GetHelper(
-              client_->client()->session()->connection()),
+              client_->client()->client_session()->connection()),
           QuicConnectionPeer::GetAlarmFactory(
-              client_->client()->session()->connection()),
+              client_->client()->client_session()->connection()),
           new ClientDelegate(client_->client()));
     }
     initialized_ = true;
@@ -521,7 +521,7 @@
   // per test.
   void VerifyCleanConnection(bool had_packet_loss) {
     QuicConnectionStats client_stats =
-        client_->client()->session()->connection()->GetStats();
+        client_->client()->client_session()->connection()->GetStats();
     // TODO(ianswett): Determine why this becomes even more flaky with BBR
     // enabled.  b/62141144
     if (!had_packet_loss && !FLAGS_quic_reloadable_flag_quic_default_to_bbr) {
@@ -537,7 +537,7 @@
 
     const int num_expected_stateless_rejects =
         (BothSidesSupportStatelessRejects() &&
-         client_->client()->session()->GetNumSentClientHellos() > 0)
+         client_->client()->client_session()->GetNumSentClientHellos() > 0)
             ? 1
             : 0;
     EXPECT_EQ(num_expected_stateless_rejects,
@@ -579,12 +579,12 @@
 
   QuicStreamId GetNthClientInitiatedId(int n) {
     return QuicSpdySessionPeer::GetNthClientInitiatedStreamId(
-        *client_->client()->session(), n);
+        *client_->client()->client_session(), n);
   }
 
   QuicStreamId GetNthServerInitiatedId(int n) {
     return QuicSpdySessionPeer::GetNthServerInitiatedStreamId(
-        *client_->client()->session(), n);
+        *client_->client()->client_session(), n);
   }
 
   bool initialized_;
@@ -616,8 +616,8 @@
 TEST_P(EndToEndTest, HandshakeSuccessful) {
   ASSERT_TRUE(Initialize());
   EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
-  QuicCryptoStream* crypto_stream =
-      QuicSessionPeer::GetMutableCryptoStream(client_->client()->session());
+  QuicCryptoStream* crypto_stream = QuicSessionPeer::GetMutableCryptoStream(
+      client_->client()->client_session());
   QuicStreamSequencer* sequencer = QuicStreamPeer::sequencer(crypto_stream);
   EXPECT_NE(FLAGS_quic_reloadable_flag_quic_release_crypto_stream_buffer,
             QuicStreamSequencerPeer::IsUnderlyingBufferAllocated(sequencer));
@@ -921,7 +921,7 @@
   const int expected_num_hellos_latest_session =
       BothSidesSupportStatelessRejects() ? 1 : 2;
   EXPECT_EQ(expected_num_hellos_latest_session,
-            client_->client()->session()->GetNumSentClientHellos());
+            client_->client()->client_session()->GetNumSentClientHellos());
   EXPECT_EQ(2, client_->client()->GetNumSentClientHellos());
 
   client_->Disconnect();
@@ -933,7 +933,7 @@
   EXPECT_EQ(kFooResponseBody,
             client_->SendCustomSynchronousRequest(headers, body));
 
-  EXPECT_EQ(1, client_->client()->session()->GetNumSentClientHellos());
+  EXPECT_EQ(1, client_->client()->client_session()->GetNumSentClientHellos());
   EXPECT_EQ(1, client_->client()->GetNumSentClientHellos());
 
   client_->Disconnect();
@@ -953,7 +953,7 @@
   // torn down after the reject.  The number of hellos sent on the
   // latest session is 1.
   EXPECT_EQ(expected_num_hellos_latest_session,
-            client_->client()->session()->GetNumSentClientHellos());
+            client_->client()->client_session()->GetNumSentClientHellos());
   EXPECT_EQ(2, client_->client()->GetNumSentClientHellos());
 
   VerifyCleanConnection(false);
@@ -973,7 +973,7 @@
   const int expected_num_hellos_latest_session =
       BothSidesSupportStatelessRejects() ? 1 : 2;
   EXPECT_EQ(expected_num_hellos_latest_session,
-            client_->client()->session()->GetNumSentClientHellos());
+            client_->client()->client_session()->GetNumSentClientHellos());
   EXPECT_EQ(2, client_->client()->GetNumSentClientHellos());
 
   client_->Disconnect();
@@ -984,7 +984,7 @@
   ASSERT_TRUE(client_->client()->connected());
   EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
 
-  EXPECT_EQ(1, client_->client()->session()->GetNumSentClientHellos());
+  EXPECT_EQ(1, client_->client()->client_session()->GetNumSentClientHellos());
   EXPECT_EQ(1, client_->client()->GetNumSentClientHellos());
 
   client_->Disconnect();
@@ -1003,7 +1003,7 @@
   // torn down after the reject.  The number of hellos sent on the
   // latest session is 1.
   EXPECT_EQ(expected_num_hellos_latest_session,
-            client_->client()->session()->GetNumSentClientHellos());
+            client_->client()->client_session()->GetNumSentClientHellos());
   EXPECT_EQ(2, client_->client()->GetNumSentClientHellos());
 
   VerifyCleanConnection(false);
@@ -1031,7 +1031,7 @@
   const int expected_num_hellos_latest_session =
       BothSidesSupportStatelessRejects() ? 1 : 2;
   EXPECT_EQ(expected_num_hellos_latest_session,
-            client_->client()->session()->GetNumSentClientHellos());
+            client_->client()->client_session()->GetNumSentClientHellos());
   EXPECT_EQ(2, client_->client()->GetNumSentClientHellos());
 
   client_->Disconnect();
@@ -1043,7 +1043,7 @@
   EXPECT_EQ(kFooResponseBody,
             client_->SendCustomSynchronousRequest(headers, body));
 
-  EXPECT_EQ(1, client_->client()->session()->GetNumSentClientHellos());
+  EXPECT_EQ(1, client_->client()->client_session()->GetNumSentClientHellos());
   EXPECT_EQ(1, client_->client()->GetNumSentClientHellos());
 
   client_->Disconnect();
@@ -1063,7 +1063,7 @@
   // torn down after the reject.  The number of hellos sent on the
   // latest session is 1.
   EXPECT_EQ(expected_num_hellos_latest_session,
-            client_->client()->session()->GetNumSentClientHellos());
+            client_->client()->client_session()->GetNumSentClientHellos());
   EXPECT_EQ(2, client_->client()->GetNumSentClientHellos());
 
   VerifyCleanConnection(false);
@@ -1142,7 +1142,7 @@
   const uint64_t flow_control_window =
       server_config_.GetInitialStreamFlowControlWindowToSend();
   QuicSpdyClientStream* stream = client_->GetOrCreateStream();
-  QuicSession* session = client_->client()->session();
+  QuicSession* session = client_->client()->client_session();
   QuicFlowControllerPeer::SetSendWindowOffset(stream->flow_controller(), 0);
   QuicFlowControllerPeer::SetSendWindowOffset(session->flow_controller(), 0);
   EXPECT_TRUE(stream->flow_controller()->IsBlocked());
@@ -1181,7 +1181,7 @@
 
   // Force the client to write with a stream ID belonging to a nonexistent
   // server-side stream.
-  QuicSpdySession* session = client_->client()->session();
+  QuicSpdySession* session = client_->client()->client_session();
   QuicSessionPeer::SetNextOutgoingStreamId(session, GetNthServerInitiatedId(0));
 
   client_->SendCustomSynchronousRequest(headers, body);
@@ -1189,7 +1189,7 @@
   EXPECT_EQ(QUIC_INVALID_STREAM_ID, client_->connection_error());
 }
 
-// Test that if the the server will close the connection if the client attempts
+// Test that if the server will close the connection if the client attempts
 // to send a request with overly large headers.
 TEST_P(EndToEndTest, LargeHeaders) {
   ASSERT_TRUE(Initialize());
@@ -1274,8 +1274,8 @@
 
   // Make the client misbehave after negotiation.
   const int kServerMaxStreams = kMaxStreamsMinimumIncrement + 1;
-  QuicSessionPeer::SetMaxOpenOutgoingStreams(client_->client()->session(),
-                                             kServerMaxStreams + 1);
+  QuicSessionPeer::SetMaxOpenOutgoingStreams(
+      client_->client()->client_session(), kServerMaxStreams + 1);
 
   SpdyHeaderBlock headers;
   headers[":method"] = "POST";
@@ -1309,7 +1309,7 @@
 
   // The client has received the server's limit and vice versa.
   EXPECT_EQ(kServerMaxIncomingDynamicStreams,
-            client_->client()->session()->max_open_outgoing_streams());
+            client_->client()->client_session()->max_open_outgoing_streams());
   server_thread_->Pause();
   QuicDispatcher* dispatcher =
       QuicServerPeer::GetDispatcher(server_thread_->server());
@@ -1372,7 +1372,7 @@
       QuicServerPeer::GetDispatcher(server_thread_->server());
   ASSERT_EQ(1u, dispatcher->session_map().size());
   const QuicSentPacketManager& client_sent_packet_manager =
-      client_->client()->session()->connection()->sent_packet_manager();
+      client_->client()->client_session()->connection()->sent_packet_manager();
   const QuicSentPacketManager* server_sent_packet_manager =
       GetSentPacketManagerFromFirstServerSession();
 
@@ -1400,7 +1400,7 @@
   ASSERT_EQ(1u, dispatcher->session_map().size());
   QuicSession* session = dispatcher->session_map().begin()->second.get();
   const QuicSentPacketManager& client_sent_packet_manager =
-      client_->client()->session()->connection()->sent_packet_manager();
+      client_->client()->client_session()->connection()->sent_packet_manager();
 
   // Now that acks have been exchanged, the RTT estimate has decreased on the
   // server and is not infinite on the client.
@@ -1430,7 +1430,7 @@
   ASSERT_EQ(1u, dispatcher->session_map().size());
   QuicSession* session = dispatcher->session_map().begin()->second.get();
   const QuicSentPacketManager& client_sent_packet_manager =
-      client_->client()->session()->connection()->sent_packet_manager();
+      client_->client()->client_session()->connection()->sent_packet_manager();
   const QuicSentPacketManager& server_sent_packet_manager =
       session->connection()->sent_packet_manager();
 
@@ -1455,7 +1455,7 @@
   EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
 
   QuicPacketHeader* header = QuicConnectionPeer::GetLastHeader(
-      client_->client()->session()->connection());
+      client_->client()->client_session()->connection());
   EXPECT_EQ(PACKET_0BYTE_CONNECTION_ID,
             header->public_header.connection_id_length);
 }
@@ -1467,7 +1467,7 @@
   EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
   EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
   QuicPacketHeader* header = QuicConnectionPeer::GetLastHeader(
-      client_->client()->session()->connection());
+      client_->client()->client_session()->connection());
   EXPECT_EQ(PACKET_8BYTE_CONNECTION_ID,
             header->public_header.connection_id_length);
 }
@@ -1480,7 +1480,7 @@
   EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
   EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
   QuicPacketHeader* header = QuicConnectionPeer::GetLastHeader(
-      client_->client()->session()->connection());
+      client_->client()->client_session()->connection());
   EXPECT_EQ(PACKET_8BYTE_CONNECTION_ID,
             header->public_header.connection_id_length);
 }
@@ -1530,7 +1530,7 @@
 
   EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
 
-  QuicSession* session = client_->client()->session();
+  QuicSession* session = client_->client()->client_session();
   // Lose the request.
   SetPacketLossPercentage(100);
   EXPECT_LT(0, client_->SendRequest("/small_response"));
@@ -1574,7 +1574,8 @@
   EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
 
   // Store the client IP address which was used to send the first request.
-  QuicIpAddress old_host = client_->client()->GetLatestClientAddress().host();
+  QuicIpAddress old_host =
+      client_->client()->network_helper()->GetLatestClientAddress().host();
 
   // Migrate socket to the new IP address.
   QuicIpAddress new_host = TestLoopback(2);
@@ -1596,7 +1597,8 @@
   EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
 
   // Store the client address which was used to send the first request.
-  QuicSocketAddress old_address = client_->client()->GetLatestClientAddress();
+  QuicSocketAddress old_address =
+      client_->client()->network_helper()->GetLatestClientAddress();
   int old_fd = client_->client()->GetLatestFD();
 
   // Create a new socket before closing the old one, which will result in a new
@@ -1607,32 +1609,38 @@
   QuicClientPeer::CleanUpUDPSocket(client_->client(), old_fd);
 
   // The packet writer needs to be updated to use the new FD.
-  client_->client()->CreateQuicPacketWriter();
+  client_->client()->network_helper()->CreateQuicPacketWriter();
 
   // Change the internal state of the client and connection to use the new port,
   // this is done because in a real NAT rebinding the client wouldn't see any
   // port change, and so expects no change to incoming port.
   // This is kind of ugly, but needed as we are simply swapping out the client
   // FD rather than any more complex NAT rebinding simulation.
-  int new_port = client_->client()->GetLatestClientAddress().port();
+  int new_port =
+      client_->client()->network_helper()->GetLatestClientAddress().port();
   QuicClientPeer::SetClientPort(client_->client(), new_port);
   QuicConnectionPeer::SetSelfAddress(
-      client_->client()->session()->connection(),
-      QuicSocketAddress(
-          client_->client()->session()->connection()->self_address().host(),
-          new_port));
+      client_->client()->client_session()->connection(),
+      QuicSocketAddress(client_->client()
+                            ->client_session()
+                            ->connection()
+                            ->self_address()
+                            .host(),
+                        new_port));
 
   // Register the new FD for epoll events.
   int new_fd = client_->client()->GetLatestFD();
   EpollServer* eps = client_->epoll_server();
-  eps->RegisterFD(new_fd, client_->client(), EPOLLIN | EPOLLOUT | EPOLLET);
+  eps->RegisterFD(new_fd, client_->client()->epoll_network_helper(),
+                  EPOLLIN | EPOLLOUT | EPOLLET);
 
   // Send a second request, using the new FD.
   EXPECT_EQ(kBarResponseBody, client_->SendSynchronousRequest("/bar"));
   EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
 
   // Verify that the client's ephemeral port is different.
-  QuicSocketAddress new_address = client_->client()->GetLatestClientAddress();
+  QuicSocketAddress new_address =
+      client_->client()->network_helper()->GetLatestClientAddress();
   EXPECT_EQ(old_address.host(), new_address.host());
   EXPECT_NE(old_address.port(), new_address.port());
 }
@@ -1664,19 +1672,19 @@
   // Client should have the right values for server's receive window.
   EXPECT_EQ(kServerStreamIFCW,
             client_->client()
-                ->session()
+                ->client_session()
                 ->config()
                 ->ReceivedInitialStreamFlowControlWindowBytes());
   EXPECT_EQ(kServerSessionIFCW,
             client_->client()
-                ->session()
+                ->client_session()
                 ->config()
                 ->ReceivedInitialSessionFlowControlWindowBytes());
   EXPECT_EQ(kServerStreamIFCW, QuicFlowControllerPeer::SendWindowOffset(
                                    stream->flow_controller()));
   EXPECT_EQ(kServerSessionIFCW,
             QuicFlowControllerPeer::SendWindowOffset(
-                client_->client()->session()->flow_controller()));
+                client_->client()->client_session()->flow_controller()));
 
   // Server should have the right values for client's receive window.
   server_thread_->Pause();
@@ -1722,19 +1730,19 @@
   // Client should have the right values for server's receive window.
   EXPECT_EQ(kExpectedStreamIFCW,
             client_->client()
-                ->session()
+                ->client_session()
                 ->config()
                 ->ReceivedInitialStreamFlowControlWindowBytes());
   EXPECT_EQ(kExpectedSessionIFCW,
             client_->client()
-                ->session()
+                ->client_session()
                 ->config()
                 ->ReceivedInitialSessionFlowControlWindowBytes());
   EXPECT_EQ(kExpectedStreamIFCW, QuicFlowControllerPeer::SendWindowOffset(
                                      stream->flow_controller()));
   EXPECT_EQ(kExpectedSessionIFCW,
             QuicFlowControllerPeer::SendWindowOffset(
-                client_->client()->session()->flow_controller()));
+                client_->client()->client_session()->flow_controller()));
 }
 
 TEST_P(EndToEndTest, HeadersAndCryptoStreamsNoConnectionFlowControl) {
@@ -1755,27 +1763,29 @@
   EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
   server_thread_->WaitForCryptoHandshakeConfirmed();
 
-  QuicCryptoStream* crypto_stream =
-      QuicSessionPeer::GetMutableCryptoStream(client_->client()->session());
+  QuicCryptoStream* crypto_stream = QuicSessionPeer::GetMutableCryptoStream(
+      client_->client()->client_session());
   EXPECT_LT(
       QuicFlowControllerPeer::SendWindowSize(crypto_stream->flow_controller()),
       kStreamIFCW);
-  EXPECT_EQ(kSessionIFCW, QuicFlowControllerPeer::SendWindowSize(
-                              client_->client()->session()->flow_controller()));
+  EXPECT_EQ(kSessionIFCW,
+            QuicFlowControllerPeer::SendWindowSize(
+                client_->client()->client_session()->flow_controller()));
 
   // Send a request with no body, and verify that the connection level window
   // has not been affected.
   EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
 
-  QuicHeadersStream* headers_stream =
-      QuicSpdySessionPeer::GetHeadersStream(client_->client()->session());
-  if (!client_->client()->session()->force_hol_blocking()) {
+  QuicHeadersStream* headers_stream = QuicSpdySessionPeer::GetHeadersStream(
+      client_->client()->client_session());
+  if (!client_->client()->client_session()->force_hol_blocking()) {
     EXPECT_LT(QuicFlowControllerPeer::SendWindowSize(
                   headers_stream->flow_controller()),
               kStreamIFCW);
   }
-  EXPECT_EQ(kSessionIFCW, QuicFlowControllerPeer::SendWindowSize(
-                              client_->client()->session()->flow_controller()));
+  EXPECT_EQ(kSessionIFCW,
+            QuicFlowControllerPeer::SendWindowSize(
+                client_->client()->client_session()->flow_controller()));
 
   // Server should be in a similar state: connection flow control window should
   // not have any bytes marked as received.
@@ -1799,7 +1809,7 @@
   server_thread_->WaitForCryptoHandshakeConfirmed();
 
   server_thread_->Pause();
-  QuicSpdySession* const client_session = client_->client()->session();
+  QuicSpdySession* const client_session = client_->client()->client_session();
   QuicDispatcher* dispatcher =
       QuicServerPeer::GetDispatcher(server_thread_->server());
   auto* server_session = static_cast<QuicSpdySession*>(
@@ -1921,7 +1931,7 @@
   int num_notifications_;
 };
 
-class TestResponseListener : public QuicClient::ResponseListener {
+class TestResponseListener : public QuicSpdyClientBase::ResponseListener {
  public:
   void OnCompleteResponse(QuicStreamId id,
                           const SpdyHeaderBlock& response_headers,
@@ -1989,7 +1999,7 @@
 
   // Send the public reset.
   QuicConnectionId connection_id =
-      client_->client()->session()->connection()->connection_id();
+      client_->client()->client_session()->connection()->connection_id();
   QuicPublicResetPacket header;
   header.public_header.connection_id = connection_id;
   header.public_header.reset_flag = true;
@@ -2003,7 +2013,7 @@
   server_thread_->Pause();
   server_writer_->WritePacket(
       packet->data(), packet->length(), server_address_.host(),
-      client_->client()->GetLatestClientAddress(), nullptr);
+      client_->client()->network_helper()->GetLatestClientAddress(), nullptr);
   server_thread_->Resume();
 
   // The request should fail.
@@ -2021,7 +2031,7 @@
 
   // Send the public reset.
   QuicConnectionId incorrect_connection_id =
-      client_->client()->session()->connection()->connection_id() + 1;
+      client_->client()->client_session()->connection()->connection_id() + 1;
   QuicPublicResetPacket header;
   header.public_header.connection_id = incorrect_connection_id;
   header.public_header.reset_flag = true;
@@ -2031,7 +2041,8 @@
   std::unique_ptr<QuicEncryptedPacket> packet(
       framer.BuildPublicResetPacket(header));
   testing::NiceMock<MockQuicConnectionDebugVisitor> visitor;
-  client_->client()->session()->connection()->set_debug_visitor(&visitor);
+  client_->client()->client_session()->connection()->set_debug_visitor(
+      &visitor);
   EXPECT_CALL(visitor, OnIncorrectConnectionId(incorrect_connection_id))
       .Times(1);
   // We must pause the server's thread in order to call WritePacket without
@@ -2039,14 +2050,14 @@
   server_thread_->Pause();
   server_writer_->WritePacket(
       packet->data(), packet->length(), server_address_.host(),
-      client_->client()->GetLatestClientAddress(), nullptr);
+      client_->client()->network_helper()->GetLatestClientAddress(), nullptr);
   server_thread_->Resume();
 
   // The connection should be unaffected.
   EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
   EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
 
-  client_->client()->session()->connection()->set_debug_visitor(nullptr);
+  client_->client()->client_session()->connection()->set_debug_visitor(nullptr);
 }
 
 // Send a public reset from the client for a different connection ID.
@@ -2056,7 +2067,7 @@
 
   // Send the public reset.
   QuicConnectionId incorrect_connection_id =
-      client_->client()->session()->connection()->connection_id() + 1;
+      client_->client()->client_session()->connection()->connection_id() + 1;
   QuicPublicResetPacket header;
   header.public_header.connection_id = incorrect_connection_id;
   header.public_header.reset_flag = true;
@@ -2067,8 +2078,8 @@
       framer.BuildPublicResetPacket(header));
   client_writer_->WritePacket(
       packet->data(), packet->length(),
-      client_->client()->GetLatestClientAddress().host(), server_address_,
-      nullptr);
+      client_->client()->network_helper()->GetLatestClientAddress().host(),
+      server_address_, nullptr);
 
   // The connection should be unaffected.
   EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
@@ -2084,12 +2095,13 @@
 
   // Send the version negotiation packet.
   QuicConnectionId incorrect_connection_id =
-      client_->client()->session()->connection()->connection_id() + 1;
+      client_->client()->client_session()->connection()->connection_id() + 1;
   std::unique_ptr<QuicEncryptedPacket> packet(
       QuicFramer::BuildVersionNegotiationPacket(incorrect_connection_id,
                                                 server_supported_versions_));
   testing::NiceMock<MockQuicConnectionDebugVisitor> visitor;
-  client_->client()->session()->connection()->set_debug_visitor(&visitor);
+  client_->client()->client_session()->connection()->set_debug_visitor(
+      &visitor);
   EXPECT_CALL(visitor, OnIncorrectConnectionId(incorrect_connection_id))
       .Times(1);
   // We must pause the server's thread in order to call WritePacket without
@@ -2097,14 +2109,14 @@
   server_thread_->Pause();
   server_writer_->WritePacket(
       packet->data(), packet->length(), server_address_.host(),
-      client_->client()->GetLatestClientAddress(), nullptr);
+      client_->client()->network_helper()->GetLatestClientAddress(), nullptr);
   server_thread_->Resume();
 
   // The connection should be unaffected.
   EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
   EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
 
-  client_->client()->session()->connection()->set_debug_visitor(nullptr);
+  client_->client()->client_session()->connection()->set_debug_visitor(nullptr);
 }
 
 // A bad header shouldn't tear down the connection, because the receiver can't
@@ -2123,8 +2135,8 @@
                    0x11};
   client_writer_->WritePacket(
       &packet[0], sizeof(packet),
-      client_->client()->GetLatestClientAddress().host(), server_address_,
-      nullptr);
+      client_->client()->network_helper()->GetLatestClientAddress().host(),
+      server_address_, nullptr);
   // Give the server time to process the packet.
   base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
   // Pause the server so we can access the server's internals without races.
@@ -2154,16 +2166,28 @@
       // invalid public flags
       0xFF,
       // connection_id
-      0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE,
+      0x10,
+      0x32,
+      0x54,
+      0x76,
+      0x98,
+      0xBA,
+      0xDC,
+      0xFE,
       // packet sequence number
-      0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12,
+      0xBC,
+      0x9A,
+      0x78,
+      0x56,
+      0x34,
+      0x12,
       // private flags
       0x00,
   };
   client_writer_->WritePacket(
       &packet[0], sizeof(packet),
-      client_->client()->GetLatestClientAddress().host(), server_address_,
-      nullptr);
+      client_->client()->network_helper()->GetLatestClientAddress().host(),
+      server_address_, nullptr);
   // Give the server time to process the packet.
   base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
   // Pause the server so we can access the server's internals without races.
@@ -2189,8 +2213,8 @@
   EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
 
   std::unique_ptr<QuicEncryptedPacket> packet(ConstructEncryptedPacket(
-      client_->client()->session()->connection()->connection_id(), false, false,
-      1, "At least 20 characters.", PACKET_8BYTE_CONNECTION_ID,
+      client_->client()->client_session()->connection()->connection_id(), false,
+      false, 1, "At least 20 characters.", PACKET_8BYTE_CONNECTION_ID,
       PACKET_6BYTE_PACKET_NUMBER));
   // Damage the encrypted data.
   string damaged_packet(packet->data(), packet->length());
@@ -2198,8 +2222,8 @@
   QUIC_DLOG(INFO) << "Sending bad packet.";
   client_writer_->WritePacket(
       damaged_packet.data(), damaged_packet.length(),
-      client_->client()->GetLatestClientAddress().host(), server_address_,
-      nullptr);
+      client_->client()->network_helper()->GetLatestClientAddress().host(),
+      server_address_, nullptr);
   // Give the server time to process the packet.
   base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
   // This error is sent to the connection's OnError (which ignores it), so the
@@ -2232,7 +2256,7 @@
 
   // Cancel the stream.
   stream->Reset(QUIC_STREAM_CANCELLED);
-  QuicSession* session = client_->client()->session();
+  QuicSession* session = client_->client()->client_session();
   // Verify canceled stream does not become zombie.
   EXPECT_TRUE(QuicSessionPeer::zombie_streams(session).empty());
   EXPECT_EQ(1u, QuicSessionPeer::closed_streams(session).size());
@@ -2388,7 +2412,7 @@
 // A test client stream that drops all received body.
 class ClientStreamThatDropsBody : public QuicSpdyClientStream {
  public:
-  ClientStreamThatDropsBody(QuicStreamId id, QuicClientSession* session)
+  ClientStreamThatDropsBody(QuicStreamId id, QuicSpdyClientSession* session)
       : QuicSpdyClientStream(id, session) {}
   ~ClientStreamThatDropsBody() override {}
 
@@ -2408,18 +2432,18 @@
   }
 };
 
-class ClientSessionThatDropsBody : public QuicClientSession {
+class ClientSessionThatDropsBody : public QuicSpdyClientSession {
  public:
   ClientSessionThatDropsBody(const QuicConfig& config,
                              QuicConnection* connection,
                              const QuicServerId& server_id,
                              QuicCryptoClientConfig* crypto_config,
                              QuicClientPushPromiseIndex* push_promise_index)
-      : QuicClientSession(config,
-                          connection,
-                          server_id,
-                          crypto_config,
-                          push_promise_index) {}
+      : QuicSpdyClientSession(config,
+                              connection,
+                              server_id,
+                              crypto_config,
+                              push_promise_index) {}
 
   ~ClientSessionThatDropsBody() override {}
 
@@ -2446,13 +2470,11 @@
                            epoll_server) {}
   ~MockableQuicClientThatDropsBody() override {}
 
-  QuicClientSession* CreateQuicClientSession(
+  std::unique_ptr<QuicSession> CreateQuicClientSession(
       QuicConnection* connection) override {
-    auto* session =
-        new ClientSessionThatDropsBody(*config(), connection, server_id(),
-                                       crypto_config(), push_promise_index());
-    set_session(session);
-    return session;
+    return QuicMakeUnique<ClientSessionThatDropsBody>(
+        *config(), connection, server_id(), crypto_config(),
+        push_promise_index());
   }
 };
 
@@ -2647,14 +2669,14 @@
                                       push_urls, kNumResources, 0);
 
   client_->client()->set_response_listener(
-      std::unique_ptr<QuicClientBase::ResponseListener>(
+      std::unique_ptr<QuicSpdyClientBase::ResponseListener>(
           new TestResponseListener));
 
   QUIC_DVLOG(1) << "send request for /push_example";
   EXPECT_EQ(kBody, client_->SendSynchronousRequest(
                        "https://example.com/push_example"));
-  QuicHeadersStream* headers_stream =
-      QuicSpdySessionPeer::GetHeadersStream(client_->client()->session());
+  QuicHeadersStream* headers_stream = QuicSpdySessionPeer::GetHeadersStream(
+      client_->client()->client_session());
   QuicStreamSequencer* sequencer = QuicStreamPeer::sequencer(headers_stream);
   // Headers stream's sequencer buffer shouldn't be released because server push
   // hasn't finished yet.
@@ -2687,14 +2709,15 @@
   const string kBody = "body content";
   size_t const kNumResources = 4;
   string push_urls[] = {
-      "https://example.com/font.woff", "https://example.com/script.js",
+      "https://example.com/font.woff",
+      "https://example.com/script.js",
       "https://fonts.example.com/font.woff",
       "https://example.com/logo-hires.jpg",
   };
   AddRequestAndResponseWithServerPush("example.com", "/push_example", kBody,
                                       push_urls, kNumResources, 0);
   client_->client()->set_response_listener(
-      std::unique_ptr<QuicClientBase::ResponseListener>(
+      std::unique_ptr<QuicSpdyClientBase::ResponseListener>(
           new TestResponseListener));
 
   // Send the first request: this will trigger the server to send all the push
@@ -2743,7 +2766,7 @@
   AddRequestAndResponseWithServerPush("example.com", "/push_example", kBody,
                                       push_urls, kNumResources, 0);
   client_->client()->set_response_listener(
-      std::unique_ptr<QuicClientBase::ResponseListener>(
+      std::unique_ptr<QuicSpdyClientBase::ResponseListener>(
           new TestResponseListener));
 
   // Send the first request: this will trigger the server to send all the push
@@ -2801,7 +2824,7 @@
                                       push_urls, kNumResources, kBodySize);
 
   client_->client()->set_response_listener(
-      std::unique_ptr<QuicClientBase::ResponseListener>(
+      std::unique_ptr<QuicSpdyClientBase::ResponseListener>(
           new TestResponseListener));
 
   client_->SendRequest("https://example.com/push_example");
@@ -2911,9 +2934,10 @@
   client_.reset(client);
   static EpollEvent event(EPOLLOUT);
   client_writer_->Initialize(
-      QuicConnectionPeer::GetHelper(client_->client()->session()->connection()),
+      QuicConnectionPeer::GetHelper(
+          client_->client()->client_session()->connection()),
       QuicConnectionPeer::GetAlarmFactory(
-          client_->client()->session()->connection()),
+          client_->client()->client_session()->connection()),
       new ClientDelegate(client_->client()));
   initialized_ = true;
   ASSERT_TRUE(client_->client()->connected());
@@ -2933,8 +2957,8 @@
   // PUSH_PROMISE, its headers stream's sequencer buffer should be released.
   ASSERT_TRUE(Initialize());
   client_->SendSynchronousRequest("/foo");
-  QuicHeadersStream* headers_stream =
-      QuicSpdySessionPeer::GetHeadersStream(client_->client()->session());
+  QuicHeadersStream* headers_stream = QuicSpdySessionPeer::GetHeadersStream(
+      client_->client()->client_session());
   QuicStreamSequencer* sequencer = QuicStreamPeer::sequencer(headers_stream);
   EXPECT_FALSE(QuicStreamSequencerPeer::IsUnderlyingBufferAllocated(sequencer));
 }
@@ -2975,7 +2999,7 @@
   EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
   WindowUpdateObserver observer;
   QuicConnection* client_connection =
-      client_->client()->session()->connection();
+      client_->client()->client_session()->connection();
   client_connection->set_debug_visitor(&observer);
   QuicVersion version = client_connection->version();
   // 100KB body.
@@ -3043,7 +3067,7 @@
   client_->WaitForResponse();
   EXPECT_EQ(kBarResponseBody, client_->response_body());
   QuicConnectionStats client_stats =
-      client_->client()->session()->connection()->GetStats();
+      client_->client()->client_session()->connection()->GetStats();
   EXPECT_EQ(0u, client_stats.packets_lost);
   EXPECT_EQ(1, client_->client()->GetNumSentClientHellos());
 }
diff --git a/net/tools/quic/quic_client.cc b/net/tools/quic/quic_client.cc
index aefdc515..f68df5a 100644
--- a/net/tools/quic/quic_client.cc
+++ b/net/tools/quic/quic_client.cc
@@ -20,6 +20,7 @@
 #include "net/quic/core/spdy_utils.h"
 #include "net/quic/platform/api/quic_bug_tracker.h"
 #include "net/quic/platform/api/quic_logging.h"
+#include "net/quic/platform/api/quic_ptr_util.h"
 #include "net/tools/quic/platform/impl/quic_socket_utils.h"
 #include "net/tools/quic/quic_epoll_alarm_factory.h"
 #include "net/tools/quic/quic_epoll_connection_helper.h"
@@ -34,158 +35,62 @@
 
 namespace net {
 
-const int kEpollFlags = EPOLLIN | EPOLLOUT | EPOLLET;
-
 QuicClient::QuicClient(QuicSocketAddress server_address,
                        const QuicServerId& server_id,
                        const QuicVersionVector& supported_versions,
                        EpollServer* epoll_server,
                        std::unique_ptr<ProofVerifier> proof_verifier)
+    : QuicClient(
+          server_address,
+          server_id,
+          supported_versions,
+          QuicConfig(),
+          epoll_server,
+          QuicWrapUnique(new QuicClientEpollNetworkHelper(epoll_server, this)),
+          std::move(proof_verifier)) {}
+
+QuicClient::QuicClient(
+    QuicSocketAddress server_address,
+    const QuicServerId& server_id,
+    const QuicVersionVector& supported_versions,
+    EpollServer* epoll_server,
+    std::unique_ptr<QuicClientEpollNetworkHelper> network_helper,
+    std::unique_ptr<ProofVerifier> proof_verifier)
     : QuicClient(server_address,
                  server_id,
                  supported_versions,
                  QuicConfig(),
                  epoll_server,
+                 std::move(network_helper),
                  std::move(proof_verifier)) {}
 
-QuicClient::QuicClient(QuicSocketAddress server_address,
-                       const QuicServerId& server_id,
-                       const QuicVersionVector& supported_versions,
-                       const QuicConfig& config,
-                       EpollServer* epoll_server,
-                       std::unique_ptr<ProofVerifier> proof_verifier)
-    : QuicClientBase(
+QuicClient::QuicClient(
+    QuicSocketAddress server_address,
+    const QuicServerId& server_id,
+    const QuicVersionVector& supported_versions,
+    const QuicConfig& config,
+    EpollServer* epoll_server,
+    std::unique_ptr<QuicClientEpollNetworkHelper> network_helper,
+    std::unique_ptr<ProofVerifier> proof_verifier)
+    : QuicSpdyClientBase(
           server_id,
           supported_versions,
           config,
           new QuicEpollConnectionHelper(epoll_server, QuicAllocator::SIMPLE),
           new QuicEpollAlarmFactory(epoll_server),
-          std::move(proof_verifier)),
-      epoll_server_(epoll_server),
-      packets_dropped_(0),
-      overflow_supported_(false),
-      packet_reader_(new QuicPacketReader()) {
+          std::move(network_helper),
+          std::move(proof_verifier)) {
   set_server_address(server_address);
 }
 
-QuicClient::~QuicClient() {
-  if (connected()) {
-    session()->connection()->CloseConnection(
-        QUIC_PEER_GOING_AWAY, "Client being torn down",
-        ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
-  }
+QuicClient::~QuicClient() {}
 
-  CleanUpAllUDPSockets();
+QuicClientEpollNetworkHelper* QuicClient::epoll_network_helper() {
+  return static_cast<QuicClientEpollNetworkHelper*>(network_helper());
 }
 
-bool QuicClient::CreateUDPSocketAndBind(QuicSocketAddress server_address,
-                                        QuicIpAddress bind_to_address,
-                                        int bind_to_port) {
-  epoll_server_->set_timeout_in_us(50 * 1000);
-
-  int fd =
-      QuicSocketUtils::CreateUDPSocket(server_address, &overflow_supported_);
-  if (fd < 0) {
-    return false;
-  }
-
-  QuicSocketAddress client_address;
-  if (bind_to_address.IsInitialized()) {
-    client_address = QuicSocketAddress(bind_to_address, local_port());
-  } else if (server_address.host().address_family() == IpAddressFamily::IP_V4) {
-    client_address = QuicSocketAddress(QuicIpAddress::Any4(), bind_to_port);
-  } else {
-    client_address = QuicSocketAddress(QuicIpAddress::Any6(), bind_to_port);
-  }
-
-  sockaddr_storage addr = client_address.generic_address();
-  int rc = bind(fd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
-  if (rc < 0) {
-    QUIC_LOG(ERROR) << "Bind failed: " << strerror(errno);
-    return false;
-  }
-
-  if (client_address.FromSocket(fd) != 0) {
-    QUIC_LOG(ERROR) << "Unable to get self address.  Error: "
-                    << strerror(errno);
-  }
-
-  fd_address_map_[fd] = client_address;
-
-  epoll_server_->RegisterFD(fd, this, kEpollFlags);
-  return true;
-}
-
-void QuicClient::CleanUpUDPSocket(int fd) {
-  CleanUpUDPSocketImpl(fd);
-  fd_address_map_.erase(fd);
-}
-
-void QuicClient::CleanUpAllUDPSockets() {
-  for (std::pair<int, QuicSocketAddress> fd_address : fd_address_map_) {
-    CleanUpUDPSocketImpl(fd_address.first);
-  }
-  fd_address_map_.clear();
-}
-
-void QuicClient::CleanUpUDPSocketImpl(int fd) {
-  if (fd > -1) {
-    epoll_server_->UnregisterFD(fd);
-    int rc = close(fd);
-    DCHECK_EQ(0, rc);
-  }
-}
-
-void QuicClient::RunEventLoop() {
-  base::RunLoop().RunUntilIdle();
-  epoll_server_->WaitForEventsAndExecuteCallbacks();
-}
-
-void QuicClient::OnEvent(int fd, EpollEvent* event) {
-  DCHECK_EQ(fd, GetLatestFD());
-
-  if (event->in_events & EPOLLIN) {
-    bool more_to_read = true;
-    while (connected() && more_to_read) {
-      more_to_read = packet_reader_->ReadAndDispatchPackets(
-          GetLatestFD(), QuicClient::GetLatestClientAddress().port(),
-          *helper()->GetClock(), this,
-          overflow_supported_ ? &packets_dropped_ : nullptr);
-    }
-  }
-  if (connected() && (event->in_events & EPOLLOUT)) {
-    writer()->SetWritable();
-    session()->connection()->OnCanWrite();
-  }
-  if (event->in_events & EPOLLERR) {
-    QUIC_DLOG(INFO) << "Epollerr";
-  }
-}
-
-QuicPacketWriter* QuicClient::CreateQuicPacketWriter() {
-  return new QuicDefaultPacketWriter(GetLatestFD());
-}
-
-QuicSocketAddress QuicClient::GetLatestClientAddress() const {
-  if (fd_address_map_.empty()) {
-    return QuicSocketAddress();
-  }
-
-  return fd_address_map_.back().second;
-}
-
-int QuicClient::GetLatestFD() const {
-  if (fd_address_map_.empty()) {
-    return -1;
-  }
-
-  return fd_address_map_.back().first;
-}
-
-void QuicClient::ProcessPacket(const QuicSocketAddress& self_address,
-                               const QuicSocketAddress& peer_address,
-                               const QuicReceivedPacket& packet) {
-  session()->ProcessUdpPacket(self_address, peer_address, packet);
+const QuicClientEpollNetworkHelper* QuicClient::epoll_network_helper() const {
+  return static_cast<const QuicClientEpollNetworkHelper*>(network_helper());
 }
 
 }  // namespace net
diff --git a/net/tools/quic/quic_client.h b/net/tools/quic/quic_client.h
index 4c0e673..a236d4b 100644
--- a/net/tools/quic/quic_client.h
+++ b/net/tools/quic/quic_client.h
@@ -20,9 +20,11 @@
 #include "net/quic/platform/api/quic_containers.h"
 #include "net/tools/epoll_server/epoll_server.h"
 #include "net/tools/quic/quic_client_base.h"
-#include "net/tools/quic/quic_client_session.h"
+#include "net/tools/quic/quic_client_epoll_network_helper.h"
 #include "net/tools/quic/quic_packet_reader.h"
 #include "net/tools/quic/quic_process_packet_interface.h"
+#include "net/tools/quic/quic_spdy_client_base.h"
+#include "net/tools/quic/quic_spdy_client_session.h"
 
 namespace net {
 
@@ -32,93 +34,40 @@
 class QuicClientPeer;
 }  // namespace test
 
-class QuicClient : public QuicClientBase,
-                   public EpollCallbackInterface,
-                   public ProcessPacketInterface {
+class QuicClient : public QuicSpdyClientBase {
  public:
-  // Create a quic client, which will have events managed by an externally owned
-  // EpollServer.
+  // This will create its own QuicClientEpollNetworkHelper.
   QuicClient(QuicSocketAddress server_address,
              const QuicServerId& server_id,
              const QuicVersionVector& supported_versions,
              EpollServer* epoll_server,
              std::unique_ptr<ProofVerifier> proof_verifier);
+  // This will take ownership of a passed in network primitive.
+  QuicClient(QuicSocketAddress server_address,
+             const QuicServerId& server_id,
+             const QuicVersionVector& supported_versions,
+             EpollServer* epoll_server,
+             std::unique_ptr<QuicClientEpollNetworkHelper> network_helper,
+             std::unique_ptr<ProofVerifier> proof_verifier);
   QuicClient(QuicSocketAddress server_address,
              const QuicServerId& server_id,
              const QuicVersionVector& supported_versions,
              const QuicConfig& config,
              EpollServer* epoll_server,
+             std::unique_ptr<QuicClientEpollNetworkHelper> network_helper,
              std::unique_ptr<ProofVerifier> proof_verifier);
 
   ~QuicClient() override;
 
-  // From EpollCallbackInterface
-  void OnRegistration(EpollServer* eps, int fd, int event_mask) override {}
-  void OnModification(int fd, int event_mask) override {}
-  void OnEvent(int fd, EpollEvent* event) override;
-  // |fd_| can be unregistered without the client being disconnected. This
-  // happens in b3m QuicProber where we unregister |fd_| to feed in events to
-  // the client from the SelectServer.
-  void OnUnregistration(int fd, bool replaced) override {}
-  void OnShutdown(EpollServer* eps, int fd) override {}
+  // Exposed for the quic client test.
+  int GetLatestFD() const { return epoll_network_helper()->GetLatestFD(); }
 
-  // If the client has at least one UDP socket, return the latest created one.
-  // Otherwise, return -1.
-  int GetLatestFD() const;
-
-  // From QuicClientBase
-  QuicSocketAddress GetLatestClientAddress() const override;
-
-  // Implements ProcessPacketInterface. This will be called for each received
-  // packet.
-  void ProcessPacket(const QuicSocketAddress& self_address,
-                     const QuicSocketAddress& peer_address,
-                     const QuicReceivedPacket& packet) override;
-
- protected:
-  // From QuicClientBase
-  QuicPacketWriter* CreateQuicPacketWriter() override;
-  void RunEventLoop() override;
-  bool CreateUDPSocketAndBind(QuicSocketAddress server_address,
-                              QuicIpAddress bind_to_address,
-                              int bind_to_port) override;
-  void CleanUpAllUDPSockets() override;
-
-  // If |fd| is an open UDP socket, unregister and close it. Otherwise, do
-  // nothing.
-  virtual void CleanUpUDPSocket(int fd);
-
-  EpollServer* epoll_server() { return epoll_server_; }
-
-  const QuicLinkedHashMap<int, QuicSocketAddress>& fd_address_map() const {
-    return fd_address_map_;
-  }
+  QuicClientEpollNetworkHelper* epoll_network_helper();
+  const QuicClientEpollNetworkHelper* epoll_network_helper() const;
 
  private:
   friend class test::QuicClientPeer;
 
-  // Actually clean up |fd|.
-  void CleanUpUDPSocketImpl(int fd);
-
-  // Listens for events on the client socket.
-  EpollServer* epoll_server_;
-
-  // Map mapping created UDP sockets to their addresses. By using linked hash
-  // map, the order of socket creation can be recorded.
-  QuicLinkedHashMap<int, QuicSocketAddress> fd_address_map_;
-
-  // If overflow_supported_ is true, this will be the number of packets dropped
-  // during the lifetime of the server.
-  QuicPacketCount packets_dropped_;
-
-  // True if the kernel supports SO_RXQ_OVFL, the number of packets dropped
-  // because the socket would otherwise overflow.
-  bool overflow_supported_;
-
-  // Point to a QuicPacketReader object on the heap. The reader allocates more
-  // space than allowed on the stack.
-  std::unique_ptr<QuicPacketReader> packet_reader_;
-
   DISALLOW_COPY_AND_ASSIGN(QuicClient);
 };
 
diff --git a/net/tools/quic/quic_client_base.cc b/net/tools/quic/quic_client_base.cc
index 1a7f087b..408aae63 100644
--- a/net/tools/quic/quic_client_base.cc
+++ b/net/tools/quic/quic_client_base.cc
@@ -16,24 +16,14 @@
 
 namespace net {
 
-void QuicClientBase::ClientQuicDataToResend::Resend() {
-  client_->SendRequest(*headers_, body_, fin_);
-  headers_ = nullptr;
-}
-
-QuicClientBase::QuicDataToResend::QuicDataToResend(
-    std::unique_ptr<SpdyHeaderBlock> headers,
-    QuicStringPiece body,
-    bool fin)
-    : headers_(std::move(headers)), body_(body), fin_(fin) {}
-
-QuicClientBase::QuicDataToResend::~QuicDataToResend() {}
+QuicClientBase::NetworkHelper::~NetworkHelper() {}
 
 QuicClientBase::QuicClientBase(const QuicServerId& server_id,
                                const QuicVersionVector& supported_versions,
                                const QuicConfig& config,
                                QuicConnectionHelperInterface* helper,
                                QuicAlarmFactory* alarm_factory,
+                               std::unique_ptr<NetworkHelper> network_helper,
                                std::unique_ptr<ProofVerifier> proof_verifier)
     : server_id_(server_id),
       initialized_(false),
@@ -48,39 +38,10 @@
       num_sent_client_hellos_(0),
       connection_error_(QUIC_NO_ERROR),
       connected_or_attempting_connect_(false),
-      store_response_(false),
-      latest_response_code_(-1) {}
+      network_helper_(std::move(network_helper)) {}
 
 QuicClientBase::~QuicClientBase() {}
 
-void QuicClientBase::OnClose(QuicSpdyStream* stream) {
-  DCHECK(stream != nullptr);
-  QuicSpdyClientStream* client_stream =
-      static_cast<QuicSpdyClientStream*>(stream);
-
-  const SpdyHeaderBlock& response_headers = client_stream->response_headers();
-  if (response_listener_ != nullptr) {
-    response_listener_->OnCompleteResponse(stream->id(), response_headers,
-                                           client_stream->data());
-  }
-
-  // Store response headers and body.
-  if (store_response_) {
-    auto status = response_headers.find(":status");
-    if (status == response_headers.end() ||
-        !QuicTextUtils::StringToInt(status->second, &latest_response_code_)) {
-      QUIC_LOG(ERROR) << "Invalid response headers";
-    }
-    latest_response_headers_ = response_headers.DebugString();
-    preliminary_response_headers_ =
-        client_stream->preliminary_headers().DebugString();
-    latest_response_header_block_ = response_headers.Clone();
-    latest_response_body_ = client_stream->data();
-    latest_response_trailers_ =
-        client_stream->received_trailers().DebugString();
-  }
-}
-
 bool QuicClientBase::Initialize() {
   num_sent_client_hellos_ = 0;
   num_stateless_rejects_received_ = 0;
@@ -101,7 +62,8 @@
         kSessionMaxRecvWindowSize);
   }
 
-  if (!CreateUDPSocketAndBind(server_address_, bind_to_address_, local_port_)) {
+  if (!network_helper_->CreateUDPSocketAndBind(server_address_,
+                                               bind_to_address_, local_port_)) {
     return false;
   }
 
@@ -143,9 +105,7 @@
 void QuicClientBase::StartConnect() {
   DCHECK(initialized_);
   DCHECK(!connected());
-
-  QuicPacketWriter* writer = CreateQuicPacketWriter();
-
+  QuicPacketWriter* writer = network_helper_->CreateQuicPacketWriter();
   if (connected_or_attempting_connect()) {
     // If the last error was not a stateless reject, then the queued up data
     // does not need to be resent.
@@ -157,19 +117,24 @@
     UpdateStats();
   }
 
-  CreateQuicClientSession(new QuicConnection(
+  session_ = CreateQuicClientSession(new QuicConnection(
       GetNextConnectionId(), server_address(), helper(), alarm_factory(),
       writer,
       /* owns_writer= */ false, Perspective::IS_CLIENT, supported_versions()));
-
+  if (initial_max_packet_length_ != 0) {
+    session()->connection()->SetMaxPacketLength(initial_max_packet_length_);
+  }
   // Reset |writer()| after |session()| so that the old writer outlives the old
   // session.
   set_writer(writer);
-  session()->Initialize();
-  session()->CryptoConnect();
+  InitializeSession();
   set_connected_or_attempting_connect(true);
 }
 
+void QuicClientBase::InitializeSession() {
+  session()->Initialize();
+}
+
 void QuicClientBase::Disconnect() {
   DCHECK(initialized_);
 
@@ -181,7 +146,7 @@
 
   ClearDataToResend();
 
-  CleanUpAllUDPSockets();
+  network_helper_->CleanUpAllUDPSockets();
 
   initialized_ = false;
 }
@@ -190,87 +155,15 @@
   return crypto_config_.proof_verifier();
 }
 
-QuicClientSession* QuicClientBase::CreateQuicClientSession(
-    QuicConnection* connection) {
-  session_.reset(new QuicClientSession(config_, connection, server_id_,
-                                       &crypto_config_, &push_promise_index_));
-  if (initial_max_packet_length_ != 0) {
-    session()->connection()->SetMaxPacketLength(initial_max_packet_length_);
-  }
-  return session_.get();
-}
-
 bool QuicClientBase::EncryptionBeingEstablished() {
   return !session_->IsEncryptionEstablished() &&
          session_->connection()->connected();
 }
 
-void QuicClientBase::SendRequest(const SpdyHeaderBlock& headers,
-                                 QuicStringPiece body,
-                                 bool fin) {
-  QuicClientPushPromiseIndex::TryHandle* handle;
-  QuicAsyncStatus rv = push_promise_index()->Try(headers, this, &handle);
-  if (rv == QUIC_SUCCESS)
-    return;
-
-  if (rv == QUIC_PENDING) {
-    // May need to retry request if asynchronous rendezvous fails.
-    AddPromiseDataToResend(headers, body, fin);
-    return;
-  }
-
-  QuicSpdyClientStream* stream = CreateClientStream();
-  if (stream == nullptr) {
-    QUIC_BUG << "stream creation failed!";
-    return;
-  }
-  stream->SendRequest(headers.Clone(), body, fin);
-  // Record this in case we need to resend.
-  MaybeAddDataToResend(headers, body, fin);
-}
-
-void QuicClientBase::SendRequestAndWaitForResponse(
-    const SpdyHeaderBlock& headers,
-    QuicStringPiece body,
-    bool fin) {
-  SendRequest(headers, body, fin);
-  while (WaitForEvents()) {
-  }
-}
-
-void QuicClientBase::SendRequestsAndWaitForResponse(
-    const std::vector<string>& url_list) {
-  for (size_t i = 0; i < url_list.size(); ++i) {
-    SpdyHeaderBlock headers;
-    if (!SpdyUtils::PopulateHeaderBlockFromUrl(url_list[i], &headers)) {
-      QUIC_BUG << "Unable to create request";
-      continue;
-    }
-    SendRequest(headers, "", true);
-  }
-  while (WaitForEvents()) {
-  }
-}
-
-QuicSpdyClientStream* QuicClientBase::CreateClientStream() {
-  if (!connected()) {
-    return nullptr;
-  }
-
-  auto* stream = static_cast<QuicSpdyClientStream*>(
-      FLAGS_quic_reloadable_flag_quic_refactor_stream_creation
-          ? session_->MaybeCreateOutgoingDynamicStream(kDefaultPriority)
-          : session_->CreateOutgoingDynamicStream(kDefaultPriority));
-  if (stream) {
-    stream->set_visitor(this);
-  }
-  return stream;
-}
-
 bool QuicClientBase::WaitForEvents() {
   DCHECK(connected());
 
-  RunEventLoop();
+  network_helper_->RunEventLoop();
 
   DCHECK(session() != nullptr);
   if (!connected() &&
@@ -289,22 +182,36 @@
     return false;
   }
 
-  CleanUpAllUDPSockets();
+  network_helper_->CleanUpAllUDPSockets();
 
   set_bind_to_address(new_host);
-  if (!CreateUDPSocketAndBind(server_address_, bind_to_address_, local_port_)) {
+  if (!network_helper_->CreateUDPSocketAndBind(server_address_,
+                                               bind_to_address_, local_port_)) {
     return false;
   }
 
-  session()->connection()->SetSelfAddress(GetLatestClientAddress());
+  session()->connection()->SetSelfAddress(
+      network_helper_->GetLatestClientAddress());
 
-  QuicPacketWriter* writer = CreateQuicPacketWriter();
+  QuicPacketWriter* writer = network_helper_->CreateQuicPacketWriter();
   set_writer(writer);
   session()->connection()->SetQuicPacketWriter(writer, false);
 
   return true;
 }
 
+QuicSession* QuicClientBase::session() {
+  return session_.get();
+}
+
+QuicClientBase::NetworkHelper* QuicClientBase::network_helper() {
+  return network_helper_.get();
+}
+
+const QuicClientBase::NetworkHelper* QuicClientBase::network_helper() const {
+  return network_helper_.get();
+}
+
 void QuicClientBase::WaitForStreamToClose(QuicStreamId id) {
   DCHECK(connected());
 
@@ -339,12 +246,12 @@
   // corresponds to the previous connection and should not be used.
   const int current_session_hellos = !connected_or_attempting_connect_
                                          ? 0
-                                         : session_->GetNumSentClientHellos();
+                                         : GetNumSentClientHellosFromSession();
   return num_sent_client_hellos_ + current_session_hellos;
 }
 
 void QuicClientBase::UpdateStats() {
-  num_sent_client_hellos_ += session()->GetNumSentClientHellos();
+  num_sent_client_hellos_ += GetNumSentClientHellosFromSession();
   if (session()->error() == QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT) {
     ++num_stateless_rejects_received_;
   }
@@ -358,7 +265,7 @@
   // statelessly-rejected connection.
   return !connected_or_attempting_connect_
              ? 0
-             : session_->GetNumReceivedServerConfigUpdates();
+             : GetNumReceivedServerConfigUpdatesFromSession();
 }
 
 QuicErrorCode QuicClientBase::connection_error() const {
@@ -395,102 +302,4 @@
   return QuicRandom::GetInstance()->RandUint64();
 }
 
-void QuicClientBase::MaybeAddDataToResend(const SpdyHeaderBlock& headers,
-                                          QuicStringPiece body,
-                                          bool fin) {
-  if (!FLAGS_quic_reloadable_flag_enable_quic_stateless_reject_support) {
-    return;
-  }
-
-  if (session()->IsCryptoHandshakeConfirmed()) {
-    // The handshake is confirmed.  No need to continue saving requests to
-    // resend.
-    data_to_resend_on_connect_.clear();
-    return;
-  }
-
-  // The handshake is not confirmed.  Push the data onto the queue of data to
-  // resend if statelessly rejected.
-  std::unique_ptr<SpdyHeaderBlock> new_headers(
-      new SpdyHeaderBlock(headers.Clone()));
-  std::unique_ptr<QuicDataToResend> data_to_resend(
-      new ClientQuicDataToResend(std::move(new_headers), body, fin, this));
-  MaybeAddQuicDataToResend(std::move(data_to_resend));
-}
-
-void QuicClientBase::MaybeAddQuicDataToResend(
-    std::unique_ptr<QuicDataToResend> data_to_resend) {
-  data_to_resend_on_connect_.push_back(std::move(data_to_resend));
-}
-
-void QuicClientBase::ClearDataToResend() {
-  data_to_resend_on_connect_.clear();
-}
-
-void QuicClientBase::ResendSavedData() {
-  // Calling Resend will re-enqueue the data, so swap out
-  //  data_to_resend_on_connect_ before iterating.
-  std::vector<std::unique_ptr<QuicDataToResend>> old_data;
-  old_data.swap(data_to_resend_on_connect_);
-  for (const auto& data : old_data) {
-    data->Resend();
-  }
-}
-
-void QuicClientBase::AddPromiseDataToResend(const SpdyHeaderBlock& headers,
-                                            QuicStringPiece body,
-                                            bool fin) {
-  std::unique_ptr<SpdyHeaderBlock> new_headers(
-      new SpdyHeaderBlock(headers.Clone()));
-  push_promise_data_to_resend_.reset(
-      new ClientQuicDataToResend(std::move(new_headers), body, fin, this));
-}
-
-bool QuicClientBase::CheckVary(const SpdyHeaderBlock& client_request,
-                               const SpdyHeaderBlock& promise_request,
-                               const SpdyHeaderBlock& promise_response) {
-  return true;
-}
-
-void QuicClientBase::OnRendezvousResult(QuicSpdyStream* stream) {
-  std::unique_ptr<ClientQuicDataToResend> data_to_resend =
-      std::move(push_promise_data_to_resend_);
-  if (stream) {
-    stream->set_visitor(this);
-    stream->OnDataAvailable();
-  } else if (data_to_resend.get()) {
-    data_to_resend->Resend();
-  }
-}
-
-size_t QuicClientBase::latest_response_code() const {
-  QUIC_BUG_IF(!store_response_) << "Response not stored!";
-  return latest_response_code_;
-}
-
-const string& QuicClientBase::latest_response_headers() const {
-  QUIC_BUG_IF(!store_response_) << "Response not stored!";
-  return latest_response_headers_;
-}
-
-const string& QuicClientBase::preliminary_response_headers() const {
-  QUIC_BUG_IF(!store_response_) << "Response not stored!";
-  return preliminary_response_headers_;
-}
-
-const SpdyHeaderBlock& QuicClientBase::latest_response_header_block() const {
-  QUIC_BUG_IF(!store_response_) << "Response not stored!";
-  return latest_response_header_block_;
-}
-
-const string& QuicClientBase::latest_response_body() const {
-  QUIC_BUG_IF(!store_response_) << "Response not stored!";
-  return latest_response_body_;
-}
-
-const string& QuicClientBase::latest_response_trailers() const {
-  QUIC_BUG_IF(!store_response_) << "Response not stored!";
-  return latest_response_trailers_;
-}
-
 }  // namespace net
diff --git a/net/tools/quic/quic_client_base.h b/net/tools/quic/quic_client_base.h
index c95a203..8252e1a5 100644
--- a/net/tools/quic/quic_client_base.h
+++ b/net/tools/quic/quic_client_base.h
@@ -16,7 +16,7 @@
 #include "net/quic/core/quic_config.h"
 #include "net/quic/platform/api/quic_socket_address.h"
 #include "net/quic/platform/api/quic_string_piece.h"
-#include "net/tools/quic/quic_client_session.h"
+#include "net/tools/quic/quic_spdy_client_session.h"
 #include "net/tools/quic/quic_spdy_client_stream.h"
 
 namespace net {
@@ -24,46 +24,38 @@
 class ProofVerifier;
 class QuicServerId;
 
-class QuicClientBase : public QuicClientPushPromiseIndex::Delegate,
-                       public QuicSpdyStream::Visitor {
+// QuicClientBase handles establishing a connection to the passed in
+// server id, including ensuring that it supports the passed in versions
+// and config.
+// Subclasses derived from this class are responsible for creating the
+// actual QuicSession instance, as well as defining functions that
+// create and run the underlying network transport.
+class QuicClientBase {
  public:
-  // A ResponseListener is notified when a complete response is received.
-  class ResponseListener {
+  // An interface to various network events that the QuicClient will need to
+  // interact with.
+  class NetworkHelper {
    public:
-    ResponseListener() {}
-    virtual ~ResponseListener() {}
-    virtual void OnCompleteResponse(QuicStreamId id,
-                                    const SpdyHeaderBlock& response_headers,
-                                    const std::string& response_body) = 0;
-  };
+    virtual ~NetworkHelper();
 
-  // The client uses these objects to keep track of any data to resend upon
-  // receipt of a stateless reject.  Recall that the client API allows callers
-  // to optimistically send data to the server prior to handshake-confirmation.
-  // If the client subsequently receives a stateless reject, it must tear down
-  // its existing session, create a new session, and resend all previously sent
-  // data.  It uses these objects to keep track of all the sent data, and to
-  // resend the data upon a subsequent connection.
-  class QuicDataToResend {
-   public:
-    // |headers| may be null, since it's possible to send data without headers.
-    QuicDataToResend(std::unique_ptr<SpdyHeaderBlock> headers,
-                     QuicStringPiece body,
-                     bool fin);
+    // Runs one iteration of the event loop.
+    virtual void RunEventLoop() = 0;
 
-    virtual ~QuicDataToResend();
+    // Used during initialization: creates the UDP socket FD, sets socket
+    // options, and binds the socket to our address.
+    virtual bool CreateUDPSocketAndBind(QuicSocketAddress server_address,
+                                        QuicIpAddress bind_to_address,
+                                        int bind_to_port) = 0;
 
-    // Must be overridden by specific classes with the actual method for
-    // re-sending data.
-    virtual void Resend() = 0;
+    // Unregister and close all open UDP sockets.
+    virtual void CleanUpAllUDPSockets() = 0;
 
-   protected:
-    std::unique_ptr<SpdyHeaderBlock> headers_;
-    QuicStringPiece body_;
-    bool fin_;
+    // If the client has at least one UDP socket, return address of the latest
+    // created one. Otherwise, return an empty socket address.
+    virtual QuicSocketAddress GetLatestClientAddress() const = 0;
 
-   private:
-    DISALLOW_COPY_AND_ASSIGN(QuicDataToResend);
+    // Creates a packet writer to be used for the next connection.
+    virtual QuicPacketWriter* CreateQuicPacketWriter() = 0;
   };
 
   QuicClientBase(const QuicServerId& server_id,
@@ -71,12 +63,10 @@
                  const QuicConfig& config,
                  QuicConnectionHelperInterface* helper,
                  QuicAlarmFactory* alarm_factory,
+                 std::unique_ptr<NetworkHelper> network_helper,
                  std::unique_ptr<ProofVerifier> proof_verifier);
 
-  ~QuicClientBase() override;
-
-  // QuicSpdyStream::Visitor
-  void OnClose(QuicSpdyStream* stream) override;
+  virtual ~QuicClientBase();
 
   // Initializes the client to create a connection. Should be called exactly
   // once before calling StartConnect or Connect. Returns true if the
@@ -92,6 +82,11 @@
   // completes.
   void StartConnect();
 
+  // Calls session()->Initialize(). Subclasses may override this if any extra
+  // initialization needs to be done. Subclasses should expect that session()
+  // is non-null and valid.
+  virtual void InitializeSession();
+
   // Disconnects from the QUIC server.
   void Disconnect();
 
@@ -100,24 +95,6 @@
   // the handshake) or if the connection has been closed.
   bool EncryptionBeingEstablished();
 
-  // Sends an HTTP request and does not wait for response before returning.
-  void SendRequest(const SpdyHeaderBlock& headers,
-                   QuicStringPiece body,
-                   bool fin);
-
-  // Sends an HTTP request and waits for response before returning.
-  void SendRequestAndWaitForResponse(const SpdyHeaderBlock& headers,
-                                     QuicStringPiece body,
-                                     bool fin);
-
-  // Sends a request simple GET for each URL in |url_list|, and then waits for
-  // each to complete.
-  void SendRequestsAndWaitForResponse(const std::vector<std::string>& url_list);
-
-  // Returns a newly created QuicSpdyClientStream, owned by the
-  // QuicSimpleClient.
-  virtual QuicSpdyClientStream* CreateClientStream();
-
   // Wait for events until the stream with the given ID is closed.
   void WaitForStreamToClose(QuicStreamId id);
 
@@ -132,7 +109,7 @@
   // Migrate to a new socket during an active connection.
   bool MigrateSocket(const QuicIpAddress& new_host);
 
-  QuicClientSession* session() { return session_.get(); }
+  QuicSession* session();
 
   bool connected() const;
   bool goaway_received() const;
@@ -235,36 +212,6 @@
 
   ProofVerifier* proof_verifier() const;
 
-  void set_session(QuicClientSession* session) { session_.reset(session); }
-
-  QuicClientPushPromiseIndex* push_promise_index() {
-    return &push_promise_index_;
-  }
-
-  bool CheckVary(const SpdyHeaderBlock& client_request,
-                 const SpdyHeaderBlock& promise_request,
-                 const SpdyHeaderBlock& promise_response) override;
-  void OnRendezvousResult(QuicSpdyStream*) override;
-
-  // If the crypto handshake has not yet been confirmed, adds the data to the
-  // queue of data to resend if the client receives a stateless reject.
-  // Otherwise, deletes the data.
-  void MaybeAddQuicDataToResend(
-      std::unique_ptr<QuicDataToResend> data_to_resend);
-
-  void set_store_response(bool val) { store_response_ = val; }
-
-  size_t latest_response_code() const;
-  const std::string& latest_response_headers() const;
-  const std::string& preliminary_response_headers() const;
-  const SpdyHeaderBlock& latest_response_header_block() const;
-  const std::string& latest_response_body() const;
-  const std::string& latest_response_trailers() const;
-
-  void set_response_listener(std::unique_ptr<ResponseListener> listener) {
-    response_listener_ = std::move(listener);
-  }
-
   void set_bind_to_address(QuicIpAddress address) {
     bind_to_address_ = address;
   }
@@ -281,29 +228,39 @@
     server_address_ = server_address;
   }
 
+  QuicConnectionHelperInterface* helper() { return helper_.get(); }
+
+  NetworkHelper* network_helper();
+  const NetworkHelper* network_helper() const;
+
  protected:
-  // Creates a packet writer to be used for the next connection.
-  virtual QuicPacketWriter* CreateQuicPacketWriter() = 0;
+  // TODO(rch): Move GetNumSentClientHellosFromSession and
+  // GetNumReceivedServerConfigUpdatesFromSession into a new/better
+  // QuicSpdyClientSession class. The current inherits dependencies from
+  // Spdy. When that happens this class and all its subclasses should
+  // work with QuicSpdyClientSession instead of QuicSession.
+  // That will obviate the need for the pure virtual functions below.
 
-  // Takes ownership of |connection|.
-  virtual QuicClientSession* CreateQuicClientSession(
-      QuicConnection* connection);
+  // Extract the number of sent client hellos from the session.
+  virtual int GetNumSentClientHellosFromSession() = 0;
 
-  // Runs one iteration of the event loop.
-  virtual void RunEventLoop() = 0;
+  // The number of server config updates received.  We assume no
+  // updates can be sent during a previously, statelessly rejected
+  // connection, so only the latest session is taken into account.
+  virtual int GetNumReceivedServerConfigUpdatesFromSession() = 0;
 
-  // Used during initialization: creates the UDP socket FD, sets socket options,
-  // and binds the socket to our address.
-  virtual bool CreateUDPSocketAndBind(QuicSocketAddress server_address,
-                                      QuicIpAddress bind_to_address,
-                                      int bind_to_port) = 0;
+  // If this client supports buffering data, resend it.
+  virtual void ResendSavedData() = 0;
 
-  // Unregister and close all open UDP sockets.
-  virtual void CleanUpAllUDPSockets() = 0;
+  // If this client supports buffering data, clear it.
+  virtual void ClearDataToResend() = 0;
 
-  // If the client has at least one UDP socket, return address of the latest
-  // created one. Otherwise, return an empty socket address.
-  virtual QuicSocketAddress GetLatestClientAddress() const = 0;
+  // Takes ownership of |connection|. If you override this function,
+  // you probably want to call ResetSession() in your destructor.
+  // TODO(rch): Change the connection parameter to take in a
+  // std::unique_ptr<QuicConnection> instead.
+  virtual std::unique_ptr<QuicSession> CreateQuicClientSession(
+      QuicConnection* connection) = 0;
 
   // Generates the next ConnectionId for |server_id_|.  By default, if the
   // cached server config contains a server-designated ID, that ID will be
@@ -318,23 +275,6 @@
   // connection ID).
   virtual QuicConnectionId GenerateNewConnectionId();
 
-  // If the crypto handshake has not yet been confirmed, adds the data to the
-  // queue of data to resend if the client receives a stateless reject.
-  // Otherwise, deletes the data.
-  void MaybeAddDataToResend(const SpdyHeaderBlock& headers,
-                            QuicStringPiece body,
-                            bool fin);
-
-  void ClearDataToResend();
-
-  void ResendSavedData();
-
-  void AddPromiseDataToResend(const SpdyHeaderBlock& headers,
-                              QuicStringPiece body,
-                              bool fin);
-
-  QuicConnectionHelperInterface* helper() { return helper_.get(); }
-
   QuicAlarmFactory* alarm_factory() { return alarm_factory_.get(); }
 
   void set_num_sent_client_hellos(int num_sent_client_hellos) {
@@ -345,29 +285,12 @@
     num_stateless_rejects_received_ = num_stateless_rejects_received;
   }
 
+  // Subclasses may need to explicitly clear the session on destruction
+  // if they create it with objects that will be destroyed before this is.
+  // You probably want to call this if you override CreateQuicSpdyClientSession.
+  void ResetSession() { session_.reset(); }
+
  private:
-  // Specific QuicClient class for storing data to resend.
-  class ClientQuicDataToResend : public QuicDataToResend {
-   public:
-    ClientQuicDataToResend(std::unique_ptr<SpdyHeaderBlock> headers,
-                           QuicStringPiece body,
-                           bool fin,
-                           QuicClientBase* client)
-        : QuicDataToResend(std::move(headers), body, fin), client_(client) {
-      DCHECK(headers_);
-      DCHECK(client);
-    }
-
-    ~ClientQuicDataToResend() override {}
-
-    void Resend() override;
-
-   private:
-    QuicClientBase* client_;
-
-    DISALLOW_COPY_AND_ASSIGN(ClientQuicDataToResend);
-  };
-
   // |server_id_| is a tuple (hostname, port, is_https) of the server.
   QuicServerId server_id_;
 
@@ -397,11 +320,8 @@
   // Writer used to actually send packets to the wire. Must outlive |session_|.
   std::unique_ptr<QuicPacketWriter> writer_;
 
-  // Index of pending promised streams. Must outlive |session_|.
-  QuicClientPushPromiseIndex push_promise_index_;
-
   // Session which manages streams.
-  std::unique_ptr<QuicClientSession> session_;
+  std::unique_ptr<QuicSession> session_;
 
   // This vector contains QUIC versions which we currently support.
   // This should be ordered such that the highest supported version is the first
@@ -435,29 +355,9 @@
   // to the previous client-level connection.
   bool connected_or_attempting_connect_;
 
-  // If true, store the latest response code, headers, and body.
-  bool store_response_;
-  // HTTP response code from most recent response.
-  int latest_response_code_;
-  // HTTP/2 headers from most recent response.
-  std::string latest_response_headers_;
-  // preliminary 100 Continue HTTP/2 headers from most recent response, if any.
-  std::string preliminary_response_headers_;
-  // HTTP/2 headers from most recent response.
-  SpdyHeaderBlock latest_response_header_block_;
-  // Body of most recent response.
-  std::string latest_response_body_;
-  // HTTP/2 trailers from most recent response.
-  std::string latest_response_trailers_;
-
-  // Listens for full responses.
-  std::unique_ptr<ResponseListener> response_listener_;
-
-  // Keeps track of any data that must be resent upon a subsequent successful
-  // connection, in case the client receives a stateless reject.
-  std::vector<std::unique_ptr<QuicDataToResend>> data_to_resend_on_connect_;
-
-  std::unique_ptr<ClientQuicDataToResend> push_promise_data_to_resend_;
+  // The network helper used to create sockets and manage the event loop.
+  // Not owned by this class.
+  std::unique_ptr<NetworkHelper> network_helper_;
 
   DISALLOW_COPY_AND_ASSIGN(QuicClientBase);
 };
diff --git a/net/tools/quic/quic_client_epoll_network_helper.cc b/net/tools/quic/quic_client_epoll_network_helper.cc
new file mode 100644
index 0000000..57b064b
--- /dev/null
+++ b/net/tools/quic/quic_client_epoll_network_helper.cc
@@ -0,0 +1,185 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/tools/quic/quic_client_epoll_network_helper.h"
+
+#include <errno.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include "base/run_loop.h"
+#include "net/quic/core/crypto/quic_random.h"
+#include "net/quic/core/quic_connection.h"
+#include "net/quic/core/quic_data_reader.h"
+#include "net/quic/core/quic_packets.h"
+#include "net/quic/core/quic_server_id.h"
+#include "net/quic/core/spdy_utils.h"
+#include "net/quic/platform/api/quic_bug_tracker.h"
+#include "net/quic/platform/api/quic_logging.h"
+#include "net/quic/platform/api/quic_ptr_util.h"
+#include "net/tools/quic/platform/impl/quic_socket_utils.h"
+#include "net/tools/quic/quic_epoll_alarm_factory.h"
+#include "net/tools/quic/quic_epoll_connection_helper.h"
+
+#ifndef SO_RXQ_OVFL
+#define SO_RXQ_OVFL 40
+#endif
+
+// TODO(rtenneti): Add support for MMSG_MORE.
+#define MMSG_MORE 0
+using std::string;
+
+namespace net {
+
+namespace {
+const int kEpollFlags = EPOLLIN | EPOLLOUT | EPOLLET;
+}  // namespace
+
+QuicClientEpollNetworkHelper::QuicClientEpollNetworkHelper(
+    EpollServer* epoll_server,
+    QuicClientBase* client)
+    : epoll_server_(epoll_server),
+      packets_dropped_(0),
+      overflow_supported_(false),
+      packet_reader_(new QuicPacketReader()),
+      client_(client) {}
+
+QuicClientEpollNetworkHelper::~QuicClientEpollNetworkHelper() {
+  if (client_->connected()) {
+    client_->session()->connection()->CloseConnection(
+        QUIC_PEER_GOING_AWAY, "Client being torn down",
+        ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+  }
+
+  CleanUpAllUDPSockets();
+}
+
+bool QuicClientEpollNetworkHelper::CreateUDPSocketAndBind(
+    QuicSocketAddress server_address,
+    QuicIpAddress bind_to_address,
+    int bind_to_port) {
+  epoll_server_->set_timeout_in_us(50 * 1000);
+
+  int fd =
+      QuicSocketUtils::CreateUDPSocket(server_address, &overflow_supported_);
+  if (fd < 0) {
+    return false;
+  }
+
+  QuicSocketAddress client_address;
+  if (bind_to_address.IsInitialized()) {
+    client_address = QuicSocketAddress(bind_to_address, client_->local_port());
+  } else if (server_address.host().address_family() == IpAddressFamily::IP_V4) {
+    client_address = QuicSocketAddress(QuicIpAddress::Any4(), bind_to_port);
+  } else {
+    client_address = QuicSocketAddress(QuicIpAddress::Any6(), bind_to_port);
+  }
+
+  sockaddr_storage addr = client_address.generic_address();
+  int rc = bind(fd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
+  if (rc < 0) {
+    QUIC_LOG(ERROR) << "Bind failed: " << strerror(errno);
+    return false;
+  }
+
+  if (client_address.FromSocket(fd) != 0) {
+    QUIC_LOG(ERROR) << "Unable to get self address.  Error: "
+                    << strerror(errno);
+  }
+
+  fd_address_map_[fd] = client_address;
+
+  epoll_server_->RegisterFD(fd, this, kEpollFlags);
+  return true;
+}
+
+void QuicClientEpollNetworkHelper::CleanUpUDPSocket(int fd) {
+  CleanUpUDPSocketImpl(fd);
+  fd_address_map_.erase(fd);
+}
+
+void QuicClientEpollNetworkHelper::CleanUpAllUDPSockets() {
+  for (std::pair<int, QuicSocketAddress> fd_address : fd_address_map_) {
+    CleanUpUDPSocketImpl(fd_address.first);
+  }
+  fd_address_map_.clear();
+}
+
+void QuicClientEpollNetworkHelper::CleanUpUDPSocketImpl(int fd) {
+  if (fd > -1) {
+    epoll_server_->UnregisterFD(fd);
+    int rc = close(fd);
+    DCHECK_EQ(0, rc);
+  }
+}
+
+void QuicClientEpollNetworkHelper::RunEventLoop() {
+  base::RunLoop().RunUntilIdle();
+  epoll_server_->WaitForEventsAndExecuteCallbacks();
+}
+
+void QuicClientEpollNetworkHelper::OnRegistration(EpollServer* eps,
+                                                  int fd,
+                                                  int event_mask) {}
+void QuicClientEpollNetworkHelper::OnModification(int fd, int event_mask) {}
+void QuicClientEpollNetworkHelper::OnUnregistration(int fd, bool replaced) {}
+void QuicClientEpollNetworkHelper::OnShutdown(EpollServer* eps, int fd) {}
+
+void QuicClientEpollNetworkHelper::OnEvent(int fd, EpollEvent* event) {
+  DCHECK_EQ(fd, GetLatestFD());
+
+  if (event->in_events & EPOLLIN) {
+    bool more_to_read = true;
+    while (client_->connected() && more_to_read) {
+      more_to_read = packet_reader_->ReadAndDispatchPackets(
+          GetLatestFD(), GetLatestClientAddress().port(),
+          *client_->helper()->GetClock(), this,
+          overflow_supported_ ? &packets_dropped_ : nullptr);
+    }
+  }
+  if (client_->connected() && (event->in_events & EPOLLOUT)) {
+    client_->writer()->SetWritable();
+    client_->session()->connection()->OnCanWrite();
+  }
+  if (event->in_events & EPOLLERR) {
+    QUIC_DLOG(INFO) << "Epollerr";
+  }
+}
+
+QuicPacketWriter* QuicClientEpollNetworkHelper::CreateQuicPacketWriter() {
+  return new QuicDefaultPacketWriter(GetLatestFD());
+}
+
+void QuicClientEpollNetworkHelper::SetClientPort(int port) {
+  fd_address_map_.back().second =
+      QuicSocketAddress(GetLatestClientAddress().host(), port);
+}
+
+QuicSocketAddress QuicClientEpollNetworkHelper::GetLatestClientAddress() const {
+  if (fd_address_map_.empty()) {
+    return QuicSocketAddress();
+  }
+
+  return fd_address_map_.back().second;
+}
+
+int QuicClientEpollNetworkHelper::GetLatestFD() const {
+  if (fd_address_map_.empty()) {
+    return -1;
+  }
+
+  return fd_address_map_.back().first;
+}
+
+void QuicClientEpollNetworkHelper::ProcessPacket(
+    const QuicSocketAddress& self_address,
+    const QuicSocketAddress& peer_address,
+    const QuicReceivedPacket& packet) {
+  client_->session()->ProcessUdpPacket(self_address, peer_address, packet);
+}
+
+}  // namespace net
diff --git a/net/tools/quic/quic_client_epoll_network_helper.h b/net/tools/quic/quic_client_epoll_network_helper.h
new file mode 100644
index 0000000..bc2fa1ba
--- /dev/null
+++ b/net/tools/quic/quic_client_epoll_network_helper.h
@@ -0,0 +1,125 @@
+// Copyright (c) 2012 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.
+//
+// An implementation of the QuicClientBase::NetworkHelper
+// that is based off the epoll server.
+
+#ifndef NET_TOOLS_QUIC_QUIC_CLIENT_EPOLL_NETWORK_HELPER_H_
+#define NET_TOOLS_QUIC_QUIC_CLIENT_EPOLL_NETWORK_HELPER_H_
+
+#include <cstdint>
+#include <memory>
+#include <string>
+
+#include "base/command_line.h"
+#include "base/macros.h"
+#include "net/quic/core/quic_client_push_promise_index.h"
+#include "net/quic/core/quic_config.h"
+#include "net/quic/core/quic_spdy_stream.h"
+#include "net/quic/platform/api/quic_containers.h"
+#include "net/tools/epoll_server/epoll_server.h"
+#include "net/tools/quic/quic_client_base.h"
+#include "net/tools/quic/quic_packet_reader.h"
+#include "net/tools/quic/quic_process_packet_interface.h"
+#include "net/tools/quic/quic_spdy_client_base.h"
+#include "net/tools/quic/quic_spdy_client_session.h"
+
+namespace net {
+
+namespace test {
+class QuicClientPeer;
+}  // namespace test
+
+// An implementation of the QuicClientBase::NetworkHelper based off
+// the epoll server.
+class QuicClientEpollNetworkHelper : public QuicClientBase::NetworkHelper,
+                                     public EpollCallbackInterface,
+                                     public ProcessPacketInterface {
+ public:
+  // Create a quic client, which will have events managed by an externally owned
+  // EpollServer.
+  QuicClientEpollNetworkHelper(EpollServer* epoll_server,
+                               QuicClientBase* client);
+
+  ~QuicClientEpollNetworkHelper() override;
+
+  // From EpollCallbackInterface
+  void OnRegistration(EpollServer* eps, int fd, int event_mask) override;
+  void OnModification(int fd, int event_mask) override;
+  void OnEvent(int fd, EpollEvent* event) override;
+  // |fd_| can be unregistered without the client being disconnected. This
+  // happens in b3m QuicProber where we unregister |fd_| to feed in events to
+  // the client from the SelectServer.
+  void OnUnregistration(int fd, bool replaced) override;
+  void OnShutdown(EpollServer* eps, int fd) override;
+
+  // From ProcessPacketInterface. This will be called for each received
+  // packet.
+  void ProcessPacket(const QuicSocketAddress& self_address,
+                     const QuicSocketAddress& peer_address,
+                     const QuicReceivedPacket& packet) override;
+
+  // From NetworkHelper.
+  void RunEventLoop() override;
+  bool CreateUDPSocketAndBind(QuicSocketAddress server_address,
+                              QuicIpAddress bind_to_address,
+                              int bind_to_port) override;
+  void CleanUpAllUDPSockets() override;
+  QuicSocketAddress GetLatestClientAddress() const override;
+  QuicPacketWriter* CreateQuicPacketWriter() override;
+
+  // Accessors provided for convenience, not part of any interface.
+
+  EpollServer* epoll_server() { return epoll_server_; }
+
+  const QuicLinkedHashMap<int, QuicSocketAddress>& fd_address_map() const {
+    return fd_address_map_;
+  }
+
+  // If the client has at least one UDP socket, return the latest created one.
+  // Otherwise, return -1.
+  int GetLatestFD() const;
+
+  QuicClientBase* client() { return client_; }
+
+ private:
+  friend class test::QuicClientPeer;
+
+  // Used for testing.
+  void SetClientPort(int port);
+
+  // If |fd| is an open UDP socket, unregister and close it. Otherwise, do
+  // nothing.
+  void CleanUpUDPSocket(int fd);
+
+  // Actually clean up |fd|.
+  void CleanUpUDPSocketImpl(int fd);
+
+  // Listens for events on the client socket.
+  EpollServer* epoll_server_;
+
+  // Map mapping created UDP sockets to their addresses. By using linked hash
+  // map, the order of socket creation can be recorded.
+  QuicLinkedHashMap<int, QuicSocketAddress> fd_address_map_;
+
+  // If overflow_supported_ is true, this will be the number of packets dropped
+  // during the lifetime of the server.
+  QuicPacketCount packets_dropped_;
+
+  // True if the kernel supports SO_RXQ_OVFL, the number of packets dropped
+  // because the socket would otherwise overflow.
+  bool overflow_supported_;
+
+  // Point to a QuicPacketReader object on the heap. The reader allocates more
+  // space than allowed on the stack.
+  std::unique_ptr<QuicPacketReader> packet_reader_;
+
+  QuicClientBase* client_;
+
+  DISALLOW_COPY_AND_ASSIGN(QuicClientEpollNetworkHelper);
+};
+
+}  // namespace net
+
+#endif  // NET_TOOLS_QUIC_QUIC_CLIENT_EPOLL_NETWORK_HELPER_H_
diff --git a/net/tools/quic/quic_client_message_loop_network_helper.cc b/net/tools/quic/quic_client_message_loop_network_helper.cc
new file mode 100644
index 0000000..053979fa
--- /dev/null
+++ b/net/tools/quic/quic_client_message_loop_network_helper.cc
@@ -0,0 +1,146 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/tools/quic/quic_client_message_loop_network_helper.h"
+
+#include <utility>
+
+#include "base/logging.h"
+#include "base/run_loop.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_request_info.h"
+#include "net/http/http_response_info.h"
+#include "net/log/net_log_source.h"
+#include "net/log/net_log_with_source.h"
+#include "net/quic/chromium/quic_chromium_alarm_factory.h"
+#include "net/quic/chromium/quic_chromium_connection_helper.h"
+#include "net/quic/chromium/quic_chromium_packet_reader.h"
+#include "net/quic/chromium/quic_chromium_packet_writer.h"
+#include "net/quic/core/crypto/quic_random.h"
+#include "net/quic/core/quic_connection.h"
+#include "net/quic/core/quic_packets.h"
+#include "net/quic/core/quic_server_id.h"
+#include "net/quic/core/spdy_utils.h"
+#include "net/quic/platform/api/quic_flags.h"
+#include "net/quic/platform/api/quic_ptr_util.h"
+#include "net/socket/udp_client_socket.h"
+#include "net/spdy/chromium/spdy_http_utils.h"
+#include "net/spdy/core/spdy_header_block.h"
+
+using std::string;
+
+namespace net {
+
+QuicClientMessageLooplNetworkHelper::QuicClientMessageLooplNetworkHelper(
+    QuicChromiumClock* clock,
+    QuicClientBase* client)
+    : packet_reader_started_(false), clock_(clock), client_(client) {}
+
+QuicClientMessageLooplNetworkHelper::~QuicClientMessageLooplNetworkHelper() {}
+
+bool QuicClientMessageLooplNetworkHelper::CreateUDPSocketAndBind(
+    QuicSocketAddress server_address,
+    QuicIpAddress bind_to_address,
+    int bind_to_port) {
+  std::unique_ptr<UDPClientSocket> socket(
+      new UDPClientSocket(DatagramSocket::DEFAULT_BIND, RandIntCallback(),
+                          &net_log_, NetLogSource()));
+
+  if (bind_to_address.IsInitialized()) {
+    client_address_ = QuicSocketAddress(bind_to_address, client_->local_port());
+  } else if (server_address.host().address_family() == IpAddressFamily::IP_V4) {
+    client_address_ = QuicSocketAddress(QuicIpAddress::Any4(), bind_to_port);
+  } else {
+    client_address_ = QuicSocketAddress(QuicIpAddress::Any6(), bind_to_port);
+  }
+
+  int rc = socket->Connect(server_address.impl().socket_address());
+  if (rc != OK) {
+    LOG(ERROR) << "Connect failed: " << ErrorToShortString(rc);
+    return false;
+  }
+
+  rc = socket->SetReceiveBufferSize(kDefaultSocketReceiveBuffer);
+  if (rc != OK) {
+    LOG(ERROR) << "SetReceiveBufferSize() failed: " << ErrorToShortString(rc);
+    return false;
+  }
+
+  rc = socket->SetSendBufferSize(kDefaultSocketReceiveBuffer);
+  if (rc != OK) {
+    LOG(ERROR) << "SetSendBufferSize() failed: " << ErrorToShortString(rc);
+    return false;
+  }
+
+  IPEndPoint address;
+  rc = socket->GetLocalAddress(&address);
+  if (rc != OK) {
+    LOG(ERROR) << "GetLocalAddress failed: " << ErrorToShortString(rc);
+    return false;
+  }
+  client_address_ = QuicSocketAddress(QuicSocketAddressImpl(address));
+
+  socket_.swap(socket);
+  packet_reader_.reset(new QuicChromiumPacketReader(
+      socket_.get(), clock_, this, kQuicYieldAfterPacketsRead,
+      QuicTime::Delta::FromMilliseconds(kQuicYieldAfterDurationMilliseconds),
+      NetLogWithSource()));
+
+  if (socket != nullptr) {
+    socket->Close();
+  }
+
+  return true;
+}
+
+void QuicClientMessageLooplNetworkHelper::CleanUpAllUDPSockets() {
+  client_->reset_writer();
+  packet_reader_.reset();
+  packet_reader_started_ = false;
+}
+
+void QuicClientMessageLooplNetworkHelper::StartPacketReaderIfNotStarted() {
+  if (!packet_reader_started_) {
+    packet_reader_->StartReading();
+    packet_reader_started_ = true;
+  }
+}
+
+void QuicClientMessageLooplNetworkHelper::RunEventLoop() {
+  StartPacketReaderIfNotStarted();
+  base::RunLoop().RunUntilIdle();
+}
+
+QuicPacketWriter*
+QuicClientMessageLooplNetworkHelper::CreateQuicPacketWriter() {
+  return new QuicChromiumPacketWriter(socket_.get());
+}
+
+void QuicClientMessageLooplNetworkHelper::OnReadError(
+    int result,
+    const DatagramClientSocket* socket) {
+  LOG(ERROR) << "QuicSimpleClient read failed: " << ErrorToShortString(result);
+  client_->Disconnect();
+}
+
+QuicSocketAddress QuicClientMessageLooplNetworkHelper::GetLatestClientAddress()
+    const {
+  return client_address_;
+}
+
+bool QuicClientMessageLooplNetworkHelper::OnPacket(
+    const QuicReceivedPacket& packet,
+    const QuicSocketAddress& local_address,
+    const QuicSocketAddress& peer_address) {
+  client_->session()->connection()->ProcessUdpPacket(local_address,
+                                                     peer_address, packet);
+  if (!client_->session()->connection()->connected()) {
+    return false;
+  }
+
+  return true;
+}
+
+}  // namespace net
diff --git a/net/tools/quic/quic_client_message_loop_network_helper.h b/net/tools/quic/quic_client_message_loop_network_helper.h
new file mode 100644
index 0000000..891f19f
--- /dev/null
+++ b/net/tools/quic/quic_client_message_loop_network_helper.h
@@ -0,0 +1,84 @@
+// Copyright (c) 2012 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.
+//
+// A toy client, which connects to a specified port and sends QUIC
+// request to that endpoint.
+
+#ifndef NET_TOOLS_QUIC_QUIC_CLIENT_MESSAGE_LOOP_NETWORK_HELPER_H_
+#define NET_TOOLS_QUIC_QUIC_CLIENT_MESSAGE_LOOP_NETWORK_HELPER_H_
+
+#include <stddef.h>
+
+#include <memory>
+#include <string>
+
+#include "base/command_line.h"
+#include "base/macros.h"
+#include "net/base/ip_address.h"
+#include "net/base/ip_endpoint.h"
+#include "net/http/http_response_headers.h"
+#include "net/log/net_log.h"
+#include "net/quic/chromium/quic_chromium_packet_reader.h"
+#include "net/quic/core/quic_config.h"
+#include "net/quic/core/quic_spdy_stream.h"
+#include "net/quic/platform/impl/quic_chromium_clock.h"
+#include "net/tools/quic/quic_spdy_client_base.h"
+
+namespace net {
+
+class UDPClientSocket;
+
+// An implementation of the QuicClientBase::NetworkHelper based off
+// the chromium epoll server.
+class QuicClientMessageLooplNetworkHelper
+    : public QuicClientBase::NetworkHelper,
+      public QuicChromiumPacketReader::Visitor {
+ public:
+  // Create a quic client, which will have events managed by an externally owned
+  // EpollServer.
+  QuicClientMessageLooplNetworkHelper(QuicChromiumClock* clock,
+                                      QuicClientBase* client);
+
+  ~QuicClientMessageLooplNetworkHelper() override;
+
+  // QuicChromiumPacketReader::Visitor
+  void OnReadError(int result, const DatagramClientSocket* socket) override;
+  bool OnPacket(const QuicReceivedPacket& packet,
+                const QuicSocketAddress& local_address,
+                const QuicSocketAddress& peer_address) override;
+
+  // From NetworkHelper.
+  void RunEventLoop() override;
+  bool CreateUDPSocketAndBind(QuicSocketAddress server_address,
+                              QuicIpAddress bind_to_address,
+                              int bind_to_port) override;
+  void CleanUpAllUDPSockets() override;
+  QuicSocketAddress GetLatestClientAddress() const override;
+  QuicPacketWriter* CreateQuicPacketWriter() override;
+
+ private:
+  void StartPacketReaderIfNotStarted();
+
+  // Address of the client if the client is connected to the server.
+  QuicSocketAddress client_address_;
+
+  // UDP socket connected to the server.
+  std::unique_ptr<UDPClientSocket> socket_;
+
+  // The log used for the sockets.
+  NetLog net_log_;
+
+  std::unique_ptr<QuicChromiumPacketReader> packet_reader_;
+
+  bool packet_reader_started_;
+
+  QuicChromiumClock* clock_;
+  QuicClientBase* client_;
+
+  DISALLOW_COPY_AND_ASSIGN(QuicClientMessageLooplNetworkHelper);
+};
+
+}  // namespace net
+
+#endif  // NET_TOOLS_QUIC_QUIC_CLIENT_MESSAGE_LOOP_NETWORK_HELPER_H_
diff --git a/net/tools/quic/quic_dispatcher.cc b/net/tools/quic/quic_dispatcher.cc
index e322b68..0fec950 100644
--- a/net/tools/quic/quic_dispatcher.cc
+++ b/net/tools/quic/quic_dispatcher.cc
@@ -1003,7 +1003,10 @@
       break;
 
     case StatelessRejector::REJECTED: {
-      DCHECK_EQ(framer_.version(), first_version);
+      QUIC_BUG_IF(first_version != framer_.version())
+          << "SREJ: Client's version: " << QuicVersionToString(first_version)
+          << " is different from current dispatcher framer's version: "
+          << QuicVersionToString(framer_.version());
       StatelessConnectionTerminator terminator(rejector->connection_id(),
                                                &framer_, helper(),
                                                time_wait_list_manager_.get());
diff --git a/net/tools/quic/quic_epoll_alarm_factory_test.cc b/net/tools/quic/quic_epoll_alarm_factory_test.cc
index 1ee2b84..2e0f743a4 100644
--- a/net/tools/quic/quic_epoll_alarm_factory_test.cc
+++ b/net/tools/quic/quic_epoll_alarm_factory_test.cc
@@ -40,6 +40,10 @@
   QuicConnectionArena arena_;
 };
 
+INSTANTIATE_TEST_CASE_P(UseArena,
+                        QuicEpollAlarmFactoryTest,
+                        ::testing::ValuesIn({true, false}));
+
 TEST_P(QuicEpollAlarmFactoryTest, CreateAlarm) {
   QuicArenaScopedPtr<TestDelegate> delegate =
       QuicArenaScopedPtr<TestDelegate>(new TestDelegate());
diff --git a/net/tools/quic/quic_simple_client.cc b/net/tools/quic/quic_simple_client.cc
index cd47622..12b511f6 100644
--- a/net/tools/quic/quic_simple_client.cc
+++ b/net/tools/quic/quic_simple_client.cc
@@ -24,6 +24,7 @@
 #include "net/quic/core/quic_server_id.h"
 #include "net/quic/core/spdy_utils.h"
 #include "net/quic/platform/api/quic_flags.h"
+#include "net/quic/platform/api/quic_ptr_util.h"
 #include "net/socket/udp_client_socket.h"
 #include "net/spdy/chromium/spdy_http_utils.h"
 #include "net/spdy/core/spdy_header_block.h"
@@ -37,14 +38,16 @@
     const QuicServerId& server_id,
     const QuicVersionVector& supported_versions,
     std::unique_ptr<ProofVerifier> proof_verifier)
-    : QuicClientBase(server_id,
-                     supported_versions,
-                     QuicConfig(),
-                     CreateQuicConnectionHelper(),
-                     CreateQuicAlarmFactory(),
-                     std::move(proof_verifier)),
+    : QuicSpdyClientBase(
+          server_id,
+          supported_versions,
+          QuicConfig(),
+          CreateQuicConnectionHelper(),
+          CreateQuicAlarmFactory(),
+          QuicWrapUnique(
+              new QuicClientMessageLooplNetworkHelper(&clock_, this)),
+          std::move(proof_verifier)),
       initialized_(false),
-      packet_reader_started_(false),
       weak_factory_(this) {
   set_server_address(server_address);
 }
@@ -57,79 +60,6 @@
   }
 }
 
-bool QuicSimpleClient::CreateUDPSocketAndBind(QuicSocketAddress server_address,
-                                              QuicIpAddress bind_to_address,
-                                              int bind_to_port) {
-  std::unique_ptr<UDPClientSocket> socket(
-      new UDPClientSocket(DatagramSocket::DEFAULT_BIND, RandIntCallback(),
-                          &net_log_, NetLogSource()));
-
-  if (bind_to_address.IsInitialized()) {
-    client_address_ = QuicSocketAddress(bind_to_address, local_port());
-  } else if (server_address.host().address_family() == IpAddressFamily::IP_V4) {
-    client_address_ = QuicSocketAddress(QuicIpAddress::Any4(), bind_to_port);
-  } else {
-    client_address_ = QuicSocketAddress(QuicIpAddress::Any6(), bind_to_port);
-  }
-
-  int rc = socket->Connect(server_address.impl().socket_address());
-  if (rc != OK) {
-    LOG(ERROR) << "Connect failed: " << ErrorToShortString(rc);
-    return false;
-  }
-
-  rc = socket->SetReceiveBufferSize(kDefaultSocketReceiveBuffer);
-  if (rc != OK) {
-    LOG(ERROR) << "SetReceiveBufferSize() failed: " << ErrorToShortString(rc);
-    return false;
-  }
-
-  rc = socket->SetSendBufferSize(kDefaultSocketReceiveBuffer);
-  if (rc != OK) {
-    LOG(ERROR) << "SetSendBufferSize() failed: " << ErrorToShortString(rc);
-    return false;
-  }
-
-  IPEndPoint address;
-  rc = socket->GetLocalAddress(&address);
-  if (rc != OK) {
-    LOG(ERROR) << "GetLocalAddress failed: " << ErrorToShortString(rc);
-    return false;
-  }
-  client_address_ = QuicSocketAddress(QuicSocketAddressImpl(address));
-
-  socket_.swap(socket);
-  packet_reader_.reset(new QuicChromiumPacketReader(
-      socket_.get(), &clock_, this, kQuicYieldAfterPacketsRead,
-      QuicTime::Delta::FromMilliseconds(kQuicYieldAfterDurationMilliseconds),
-      NetLogWithSource()));
-
-  if (socket != nullptr) {
-    socket->Close();
-  }
-
-  return true;
-}
-
-void QuicSimpleClient::CleanUpAllUDPSockets() {
-  reset_writer();
-  packet_reader_.reset();
-  packet_reader_started_ = false;
-
-}
-
-void QuicSimpleClient::StartPacketReaderIfNotStarted() {
-  if (!packet_reader_started_) {
-    packet_reader_->StartReading();
-    packet_reader_started_ = true;
-  }
-}
-
-void QuicSimpleClient::RunEventLoop() {
-  StartPacketReaderIfNotStarted();
-  base::RunLoop().RunUntilIdle();
-}
-
 QuicChromiumConnectionHelper* QuicSimpleClient::CreateQuicConnectionHelper() {
   return new QuicChromiumConnectionHelper(&clock_, QuicRandom::GetInstance());
 }
@@ -139,30 +69,4 @@
                                       &clock_);
 }
 
-QuicPacketWriter* QuicSimpleClient::CreateQuicPacketWriter() {
-  return new QuicChromiumPacketWriter(socket_.get());
-}
-
-void QuicSimpleClient::OnReadError(int result,
-                                   const DatagramClientSocket* socket) {
-  LOG(ERROR) << "QuicSimpleClient read failed: " << ErrorToShortString(result);
-  Disconnect();
-}
-
-QuicSocketAddress QuicSimpleClient::GetLatestClientAddress() const {
-  return client_address_;
-}
-
-bool QuicSimpleClient::OnPacket(const QuicReceivedPacket& packet,
-                                const QuicSocketAddress& local_address,
-                                const QuicSocketAddress& peer_address) {
-  session()->connection()->ProcessUdpPacket(local_address, peer_address,
-                                            packet);
-  if (!session()->connection()->connected()) {
-    return false;
-  }
-
-  return true;
-}
-
 }  // namespace net
diff --git a/net/tools/quic/quic_simple_client.h b/net/tools/quic/quic_simple_client.h
index b0c25ca..1aecc2a2 100644
--- a/net/tools/quic/quic_simple_client.h
+++ b/net/tools/quic/quic_simple_client.h
@@ -23,21 +23,19 @@
 #include "net/quic/core/quic_config.h"
 #include "net/quic/core/quic_spdy_stream.h"
 #include "net/quic/platform/impl/quic_chromium_clock.h"
-#include "net/tools/quic/quic_client_base.h"
+#include "net/tools/quic/quic_client_message_loop_network_helper.h"
+#include "net/tools/quic/quic_spdy_client_base.h"
 
 namespace net {
 
 class QuicChromiumAlarmFactory;
 class QuicChromiumConnectionHelper;
-class UDPClientSocket;
-
 
 namespace test {
 class QuicClientPeer;
 }  // namespace test
 
-class QuicSimpleClient : public QuicClientBase,
-                         public QuicChromiumPacketReader::Visitor {
+class QuicSimpleClient : public QuicSpdyClientBase {
  public:
   // Create a quic client, which will have events managed by the message loop.
   QuicSimpleClient(QuicSocketAddress server_address,
@@ -47,54 +45,18 @@
 
   ~QuicSimpleClient() override;
 
-  // QuicChromiumPacketReader::Visitor
-  void OnReadError(int result, const DatagramClientSocket* socket) override;
-  bool OnPacket(const QuicReceivedPacket& packet,
-                const QuicSocketAddress& local_address,
-                const QuicSocketAddress& peer_address) override;
-
-  // From QuicClientBase
-  QuicSocketAddress GetLatestClientAddress() const override;
-
- protected:
-  // From QuicClientBase
-  QuicPacketWriter* CreateQuicPacketWriter() override;
-  void RunEventLoop() override;
-  bool CreateUDPSocketAndBind(QuicSocketAddress server_address,
-                              QuicIpAddress bind_to_address,
-                              int bind_to_port) override;
-  void CleanUpAllUDPSockets() override;
-
  private:
   friend class net::test::QuicClientPeer;
 
   QuicChromiumAlarmFactory* CreateQuicAlarmFactory();
   QuicChromiumConnectionHelper* CreateQuicConnectionHelper();
 
-  // Read a UDP packet and hand it to the framer.
-  bool ReadAndProcessPacket();
-
-  void StartPacketReaderIfNotStarted();
-
   //  Used by |helper_| to time alarms.
   QuicChromiumClock clock_;
 
-  // Address of the client if the client is connected to the server.
-  QuicSocketAddress client_address_;
-
-  // UDP socket connected to the server.
-  std::unique_ptr<UDPClientSocket> socket_;
-
   // Tracks if the client is initialized to connect.
   bool initialized_;
 
-  // The log used for the sockets.
-  NetLog net_log_;
-
-  std::unique_ptr<QuicChromiumPacketReader> packet_reader_;
-
-  bool packet_reader_started_;
-
   base::WeakPtrFactory<QuicSimpleClient> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(QuicSimpleClient);
diff --git a/net/tools/quic/quic_spdy_client_base.cc b/net/tools/quic/quic_spdy_client_base.cc
new file mode 100644
index 0000000..7fc0ab6
--- /dev/null
+++ b/net/tools/quic/quic_spdy_client_base.cc
@@ -0,0 +1,270 @@
+// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/tools/quic/quic_spdy_client_base.h"
+
+#include "net/quic/core/crypto/quic_random.h"
+#include "net/quic/core/quic_server_id.h"
+#include "net/quic/core/spdy_utils.h"
+#include "net/quic/platform/api/quic_flags.h"
+#include "net/quic/platform/api/quic_logging.h"
+#include "net/quic/platform/api/quic_ptr_util.h"
+#include "net/quic/platform/api/quic_text_utils.h"
+
+using base::StringToInt;
+using std::string;
+
+namespace net {
+
+void QuicSpdyClientBase::ClientQuicDataToResend::Resend() {
+  client_->SendRequest(*headers_, body_, fin_);
+  headers_ = nullptr;
+}
+
+QuicSpdyClientBase::QuicDataToResend::QuicDataToResend(
+    std::unique_ptr<SpdyHeaderBlock> headers,
+    QuicStringPiece body,
+    bool fin)
+    : headers_(std::move(headers)), body_(body), fin_(fin) {}
+
+QuicSpdyClientBase::QuicDataToResend::~QuicDataToResend() {}
+
+QuicSpdyClientBase::QuicSpdyClientBase(
+    const QuicServerId& server_id,
+    const QuicVersionVector& supported_versions,
+    const QuicConfig& config,
+    QuicConnectionHelperInterface* helper,
+    QuicAlarmFactory* alarm_factory,
+    std::unique_ptr<NetworkHelper> network_helper,
+    std::unique_ptr<ProofVerifier> proof_verifier)
+    : QuicClientBase(server_id,
+                     supported_versions,
+                     config,
+                     helper,
+                     alarm_factory,
+                     std::move(network_helper),
+                     std::move(proof_verifier)),
+      store_response_(false),
+      latest_response_code_(-1) {}
+
+QuicSpdyClientBase::~QuicSpdyClientBase() {
+  // We own the push promise index. We need to explicitly kill
+  // the session before the push promise index goes out of scope.
+  ResetSession();
+}
+
+QuicSpdyClientSession* QuicSpdyClientBase::client_session() {
+  return static_cast<QuicSpdyClientSession*>(QuicClientBase::session());
+}
+
+void QuicSpdyClientBase::InitializeSession() {
+  client_session()->Initialize();
+  client_session()->CryptoConnect();
+}
+
+void QuicSpdyClientBase::OnClose(QuicSpdyStream* stream) {
+  DCHECK(stream != nullptr);
+  QuicSpdyClientStream* client_stream =
+      static_cast<QuicSpdyClientStream*>(stream);
+
+  const SpdyHeaderBlock& response_headers = client_stream->response_headers();
+  if (response_listener_ != nullptr) {
+    response_listener_->OnCompleteResponse(stream->id(), response_headers,
+                                           client_stream->data());
+  }
+
+  // Store response headers and body.
+  if (store_response_) {
+    auto status = response_headers.find(":status");
+    if (status == response_headers.end() ||
+        !QuicTextUtils::StringToInt(status->second, &latest_response_code_)) {
+      QUIC_LOG(ERROR) << "Invalid response headers";
+    }
+    latest_response_headers_ = response_headers.DebugString();
+    preliminary_response_headers_ =
+        client_stream->preliminary_headers().DebugString();
+    latest_response_header_block_ = response_headers.Clone();
+    latest_response_body_ = client_stream->data();
+    latest_response_trailers_ =
+        client_stream->received_trailers().DebugString();
+  }
+}
+
+std::unique_ptr<QuicSession> QuicSpdyClientBase::CreateQuicClientSession(
+    QuicConnection* connection) {
+  return QuicMakeUnique<QuicSpdyClientSession>(*config(), connection,
+                                               server_id(), crypto_config(),
+                                               &push_promise_index_);
+}
+
+void QuicSpdyClientBase::SendRequest(const SpdyHeaderBlock& headers,
+                                     QuicStringPiece body,
+                                     bool fin) {
+  QuicClientPushPromiseIndex::TryHandle* handle;
+  QuicAsyncStatus rv = push_promise_index()->Try(headers, this, &handle);
+  if (rv == QUIC_SUCCESS)
+    return;
+
+  if (rv == QUIC_PENDING) {
+    // May need to retry request if asynchronous rendezvous fails.
+    AddPromiseDataToResend(headers, body, fin);
+    return;
+  }
+
+  QuicSpdyClientStream* stream = CreateClientStream();
+  if (stream == nullptr) {
+    QUIC_BUG << "stream creation failed!";
+    return;
+  }
+  stream->SendRequest(headers.Clone(), body, fin);
+  // Record this in case we need to resend.
+  MaybeAddDataToResend(headers, body, fin);
+}
+
+void QuicSpdyClientBase::SendRequestAndWaitForResponse(
+    const SpdyHeaderBlock& headers,
+    QuicStringPiece body,
+    bool fin) {
+  SendRequest(headers, body, fin);
+  while (WaitForEvents()) {
+  }
+}
+
+void QuicSpdyClientBase::SendRequestsAndWaitForResponse(
+    const std::vector<string>& url_list) {
+  for (size_t i = 0; i < url_list.size(); ++i) {
+    SpdyHeaderBlock headers;
+    if (!SpdyUtils::PopulateHeaderBlockFromUrl(url_list[i], &headers)) {
+      QUIC_BUG << "Unable to create request";
+      continue;
+    }
+    SendRequest(headers, "", true);
+  }
+  while (WaitForEvents()) {
+  }
+}
+
+QuicSpdyClientStream* QuicSpdyClientBase::CreateClientStream() {
+  if (!connected()) {
+    return nullptr;
+  }
+
+  auto* stream = static_cast<QuicSpdyClientStream*>(
+      FLAGS_quic_reloadable_flag_quic_refactor_stream_creation
+          ? client_session()->MaybeCreateOutgoingDynamicStream(kDefaultPriority)
+          : client_session()->CreateOutgoingDynamicStream(kDefaultPriority));
+  if (stream) {
+    stream->set_visitor(this);
+  }
+  return stream;
+}
+
+int QuicSpdyClientBase::GetNumSentClientHellosFromSession() {
+  return client_session()->GetNumSentClientHellos();
+}
+
+int QuicSpdyClientBase::GetNumReceivedServerConfigUpdatesFromSession() {
+  return client_session()->GetNumReceivedServerConfigUpdates();
+}
+
+void QuicSpdyClientBase::MaybeAddDataToResend(const SpdyHeaderBlock& headers,
+                                              QuicStringPiece body,
+                                              bool fin) {
+  if (!FLAGS_quic_reloadable_flag_enable_quic_stateless_reject_support) {
+    return;
+  }
+
+  if (client_session()->IsCryptoHandshakeConfirmed()) {
+    // The handshake is confirmed.  No need to continue saving requests to
+    // resend.
+    data_to_resend_on_connect_.clear();
+    return;
+  }
+
+  // The handshake is not confirmed.  Push the data onto the queue of data to
+  // resend if statelessly rejected.
+  std::unique_ptr<SpdyHeaderBlock> new_headers(
+      new SpdyHeaderBlock(headers.Clone()));
+  std::unique_ptr<QuicDataToResend> data_to_resend(
+      new ClientQuicDataToResend(std::move(new_headers), body, fin, this));
+  MaybeAddQuicDataToResend(std::move(data_to_resend));
+}
+
+void QuicSpdyClientBase::MaybeAddQuicDataToResend(
+    std::unique_ptr<QuicDataToResend> data_to_resend) {
+  data_to_resend_on_connect_.push_back(std::move(data_to_resend));
+}
+
+void QuicSpdyClientBase::ClearDataToResend() {
+  data_to_resend_on_connect_.clear();
+}
+
+void QuicSpdyClientBase::ResendSavedData() {
+  // Calling Resend will re-enqueue the data, so swap out
+  //  data_to_resend_on_connect_ before iterating.
+  std::vector<std::unique_ptr<QuicDataToResend>> old_data;
+  old_data.swap(data_to_resend_on_connect_);
+  for (const auto& data : old_data) {
+    data->Resend();
+  }
+}
+
+void QuicSpdyClientBase::AddPromiseDataToResend(const SpdyHeaderBlock& headers,
+                                                QuicStringPiece body,
+                                                bool fin) {
+  std::unique_ptr<SpdyHeaderBlock> new_headers(
+      new SpdyHeaderBlock(headers.Clone()));
+  push_promise_data_to_resend_.reset(
+      new ClientQuicDataToResend(std::move(new_headers), body, fin, this));
+}
+
+bool QuicSpdyClientBase::CheckVary(const SpdyHeaderBlock& client_request,
+                                   const SpdyHeaderBlock& promise_request,
+                                   const SpdyHeaderBlock& promise_response) {
+  return true;
+}
+
+void QuicSpdyClientBase::OnRendezvousResult(QuicSpdyStream* stream) {
+  std::unique_ptr<ClientQuicDataToResend> data_to_resend =
+      std::move(push_promise_data_to_resend_);
+  if (stream) {
+    stream->set_visitor(this);
+    stream->OnDataAvailable();
+  } else if (data_to_resend.get()) {
+    data_to_resend->Resend();
+  }
+}
+
+size_t QuicSpdyClientBase::latest_response_code() const {
+  QUIC_BUG_IF(!store_response_) << "Response not stored!";
+  return latest_response_code_;
+}
+
+const string& QuicSpdyClientBase::latest_response_headers() const {
+  QUIC_BUG_IF(!store_response_) << "Response not stored!";
+  return latest_response_headers_;
+}
+
+const string& QuicSpdyClientBase::preliminary_response_headers() const {
+  QUIC_BUG_IF(!store_response_) << "Response not stored!";
+  return preliminary_response_headers_;
+}
+
+const SpdyHeaderBlock& QuicSpdyClientBase::latest_response_header_block()
+    const {
+  QUIC_BUG_IF(!store_response_) << "Response not stored!";
+  return latest_response_header_block_;
+}
+
+const string& QuicSpdyClientBase::latest_response_body() const {
+  QUIC_BUG_IF(!store_response_) << "Response not stored!";
+  return latest_response_body_;
+}
+
+const string& QuicSpdyClientBase::latest_response_trailers() const {
+  QUIC_BUG_IF(!store_response_) << "Response not stored!";
+  return latest_response_trailers_;
+}
+
+}  // namespace net
diff --git a/net/tools/quic/quic_spdy_client_base.h b/net/tools/quic/quic_spdy_client_base.h
new file mode 100644
index 0000000..76a2e92
--- /dev/null
+++ b/net/tools/quic/quic_spdy_client_base.h
@@ -0,0 +1,215 @@
+// Copyright (c) 2015 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.
+//
+// A base class for the toy client, which connects to a specified port and sends
+// QUIC request to that endpoint.
+
+#ifndef NET_TOOLS_QUIC_QUIC_SPDY_CLIENT_BASE_H_
+#define NET_TOOLS_QUIC_QUIC_SPDY_CLIENT_BASE_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "net/quic/core/crypto/crypto_handshake.h"
+#include "net/quic/core/quic_client_push_promise_index.h"
+#include "net/quic/core/quic_config.h"
+#include "net/quic/platform/api/quic_socket_address.h"
+#include "net/quic/platform/api/quic_string_piece.h"
+#include "net/tools/quic/quic_client_base.h"
+#include "net/tools/quic/quic_spdy_client_session.h"
+#include "net/tools/quic/quic_spdy_client_stream.h"
+
+namespace net {
+
+class ProofVerifier;
+class QuicServerId;
+
+class QuicSpdyClientBase : public QuicClientBase,
+                           public QuicClientPushPromiseIndex::Delegate,
+                           public QuicSpdyStream::Visitor {
+ public:
+  // A ResponseListener is notified when a complete response is received.
+  class ResponseListener {
+   public:
+    ResponseListener() {}
+    virtual ~ResponseListener() {}
+    virtual void OnCompleteResponse(QuicStreamId id,
+                                    const SpdyHeaderBlock& response_headers,
+                                    const std::string& response_body) = 0;
+  };
+
+  // The client uses these objects to keep track of any data to resend upon
+  // receipt of a stateless reject.  Recall that the client API allows callers
+  // to optimistically send data to the server prior to handshake-confirmation.
+  // If the client subsequently receives a stateless reject, it must tear down
+  // its existing session, create a new session, and resend all previously sent
+  // data.  It uses these objects to keep track of all the sent data, and to
+  // resend the data upon a subsequent connection.
+  class QuicDataToResend {
+   public:
+    // |headers| may be null, since it's possible to send data without headers.
+    QuicDataToResend(std::unique_ptr<SpdyHeaderBlock> headers,
+                     QuicStringPiece body,
+                     bool fin);
+
+    virtual ~QuicDataToResend();
+
+    // Must be overridden by specific classes with the actual method for
+    // re-sending data.
+    virtual void Resend() = 0;
+
+   protected:
+    std::unique_ptr<SpdyHeaderBlock> headers_;
+    QuicStringPiece body_;
+    bool fin_;
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(QuicDataToResend);
+  };
+
+  QuicSpdyClientBase(const QuicServerId& server_id,
+                     const QuicVersionVector& supported_versions,
+                     const QuicConfig& config,
+                     QuicConnectionHelperInterface* helper,
+                     QuicAlarmFactory* alarm_factory,
+                     std::unique_ptr<NetworkHelper> network_helper,
+                     std::unique_ptr<ProofVerifier> proof_verifier);
+
+  ~QuicSpdyClientBase() override;
+
+  // QuicSpdyStream::Visitor
+  void OnClose(QuicSpdyStream* stream) override;
+
+  // A spdy session has to call CryptoConnect on top of the regular
+  // initialization.
+  void InitializeSession() override;
+
+  // Sends an HTTP request and does not wait for response before returning.
+  void SendRequest(const SpdyHeaderBlock& headers,
+                   QuicStringPiece body,
+                   bool fin);
+
+  // Sends an HTTP request and waits for response before returning.
+  void SendRequestAndWaitForResponse(const SpdyHeaderBlock& headers,
+                                     QuicStringPiece body,
+                                     bool fin);
+
+  // Sends a request simple GET for each URL in |url_list|, and then waits for
+  // each to complete.
+  void SendRequestsAndWaitForResponse(const std::vector<std::string>& url_list);
+
+  // Returns a newly created QuicSpdyClientStream.
+  QuicSpdyClientStream* CreateClientStream();
+
+  // Returns a the session used for this client downcasted to a
+  // QuicSpdyClientSession.
+  QuicSpdyClientSession* client_session();
+
+  QuicClientPushPromiseIndex* push_promise_index() {
+    return &push_promise_index_;
+  }
+
+  bool CheckVary(const SpdyHeaderBlock& client_request,
+                 const SpdyHeaderBlock& promise_request,
+                 const SpdyHeaderBlock& promise_response) override;
+  void OnRendezvousResult(QuicSpdyStream*) override;
+
+  // If the crypto handshake has not yet been confirmed, adds the data to the
+  // queue of data to resend if the client receives a stateless reject.
+  // Otherwise, deletes the data.
+  void MaybeAddQuicDataToResend(
+      std::unique_ptr<QuicDataToResend> data_to_resend);
+
+  void set_store_response(bool val) { store_response_ = val; }
+
+  size_t latest_response_code() const;
+  const std::string& latest_response_headers() const;
+  const std::string& preliminary_response_headers() const;
+  const SpdyHeaderBlock& latest_response_header_block() const;
+  const std::string& latest_response_body() const;
+  const std::string& latest_response_trailers() const;
+
+  void set_response_listener(std::unique_ptr<ResponseListener> listener) {
+    response_listener_ = std::move(listener);
+  }
+
+ protected:
+  int GetNumSentClientHellosFromSession() override;
+  int GetNumReceivedServerConfigUpdatesFromSession() override;
+
+  // Takes ownership of |connection|.
+  std::unique_ptr<QuicSession> CreateQuicClientSession(
+      QuicConnection* connection) override;
+
+  // If the crypto handshake has not yet been confirmed, adds the data to the
+  // queue of data to resend if the client receives a stateless reject.
+  // Otherwise, deletes the data.
+  void MaybeAddDataToResend(const SpdyHeaderBlock& headers,
+                            QuicStringPiece body,
+                            bool fin);
+
+  void ClearDataToResend() override;
+
+  void ResendSavedData() override;
+
+  void AddPromiseDataToResend(const SpdyHeaderBlock& headers,
+                              QuicStringPiece body,
+                              bool fin);
+
+ private:
+  // Specific QuicClient class for storing data to resend.
+  class ClientQuicDataToResend : public QuicDataToResend {
+   public:
+    ClientQuicDataToResend(std::unique_ptr<SpdyHeaderBlock> headers,
+                           QuicStringPiece body,
+                           bool fin,
+                           QuicSpdyClientBase* client)
+        : QuicDataToResend(std::move(headers), body, fin), client_(client) {
+      DCHECK(headers_);
+      DCHECK(client);
+    }
+
+    ~ClientQuicDataToResend() override {}
+
+    void Resend() override;
+
+   private:
+    QuicSpdyClientBase* client_;
+
+    DISALLOW_COPY_AND_ASSIGN(ClientQuicDataToResend);
+  };
+
+  // Index of pending promised streams. Must outlive |session_|.
+  QuicClientPushPromiseIndex push_promise_index_;
+
+  // If true, store the latest response code, headers, and body.
+  bool store_response_;
+  // HTTP response code from most recent response.
+  int latest_response_code_;
+  // HTTP/2 headers from most recent response.
+  std::string latest_response_headers_;
+  // preliminary 100 Continue HTTP/2 headers from most recent response, if any.
+  std::string preliminary_response_headers_;
+  // HTTP/2 headers from most recent response.
+  SpdyHeaderBlock latest_response_header_block_;
+  // Body of most recent response.
+  std::string latest_response_body_;
+  // HTTP/2 trailers from most recent response.
+  std::string latest_response_trailers_;
+
+  // Listens for full responses.
+  std::unique_ptr<ResponseListener> response_listener_;
+
+  // Keeps track of any data that must be resent upon a subsequent successful
+  // connection, in case the client receives a stateless reject.
+  std::vector<std::unique_ptr<QuicDataToResend>> data_to_resend_on_connect_;
+
+  std::unique_ptr<ClientQuicDataToResend> push_promise_data_to_resend_;
+
+  DISALLOW_COPY_AND_ASSIGN(QuicSpdyClientBase);
+};
+
+}  // namespace net
+
+#endif  // NET_TOOLS_QUIC_QUIC_SPDY_CLIENT_BASE_H_
diff --git a/net/tools/quic/quic_client_session.cc b/net/tools/quic/quic_spdy_client_session.cc
similarity index 72%
rename from net/tools/quic/quic_client_session.cc
rename to net/tools/quic/quic_spdy_client_session.cc
index a7408da..d13a84e 100644
--- a/net/tools/quic/quic_client_session.cc
+++ b/net/tools/quic/quic_spdy_client_session.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 "net/tools/quic/quic_client_session.h"
+#include "net/tools/quic/quic_spdy_client_session.h"
 
 #include "net/log/net_log_with_source.h"
 #include "net/quic/chromium/crypto/proof_verifier_chromium.h"
@@ -18,30 +18,30 @@
 
 namespace net {
 
-QuicClientSession::QuicClientSession(
+QuicSpdyClientSession::QuicSpdyClientSession(
     const QuicConfig& config,
     QuicConnection* connection,
     const QuicServerId& server_id,
     QuicCryptoClientConfig* crypto_config,
     QuicClientPushPromiseIndex* push_promise_index)
-    : QuicClientSessionBase(connection, push_promise_index, config),
+    : QuicSpdyClientSessionBase(connection, push_promise_index, config),
       server_id_(server_id),
       crypto_config_(crypto_config) {}
 
-QuicClientSession::~QuicClientSession() {}
+QuicSpdyClientSession::~QuicSpdyClientSession() {}
 
-void QuicClientSession::Initialize() {
+void QuicSpdyClientSession::Initialize() {
   crypto_stream_ = CreateQuicCryptoStream();
-  QuicClientSessionBase::Initialize();
+  QuicSpdyClientSessionBase::Initialize();
 }
 
-void QuicClientSession::OnProofValid(
+void QuicSpdyClientSession::OnProofValid(
     const QuicCryptoClientConfig::CachedState& /*cached*/) {}
 
-void QuicClientSession::OnProofVerifyDetailsAvailable(
+void QuicSpdyClientSession::OnProofVerifyDetailsAvailable(
     const ProofVerifyDetails& /*verify_details*/) {}
 
-bool QuicClientSession::ShouldCreateOutgoingDynamicStream() {
+bool QuicSpdyClientSession::ShouldCreateOutgoingDynamicStream() {
   DCHECK(!FLAGS_quic_reloadable_flag_quic_refactor_stream_creation);
   if (!crypto_stream_->encryption_established()) {
     QUIC_DLOG(INFO) << "Encryption not active so no outgoing stream created.";
@@ -60,7 +60,7 @@
   return true;
 }
 
-QuicSpdyClientStream* QuicClientSession::CreateOutgoingDynamicStream(
+QuicSpdyClientStream* QuicSpdyClientSession::CreateOutgoingDynamicStream(
     SpdyPriority priority) {
   if (!ShouldCreateOutgoingDynamicStream()) {
     return nullptr;
@@ -72,32 +72,34 @@
   return stream_ptr;
 }
 
-std::unique_ptr<QuicSpdyClientStream> QuicClientSession::CreateClientStream() {
+std::unique_ptr<QuicSpdyClientStream>
+QuicSpdyClientSession::CreateClientStream() {
   return QuicMakeUnique<QuicSpdyClientStream>(GetNextOutgoingStreamId(), this);
 }
 
-QuicCryptoClientStreamBase* QuicClientSession::GetMutableCryptoStream() {
+QuicCryptoClientStreamBase* QuicSpdyClientSession::GetMutableCryptoStream() {
   return crypto_stream_.get();
 }
 
-const QuicCryptoClientStreamBase* QuicClientSession::GetCryptoStream() const {
+const QuicCryptoClientStreamBase* QuicSpdyClientSession::GetCryptoStream()
+    const {
   return crypto_stream_.get();
 }
 
-void QuicClientSession::CryptoConnect() {
+void QuicSpdyClientSession::CryptoConnect() {
   DCHECK(flow_controller());
   crypto_stream_->CryptoConnect();
 }
 
-int QuicClientSession::GetNumSentClientHellos() const {
+int QuicSpdyClientSession::GetNumSentClientHellos() const {
   return crypto_stream_->num_sent_client_hellos();
 }
 
-int QuicClientSession::GetNumReceivedServerConfigUpdates() const {
+int QuicSpdyClientSession::GetNumReceivedServerConfigUpdates() const {
   return crypto_stream_->num_scup_messages_received();
 }
 
-bool QuicClientSession::ShouldCreateIncomingDynamicStream(QuicStreamId id) {
+bool QuicSpdyClientSession::ShouldCreateIncomingDynamicStream(QuicStreamId id) {
   DCHECK(!FLAGS_quic_reloadable_flag_quic_refactor_stream_creation);
   if (!connection()->connected()) {
     QUIC_BUG << "ShouldCreateIncomingDynamicStream called when disconnected";
@@ -118,7 +120,7 @@
   return true;
 }
 
-QuicSpdyStream* QuicClientSession::CreateIncomingDynamicStream(
+QuicSpdyStream* QuicSpdyClientSession::CreateIncomingDynamicStream(
     QuicStreamId id) {
   if (!ShouldCreateIncomingDynamicStream(id)) {
     return nullptr;
@@ -130,23 +132,23 @@
 }
 
 std::unique_ptr<QuicCryptoClientStreamBase>
-QuicClientSession::CreateQuicCryptoStream() {
+QuicSpdyClientSession::CreateQuicCryptoStream() {
   return QuicMakeUnique<QuicCryptoClientStream>(
       server_id_, this, new ProofVerifyContextChromium(0, NetLogWithSource()),
       crypto_config_, this);
 }
 
-bool QuicClientSession::IsAuthorized(const string& authority) {
+bool QuicSpdyClientSession::IsAuthorized(const string& authority) {
   return true;
 }
 
-QuicSpdyClientStream* QuicClientSession::MaybeCreateOutgoingDynamicStream(
+QuicSpdyClientStream* QuicSpdyClientSession::MaybeCreateOutgoingDynamicStream(
     SpdyPriority priority) {
   return static_cast<QuicSpdyClientStream*>(
       QuicSpdySession::MaybeCreateOutgoingDynamicStream(priority));
 }
 
-QuicSpdyStream* QuicClientSession::MaybeCreateIncomingDynamicStream(
+QuicSpdyStream* QuicSpdyClientSession::MaybeCreateIncomingDynamicStream(
     QuicStreamId id) {
   QuicSpdyStream* stream =
       QuicSpdySession::MaybeCreateIncomingDynamicStream(id);
@@ -157,7 +159,8 @@
   return stream;
 }
 
-std::unique_ptr<QuicStream> QuicClientSession::CreateStream(QuicStreamId id) {
+std::unique_ptr<QuicStream> QuicSpdyClientSession::CreateStream(
+    QuicStreamId id) {
   return QuicMakeUnique<QuicSpdyClientStream>(id, this);
 }
 
diff --git a/net/tools/quic/quic_client_session.h b/net/tools/quic/quic_spdy_client_session.h
similarity index 79%
rename from net/tools/quic/quic_client_session.h
rename to net/tools/quic/quic_spdy_client_session.h
index a7dc1804..1a252ac 100644
--- a/net/tools/quic/quic_client_session.h
+++ b/net/tools/quic/quic_spdy_client_session.h
@@ -4,16 +4,16 @@
 //
 // A client specific QuicSession subclass.
 
-#ifndef NET_TOOLS_QUIC_QUIC_CLIENT_SESSION_H_
-#define NET_TOOLS_QUIC_QUIC_CLIENT_SESSION_H_
+#ifndef NET_TOOLS_QUIC_QUIC_SPDY_CLIENT_SESSION_H_
+#define NET_TOOLS_QUIC_QUIC_SPDY_CLIENT_SESSION_H_
 
 #include <memory>
 #include <string>
 
 #include "base/macros.h"
-#include "net/quic/core/quic_client_session_base.h"
 #include "net/quic/core/quic_crypto_client_stream.h"
 #include "net/quic/core/quic_packets.h"
+#include "net/quic/core/quic_spdy_client_session_base.h"
 #include "net/tools/quic/quic_spdy_client_stream.h"
 
 namespace net {
@@ -21,17 +21,17 @@
 class QuicConnection;
 class QuicServerId;
 
-class QuicClientSession : public QuicClientSessionBase {
+class QuicSpdyClientSession : public QuicSpdyClientSessionBase {
  public:
   // Takes ownership of |connection|. Caller retains ownership of
   // |promised_by_url|.
-  QuicClientSession(const QuicConfig& config,
-                    QuicConnection* connection,
-                    const QuicServerId& server_id,
-                    QuicCryptoClientConfig* crypto_config,
-                    QuicClientPushPromiseIndex* push_promise_index);
-  ~QuicClientSession() override;
-  // Set up the QuicClientSession. Must be called prior to use.
+  QuicSpdyClientSession(const QuicConfig& config,
+                        QuicConnection* connection,
+                        const QuicServerId& server_id,
+                        QuicCryptoClientConfig* crypto_config,
+                        QuicClientPushPromiseIndex* push_promise_index);
+  ~QuicSpdyClientSession() override;
+  // Set up the QuicSpdyClientSession. Must be called prior to use.
   void Initialize() override;
 
   // QuicSession methods:
@@ -44,7 +44,7 @@
 
   bool IsAuthorized(const std::string& authority) override;
 
-  // QuicClientSessionBase methods:
+  // QuicSpdyClientSessionBase methods:
   void OnProofValid(const QuicCryptoClientConfig::CachedState& cached) override;
   void OnProofVerifyDetailsAvailable(
       const ProofVerifyDetails& verify_details) override;
@@ -89,9 +89,9 @@
   QuicServerId server_id_;
   QuicCryptoClientConfig* crypto_config_;
 
-  DISALLOW_COPY_AND_ASSIGN(QuicClientSession);
+  DISALLOW_COPY_AND_ASSIGN(QuicSpdyClientSession);
 };
 
 }  // namespace net
 
-#endif  // NET_TOOLS_QUIC_QUIC_CLIENT_SESSION_H_
+#endif  // NET_TOOLS_QUIC_QUIC_SPDY_CLIENT_SESSION_H_
diff --git a/net/tools/quic/quic_client_session_test.cc b/net/tools/quic/quic_spdy_client_session_test.cc
similarity index 89%
rename from net/tools/quic/quic_client_session_test.cc
rename to net/tools/quic/quic_spdy_client_session_test.cc
index 5b6cd71a..cba8935 100644
--- a/net/tools/quic/quic_client_session_test.cc
+++ b/net/tools/quic/quic_spdy_client_session_test.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 "net/tools/quic/quic_client_session.h"
+#include "net/tools/quic/quic_spdy_client_session.h"
 
 #include <vector>
 
@@ -36,18 +36,19 @@
 const char kServerHostname[] = "test.example.com";
 const uint16_t kPort = 443;
 
-class TestQuicClientSession : public QuicClientSession {
+class TestQuicSpdyClientSession : public QuicSpdyClientSession {
  public:
-  explicit TestQuicClientSession(const QuicConfig& config,
-                                 QuicConnection* connection,
-                                 const QuicServerId& server_id,
-                                 QuicCryptoClientConfig* crypto_config,
-                                 QuicClientPushPromiseIndex* push_promise_index)
-      : QuicClientSession(config,
-                          connection,
-                          server_id,
-                          crypto_config,
-                          push_promise_index) {}
+  explicit TestQuicSpdyClientSession(
+      const QuicConfig& config,
+      QuicConnection* connection,
+      const QuicServerId& server_id,
+      QuicCryptoClientConfig* crypto_config,
+      QuicClientPushPromiseIndex* push_promise_index)
+      : QuicSpdyClientSession(config,
+                              connection,
+                              server_id,
+                              crypto_config,
+                              push_promise_index) {}
 
   std::unique_ptr<QuicSpdyClientStream> CreateClientStream() override {
     return QuicMakeUnique<MockQuicSpdyClientStream>(GetNextOutgoingStreamId(),
@@ -69,13 +70,13 @@
       SpdyPriority priority) override {
     return FLAGS_quic_reloadable_flag_quic_refactor_stream_creation
                ? MaybeCreateOutgoingDynamicStream(priority)
-               : QuicClientSession::CreateOutgoingDynamicStream(priority);
+               : QuicSpdyClientSession::CreateOutgoingDynamicStream(priority);
   }
 };
 
-class QuicClientSessionTest : public QuicTestWithParam<QuicVersion> {
+class QuicSpdyClientSessionTest : public QuicTestWithParam<QuicVersion> {
  protected:
-  QuicClientSessionTest()
+  QuicSpdyClientSessionTest()
       : crypto_config_(crypto_test_utils::ProofVerifierForTesting()),
         promised_stream_id_(kInvalidStreamId),
         associated_stream_id_(kInvalidStreamId) {
@@ -84,7 +85,7 @@
     connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1));
   }
 
-  ~QuicClientSessionTest() override {
+  ~QuicSpdyClientSessionTest() override {
     // Session must be destroyed before promised_by_url_
     session_.reset(nullptr);
   }
@@ -94,7 +95,7 @@
     connection_ = new PacketSavingConnection(&helper_, &alarm_factory_,
                                              Perspective::IS_CLIENT,
                                              SupportedVersions(GetParam()));
-    session_.reset(new TestQuicClientSession(
+    session_.reset(new TestQuicSpdyClientSession(
         DefaultQuicConfig(), connection_,
         QuicServerId(kServerHostname, kPort, PRIVACY_MODE_DISABLED),
         &crypto_config_, &push_promise_index_));
@@ -130,7 +131,7 @@
   MockQuicConnectionHelper helper_;
   MockAlarmFactory alarm_factory_;
   PacketSavingConnection* connection_;
-  std::unique_ptr<TestQuicClientSession> session_;
+  std::unique_ptr<TestQuicSpdyClientSession> session_;
   QuicClientPushPromiseIndex push_promise_index_;
   SpdyHeaderBlock push_promise_;
   string promise_url_;
@@ -139,14 +140,14 @@
 };
 
 INSTANTIATE_TEST_CASE_P(Tests,
-                        QuicClientSessionTest,
+                        QuicSpdyClientSessionTest,
                         ::testing::ValuesIn(AllSupportedVersions()));
 
-TEST_P(QuicClientSessionTest, CryptoConnect) {
+TEST_P(QuicSpdyClientSessionTest, CryptoConnect) {
   CompleteCryptoHandshake();
 }
 
-TEST_P(QuicClientSessionTest, NoEncryptionAfterInitialEncryption) {
+TEST_P(QuicSpdyClientSessionTest, NoEncryptionAfterInitialEncryption) {
   // Complete a handshake in order to prime the crypto config for 0-RTT.
   CompleteCryptoHandshake();
 
@@ -192,7 +193,7 @@
   EXPECT_EQ(0u, consumed.bytes_consumed);
 }
 
-TEST_P(QuicClientSessionTest, MaxNumStreamsWithNoFinOrRst) {
+TEST_P(QuicSpdyClientSessionTest, MaxNumStreamsWithNoFinOrRst) {
   EXPECT_CALL(*connection_, SendRstStream(_, _, _)).Times(AnyNumber());
 
   const uint32_t kServerMaxIncomingStreams = 1;
@@ -212,7 +213,7 @@
   EXPECT_FALSE(stream);
 }
 
-TEST_P(QuicClientSessionTest, MaxNumStreamsWithRst) {
+TEST_P(QuicSpdyClientSessionTest, MaxNumStreamsWithRst) {
   EXPECT_CALL(*connection_, SendRstStream(_, _, _)).Times(AnyNumber());
 
   const uint32_t kServerMaxIncomingStreams = 1;
@@ -233,7 +234,7 @@
   EXPECT_NE(nullptr, stream);
 }
 
-TEST_P(QuicClientSessionTest, ResetAndTrailers) {
+TEST_P(QuicSpdyClientSessionTest, ResetAndTrailers) {
   // Tests the situation in which the client sends a RST at the same time that
   // the server sends trailing headers (trailers). Receipt of the trailers by
   // the client should result in all outstanding stream state being tidied up
@@ -272,7 +273,7 @@
   EXPECT_NE(nullptr, stream);
 }
 
-TEST_P(QuicClientSessionTest, ReceivedMalformedTrailersAfterSendingRst) {
+TEST_P(QuicSpdyClientSessionTest, ReceivedMalformedTrailersAfterSendingRst) {
   // Tests the situation where the client has sent a RST to the server, and has
   // received trailing headers with a malformed final byte offset value.
   CompleteCryptoHandshake();
@@ -298,7 +299,7 @@
   session_->OnStreamHeaderList(stream_id, /*fin=*/false, 0, trailers);
 }
 
-TEST_P(QuicClientSessionTest, GoAwayReceived) {
+TEST_P(QuicSpdyClientSessionTest, GoAwayReceived) {
   CompleteCryptoHandshake();
 
   // After receiving a GoAway, I should no longer be able to create outgoing
@@ -312,8 +313,9 @@
   return framer->error() == QUIC_DECRYPTION_FAILURE;
 }
 
-// Regression test for b/17206611.
-TEST_P(QuicClientSessionTest, InvalidPacketReceived) {
+// Various sorts of invalid packets that should not cause a connection
+// to be closed.
+TEST_P(QuicSpdyClientSessionTest, InvalidPacketReceived) {
   QuicSocketAddress server_address(TestPeerIPAddress(), kTestPort);
   QuicSocketAddress client_address(TestPeerIPAddress(), kTestPort);
 
@@ -351,7 +353,7 @@
 }
 
 // A packet with invalid framing should cause a connection to be closed.
-TEST_P(QuicClientSessionTest, InvalidFramedPacketReceived) {
+TEST_P(QuicSpdyClientSessionTest, InvalidFramedPacketReceived) {
   QuicSocketAddress server_address(TestPeerIPAddress(), kTestPort);
   QuicSocketAddress client_address(TestPeerIPAddress(), kTestPort);
 
@@ -372,7 +374,7 @@
   session_->ProcessUdpPacket(client_address, server_address, *received);
 }
 
-TEST_P(QuicClientSessionTest, PushPromiseOnPromiseHeaders) {
+TEST_P(QuicSpdyClientSessionTest, PushPromiseOnPromiseHeaders) {
   // Initialize crypto before the client session will create a stream.
   CompleteCryptoHandshake();
 
@@ -384,7 +386,7 @@
                                 QuicHeaderList());
 }
 
-TEST_P(QuicClientSessionTest, PushPromiseOnPromiseHeadersAlreadyClosed) {
+TEST_P(QuicSpdyClientSessionTest, PushPromiseOnPromiseHeadersAlreadyClosed) {
   // Initialize crypto before the client session will create a stream.
   CompleteCryptoHandshake();
 
@@ -398,7 +400,7 @@
                                 QuicHeaderList());
 }
 
-TEST_P(QuicClientSessionTest, PushPromiseOutOfOrder) {
+TEST_P(QuicSpdyClientSessionTest, PushPromiseOutOfOrder) {
   // Initialize crypto before the client session will create a stream.
   CompleteCryptoHandshake();
 
@@ -418,7 +420,7 @@
                                 QuicHeaderList());
 }
 
-TEST_P(QuicClientSessionTest, PushPromiseHandlePromise) {
+TEST_P(QuicSpdyClientSessionTest, PushPromiseHandlePromise) {
   // Initialize crypto before the client session will create a stream.
   CompleteCryptoHandshake();
 
@@ -431,7 +433,7 @@
   EXPECT_NE(session_->GetPromisedByUrl(promise_url_), nullptr);
 }
 
-TEST_P(QuicClientSessionTest, PushPromiseAlreadyClosed) {
+TEST_P(QuicSpdyClientSessionTest, PushPromiseAlreadyClosed) {
   // Initialize crypto before the client session will create a stream.
   CompleteCryptoHandshake();
 
@@ -451,7 +453,7 @@
   EXPECT_EQ(session_->GetPromisedByUrl(promise_url_), nullptr);
 }
 
-TEST_P(QuicClientSessionTest, PushPromiseDuplicateUrl) {
+TEST_P(QuicSpdyClientSessionTest, PushPromiseDuplicateUrl) {
   // Initialize crypto before the client session will create a stream.
   CompleteCryptoHandshake();
 
@@ -474,7 +476,7 @@
   EXPECT_EQ(session_->GetPromisedById(promised_stream_id_), nullptr);
 }
 
-TEST_P(QuicClientSessionTest, ReceivingPromiseEnhanceYourCalm) {
+TEST_P(QuicSpdyClientSessionTest, ReceivingPromiseEnhanceYourCalm) {
   for (size_t i = 0u; i < session_->get_max_promises(); i++) {
     push_promise_[":path"] = QuicStringPrintf("/bar%zu", i);
 
@@ -502,7 +504,7 @@
   EXPECT_EQ(session_->GetPromisedByUrl(promise_url), nullptr);
 }
 
-TEST_P(QuicClientSessionTest, IsClosedTrueAfterResetPromisedAlreadyOpen) {
+TEST_P(QuicSpdyClientSessionTest, IsClosedTrueAfterResetPromisedAlreadyOpen) {
   // Initialize crypto before the client session will create a stream.
   CompleteCryptoHandshake();
 
@@ -513,7 +515,7 @@
   EXPECT_TRUE(session_->IsClosedStream(promised_stream_id_));
 }
 
-TEST_P(QuicClientSessionTest, IsClosedTrueAfterResetPromisedNonexistant) {
+TEST_P(QuicSpdyClientSessionTest, IsClosedTrueAfterResetPromisedNonexistant) {
   // Initialize crypto before the client session will create a stream.
   CompleteCryptoHandshake();
 
@@ -523,7 +525,7 @@
   EXPECT_TRUE(session_->IsClosedStream(promised_stream_id_));
 }
 
-TEST_P(QuicClientSessionTest, OnInitialHeadersCompleteIsPush) {
+TEST_P(QuicSpdyClientSessionTest, OnInitialHeadersCompleteIsPush) {
   // Initialize crypto before the client session will create a stream.
   CompleteCryptoHandshake();
   session_->GetOrCreateStream(promised_stream_id_);
@@ -536,14 +538,14 @@
   session_->OnInitialHeadersComplete(promised_stream_id_, SpdyHeaderBlock());
 }
 
-TEST_P(QuicClientSessionTest, OnInitialHeadersCompleteIsNotPush) {
+TEST_P(QuicSpdyClientSessionTest, OnInitialHeadersCompleteIsNotPush) {
   // Initialize crypto before the client session will create a stream.
   CompleteCryptoHandshake();
   session_->CreateOutgoingDynamicStream(kDefaultPriority);
   session_->OnInitialHeadersComplete(promised_stream_id_, SpdyHeaderBlock());
 }
 
-TEST_P(QuicClientSessionTest, DeletePromised) {
+TEST_P(QuicSpdyClientSessionTest, DeletePromised) {
   // Initialize crypto before the client session will create a stream.
   CompleteCryptoHandshake();
   session_->GetOrCreateStream(promised_stream_id_);
@@ -560,7 +562,7 @@
   EXPECT_EQ(session_->GetPromisedByUrl(promise_url_), nullptr);
 }
 
-TEST_P(QuicClientSessionTest, ResetPromised) {
+TEST_P(QuicSpdyClientSessionTest, ResetPromised) {
   // Initialize crypto before the client session will create a stream.
   CompleteCryptoHandshake();
   session_->GetOrCreateStream(promised_stream_id_);
diff --git a/net/tools/quic/quic_spdy_client_stream.cc b/net/tools/quic/quic_spdy_client_stream.cc
index b5b6044..664414a 100644
--- a/net/tools/quic/quic_spdy_client_stream.cc
+++ b/net/tools/quic/quic_spdy_client_stream.cc
@@ -11,14 +11,14 @@
 #include "net/quic/core/spdy_utils.h"
 #include "net/quic/platform/api/quic_logging.h"
 #include "net/spdy/core/spdy_protocol.h"
-#include "net/tools/quic/quic_client_session.h"
+#include "net/tools/quic/quic_spdy_client_session.h"
 
 using std::string;
 
 namespace net {
 
 QuicSpdyClientStream::QuicSpdyClientStream(QuicStreamId id,
-                                           QuicClientSession* session)
+                                           QuicSpdyClientSession* session)
     : QuicSpdyStream(id, session),
       content_length_(-1),
       response_code_(0),
diff --git a/net/tools/quic/quic_spdy_client_stream.h b/net/tools/quic/quic_spdy_client_stream.h
index 2bff0e4..606c2bbe 100644
--- a/net/tools/quic/quic_spdy_client_stream.h
+++ b/net/tools/quic/quic_spdy_client_stream.h
@@ -17,13 +17,13 @@
 
 namespace net {
 
-class QuicClientSession;
+class QuicSpdyClientSession;
 
 // All this does right now is send an SPDY request, and aggregate the
 // SPDY response.
 class QuicSpdyClientStream : public QuicSpdyStream {
  public:
-  QuicSpdyClientStream(QuicStreamId id, QuicClientSession* session);
+  QuicSpdyClientStream(QuicStreamId id, QuicSpdyClientSession* session);
   ~QuicSpdyClientStream() override;
 
   // Override the base class to parse and store headers.
@@ -77,7 +77,7 @@
   size_t header_bytes_read_;
   size_t header_bytes_written_;
 
-  QuicClientSession* session_;
+  QuicSpdyClientSession* session_;
 
   // These preliminary headers are used for the 100 Continue headers
   // that may arrive before the response headers when the request has
diff --git a/net/tools/quic/quic_spdy_client_stream_test.cc b/net/tools/quic/quic_spdy_client_stream_test.cc
index bd78c145..3197db2f 100644
--- a/net/tools/quic/quic_spdy_client_stream_test.cc
+++ b/net/tools/quic/quic_spdy_client_stream_test.cc
@@ -16,7 +16,7 @@
 #include "net/quic/test_tools/crypto_test_utils.h"
 #include "net/quic/test_tools/quic_spdy_session_peer.h"
 #include "net/quic/test_tools/quic_test_utils.h"
-#include "net/tools/quic/quic_client_session.h"
+#include "net/tools/quic/quic_spdy_client_session.h"
 
 using base::IntToString;
 using std::string;
@@ -28,25 +28,26 @@
 
 namespace {
 
-class MockQuicClientSession : public QuicClientSession {
+class MockQuicSpdyClientSession : public QuicSpdyClientSession {
  public:
-  explicit MockQuicClientSession(QuicConnection* connection,
-                                 QuicClientPushPromiseIndex* push_promise_index)
-      : QuicClientSession(
+  explicit MockQuicSpdyClientSession(
+      QuicConnection* connection,
+      QuicClientPushPromiseIndex* push_promise_index)
+      : QuicSpdyClientSession(
             DefaultQuicConfig(),
             connection,
             QuicServerId("example.com", 443, PRIVACY_MODE_DISABLED),
             &crypto_config_,
             push_promise_index),
         crypto_config_(crypto_test_utils::ProofVerifierForTesting()) {}
-  ~MockQuicClientSession() override {}
+  ~MockQuicSpdyClientSession() override {}
 
   MOCK_METHOD1(CloseStream, void(QuicStreamId stream_id));
 
  private:
   QuicCryptoClientConfig crypto_config_;
 
-  DISALLOW_COPY_AND_ASSIGN(MockQuicClientSession);
+  DISALLOW_COPY_AND_ASSIGN(MockQuicSpdyClientSession);
 };
 
 class QuicSpdyClientStreamTest : public QuicTest {
@@ -82,7 +83,7 @@
   StrictMock<MockQuicConnection>* connection_;
   QuicClientPushPromiseIndex push_promise_index_;
 
-  MockQuicClientSession session_;
+  MockQuicSpdyClientSession session_;
   std::unique_ptr<QuicSpdyClientStream> stream_;
   std::unique_ptr<StreamVisitor> stream_visitor_;
   SpdyHeaderBlock headers_;
diff --git a/net/tools/quic/test_tools/quic_client_peer.cc b/net/tools/quic/test_tools/quic_client_peer.cc
index 0f6d1ba..2936e77 100644
--- a/net/tools/quic/test_tools/quic_client_peer.cc
+++ b/net/tools/quic/test_tools/quic_client_peer.cc
@@ -16,20 +16,19 @@
 
 // static
 bool QuicClientPeer::CreateUDPSocketAndBind(QuicClient* client) {
-  return client->CreateUDPSocketAndBind(client->server_address(),
-                                        client->bind_to_address(),
-                                        client->local_port());
+  return client->network_helper()->CreateUDPSocketAndBind(
+      client->server_address(), client->bind_to_address(),
+      client->local_port());
 }
 
 // static
 void QuicClientPeer::CleanUpUDPSocket(QuicClient* client, int fd) {
-  client->CleanUpUDPSocket(fd);
+  client->epoll_network_helper()->CleanUpUDPSocket(fd);
 }
 
 // static
 void QuicClientPeer::SetClientPort(QuicClient* client, int port) {
-  client->fd_address_map_.back().second =
-      QuicSocketAddress(client->GetLatestClientAddress().host(), port);
+  client->epoll_network_helper()->SetClientPort(port);
 }
 
 // static
diff --git a/net/tools/quic/test_tools/quic_test_client.cc b/net/tools/quic/test_tools/quic_test_client.cc
index 4fa9146..2fa3f9e 100644
--- a/net/tools/quic/test_tools/quic_test_client.cc
+++ b/net/tools/quic/test_tools/quic_test_client.cc
@@ -30,7 +30,6 @@
 
 using std::string;
 using testing::_;
-using testing::Invoke;
 
 namespace net {
 namespace test {
@@ -108,8 +107,59 @@
   string common_name_;
   string cert_sct_;
 };
+}  // namespace
 
-}  // anonymous namespace
+class MockableQuicClientEpollNetworkHelper
+    : public QuicClientEpollNetworkHelper {
+ public:
+  using QuicClientEpollNetworkHelper::QuicClientEpollNetworkHelper;
+  ~MockableQuicClientEpollNetworkHelper() override {}
+
+  void ProcessPacket(const QuicSocketAddress& self_address,
+                     const QuicSocketAddress& peer_address,
+                     const QuicReceivedPacket& packet) override {
+    QuicClientEpollNetworkHelper::ProcessPacket(self_address, peer_address,
+                                                packet);
+    if (track_last_incoming_packet_) {
+      last_incoming_packet_ = packet.Clone();
+    }
+  }
+
+  QuicPacketWriter* CreateQuicPacketWriter() override {
+    QuicPacketWriter* writer =
+        QuicClientEpollNetworkHelper::CreateQuicPacketWriter();
+    if (!test_writer_) {
+      return writer;
+    }
+    test_writer_->set_writer(writer);
+    return test_writer_;
+  }
+
+  const QuicReceivedPacket* last_incoming_packet() {
+    return last_incoming_packet_.get();
+  }
+
+  void set_track_last_incoming_packet(bool track) {
+    track_last_incoming_packet_ = track;
+  }
+
+  void UseWriter(QuicPacketWriterWrapper* writer) {
+    CHECK(test_writer_ == nullptr);
+    test_writer_ = writer;
+  }
+
+  void set_peer_address(const QuicSocketAddress& address) {
+    CHECK(test_writer_ != nullptr);
+    test_writer_->set_peer_address(address);
+  }
+
+ private:
+  QuicPacketWriterWrapper* test_writer_ = nullptr;
+  // The last incoming packet, iff |track_last_incoming_packet_| is true.
+  std::unique_ptr<QuicReceivedPacket> last_incoming_packet_;
+  // If true, copy each packet from ProcessPacket into |last_incoming_packet_|
+  bool track_last_incoming_packet_ = false;
+};
 
 MockableQuicClient::MockableQuicClient(
     QuicSocketAddress server_address,
@@ -142,25 +192,17 @@
     const QuicVersionVector& supported_versions,
     EpollServer* epoll_server,
     std::unique_ptr<ProofVerifier> proof_verifier)
-    : QuicClient(server_address,
-                 server_id,
-                 supported_versions,
-                 config,
-                 epoll_server,
-                 QuicWrapUnique(
-                     new RecordingProofVerifier(std::move(proof_verifier)))),
-      override_connection_id_(0),
-      test_writer_(nullptr),
-      track_last_incoming_packet_(false) {}
-
-void MockableQuicClient::ProcessPacket(const QuicSocketAddress& self_address,
-                                       const QuicSocketAddress& peer_address,
-                                       const QuicReceivedPacket& packet) {
-  QuicClient::ProcessPacket(self_address, peer_address, packet);
-  if (track_last_incoming_packet_) {
-    last_incoming_packet_ = packet.Clone();
-  }
-}
+    : QuicClient(
+          server_address,
+          server_id,
+          supported_versions,
+          config,
+          epoll_server,
+          QuicMakeUnique<MockableQuicClientEpollNetworkHelper>(epoll_server,
+                                                               this),
+          QuicWrapUnique(
+              new RecordingProofVerifier(std::move(proof_verifier)))),
+      override_connection_id_(0) {}
 
 MockableQuicClient::~MockableQuicClient() {
   if (connected()) {
@@ -168,13 +210,16 @@
   }
 }
 
-QuicPacketWriter* MockableQuicClient::CreateQuicPacketWriter() {
-  QuicPacketWriter* writer = QuicClient::CreateQuicPacketWriter();
-  if (!test_writer_) {
-    return writer;
-  }
-  test_writer_->set_writer(writer);
-  return test_writer_;
+MockableQuicClientEpollNetworkHelper*
+MockableQuicClient::mockable_network_helper() {
+  return static_cast<MockableQuicClientEpollNetworkHelper*>(
+      epoll_network_helper());
+}
+
+const MockableQuicClientEpollNetworkHelper*
+MockableQuicClient::mockable_network_helper() const {
+  return static_cast<const MockableQuicClientEpollNetworkHelper*>(
+      epoll_network_helper());
 }
 
 QuicConnectionId MockableQuicClient::GenerateNewConnectionId() {
@@ -182,19 +227,24 @@
                                  : QuicClient::GenerateNewConnectionId();
 }
 
-// Takes ownership of writer.
-void MockableQuicClient::UseWriter(QuicPacketWriterWrapper* writer) {
-  CHECK(test_writer_ == nullptr);
-  test_writer_ = writer;
-}
-
 void MockableQuicClient::UseConnectionId(QuicConnectionId connection_id) {
   override_connection_id_ = connection_id;
 }
 
+void MockableQuicClient::UseWriter(QuicPacketWriterWrapper* writer) {
+  mockable_network_helper()->UseWriter(writer);
+}
+
 void MockableQuicClient::set_peer_address(const QuicSocketAddress& address) {
-  CHECK(test_writer_ != nullptr);
-  test_writer_->set_peer_address(address);
+  mockable_network_helper()->set_peer_address(address);
+}
+
+const QuicReceivedPacket* MockableQuicClient::last_incoming_packet() {
+  return mockable_network_helper()->last_incoming_packet();
+}
+
+void MockableQuicClient::set_track_last_incoming_packet(bool track) {
+  mockable_network_helper()->set_track_last_incoming_packet(track);
 }
 
 QuicTestClient::QuicTestClient(QuicSocketAddress server_address,
@@ -329,7 +379,7 @@
     if (headers) {
       new_headers.reset(new SpdyHeaderBlock(headers->Clone()));
     }
-    std::unique_ptr<QuicClientBase::QuicDataToResend> data_to_resend(
+    std::unique_ptr<QuicSpdyClientBase::QuicDataToResend> data_to_resend(
         new TestClientDataToResend(std::move(new_headers), body, fin, this,
                                    ack_listener));
     client()->MaybeAddQuicDataToResend(std::move(data_to_resend));
@@ -502,7 +552,7 @@
 }
 
 QuicSocketAddress QuicTestClient::local_address() const {
-  return client_->GetLatestClientAddress();
+  return client_->network_helper()->GetLatestClientAddress();
 }
 
 void QuicTestClient::ClearPerRequestState() {
diff --git a/net/tools/quic/test_tools/quic_test_client.h b/net/tools/quic/test_tools/quic_test_client.h
index 26fca3c..cd9fa84 100644
--- a/net/tools/quic/test_tools/quic_test_client.h
+++ b/net/tools/quic/test_tools/quic_test_client.h
@@ -27,6 +27,8 @@
 
 namespace test {
 
+class MockableQuicClientEpollNetworkHelper;
+
 // A quic client which allows mocking out reads and writes.
 class MockableQuicClient : public QuicClient {
  public:
@@ -50,30 +52,23 @@
 
   ~MockableQuicClient() override;
 
-  void ProcessPacket(const QuicSocketAddress& self_address,
-                     const QuicSocketAddress& peer_address,
-                     const QuicReceivedPacket& packet) override;
-
-  QuicPacketWriter* CreateQuicPacketWriter() override;
   QuicConnectionId GenerateNewConnectionId() override;
-  void UseWriter(QuicPacketWriterWrapper* writer);
   void UseConnectionId(QuicConnectionId connection_id);
-  const QuicReceivedPacket* last_incoming_packet() {
-    return last_incoming_packet_.get();
-  }
-  void set_track_last_incoming_packet(bool track) {
-    track_last_incoming_packet_ = track;
-  }
+
+  void UseWriter(QuicPacketWriterWrapper* writer);
   void set_peer_address(const QuicSocketAddress& address);
+  // The last incoming packet, iff |track_last_incoming_packet| is true.
+  const QuicReceivedPacket* last_incoming_packet();
+  // If true, copy each packet from ProcessPacket into |last_incoming_packet|
+  void set_track_last_incoming_packet(bool track);
+
+  // Casts the network helper to a MockableQuicClientEpollNetworkHelper.
+  MockableQuicClientEpollNetworkHelper* mockable_network_helper();
+  const MockableQuicClientEpollNetworkHelper* mockable_network_helper() const;
 
  private:
   QuicConnectionId override_connection_id_;  // ConnectionId to use, if nonzero
-  QuicPacketWriterWrapper* test_writer_;
   CachedNetworkParameters cached_network_paramaters_;
-  // The last incoming packet, iff |track_last_incoming_packet_| is true.
-  std::unique_ptr<QuicReceivedPacket> last_incoming_packet_;
-  // If true, copy each packet from ProcessPacket into |last_incoming_packet_|
-  bool track_last_incoming_packet_;
 
   DISALLOW_COPY_AND_ASSIGN(MockableQuicClient);
 };
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 061482d..7a0dd810 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -10904,6 +10904,1582 @@
       }
     ]
   },
+  "Out of Process Profiling Android": {
+    "gtest_tests": [
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "angle_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "base_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "blink_heap_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "breakpad_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "cacheinvalidation_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "capture_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "cast_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "cc_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "chrome_public_test_apk"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "chrome_sync_shell_test_apk"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "components_browsertests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "components_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "content_shell_test_apk"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "content_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "crypto_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "device_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "display_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "events_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "gcm_unit_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "gfx_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "gl_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "gl_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "google_apis_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "gpu_ipc_service_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "gpu_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "ipc_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "jingle_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "media_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "midi_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "mojo_common_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "mojo_public_bindings_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "mojo_public_system_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "mojo_system_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "net_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "oop_heap_profiling_unit_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "remoting_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "sandbox_linux_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "services_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "skia_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "sql_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "storage_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "ui_android_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "ui_base_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "ui_touch_selection_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "unit_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "url_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "webkit_unit_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_type": "coho"
+            }
+          ]
+        },
+        "test": "wtf_unittests"
+      }
+    ]
+  },
+  "Out of Process Profiling Linux": {
+    "gtest_tests": [
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "accessibility_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "angle_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "app_shell_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "aura_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "base_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_heap_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_platform_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "shards": 5
+        },
+        "test": "browser_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "cacheinvalidation_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "capture_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "cast_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "cc_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "chromedriver_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "components_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "compositor_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "content_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "crypto_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "dbus_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "device_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "display_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "events_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "extensions_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "gcm_unit_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "gfx_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "google_apis_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "gpu_ipc_service_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "gpu_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "interactive_ui_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "ipc_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "jingle_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "media_blink_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "media_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "midi_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "mojo_common_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "mojo_public_bindings_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "mojo_public_system_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "mojo_system_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "nacl_helper_nonsfi_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "nacl_loader_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "native_theme_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "net_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "oop_heap_profiling_unit_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "pdf_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "ppapi_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "printing_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "remoting_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "sandbox_linux_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "services_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "skia_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "sql_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "storage_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "ui_base_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "ui_touch_selection_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "unit_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "url_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "views_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "webkit_unit_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "wm_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "wtf_unittests"
+      }
+    ]
+  },
+  "Out of Process Profiling Mac": {
+    "gtest_tests": [
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "accessibility_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "angle_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "shards": 5
+        },
+        "test": "base_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_heap_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_platform_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "shards": 10
+        },
+        "test": "browser_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "cacheinvalidation_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "capture_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "cast_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "cc_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "chromedriver_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "components_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "content_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "crypto_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "display_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "extensions_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "gcm_unit_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "gfx_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "google_apis_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "gpu_ipc_service_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "gpu_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "interactive_ui_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "ipc_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "jingle_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "media_blink_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "media_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "midi_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "mojo_common_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "mojo_public_bindings_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "mojo_public_system_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "mojo_system_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "native_theme_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "net_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "oop_heap_profiling_unit_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "pdf_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "ppapi_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "printing_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "remoting_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "sandbox_mac_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "services_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "skia_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "sql_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "sync_integration_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "ui_base_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "unit_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "url_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "views_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "webkit_unit_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "wtf_unittests"
+      }
+    ],
+    "isolated_scripts": [
+      {
+        "isolate_name": "telemetry_gpu_unittests",
+        "name": "telemetry_gpu_unittests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        }
+      },
+      {
+        "isolate_name": "telemetry_perf_unittests",
+        "name": "telemetry_perf_unittests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "hard_timeout": 960,
+          "shards": 12
+        }
+      },
+      {
+        "isolate_name": "telemetry_unittests",
+        "name": "telemetry_unittests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "shards": 2
+        }
+      }
+    ]
+  },
+  "Out of Process Profiling Windows": {
+    "gtest_tests": [
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "angle_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "base_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_heap_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "blink_platform_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "shards": 10
+        },
+        "test": "browser_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "cacheinvalidation_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "capture_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "chrome_app_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "chrome_elf_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "components_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "content_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "courgette_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "crypto_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "device_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "extensions_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "gcm_unit_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "google_apis_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "gpu_ipc_service_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "gpu_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "installer_util_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "ipc_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "jingle_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "media_blink_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "media_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "midi_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "native_theme_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "net_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "oop_heap_profiling_unit_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "pdf_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "ppapi_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "printing_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "remoting_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "sbox_integration_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "sbox_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "sbox_validation_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "services_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "setup_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "skia_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "sql_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "ui_base_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "unit_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "url_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "views_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "webkit_unit_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "wtf_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "zucchini_unittests"
+      }
+    ]
+  },
   "Ozone Linux": {
     "additional_compile_targets": [
       "chrome"
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl
index 71a4061..f0c0957 100644
--- a/testing/buildbot/gn_isolate_map.pyl
+++ b/testing/buildbot/gn_isolate_map.pyl
@@ -777,6 +777,10 @@
     "label": "//chrome/installer/mini_installer:next_version_mini_installer",
     "type": "additional_compile_target",
   },
+  "oop_heap_profiling_unit_tests": {
+    "label": "//chrome/profiling:unit_tests",
+    "type": "console_test_launcher",
+  },
   "ozone_unittests": {
     "label": "//ui/ozone:ozone_unittests",
     "type": "console_test_launcher",
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 8962dcc6..b655dcd4 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -750,7 +750,7 @@
             ],
             "experiments": [
                 {
-                    "name": "DirectCompositionOverlays",
+                    "name": "Enabled",
                     "enable_features": [
                         "DirectCompositionOverlays"
                     ]
diff --git a/third_party/WebKit/LayoutTests/external/wpt/XMLHttpRequest/access-control-basic-allow-preflight-cache-invalidation-by-header.htm b/third_party/WebKit/LayoutTests/external/wpt/XMLHttpRequest/access-control-basic-allow-preflight-cache-invalidation-by-header.htm
new file mode 100644
index 0000000..5a1e396
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/XMLHttpRequest/access-control-basic-allow-preflight-cache-invalidation-by-header.htm
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Preflight cache should be invalidated in presence of custom header</title>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="/common/get-host-info.sub.js"></script>
+    <script src="/common/utils.js"></script>
+  </head>
+  <body>
+    <script type="text/javascript">
+    const uuid = token();
+    const xhr = new XMLHttpRequest;
+
+    async_test(function(test) {
+      xhr.onerror = test.unreached_func("FAIL: Network error.");
+      xhr.onload = test.step_func(function() {
+        // Token reset.  We can start the test now.
+        assert_equals(xhr.responseText, "PASS");
+        firstRequest();
+      });
+
+      xhr.open("GET", get_host_info().HTTP_REMOTE_ORIGIN + "/XMLHttpRequest/resources/reset-token.py?token=" + uuid, true);
+      xhr.send();
+
+      function firstRequest() {
+        xhr.onload = test.step_func(function() {
+          assert_equals(xhr.responseText, "PASS: First PUT request.");
+          secondRequest();
+        });
+        xhr.open("PUT", get_host_info().HTTP_REMOTE_ORIGIN + "/XMLHttpRequest/resources/access-control-basic-preflight-cache-invalidation.py?token=" + uuid, true);
+        xhr.send();
+      }
+
+      function secondRequest() {
+        xhr.onload = test.step_func(function() {
+          assert_equals(xhr.responseText, "PASS: Second OPTIONS request was sent.");
+          test.done();
+        });
+        // Send a header not included in the inital cache.
+        xhr.open("PUT", get_host_info().HTTP_REMOTE_ORIGIN + "/XMLHttpRequest/resources/access-control-basic-preflight-cache-invalidation.py?token=" + uuid, true);
+        xhr.setRequestHeader("x-test", "headerValue");
+        xhr.send();
+      }
+    }, "Preflight cache should be invalidated in presence of custom header");
+    </script>
+  </body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/XMLHttpRequest/resources/access-control-basic-preflight-cache-invalidation.py b/third_party/WebKit/LayoutTests/external/wpt/XMLHttpRequest/resources/access-control-basic-preflight-cache-invalidation.py
new file mode 100644
index 0000000..6dc8a2a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/XMLHttpRequest/resources/access-control-basic-preflight-cache-invalidation.py
@@ -0,0 +1,47 @@
+def main(request, response):
+    def fail(message):
+        response.content = "FAIL " + request.method + ": " + str(message)
+
+    def getState(token):
+        server_state = request.server.stash.take(token)
+        if not server_state:
+            return "Uninitialized"
+        return server_state
+
+    def setState(state, token):
+        request.server.stash.put(token, state)
+
+    response.headers.set("Access-Control-Allow-Origin", request.headers.get("origin"))
+    response.headers.set("Access-Control-Allow-Credentials", "true")
+    token = request.GET.first("token", None)
+    state = getState(token)
+
+    if state == "Uninitialized":
+        if request.method == "OPTIONS":
+            response.headers.set("Access-Control-Allow-Methods", "PUT")
+            response.headers.set("Access-Control-Max-Age", 10)
+            setState("OPTIONSSent", token)
+        else:
+            fail(state)
+    elif state == "OPTIONSSent":
+        if request.method == "PUT":
+            response.content = "PASS: First PUT request."
+            setState("FirstPUTSent", token)
+        else:
+            fail(state)
+    elif state == "FirstPUTSent":
+        if request.method == "OPTIONS":
+            response.headers.set("Access-Control-Allow-Methods", "PUT, XMETHOD")
+            response.headers.set("Access-Control-Allow-Headers", "x-test")
+            setState("SecondOPTIONSSent", token)
+        elif request.method == "PUT":
+            fail("Second PUT request sent without preflight")
+        else:
+            fail(state)
+    elif state == "SecondOPTIONSSent":
+        if request.method == "PUT" or request.method == "XMETHOD":
+            response.content = "PASS: Second OPTIONS request was sent."
+        else:
+            fail(state)
+    else:
+        fail(state)
diff --git a/third_party/WebKit/LayoutTests/external/wpt/XMLHttpRequest/resources/reset-token.py b/third_party/WebKit/LayoutTests/external/wpt/XMLHttpRequest/resources/reset-token.py
new file mode 100644
index 0000000..5c9a577
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/XMLHttpRequest/resources/reset-token.py
@@ -0,0 +1,5 @@
+def main(request, response):
+    response.headers.set("Access-Control-Allow-Origin", request.headers.get("origin"))
+    token = request.GET["token"]
+    request.server.stash.put(token, "")
+    response.content = "PASS"
diff --git a/third_party/WebKit/LayoutTests/external/wpt/payment-request/payment-request-update-event-updatewith-method.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/payment-request/payment-request-update-event-updatewith-method.https-expected.txt
index 383d733..83bad4c 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/payment-request/payment-request-update-event-updatewith-method.https-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/payment-request/payment-request-update-event-updatewith-method.https-expected.txt
@@ -1,8 +1,6 @@
 This is a testharness.js-based test.
 FAIL Let target be the request which is dispatching the event. Failed to construct 'PaymentRequest': The provided value cannot be converted to a sequence.
-FAIL Calling .updateWith() with an undispatched untrusted event throws "InvalidStateError" assert_throws: untrusted event of type "just a test" must throw "InvalidStateError" function "() => {
-        ev.updateWith(Promise.resolve());
-      }" did not throw
+PASS Calling .updateWith() with an undispatched untrusted event throws "InvalidStateError" 
 FAIL Calling .updateWith() with a dispatched, untrusted event, throws "InvalidStateError" Failed to construct 'PaymentRequest': The provided value cannot be converted to a sequence.
 Harness: the test ran to completion.
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/xmlhttprequest/access-control-basic-allow-preflight-cache-invalidation-by-header-expected.txt b/third_party/WebKit/LayoutTests/http/tests/xmlhttprequest/access-control-basic-allow-preflight-cache-invalidation-by-header-expected.txt
deleted file mode 100644
index f81335a3..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/xmlhttprequest/access-control-basic-allow-preflight-cache-invalidation-by-header-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-PASS: First PUT request.
-PASS: First request complete
-PASS: Second OPTIONS request was sent.
-PASS: Second request complete
-
diff --git a/third_party/WebKit/LayoutTests/http/tests/xmlhttprequest/access-control-basic-allow-preflight-cache-invalidation-by-header.html b/third_party/WebKit/LayoutTests/http/tests/xmlhttprequest/access-control-basic-allow-preflight-cache-invalidation-by-header.html
deleted file mode 100644
index d9dbf1f..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/xmlhttprequest/access-control-basic-allow-preflight-cache-invalidation-by-header.html
+++ /dev/null
@@ -1,74 +0,0 @@
-<html>
-<body>
-<pre id='console'></pre>
-<script type="text/javascript">
-function log(message)
-{
-    document.getElementById('console').appendChild(document.createTextNode(message + "\n"));
-}
-
-if (window.testRunner) {
-    testRunner.dumpAsText();
-    testRunner.waitUntilDone();
-}
-
-function errorHandler(event)
-{
-    log("FAIL: Network error. ");
-    if (window.testRunner)
-        testRunner.notifyDone();
-}
-
-var filename = "filename=preflightCacheInvalidationByHeader.txt";
-
-var xhr = new XMLHttpRequest;
-xhr.onerror = errorHandler;
-
-start = function()
-{
-    // Temp file removed.  We can start the test now.
-    if (xhr.readyState == xhr.DONE) {
-        firstRequest();
-    }
-}
-
-xhr.open("GET", "/resources/reset-temp-file.php?" + filename, true);
-xhr.onreadystatechange = start;
-xhr.send();
-
-function firstRequest()
-{
-    xhr.onreadystatechange = function()
-    {
-        if (xhr.readyState == xhr.DONE) {
-            log(xhr.responseText);
-            log("PASS: First request complete");
-            secondRequest();
-        }
-    }
-
-    xhr.open("PUT", "http://localhost:8000/xmlhttprequest/resources/access-control-basic-preflight-cache-invalidation.php?" + filename, true);
-    xhr.send();
-}
-
-function secondRequest()
-{
-    xhr.onreadystatechange = function()
-    {
-        if (xhr.readyState == xhr.DONE) {
-            log(xhr.responseText);
-            log("PASS: Second request complete");
-            if (window.testRunner)
-                testRunner.notifyDone();
-        }
-    }
-
-    // Send a header not included in the inital cache. 
-    xhr.open("PUT", "http://localhost:8000/xmlhttprequest/resources/access-control-basic-preflight-cache-invalidation.php?" + filename, true);
-    xhr.setRequestHeader("x-webkit-test", "headerValue");
-    xhr.send();
-}
-
-</script>
-</body>
-</html>
diff --git a/third_party/WebKit/Source/core/paint/InlineTextBoxPainter.cpp b/third_party/WebKit/Source/core/paint/InlineTextBoxPainter.cpp
index ec83afe..8108d34 100644
--- a/third_party/WebKit/Source/core/paint/InlineTextBoxPainter.cpp
+++ b/third_party/WebKit/Source/core/paint/InlineTextBoxPainter.cpp
@@ -779,8 +779,8 @@
       pts, colors, nullptr, ARRAY_SIZE(colors), SkShader::kClamp_TileMode));
   PaintRecorder recorder;
   recorder.beginRecording(kMarkerWidth, kMarkerHeight);
-  recorder.getRecordingCanvas()->drawCircle(kR, kR, kR, flags);
-
+  recorder.getRecordingCanvas()->drawOval(SkRect::MakeWH(2 * kR, 2 * kR),
+                                          flags);
   return recorder.finishRecordingAsPicture();
 }
 
diff --git a/third_party/WebKit/Source/devtools/front_end/elements/stylesSidebarPane.css b/third_party/WebKit/Source/devtools/front_end/elements/stylesSidebarPane.css
index ffc732d..4675fca 100644
--- a/third_party/WebKit/Source/devtools/front_end/elements/stylesSidebarPane.css
+++ b/third_party/WebKit/Source/devtools/front_end/elements/stylesSidebarPane.css
@@ -145,6 +145,10 @@
 .styles-sidebar-pane-toolbar-container {
     flex-shrink: 0;
     overflow: hidden;
+    position: sticky;
+    top: 0;
+    background-color: #fff;
+    z-index: 1;
 }
 
 .styles-sidebar-pane-toolbar {
@@ -196,6 +200,7 @@
     bottom: 0;
     visibility: hidden;
     background-color: rgba(255, 255, 255, 0.9);
+    z-index: 0;
 }
 
 .styles-pane:not(.is-editing-style) .styles-section.matched-styles:not(.read-only):hover .sidebar-pane-section-toolbar {
diff --git a/third_party/WebKit/Source/modules/payments/PaymentRequestUpdateEvent.cpp b/third_party/WebKit/Source/modules/payments/PaymentRequestUpdateEvent.cpp
index 109ecb6..12d9b03 100644
--- a/third_party/WebKit/Source/modules/payments/PaymentRequestUpdateEvent.cpp
+++ b/third_party/WebKit/Source/modules/payments/PaymentRequestUpdateEvent.cpp
@@ -104,16 +104,16 @@
 void PaymentRequestUpdateEvent::updateWith(ScriptState* script_state,
                                            ScriptPromise promise,
                                            ExceptionState& exception_state) {
-  if (!updater_)
-    return;
-
-  if (!IsBeingDispatched()) {
+  if (!isTrusted()) {
     exception_state.ThrowDOMException(
         kInvalidStateError,
-        "Cannot update details when the event is not being dispatched");
+        "Cannot update details when the event is not trusted");
     return;
   }
 
+  if (!updater_)
+    return;
+
   if (wait_for_update_) {
     exception_state.ThrowDOMException(kInvalidStateError,
                                       "Cannot update details twice");
diff --git a/third_party/WebKit/Source/modules/payments/PaymentRequestUpdateEventTest.cpp b/third_party/WebKit/Source/modules/payments/PaymentRequestUpdateEventTest.cpp
index f65812d..852d11c9 100644
--- a/third_party/WebKit/Source/modules/payments/PaymentRequestUpdateEventTest.cpp
+++ b/third_party/WebKit/Source/modules/payments/PaymentRequestUpdateEventTest.cpp
@@ -40,6 +40,7 @@
   PaymentRequestUpdateEvent* event = PaymentRequestUpdateEvent::Create(
       scope.GetExecutionContext(), EventTypeNames::shippingaddresschange);
   MockPaymentUpdater* updater = new MockPaymentUpdater;
+  event->SetTrusted(true);
   event->SetPaymentDetailsUpdater(updater);
   event->SetEventPhase(Event::kCapturingPhase);
   ScriptPromiseResolver* payment_details =
@@ -59,6 +60,7 @@
   PaymentRequestUpdateEvent* event = PaymentRequestUpdateEvent::Create(
       scope.GetExecutionContext(), EventTypeNames::shippingaddresschange);
   MockPaymentUpdater* updater = new MockPaymentUpdater;
+  event->SetTrusted(true);
   event->SetPaymentDetailsUpdater(updater);
   event->SetEventPhase(Event::kCapturingPhase);
   ScriptPromiseResolver* payment_details =
@@ -92,6 +94,7 @@
   PaymentRequestUpdateEvent* event = PaymentRequestUpdateEvent::Create(
       scope.GetExecutionContext(), EventTypeNames::shippingaddresschange);
   MockPaymentUpdater* updater = new MockPaymentUpdater;
+  event->SetTrusted(true);
   event->SetPaymentDetailsUpdater(updater);
   event->SetEventPhase(Event::kCapturingPhase);
   event->updateWith(
@@ -112,6 +115,7 @@
   V8TestingScope scope;
   PaymentRequestUpdateEvent* event = PaymentRequestUpdateEvent::Create(
       scope.GetExecutionContext(), EventTypeNames::shippingaddresschange);
+  event->SetTrusted(true);
 
   event->updateWith(
       scope.GetScriptState(),
@@ -131,6 +135,7 @@
   PaymentRequestUpdateEvent* event = PaymentRequestUpdateEvent::Create(
       scope.GetExecutionContext(), EventTypeNames::shippingaddresschange);
   event->SetPaymentDetailsUpdater(request);
+  event->SetTrusted(true);
   EXPECT_FALSE(scope.GetExceptionState().HadException());
 
   String error_message;
@@ -162,6 +167,7 @@
       BuildPaymentDetailsInitForTest(), scope.GetExceptionState());
   PaymentRequestUpdateEvent* event = PaymentRequestUpdateEvent::Create(
       scope.GetExecutionContext(), EventTypeNames::shippingoptionchange);
+  event->SetTrusted(true);
   event->SetPaymentDetailsUpdater(request);
   EXPECT_FALSE(scope.GetExceptionState().HadException());
 
@@ -195,6 +201,7 @@
   EXPECT_FALSE(scope.GetExceptionState().HadException());
   PaymentRequestUpdateEvent* event = PaymentRequestUpdateEvent::Create(
       scope.GetExecutionContext(), EventTypeNames::shippingaddresschange);
+  event->SetTrusted(true);
   event->SetPaymentDetailsUpdater(request);
   event->SetEventPhase(Event::kCapturingPhase);
   ScriptPromiseResolver* payment_details =
@@ -227,6 +234,7 @@
   EXPECT_FALSE(scope.GetExceptionState().HadException());
   PaymentRequestUpdateEvent* event = PaymentRequestUpdateEvent::Create(
       scope.GetExecutionContext(), EventTypeNames::shippingoptionchange);
+  event->SetTrusted(true);
   event->SetPaymentDetailsUpdater(request);
   event->SetEventPhase(Event::kCapturingPhase);
   ScriptPromiseResolver* payment_details =
@@ -249,5 +257,19 @@
   payment_details->Resolve("foo");
 }
 
+TEST(PaymentRequestUpdateEventTest, NotAllowUntrustedEvent) {
+  V8TestingScope scope;
+  PaymentRequestUpdateEvent* event = PaymentRequestUpdateEvent::Create(
+      scope.GetExecutionContext(), EventTypeNames::shippingaddresschange);
+  event->SetTrusted(false);
+
+  event->updateWith(
+      scope.GetScriptState(),
+      ScriptPromiseResolver::Create(scope.GetScriptState())->Promise(),
+      scope.GetExceptionState());
+
+  EXPECT_TRUE(scope.GetExceptionState().HadException());
+}
+
 }  // namespace
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/InstanceCountersMemoryDumpProvider.cc b/third_party/WebKit/Source/platform/InstanceCountersMemoryDumpProvider.cc
index bf3678e..a04801c5 100644
--- a/third_party/WebKit/Source/platform/InstanceCountersMemoryDumpProvider.cc
+++ b/third_party/WebKit/Source/platform/InstanceCountersMemoryDumpProvider.cc
@@ -21,7 +21,7 @@
     base::trace_event::ProcessMemoryDump* memory_dump) {
   using base::trace_event::MemoryAllocatorDump;
 #define DUMP_COUNTER(CounterType)                                     \
-  memory_dump->CreateAllocatorDump("counter/" #CounterType)           \
+  memory_dump->CreateAllocatorDump("blink_objects/" #CounterType)     \
       ->AddScalar("object_count", MemoryAllocatorDump::kUnitsObjects, \
                   InstanceCounters::CounterValue(                     \
                       InstanceCounters::k##CounterType##Counter));
diff --git a/third_party/WebKit/Source/platform/graphics/test/MockPaintCanvas.h b/third_party/WebKit/Source/platform/graphics/test/MockPaintCanvas.h
index c386128..3776ba9a1 100644
--- a/third_party/WebKit/Source/platform/graphics/test/MockPaintCanvas.h
+++ b/third_party/WebKit/Source/platform/graphics/test/MockPaintCanvas.h
@@ -59,9 +59,6 @@
                void(const SkRRect& outer,
                     const SkRRect& inner,
                     const PaintFlags& flags));
-  MOCK_METHOD4(
-      drawCircle,
-      void(SkScalar cx, SkScalar cy, SkScalar radius, const PaintFlags& flags));
   MOCK_METHOD5(drawArc,
                void(const SkRect& oval,
                     SkScalar start_angle,
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl.cc b/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl.cc
index ed5e756..a5ddef26 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl.cc
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl.cc
@@ -27,6 +27,7 @@
 #include "platform/scheduler/renderer/web_view_scheduler_impl.h"
 #include "platform/scheduler/renderer/webthread_impl_for_renderer_scheduler.h"
 #include "public/platform/Platform.h"
+#include "public/platform/scheduler/renderer_process_type.h"
 
 namespace blink {
 namespace scheduler {
@@ -165,7 +166,9 @@
 
 #define TASK_DURATION_METRIC_NAME "RendererScheduler.TaskDurationPerQueueType2"
 #define TASK_COUNT_METRIC_NAME "RendererScheduler.TaskCountPerQueueType"
-#define MAIN_THREAD_LOAD_METRIC_NAME "RendererScheduler.RendererMainThreadLoad4"
+#define MAIN_THREAD_LOAD_METRIC_NAME "RendererScheduler.RendererMainThreadLoad5"
+#define EXTENSIONS_MAIN_THREAD_LOAD_METRIC_NAME \
+  MAIN_THREAD_LOAD_METRIC_NAME ".Extension"
 
 RendererSchedulerImpl::MainThreadOnly::MainThreadOnly(
     RendererSchedulerImpl* renderer_scheduler_impl,
@@ -252,7 +255,8 @@
       hidden_task_duration_reporter(TASK_DURATION_METRIC_NAME ".Hidden"),
       visible_task_duration_reporter(TASK_DURATION_METRIC_NAME ".Visible"),
       hidden_music_task_duration_reporter(TASK_DURATION_METRIC_NAME
-                                          ".HiddenMusic") {
+                                          ".HiddenMusic"),
+      process_type(RendererProcessType::kRenderer) {
   main_thread_load_tracker.Resume(now);
   foreground_main_thread_load_tracker.Resume(now);
 }
@@ -1863,6 +1867,10 @@
   return main_thread_seems_unresponsive;
 }
 
+void RendererSchedulerImpl::SetRendererProcessType(RendererProcessType type) {
+  main_thread_only().process_type = type;
+}
+
 void RendererSchedulerImpl::RegisterTimeDomain(TimeDomain* time_domain) {
   helper_.RegisterTimeDomain(time_domain);
 }
@@ -2157,6 +2165,13 @@
   DCHECK_LE(load_percentage, 100);
 
   UMA_HISTOGRAM_PERCENTAGE(MAIN_THREAD_LOAD_METRIC_NAME, load_percentage);
+
+  if (main_thread_only().process_type ==
+      RendererProcessType::kExtensionRenderer) {
+    UMA_HISTOGRAM_PERCENTAGE(EXTENSIONS_MAIN_THREAD_LOAD_METRIC_NAME,
+                             load_percentage);
+  }
+
   TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
                  "RendererScheduler.RendererMainThreadLoad", load_percentage);
 }
@@ -2167,14 +2182,23 @@
   int load_percentage = static_cast<int>(load * 100);
   DCHECK_LE(load_percentage, 100);
 
-  UMA_HISTOGRAM_PERCENTAGE(MAIN_THREAD_LOAD_METRIC_NAME ".Foreground",
-                           load_percentage);
+  switch (main_thread_only().process_type) {
+    case RendererProcessType::kExtensionRenderer:
+      UMA_HISTOGRAM_PERCENTAGE(EXTENSIONS_MAIN_THREAD_LOAD_METRIC_NAME
+                               ".Foreground",
+                               load_percentage);
+      break;
+    case RendererProcessType::kRenderer:
+      UMA_HISTOGRAM_PERCENTAGE(MAIN_THREAD_LOAD_METRIC_NAME ".Foreground",
+                               load_percentage);
 
-  if (time - main_thread_only().background_status_changed_at >
-      base::TimeDelta::FromMinutes(1)) {
-    UMA_HISTOGRAM_PERCENTAGE(MAIN_THREAD_LOAD_METRIC_NAME
-                             ".Foreground.AfterFirstMinute",
-                             load_percentage);
+      if (time - main_thread_only().background_status_changed_at >
+          base::TimeDelta::FromMinutes(1)) {
+        UMA_HISTOGRAM_PERCENTAGE(MAIN_THREAD_LOAD_METRIC_NAME
+                                 ".Foreground.AfterFirstMinute",
+                                 load_percentage);
+      }
+      break;
   }
 
   TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
@@ -2188,14 +2212,23 @@
   int load_percentage = static_cast<int>(load * 100);
   DCHECK_LE(load_percentage, 100);
 
-  UMA_HISTOGRAM_PERCENTAGE(MAIN_THREAD_LOAD_METRIC_NAME ".Background",
-                           load_percentage);
+  switch (main_thread_only().process_type) {
+    case RendererProcessType::kExtensionRenderer:
+      UMA_HISTOGRAM_PERCENTAGE(EXTENSIONS_MAIN_THREAD_LOAD_METRIC_NAME
+                               ".Background",
+                               load_percentage);
+      break;
+    case RendererProcessType::kRenderer:
+      UMA_HISTOGRAM_PERCENTAGE(MAIN_THREAD_LOAD_METRIC_NAME ".Background",
+                               load_percentage);
 
-  if (time - main_thread_only().background_status_changed_at >
-      base::TimeDelta::FromMinutes(1)) {
-    UMA_HISTOGRAM_PERCENTAGE(MAIN_THREAD_LOAD_METRIC_NAME
-                             ".Background.AfterFirstMinute",
-                             load_percentage);
+      if (time - main_thread_only().background_status_changed_at >
+          base::TimeDelta::FromMinutes(1)) {
+        UMA_HISTOGRAM_PERCENTAGE(MAIN_THREAD_LOAD_METRIC_NAME
+                                 ".Background.AfterFirstMinute",
+                                 load_percentage);
+      }
+      break;
   }
 
   TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl.h b/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl.h
index 599847a..d314ef9 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl.h
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl.h
@@ -127,6 +127,7 @@
   void SetRAILModeObserver(RAILModeObserver* observer) override;
   bool MainThreadSeemsUnresponsive(
       base::TimeDelta main_thread_responsiveness_threshold) override;
+  void SetRendererProcessType(RendererProcessType type) override;
 
   // RenderWidgetSignals::Observer implementation:
   void SetAllRenderWidgetsHidden(bool hidden) override;
@@ -619,6 +620,7 @@
     TaskDurationMetricReporter hidden_task_duration_reporter;
     TaskDurationMetricReporter visible_task_duration_reporter;
     TaskDurationMetricReporter hidden_music_task_duration_reporter;
+    RendererProcessType process_type;
   };
 
   struct AnyThread {
diff --git a/third_party/WebKit/Source/platform/scheduler/test/fake_renderer_scheduler.cc b/third_party/WebKit/Source/platform/scheduler/test/fake_renderer_scheduler.cc
index 4fbe36f..8e7b6527 100644
--- a/third_party/WebKit/Source/platform/scheduler/test/fake_renderer_scheduler.cc
+++ b/third_party/WebKit/Source/platform/scheduler/test/fake_renderer_scheduler.cc
@@ -125,5 +125,7 @@
   return false;
 }
 
+void FakeRendererScheduler::SetRendererProcessType(RendererProcessType type) {}
+
 }  // namespace scheduler
 }  // namespace blink
diff --git a/third_party/WebKit/public/BUILD.gn b/third_party/WebKit/public/BUILD.gn
index ff35a751..14bf01b 100644
--- a/third_party/WebKit/public/BUILD.gn
+++ b/third_party/WebKit/public/BUILD.gn
@@ -458,6 +458,7 @@
     "platform/scheduler/child/webthread_base.h",
     "platform/scheduler/renderer/render_widget_scheduling_state.h",
     "platform/scheduler/renderer/renderer_scheduler.h",
+    "platform/scheduler/renderer_process_type.h",
     "web/WebAXEnums.h",
     "web/WebAXObject.h",
     "web/WebActiveWheelFlingParameters.h",
diff --git a/third_party/WebKit/public/platform/scheduler/renderer/renderer_scheduler.h b/third_party/WebKit/public/platform/scheduler/renderer/renderer_scheduler.h
index 8b8e92e..9197280 100644
--- a/third_party/WebKit/public/platform/scheduler/renderer/renderer_scheduler.h
+++ b/third_party/WebKit/public/platform/scheduler/renderer/renderer_scheduler.h
@@ -36,6 +36,7 @@
 namespace blink {
 namespace scheduler {
 
+enum class RendererProcessType;
 class RenderWidgetSchedulingState;
 
 class BLINK_PLATFORM_EXPORT RendererScheduler : public ChildScheduler {
@@ -198,6 +199,10 @@
   virtual bool MainThreadSeemsUnresponsive(
       base::TimeDelta main_thread_responsiveness_threshold) = 0;
 
+  // Sets the kind of renderer process. Should be called on the main thread
+  // once.
+  virtual void SetRendererProcessType(RendererProcessType type) = 0;
+
  protected:
   RendererScheduler();
   DISALLOW_COPY_AND_ASSIGN(RendererScheduler);
diff --git a/third_party/WebKit/public/platform/scheduler/renderer_process_type.h b/third_party/WebKit/public/platform/scheduler/renderer_process_type.h
new file mode 100644
index 0000000..23a1db4
--- /dev/null
+++ b/third_party/WebKit/public/platform/scheduler/renderer_process_type.h
@@ -0,0 +1,19 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_WEBKIT_PUBLIC_PLATFORM_SCHEDULER_RENDERER_PROCESS_TYPE_H_
+#define THIRD_PARTY_WEBKIT_PUBLIC_PLATFORM_SCHEDULER_RENDERER_PROCESS_TYPE_H_
+
+namespace blink {
+namespace scheduler {
+
+enum class RendererProcessType {
+  kExtensionRenderer,
+  kRenderer,
+};
+
+}  // namespace scheduler
+}  // namespace blink
+
+#endif  // THIRD_PARTY_WEBKIT_PUBLIC_PLATFORM_SCHEDULER_RENDERER_PROCESS_TYPE_H_
diff --git a/third_party/WebKit/public/platform/scheduler/test/fake_renderer_scheduler.h b/third_party/WebKit/public/platform/scheduler/test/fake_renderer_scheduler.h
index a85be16..ce3a646 100644
--- a/third_party/WebKit/public/platform/scheduler/test/fake_renderer_scheduler.h
+++ b/third_party/WebKit/public/platform/scheduler/test/fake_renderer_scheduler.h
@@ -61,6 +61,7 @@
   void SetRAILModeObserver(RAILModeObserver* observer) override;
   bool MainThreadSeemsUnresponsive(
       base::TimeDelta main_thread_responsiveness_threshold) override;
+  void SetRendererProcessType(RendererProcessType type) override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(FakeRendererScheduler);
diff --git a/third_party/WebKit/public/platform/scheduler/test/mock_renderer_scheduler.h b/third_party/WebKit/public/platform/scheduler/test/mock_renderer_scheduler.h
index 814d5ff..8dd05d0 100644
--- a/third_party/WebKit/public/platform/scheduler/test/mock_renderer_scheduler.h
+++ b/third_party/WebKit/public/platform/scheduler/test/mock_renderer_scheduler.h
@@ -64,6 +64,7 @@
   MOCK_METHOD1(SetTopLevelBlameContext, void(base::trace_event::BlameContext*));
   MOCK_METHOD1(SetRAILModeObserver, void(RAILModeObserver*));
   MOCK_METHOD1(MainThreadSeemsUnresponsive, bool(base::TimeDelta));
+  MOCK_METHOD1(SetRendererProcessType, void(RendererProcessType));
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockRendererScheduler);
diff --git a/tools/gritsettings/resource_ids b/tools/gritsettings/resource_ids
index 1b4b9c95..22725ab 100644
--- a/tools/gritsettings/resource_ids
+++ b/tools/gritsettings/resource_ids
@@ -90,9 +90,6 @@
   "chrome/browser/resources/invalidations_resources.grd": {
     "includes": [11910],
   },
-  "chrome/browser/resources/md_policy/policy_resources.grd": {
-    "structures": [11920],
-  },
   "chrome/browser/resources/net_internals_resources.grd": {
     "includes": [11960],
   },
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 039bdd5..71125b82 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -207,6 +207,10 @@
       'Mojo Linux': 'release_trybot',
       'Mojo Windows': 'release_bot_x86_minimal_symbols',
       'Ozone Linux': 'ozone_linux_release_bot',
+      'Out of Process Profiling Android': 'release_bot_out_of_process_profiling',
+      'Out of Process Profiling Linux': 'release_bot_out_of_process_profiling',
+      'Out of Process Profiling Mac': 'release_bot_out_of_process_profiling',
+      'Out of Process Profiling Windows': 'release_bot_out_of_process_profiling',
       'Site Isolation Android': 'android_release_bot_minimal_symbols_arm64',
       'Site Isolation Linux': 'release_trybot',
       'Site Isolation Win': 'release_trybot_x86',
@@ -1442,6 +1446,10 @@
       'release_bot', 'mac_strip',
     ],
 
+    'release_bot_out_of_process_profiling': [
+      'release_bot', 'out_of_process_profiling',
+    ],
+
     'release_bot_x86': [
       'release_bot', 'x86',
     ],
@@ -1873,6 +1881,10 @@
       'gn_args': 'optimize_for_fuzzing=true',
     },
 
+    'out_of_process_profiling': {
+      'gn_args': 'enable_oop_heap_profiling=true',
+    },
+
     'ozone_linux': {
       'gn_args': ('ozone_auto_platforms=false ozone_platform_wayland=true '
                   'ozone_platform="x11" '
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index b8274150..ec1ccb6 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -20870,9 +20870,10 @@
 </enum>
 
 <enum name="InstantTethering_HostScanResult">
-  <int value="0" label="Notification not shown"/>
+  <int value="0" label="No hosts found"/>
   <int value="1" label="Notification shown for single host"/>
   <int value="2" label="Notification shown for multiple hosts"/>
+  <int value="3" label="Hosts found but no notification shown"/>
 </enum>
 
 <enum name="IntelMaxMicroArchitecture">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 5799cad8..b7343102 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -27426,15 +27426,14 @@
     enum="InstantTethering_HostScanResult">
   <owner>hansberry@chromium.com</owner>
   <summary>
-    Provides a breakdown of the results of each host scan. The result can be 1)
-    no notification shown (no hosts found), 2) a notification was shown for a
-    single available host, or 3) a notification was shown for multiple available
-    hosts. This metric is recorded after each host scan.
+    Provides a breakdown of the results of each host scan. This metric is
+    recorded after each host scan.
 
-    Comparing 1) against the sum of 2) and 3) can help determine the success
-    rate of host scans.
+    Comparing the number of times no hosts were found against the other buckets
+    can help determine how often a host scan is useful.
 
-    The sum of 2) and 3) can be used as a baseline of comparison against
+    The sum of the buckets capturing that a notification was shown can be used
+    as a baseline of comparison against
     InstantTethering.ConnectionToHostResult.ProvisioningFailureRate to roughly
     measure engagement with the notification.
   </summary>
@@ -63657,6 +63656,23 @@
 </histogram>
 
 <histogram name="RendererScheduler.RendererMainThreadLoad4" units="%">
+  <obsolete>
+    Replaced with RendererMainThreadLoad5 as of August 2017.
+  </obsolete>
+  <owner>altimin@chromium.org</owner>
+  <summary>
+    Renderer main thread load (percentage of time spent in tasks), reported in
+    one second chunks.
+
+    See http://bit.ly/chromium-renderer-main-thread-load-metric for details.
+
+    This metric is emitted when the renderer main thread task is completed or
+    renderer is backgrounded or foregrounded, at most once per second per
+    renderer amortized.
+  </summary>
+</histogram>
+
+<histogram name="RendererScheduler.RendererMainThreadLoad5" units="%">
   <owner>altimin@chromium.org</owner>
   <summary>
     Renderer main thread load (percentage of time spent in tasks), reported in
@@ -98564,19 +98580,28 @@
 </histogram_suffixes>
 
 <histogram_suffixes name="RendererScheduler.MainThreadLoadSplit" separator=".">
+  <suffix name="Extension" label="This only includes extension renderers"/>
   <suffix name="Background"
-      label="Main thread load when the renderer is backgrounded."/>
+      label="Main thread load when the renderer is backgrounded. This does
+             not include extension renderers."/>
+  <suffix name="Extension.Background"
+      label="Main thread load when the renderer is backgrounded. This only
+             includes extension renderers."/>
   <suffix name="Background.AfterFirstMinute"
       label="Main thread load when the renderer is backgrounded for longer
              than one minute. Most of loading tasks are expected to complete
-             by then."/>
+             by then. This does not include extension renderers."/>
   <suffix name="Foreground"
-      label="Main thread load when the renderer is foregrounded."/>
+      label="Main thread load when the renderer is foregrounded. This does
+             not include extension renderers."/>
+  <suffix name="Extension.Foreground"
+      label="Main thread load when the renderer is foregrounded. This only
+             includes extension renderers."/>
   <suffix name="Foreground.AfterFirstMinute"
       label="Main thread load when the renderer is foregrounded for longer
              than one minute. Most of loading tasks are expected to complete
-             by then."/>
-  <affected-histogram name="RendererScheduler.RendererMainThreadLoad4"/>
+             by then. This does not include extension renderers."/>
+  <affected-histogram name="RendererScheduler.RendererMainThreadLoad5"/>
 </histogram_suffixes>
 
 <histogram_suffixes name="RendererScheduler.TaskCountPerTaskLength"
diff --git a/ui/app_list/folder_image.cc b/ui/app_list/folder_image.cc
index 47513c8..1d5bb67 100644
--- a/ui/app_list/folder_image.cc
+++ b/ui/app_list/folder_image.cc
@@ -85,8 +85,7 @@
   flags.setStyle(cc::PaintFlags::kFill_Style);
   flags.setAntiAlias(true);
   flags.setColor(kFolderBubbleColor);
-  canvas->sk_canvas()->drawCircle(bubble_center.x(), bubble_center.y(),
-                                  kFolderBubbleRadius, flags);
+  canvas->DrawCircle(bubble_center, kFolderBubbleRadius, flags);
 
   if (icons_.size() == 0)
     return;
diff --git a/ui/aura/test/env_test_helper.h b/ui/aura/test/env_test_helper.h
index 4ea5020..5bc59d3 100644
--- a/ui/aura/test/env_test_helper.h
+++ b/ui/aura/test/env_test_helper.h
@@ -45,6 +45,9 @@
     env_->in_mus_shutdown_ = window_tree_client ? false : true;
   }
 
+  // Use to force Env::last_mouse_location() to return the value last set.
+  // This matters for MUS, which may not return the last explicitly set
+  // location.
   void SetAlwaysUseLastMouseLocation(bool value) {
     env_->always_use_last_mouse_location_ = value;
   }
diff --git a/ui/chromeos/resources/default_100_percent/default_user_images/illustration/avatar_basketball.png b/ui/chromeos/resources/default_100_percent/default_user_images/illustration/avatar_basketball.png
new file mode 100644
index 0000000..7001045
--- /dev/null
+++ b/ui/chromeos/resources/default_100_percent/default_user_images/illustration/avatar_basketball.png
Binary files differ
diff --git a/ui/chromeos/resources/default_100_percent/default_user_images/illustration/avatar_bike.png b/ui/chromeos/resources/default_100_percent/default_user_images/illustration/avatar_bike.png
new file mode 100644
index 0000000..610ec76
--- /dev/null
+++ b/ui/chromeos/resources/default_100_percent/default_user_images/illustration/avatar_bike.png
Binary files differ
diff --git a/ui/chromeos/resources/default_100_percent/default_user_images/illustration/avatar_bird.png b/ui/chromeos/resources/default_100_percent/default_user_images/illustration/avatar_bird.png
new file mode 100644
index 0000000..06d3c11f
--- /dev/null
+++ b/ui/chromeos/resources/default_100_percent/default_user_images/illustration/avatar_bird.png
Binary files differ
diff --git a/ui/chromeos/resources/default_100_percent/default_user_images/illustration/avatar_cheese.png b/ui/chromeos/resources/default_100_percent/default_user_images/illustration/avatar_cheese.png
new file mode 100644
index 0000000..6f441e6
--- /dev/null
+++ b/ui/chromeos/resources/default_100_percent/default_user_images/illustration/avatar_cheese.png
Binary files differ
diff --git a/ui/chromeos/resources/default_100_percent/default_user_images/illustration/avatar_football.png b/ui/chromeos/resources/default_100_percent/default_user_images/illustration/avatar_football.png
new file mode 100644
index 0000000..bc031488
--- /dev/null
+++ b/ui/chromeos/resources/default_100_percent/default_user_images/illustration/avatar_football.png
Binary files differ
diff --git a/ui/chromeos/resources/default_100_percent/default_user_images/illustration/avatar_ramen.png b/ui/chromeos/resources/default_100_percent/default_user_images/illustration/avatar_ramen.png
new file mode 100644
index 0000000..ef3aff8
--- /dev/null
+++ b/ui/chromeos/resources/default_100_percent/default_user_images/illustration/avatar_ramen.png
Binary files differ
diff --git a/ui/chromeos/resources/default_100_percent/default_user_images/illustration/avatar_sunglasses.png b/ui/chromeos/resources/default_100_percent/default_user_images/illustration/avatar_sunglasses.png
new file mode 100644
index 0000000..376eacd1
--- /dev/null
+++ b/ui/chromeos/resources/default_100_percent/default_user_images/illustration/avatar_sunglasses.png
Binary files differ
diff --git a/ui/chromeos/resources/default_100_percent/default_user_images/illustration/avatar_sushi.png b/ui/chromeos/resources/default_100_percent/default_user_images/illustration/avatar_sushi.png
new file mode 100644
index 0000000..c7a542da
--- /dev/null
+++ b/ui/chromeos/resources/default_100_percent/default_user_images/illustration/avatar_sushi.png
Binary files differ
diff --git a/ui/chromeos/resources/default_100_percent/default_user_images/illustration/avatar_tamagochi.png b/ui/chromeos/resources/default_100_percent/default_user_images/illustration/avatar_tamagochi.png
new file mode 100644
index 0000000..0aca2c2
--- /dev/null
+++ b/ui/chromeos/resources/default_100_percent/default_user_images/illustration/avatar_tamagochi.png
Binary files differ
diff --git a/ui/chromeos/resources/default_100_percent/default_user_images/illustration/avatar_vinyl.png b/ui/chromeos/resources/default_100_percent/default_user_images/illustration/avatar_vinyl.png
new file mode 100644
index 0000000..30f0ee10d
--- /dev/null
+++ b/ui/chromeos/resources/default_100_percent/default_user_images/illustration/avatar_vinyl.png
Binary files differ
diff --git a/ui/chromeos/resources/default_200_percent/default_user_images/illustration/avatar_basketball.png b/ui/chromeos/resources/default_200_percent/default_user_images/illustration/avatar_basketball.png
new file mode 100644
index 0000000..0488a6a
--- /dev/null
+++ b/ui/chromeos/resources/default_200_percent/default_user_images/illustration/avatar_basketball.png
Binary files differ
diff --git a/ui/chromeos/resources/default_200_percent/default_user_images/illustration/avatar_bike.png b/ui/chromeos/resources/default_200_percent/default_user_images/illustration/avatar_bike.png
new file mode 100644
index 0000000..335f65c
--- /dev/null
+++ b/ui/chromeos/resources/default_200_percent/default_user_images/illustration/avatar_bike.png
Binary files differ
diff --git a/ui/chromeos/resources/default_200_percent/default_user_images/illustration/avatar_bird.png b/ui/chromeos/resources/default_200_percent/default_user_images/illustration/avatar_bird.png
new file mode 100644
index 0000000..d1062ed
--- /dev/null
+++ b/ui/chromeos/resources/default_200_percent/default_user_images/illustration/avatar_bird.png
Binary files differ
diff --git a/ui/chromeos/resources/default_200_percent/default_user_images/illustration/avatar_cheese.png b/ui/chromeos/resources/default_200_percent/default_user_images/illustration/avatar_cheese.png
new file mode 100644
index 0000000..d86028c
--- /dev/null
+++ b/ui/chromeos/resources/default_200_percent/default_user_images/illustration/avatar_cheese.png
Binary files differ
diff --git a/ui/chromeos/resources/default_200_percent/default_user_images/illustration/avatar_football.png b/ui/chromeos/resources/default_200_percent/default_user_images/illustration/avatar_football.png
new file mode 100644
index 0000000..732284f
--- /dev/null
+++ b/ui/chromeos/resources/default_200_percent/default_user_images/illustration/avatar_football.png
Binary files differ
diff --git a/ui/chromeos/resources/default_200_percent/default_user_images/illustration/avatar_ramen.png b/ui/chromeos/resources/default_200_percent/default_user_images/illustration/avatar_ramen.png
new file mode 100644
index 0000000..4d81939
--- /dev/null
+++ b/ui/chromeos/resources/default_200_percent/default_user_images/illustration/avatar_ramen.png
Binary files differ
diff --git a/ui/chromeos/resources/default_200_percent/default_user_images/illustration/avatar_sunglasses.png b/ui/chromeos/resources/default_200_percent/default_user_images/illustration/avatar_sunglasses.png
new file mode 100644
index 0000000..f059077
--- /dev/null
+++ b/ui/chromeos/resources/default_200_percent/default_user_images/illustration/avatar_sunglasses.png
Binary files differ
diff --git a/ui/chromeos/resources/default_200_percent/default_user_images/illustration/avatar_sushi.png b/ui/chromeos/resources/default_200_percent/default_user_images/illustration/avatar_sushi.png
new file mode 100644
index 0000000..250bbc4
--- /dev/null
+++ b/ui/chromeos/resources/default_200_percent/default_user_images/illustration/avatar_sushi.png
Binary files differ
diff --git a/ui/chromeos/resources/default_200_percent/default_user_images/illustration/avatar_tamagochi.png b/ui/chromeos/resources/default_200_percent/default_user_images/illustration/avatar_tamagochi.png
new file mode 100644
index 0000000..ae633fe
--- /dev/null
+++ b/ui/chromeos/resources/default_200_percent/default_user_images/illustration/avatar_tamagochi.png
Binary files differ
diff --git a/ui/chromeos/resources/default_200_percent/default_user_images/illustration/avatar_vinyl.png b/ui/chromeos/resources/default_200_percent/default_user_images/illustration/avatar_vinyl.png
new file mode 100644
index 0000000..8f483de12
--- /dev/null
+++ b/ui/chromeos/resources/default_200_percent/default_user_images/illustration/avatar_vinyl.png
Binary files differ
diff --git a/ui/chromeos/resources/ui_chromeos_resources.grd b/ui/chromeos/resources/ui_chromeos_resources.grd
index b84066d0..d458dea 100644
--- a/ui/chromeos/resources/ui_chromeos_resources.grd
+++ b/ui/chromeos/resources/ui_chromeos_resources.grd
@@ -68,6 +68,16 @@
       <structure type="chrome_scaled_image" name="IDR_LOGIN_DEFAULT_USER_45" file="default_user_images/origami/avatar_panda.png" />
       <structure type="chrome_scaled_image" name="IDR_LOGIN_DEFAULT_USER_46" file="default_user_images/origami/avatar_unicorn.png" />
       <structure type="chrome_scaled_image" name="IDR_LOGIN_DEFAULT_USER_47" file="default_user_images/origami/avatar_butterflies.png" />
+      <structure type="chrome_scaled_image" name="IDR_LOGIN_DEFAULT_USER_48" file="default_user_images/illustration/avatar_bird.png" />
+      <structure type="chrome_scaled_image" name="IDR_LOGIN_DEFAULT_USER_49" file="default_user_images/illustration/avatar_ramen.png" />
+      <structure type="chrome_scaled_image" name="IDR_LOGIN_DEFAULT_USER_50" file="default_user_images/illustration/avatar_tamagochi.png" />
+      <structure type="chrome_scaled_image" name="IDR_LOGIN_DEFAULT_USER_51" file="default_user_images/illustration/avatar_cheese.png" />
+      <structure type="chrome_scaled_image" name="IDR_LOGIN_DEFAULT_USER_52" file="default_user_images/illustration/avatar_football.png" />
+      <structure type="chrome_scaled_image" name="IDR_LOGIN_DEFAULT_USER_53" file="default_user_images/illustration/avatar_basketball.png" />
+      <structure type="chrome_scaled_image" name="IDR_LOGIN_DEFAULT_USER_54" file="default_user_images/illustration/avatar_vinyl.png" />
+      <structure type="chrome_scaled_image" name="IDR_LOGIN_DEFAULT_USER_55" file="default_user_images/illustration/avatar_sushi.png" />
+      <structure type="chrome_scaled_image" name="IDR_LOGIN_DEFAULT_USER_56" file="default_user_images/illustration/avatar_bike.png" />
+      <structure type="chrome_scaled_image" name="IDR_LOGIN_DEFAULT_USER_57" file="default_user_images/illustration/avatar_sunglasses.png" />
 
       <!-- Print Job Notification images. -->
       <structure type="chrome_scaled_image" name="IDR_PRINT_NOTIFICATION_CANCEL" file="print_notification/cancel.png" />
diff --git a/ui/chromeos/ui_chromeos_strings.grd b/ui/chromeos/ui_chromeos_strings.grd
index 6a0cf7e..1ff7211 100644
--- a/ui/chromeos/ui_chromeos_strings.grd
+++ b/ui/chromeos/ui_chromeos_strings.grd
@@ -209,6 +209,36 @@
       <message name="IDS_LOGIN_DEFAULT_USER_DESC_47" desc="Description of the default user icon with a picture of butterflies">
         Butterflies
       </message>
+      <message name="IDS_LOGIN_DEFAULT_USER_DESC_48" desc="Description of the default user icon with a picture of a bird">
+        Bird
+      </message>
+      <message name="IDS_LOGIN_DEFAULT_USER_DESC_49" desc="Description of the default user icon with a picture of ramen">
+        Ramen
+      </message>
+      <message name="IDS_LOGIN_DEFAULT_USER_DESC_50" desc="Description of the default user icon with a picture of a tamagochi">
+        Tamagochi
+      </message>
+      <message name="IDS_LOGIN_DEFAULT_USER_DESC_51" desc="Description of the default user icon with a picture of a cheese">
+        Cheese
+      </message>
+      <message name="IDS_LOGIN_DEFAULT_USER_DESC_52" desc="Description of the default user icon with a picture of a football">
+        Football
+      </message>
+      <message name="IDS_LOGIN_DEFAULT_USER_DESC_53" desc="Description of the default user icon with a picture of a basketball">
+        Basketball
+      </message>
+      <message name="IDS_LOGIN_DEFAULT_USER_DESC_54" desc="Description of the default user icon with a picture of a vinyl">
+        Vinyl
+      </message>
+      <message name="IDS_LOGIN_DEFAULT_USER_DESC_55" desc="Description of the default user icon with a picture of sushi">
+        Sushi
+      </message>
+      <message name="IDS_LOGIN_DEFAULT_USER_DESC_56" desc="Description of the default user icon with a picture of a bike">
+        Bike
+      </message>
+      <message name="IDS_LOGIN_DEFAULT_USER_DESC_57" desc="Description of the default user icon with a picture of a pair of sunglasses">
+        Sunglasses
+      </message>
 
       <!-- Attribution strings for stock user images, not translateable. -->
       <message translateable="false" name="IDS_LOGIN_DEFAULT_USER_AUTHOR">
diff --git a/ui/events/blink/input_handler_proxy.cc b/ui/events/blink/input_handler_proxy.cc
index 9e190b6..a551486 100644
--- a/ui/events/blink/input_handler_proxy.cc
+++ b/ui/events/blink/input_handler_proxy.cc
@@ -989,7 +989,6 @@
       const float vx = gesture_event.data.fling_start.velocity_x;
       const float vy = gesture_event.data.fling_start.velocity_y;
       current_fling_velocity_ = gfx::Vector2dF(vx, vy);
-      DCHECK(!current_fling_velocity_.IsZero());
       fling_curve_ = client_->CreateFlingAnimationCurve(
           gesture_event.source_device, WebFloatPoint(vx, vy), blink::WebSize());
       disallow_horizontal_fling_scroll_ = !vx;
diff --git a/ui/events/gestures/blink/web_gesture_curve_impl.cc b/ui/events/gestures/blink/web_gesture_curve_impl.cc
index 4febdaf..a9901956 100644
--- a/ui/events/gestures/blink/web_gesture_curve_impl.cc
+++ b/ui/events/gestures/blink/web_gesture_curve_impl.cc
@@ -32,7 +32,6 @@
 std::unique_ptr<GestureCurve> CreateDefaultPlatformCurve(
     blink::WebGestureDevice device_source,
     const gfx::Vector2dF& initial_velocity) {
-  DCHECK(!initial_velocity.IsZero());
   if (device_source == blink::kWebGestureDeviceSyntheticAutoscroll) {
     return base::MakeUnique<FixedVelocityCurve>(initial_velocity,
                                                 base::TimeTicks());
diff --git a/ui/gfx/BUILD.gn b/ui/gfx/BUILD.gn
index 877a2ba..3e49b15 100644
--- a/ui/gfx/BUILD.gn
+++ b/ui/gfx/BUILD.gn
@@ -330,8 +330,7 @@
     sources -= [ "canvas_notimplemented.cc" ]
   }
 
-  # Desktop and Android+VR only.
-  if (use_aura || (!is_ios && !is_android) || (is_android && enable_vr)) {
+  if (!is_ios) {
     sources += [
       "paint_vector_icon.cc",
       "paint_vector_icon.h",
@@ -710,11 +709,12 @@
     ]
   }
 
+  if (!is_ios) {
+    sources += [ "paint_vector_icon_unittest.cc" ]
+  }
+
   if (!is_android && !is_ios) {
-    sources += [
-      "paint_vector_icon_unittest.cc",
-      "render_text_unittest.cc",
-    ]
+    sources += [ "render_text_unittest.cc" ]
   }
 
   # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
diff --git a/ui/gfx/canvas.cc b/ui/gfx/canvas.cc
index 414c2c9..ff5f8a8 100644
--- a/ui/gfx/canvas.cc
+++ b/ui/gfx/canvas.cc
@@ -287,15 +287,19 @@
 void Canvas::DrawCircle(const Point& center_point,
                         int radius,
                         const cc::PaintFlags& flags) {
-  DrawCircle(PointF(center_point), radius, flags);
+  canvas_->drawOval(
+      SkRect::MakeLTRB(center_point.x() - radius, center_point.y() - radius,
+                       center_point.x() + radius, center_point.y() + radius),
+      flags);
 }
 
 void Canvas::DrawCircle(const PointF& center_point,
                         float radius,
                         const cc::PaintFlags& flags) {
-  canvas_->drawCircle(SkFloatToScalar(center_point.x()),
-                      SkFloatToScalar(center_point.y()),
-                      SkFloatToScalar(radius), flags);
+  canvas_->drawOval(
+      SkRect::MakeLTRB(center_point.x() - radius, center_point.y() - radius,
+                       center_point.x() + radius, center_point.y() + radius),
+      flags);
 }
 
 void Canvas::DrawRoundRect(const Rect& rect,
diff --git a/ui/gl/gl_image_stub.cc b/ui/gl/gl_image_stub.cc
index 5bfd62b..3d99c4e7 100644
--- a/ui/gl/gl_image_stub.cc
+++ b/ui/gl/gl_image_stub.cc
@@ -21,13 +21,13 @@
 bool GLImageStub::BindTexImage(unsigned target) { return true; }
 
 bool GLImageStub::CopyTexImage(unsigned target) {
-  return true;
+  return false;
 }
 
 bool GLImageStub::CopyTexSubImage(unsigned target,
                                   const gfx::Point& offset,
                                   const gfx::Rect& rect) {
-  return true;
+  return false;
 }
 
 bool GLImageStub::ScheduleOverlayPlane(gfx::AcceleratedWidget widget,
diff --git a/ui/message_center/BUILD.gn b/ui/message_center/BUILD.gn
index 96cfede..dded329 100644
--- a/ui/message_center/BUILD.gn
+++ b/ui/message_center/BUILD.gn
@@ -28,6 +28,7 @@
   deps = [
     "//base",
     "//ui/base",
+    "//ui/strings",
     "//url",
   ]
 
@@ -47,7 +48,6 @@
       "//ui/gfx/geometry",
       "//ui/native_theme",
       "//ui/resources",
-      "//ui/strings",
     ]
 
     configs += [
diff --git a/ui/message_center/fake_message_center.cc b/ui/message_center/fake_message_center.cc
index 1900b0e6..b1e67185 100644
--- a/ui/message_center/fake_message_center.cc
+++ b/ui/message_center/fake_message_center.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "ui/message_center/fake_message_center.h"
+#include "base/strings/string_util.h"
 #include "ui/message_center/notification_list.h"
 
 namespace message_center {
@@ -142,6 +143,13 @@
 
 void FakeMessageCenter::PausePopupTimers() {}
 
+const base::string16& FakeMessageCenter::GetProductOSName() const {
+  return base::EmptyString16();
+}
+
+void FakeMessageCenter::SetProductOSName(
+    const base::string16& product_os_name) {}
+
 void FakeMessageCenter::DisableTimersForTest() {}
 
 void FakeMessageCenter::EnableChangeQueueForTest(bool enabled) {}
diff --git a/ui/message_center/fake_message_center.h b/ui/message_center/fake_message_center.h
index 7a8766a..c489f67 100644
--- a/ui/message_center/fake_message_center.h
+++ b/ui/message_center/fake_message_center.h
@@ -68,6 +68,8 @@
   bool IsMessageCenterVisible() const override;
   void RestartPopupTimers() override;
   void PausePopupTimers() override;
+  const base::string16& GetProductOSName() const override;
+  void SetProductOSName(const base::string16& product_os_name) override;
 
  protected:
   void DisableTimersForTest() override;
diff --git a/ui/message_center/message_center.h b/ui/message_center/message_center.h
index 55697cb..2eebf0ff 100644
--- a/ui/message_center/message_center.h
+++ b/ui/message_center/message_center.h
@@ -193,6 +193,11 @@
   // example, after the mouse leaves the popup.)
   virtual void RestartPopupTimers() = 0;
 
+  // "Chromium OS" or "Chrome OS" in the current locale.
+  // Return empty string if not on these platforms.
+  virtual const base::string16& GetProductOSName() const = 0;
+  virtual void SetProductOSName(const base::string16& product_os_name) = 0;
+
  protected:
   friend class ::DownloadNotification;
   friend class ::DownloadNotificationTestBase;
diff --git a/ui/message_center/message_center_impl.cc b/ui/message_center/message_center_impl.cc
index c4396746..e2369ca 100644
--- a/ui/message_center/message_center_impl.cc
+++ b/ui/message_center/message_center_impl.cc
@@ -15,6 +15,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/observer_list.h"
 #include "base/stl_util.h"
+#include "base/strings/string_util.h"
 #include "build/build_config.h"
 #include "ui/message_center/message_center_style.h"
 #include "ui/message_center/message_center_switches.h"
@@ -943,6 +944,15 @@
     popup_timers_controller_->PauseAll();
 }
 
+const base::string16& MessageCenterImpl::GetProductOSName() const {
+  return product_os_name_;
+}
+
+void MessageCenterImpl::SetProductOSName(
+    const base::string16& product_os_name) {
+  product_os_name_ = product_os_name;
+}
+
 void MessageCenterImpl::DisableTimersForTest() {
   popup_timers_controller_.reset();
 }
diff --git a/ui/message_center/message_center_impl.h b/ui/message_center/message_center_impl.h
index 3dd9c7f..ea4348a3 100644
--- a/ui/message_center/message_center_impl.h
+++ b/ui/message_center/message_center_impl.h
@@ -84,6 +84,8 @@
   void EnterQuietModeWithExpire(const base::TimeDelta& expires_in) override;
   void RestartPopupTimers() override;
   void PausePopupTimers() override;
+  const base::string16& GetProductOSName() const override;
+  void SetProductOSName(const base::string16& product_os_name) override;
   void ForceNotificationFlush(const std::string& id) override;
 
   // NotificationBlocker::Observer overrides:
@@ -137,6 +139,8 @@
   // center is visible.
   std::unique_ptr<internal::ChangeQueue> notification_queue_;
 
+  base::string16 product_os_name_;
+
   DISALLOW_COPY_AND_ASSIGN(MessageCenterImpl);
 };
 
diff --git a/ui/message_center/message_center_style.h b/ui/message_center/message_center_style.h
index be5acf24..15102b9 100644
--- a/ui/message_center/message_center_style.h
+++ b/ui/message_center/message_center_style.h
@@ -9,6 +9,7 @@
 
 #include "build/build_config.h"
 #include "third_party/skia/include/core/SkColor.h"
+#include "ui/gfx/color_palette.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/message_center/message_center_export.h"
 
@@ -100,6 +101,15 @@
 const SkColor kControlButtonBackgroundColor =
     SkColorSetA(SK_ColorWHITE, 0.9 * 0xff);
 
+// Accent colors of system notifications.
+const SkColor kSystemNotificationColorNormal = SkColorSetRGB(0x33, 0x67, 0xd6);
+const SkColor kSystemNotificationColorWarning = SkColorSetRGB(0xea, 0x61, 0x0);
+const SkColor kSystemNotificationColorCriticalWarning =
+    SkColorSetRGB(0xc5, 0x39, 0x29);
+
+// Default accent color of notifications that are not generated by system.
+const SkColor kNotificationDefaultAccentColor = gfx::kChromeIconGrey;
+
 // Limits.
 
 // Given the size of an image, returns the size of the properly scaled-up image
diff --git a/ui/message_center/notification.cc b/ui/message_center/notification.cc
index c7a7389..1d5a222 100644
--- a/ui/message_center/notification.cc
+++ b/ui/message_center/notification.cc
@@ -5,8 +5,15 @@
 #include "ui/message_center/notification.h"
 
 #include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/gfx/paint_vector_icon.h"
+#include "ui/gfx/vector_icon_types.h"
+#include "ui/message_center/message_center.h"
+#include "ui/message_center/message_center_style.h"
 #include "ui/message_center/notification_delegate.h"
 #include "ui/message_center/notification_types.h"
+#include "ui/strings/grit/ui_strings.h"
 
 namespace message_center {
 
@@ -54,7 +61,9 @@
       vibration_pattern(other.vibration_pattern),
       renotify(other.renotify),
       silent(other.silent),
-      accessible_name(other.accessible_name) {}
+      accessible_name(other.accessible_name),
+      accent_color(other.accent_color) {
+}
 
 RichNotificationData::~RichNotificationData() = default;
 
@@ -170,14 +179,58 @@
     const gfx::Image& icon,
     const std::string& system_component_id,
     const base::Closure& click_callback) {
-  std::unique_ptr<Notification> notification(new Notification(
+  std::unique_ptr<Notification> notification = CreateSystemNotification(
       NOTIFICATION_TYPE_SIMPLE, notification_id, title, message, icon,
       base::string16() /* display_source */, GURL(),
       NotifierId(NotifierId::SYSTEM_COMPONENT, system_component_id),
       RichNotificationData(),
-      new HandleNotificationClickedDelegate(click_callback)));
+      new HandleNotificationClickedDelegate(click_callback), gfx::kNoneIcon,
+      SystemNotificationWarningLevel::CRITICAL_WARNING);
   notification->SetSystemPriority();
   return notification;
 }
 
+// static
+std::unique_ptr<Notification> Notification::CreateSystemNotification(
+    NotificationType type,
+    const std::string& id,
+    const base::string16& title,
+    const base::string16& message,
+    const gfx::Image& icon,
+    const base::string16& display_source,
+    const GURL& origin_url,
+    const NotifierId& notifier_id,
+    const RichNotificationData& optional_fields,
+    scoped_refptr<NotificationDelegate> delegate,
+    const gfx::VectorIcon& small_image,
+    SystemNotificationWarningLevel color_type) {
+  SkColor color = message_center::kSystemNotificationColorNormal;
+  switch (color_type) {
+    case SystemNotificationWarningLevel::NORMAL:
+      color = message_center::kSystemNotificationColorNormal;
+      break;
+    case SystemNotificationWarningLevel::WARNING:
+      color = message_center::kSystemNotificationColorWarning;
+      break;
+    case SystemNotificationWarningLevel::CRITICAL_WARNING:
+      color = message_center::kSystemNotificationColorCriticalWarning;
+      break;
+  }
+  base::string16 display_source_or_default = display_source;
+  if (display_source_or_default.empty()) {
+    display_source_or_default = l10n_util::GetStringFUTF16(
+        IDS_MESSAGE_CENTER_NOTIFICATION_CHROMEOS_SYSTEM,
+        MessageCenter::Get()->GetProductOSName());
+  }
+  std::unique_ptr<Notification> notification = base::MakeUnique<Notification>(
+      type, id, title, message, icon, display_source_or_default, origin_url,
+      notifier_id, optional_fields, delegate);
+  notification->set_accent_color(color);
+  notification->set_small_image(
+      small_image.is_empty()
+          ? gfx::Image()
+          : gfx::Image(gfx::CreateVectorIcon(small_image, color)));
+  return notification;
+}
+
 }  // namespace message_center
diff --git a/ui/message_center/notification.h b/ui/message_center/notification.h
index d08a48a8..8101cad 100644
--- a/ui/message_center/notification.h
+++ b/ui/message_center/notification.h
@@ -15,13 +15,19 @@
 #include "base/time/time.h"
 #include "base/values.h"
 #include "mojo/public/cpp/bindings/struct_traits.h"  // nogncheck
+#include "ui/gfx/color_palette.h"
 #include "ui/gfx/image/image.h"
+#include "ui/gfx/skia_util.h"
 #include "ui/message_center/message_center_export.h"
 #include "ui/message_center/notification_delegate.h"
 #include "ui/message_center/notification_types.h"
 #include "ui/message_center/notifier_settings.h"
 #include "url/gurl.h"
 
+namespace gfx {
+struct VectorIcon;
+}  // namespace gfx
+
 namespace message_center {
 
 namespace mojom {
@@ -45,6 +51,8 @@
   TEXT
 };
 
+enum class SystemNotificationWarningLevel { NORMAL, WARNING, CRITICAL_WARNING };
+
 // Represents a button to be shown as part of a notification.
 struct MESSAGE_CENTER_EXPORT ButtonInfo {
   explicit ButtonInfo(const base::string16& title);
@@ -141,6 +149,12 @@
 
   // An accessible description of the notification's contents.
   base::string16 accessible_name;
+
+  // Unified theme color used in new style notification.
+  // Usually, it should not be set directly.
+  // For system notification, CreateSystemNotification with
+  // SystemNotificationWarningLevel should be used.
+  SkColor accent_color = SK_ColorTRANSPARENT;
 };
 
 class MESSAGE_CENTER_EXPORT Notification {
@@ -345,6 +359,11 @@
     return optional_fields_.accessible_name;
   }
 
+  SkColor accent_color() const { return optional_fields_.accent_color; }
+  void set_accent_color(SkColor accent_color) {
+    optional_fields_.accent_color = accent_color;
+  }
+
   NotificationDelegate* delegate() const { return delegate_.get(); }
 
   const RichNotificationData& rich_notification_data() const {
@@ -364,6 +383,13 @@
 
   // Helper method to create a simple system notification. |click_callback|
   // will be invoked when the notification is clicked.
+  //
+  // It should only be used for critical notification, as SetSystemPriority and
+  // CRITICAL_WARNING color are set inside, which means the notification would
+  // not go away without user interaction.
+  //
+  // TODO(tetsui): Add a function parameter |small_image| of gfx::VectorIcon, so
+  // display source of critical system notification is illustrated by icon.
   static std::unique_ptr<Notification> CreateSystemNotification(
       const std::string& notification_id,
       const base::string16& title,
@@ -372,6 +398,25 @@
       const std::string& system_component_id,
       const base::Closure& click_callback);
 
+  // Factory method to create all kinds of notifications generated by system,
+  // from normal priority ones to critical priority ones.
+  // |small_image| is a small icon show on the upper left header to illustrate
+  // |display_source| of the notification.
+  // One specified in the |optional_fields| is overridden.
+  static std::unique_ptr<Notification> CreateSystemNotification(
+      NotificationType type,
+      const std::string& id,
+      const base::string16& title,
+      const base::string16& message,
+      const gfx::Image& icon,
+      const base::string16& display_source,
+      const GURL& origin_url,
+      const NotifierId& notifier_id,
+      const RichNotificationData& optional_fields,
+      scoped_refptr<NotificationDelegate> delegate,
+      const gfx::VectorIcon& small_image,
+      SystemNotificationWarningLevel color_type);
+
  protected:
   // The type of notification we'd like displayed.
   NotificationType type_;
diff --git a/ui/message_center/views/notification_header_view.cc b/ui/message_center/views/notification_header_view.cc
index a43e6eaa..5a26331 100644
--- a/ui/message_center/views/notification_header_view.cc
+++ b/ui/message_center/views/notification_header_view.cc
@@ -68,8 +68,6 @@
 };
 
 ExpandButton::ExpandButton() {
-  SetImage(gfx::CreateVectorIcon(kNotificationExpandMoreIcon, kExpandIconSize,
-                                 gfx::kChromeIconGrey));
   focus_painter_ = views::Painter::CreateSolidFocusPainter(
       kFocusBorderColor, gfx::Insets(1, 2, 2, 2));
   SetFocusBehavior(FocusBehavior::ALWAYS);
@@ -159,6 +157,7 @@
   app_name_view_ = new views::Label(base::string16());
   app_name_view_->SetFontList(font_list);
   app_name_view_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+  app_name_view_->SetEnabledColor(accent_color_);
   app_info_container->AddChildView(app_name_view_);
 
   // Summary text divider
@@ -193,6 +192,7 @@
 
   // Expand button view
   expand_button_ = new ExpandButton();
+  SetExpanded(is_expanded_);
   app_info_container->AddChildView(expand_button_);
 
   // Spacer between left-aligned views and right-aligned views
@@ -224,6 +224,10 @@
   app_icon_view_->SetImage(img);
 }
 
+void NotificationHeaderView::ClearAppIcon() {
+  app_icon_view_->SetImage(gfx::CreateVectorIcon(kProductIcon, accent_color_));
+}
+
 void NotificationHeaderView::SetAppName(const base::string16& name) {
   app_name_view_->SetText(name);
 }
@@ -277,10 +281,10 @@
 }
 
 void NotificationHeaderView::SetExpanded(bool expanded) {
-  expand_button_->SetImage(
-      gfx::CreateVectorIcon(
-          expanded ? kNotificationExpandLessIcon : kNotificationExpandMoreIcon,
-          kExpandIconSize, gfx::kChromeIconGrey));
+  is_expanded_ = expanded;
+  expand_button_->SetImage(gfx::CreateVectorIcon(
+      expanded ? kNotificationExpandLessIcon : kNotificationExpandMoreIcon,
+      kExpandIconSize, accent_color_));
 }
 
 void NotificationHeaderView::SetSettingsButtonEnabled(bool enabled) {
@@ -304,6 +308,12 @@
   }
 }
 
+void NotificationHeaderView::SetAccentColor(SkColor color) {
+  accent_color_ = color;
+  app_name_view_->SetEnabledColor(accent_color_);
+  SetExpanded(is_expanded_);
+}
+
 bool NotificationHeaderView::IsExpandButtonEnabled() {
   return expand_button_->visible();
 }
diff --git a/ui/message_center/views/notification_header_view.h b/ui/message_center/views/notification_header_view.h
index 2471c67a..d196eb5a 100644
--- a/ui/message_center/views/notification_header_view.h
+++ b/ui/message_center/views/notification_header_view.h
@@ -6,6 +6,7 @@
 #define UI_MESSAGE_CENTER_VIEWS_NOTIFICATION_HEADER_VIEW_H_
 
 #include "base/macros.h"
+#include "ui/message_center/message_center_style.h"
 #include "ui/message_center/views/padded_button.h"
 #include "ui/views/controls/button/custom_button.h"
 
@@ -30,6 +31,10 @@
   void SetSettingsButtonEnabled(bool enabled);
   void SetCloseButtonEnabled(bool enabled);
   void SetControlButtonsVisible(bool visible);
+  // Set the unified theme color used among the app icon, app name, and expand
+  // button.
+  void SetAccentColor(SkColor color);
+  void ClearAppIcon();
   void ClearProgress();
   void ClearOverflowIndicator();
   void ClearTimestamp();
@@ -53,6 +58,8 @@
   // Update visibility for both |summary_text_view_| and |timestamp_view_|.
   void UpdateSummaryTextVisibility();
 
+  SkColor accent_color_ = message_center::kNotificationDefaultAccentColor;
+
   views::Label* app_name_view_ = nullptr;
   views::Label* summary_text_divider_ = nullptr;
   views::Label* summary_text_view_ = nullptr;
@@ -69,6 +76,7 @@
   bool has_progress_ = false;
   bool has_overflow_indicator_ = false;
   bool has_timestamp_ = false;
+  bool is_expanded_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(NotificationHeaderView);
 };
diff --git a/ui/message_center/views/notification_view_md.cc b/ui/message_center/views/notification_view_md.cc
index 09ad6ba..0cbbb7f4 100644
--- a/ui/message_center/views/notification_view_md.cc
+++ b/ui/message_center/views/notification_view_md.cc
@@ -58,10 +58,6 @@
 constexpr gfx::Size kLargeImageMinSize(328, 0);
 constexpr gfx::Size kLargeImageMaxSize(328, 218);
 
-// Foreground of small icon image.
-constexpr SkColor kSmallImageBackgroundColor = SK_ColorWHITE;
-// Background of small icon image.
-const SkColor kSmallImageColor = SkColorSetRGB(0x43, 0x43, 0x43);
 // Background of inline actions area.
 const SkColor kActionsRowBackgroundColor = SkColorSetRGB(0xee, 0xee, 0xee);
 // Base ink drop color of action buttons.
@@ -87,37 +83,6 @@
     message_center::kNotificationWidth - kIconViewSize.width() -
     kContentRowPadding.left() - kContentRowPadding.right();
 
-const gfx::ImageSkia CreateSolidColorImage(int width,
-                                           int height,
-                                           SkColor color) {
-  SkBitmap bitmap;
-  bitmap.allocN32Pixels(width, height);
-  bitmap.eraseColor(color);
-  return gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
-}
-
-// Take the alpha channel of icon, mask it with the foreground,
-// then add the masked foreground on top of the background
-const gfx::ImageSkia GetMaskedIcon(const gfx::ImageSkia& icon) {
-  int width = icon.width();
-  int height = icon.height();
-
-  // Background color grey
-  const gfx::ImageSkia background = CreateSolidColorImage(
-      width, height, message_center::kSmallImageBackgroundColor);
-  // Foreground color white
-  const gfx::ImageSkia foreground =
-      CreateSolidColorImage(width, height, message_center::kSmallImageColor);
-  const gfx::ImageSkia masked_small_image =
-      gfx::ImageSkiaOperations::CreateMaskedImage(foreground, icon);
-  return gfx::ImageSkiaOperations::CreateSuperimposedImage(background,
-                                                           masked_small_image);
-}
-
-const gfx::ImageSkia GetProductIcon() {
-  return gfx::CreateVectorIcon(kProductIcon, kSmallImageColor);
-}
-
 // ItemView ////////////////////////////////////////////////////////////////////
 
 // ItemViews are responsible for drawing each list notification item's title and
@@ -621,6 +586,10 @@
 void NotificationViewMD::CreateOrUpdateContextTitleView(
     const Notification& notification) {
   header_row_->SetAppName(notification.display_source());
+  header_row_->SetAccentColor(
+      notification.accent_color() == SK_ColorTRANSPARENT
+          ? message_center::kNotificationDefaultAccentColor
+          : notification.accent_color());
   header_row_->SetTimestamp(notification.timestamp());
 }
 
@@ -805,11 +774,10 @@
 
 void NotificationViewMD::CreateOrUpdateSmallIconView(
     const Notification& notification) {
-  gfx::ImageSkia icon =
-      notification.small_image().IsEmpty()
-          ? GetProductIcon()
-          : GetMaskedIcon(notification.small_image().AsImageSkia());
-  header_row_->SetAppIcon(icon);
+  if (notification.small_image().IsEmpty())
+    header_row_->ClearAppIcon();
+  else
+    header_row_->SetAppIcon(notification.small_image().AsImageSkia());
 }
 
 void NotificationViewMD::CreateOrUpdateImageView(
@@ -857,6 +825,12 @@
       action_buttons_[i]->SchedulePaint();
       action_buttons_[i]->Layout();
     }
+
+    // Change action button color to the accent color.
+    action_buttons_[i]->SetEnabledTextColors(notification.accent_color() ==
+                                                     SK_ColorTRANSPARENT
+                                                 ? kActionButtonTextColor
+                                                 : notification.accent_color());
   }
 
   // Inherit mouse hover state when action button views reset.