diff --git a/BUILD.gn b/BUILD.gn
index 97ce0d2..6b0f6c18 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -751,7 +751,7 @@
   if (is_win || is_linux) {
     deps += [
       "//mash:all",
-      "//media/mojo/services:media_mojo_shell_unittests",
+      "//media/mojo/services:media_service_unittests",
       "//mojo",
       "//services/navigation",
       "//services/preferences:tests",
diff --git a/DEPS b/DEPS
index c1da9cc6..51f9df8 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': '287f6512f34d456b593ea030197925dfc5b15c65',
+  'skia_revision': '45565b676c86d6b4955b8643236880b016772e95',
   # 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': '355ca2541b19167eea565d3cad751c3f8c26db51',
+  'catapult_revision': '287f4bd6afc9b828782d5910b9a8b05c7b698ab9',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -188,7 +188,7 @@
     Var('chromium_git') + '/external/bidichecker/lib.git' + '@' + '97f2aa645b74c28c57eca56992235c79850fa9e0',
 
   'src/third_party/webgl/src':
-    Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '3e235f7e8495092ac4ad11eec8f226c7accc9a33',
+    Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'f554de25329a3d63754a32ae023854b8c1ad094c',
 
   'src/third_party/webdriver/pylib':
     Var('chromium_git') + '/external/selenium/py.git' + '@' + '5fd78261a75fe08d27ca4835fb6c5ce4b42275bd',
diff --git a/ash/common/system/tray/tray_details_view.cc b/ash/common/system/tray/tray_details_view.cc
index 5ee4570..d48c0e36 100644
--- a/ash/common/system/tray/tray_details_view.cc
+++ b/ash/common/system/tray/tray_details_view.cc
@@ -230,7 +230,7 @@
                         kSeparatorColor);
     paint.setLooper(gfx::CreateShadowDrawLooperCorrectBlur(shadow));
     paint.setAntiAlias(true);
-    canvas->ClipRect(shadowed_area, SkRegion::kDifference_Op);
+    canvas->ClipRect(shadowed_area, kDifference_SkClipOp);
     canvas->DrawRect(shadowed_area, paint);
   }
 
diff --git a/ash/display/display_manager_unittest.cc b/ash/display/display_manager_unittest.cc
index 7616d881..31de493e 100644
--- a/ash/display/display_manager_unittest.cc
+++ b/ash/display/display_manager_unittest.cc
@@ -628,6 +628,81 @@
   EXPECT_EQ(display2_id, changed()[0].id());
 }
 
+#if defined(OS_CHROMEOS)
+TEST_P(DisplayManagerTest, TouchCalibrationTest) {
+  if (!SupportsMultipleDisplays())
+    return;
+
+  UpdateDisplay("0+0-500x500,0+501-1024x600");
+  reset();
+
+  ASSERT_EQ(2u, display_manager()->GetNumDisplays());
+  const display::ManagedDisplayInfo display_info1 = GetDisplayInfoAt(0);
+  const display::ManagedDisplayInfo display_info2 = GetDisplayInfoAt(1);
+
+  EXPECT_FALSE(display_info2.has_touch_calibration_data());
+
+  display::TouchCalibrationData::CalibrationPointPairQuad point_pair_quad = {
+      {std::make_pair(gfx::Point(50, 50), gfx::Point(43, 51)),
+       std::make_pair(gfx::Point(950, 50), gfx::Point(975, 45)),
+       std::make_pair(gfx::Point(50, 550), gfx::Point(48, 534)),
+       std::make_pair(gfx::Point(950, 550), gfx::Point(967, 574))}};
+  gfx::Size bounds_at_calibration(display_info2.size_in_pixel());
+  const display::TouchCalibrationData touch_data(point_pair_quad,
+                                                 bounds_at_calibration);
+
+  // Set the touch calibration data for the secondary display.
+  display_manager()->SetTouchCalibrationData(
+      display_info2.id(), point_pair_quad, bounds_at_calibration);
+
+  display::ManagedDisplayInfo updated_display_info2 = GetDisplayInfoAt(1);
+  EXPECT_TRUE(updated_display_info2.has_touch_calibration_data());
+  EXPECT_EQ(touch_data, updated_display_info2.GetTouchCalibrationData());
+
+  // Clearing touch calibration data from the secondary display.
+  display_manager()->ClearTouchCalibrationData(display_info2.id());
+  updated_display_info2 = GetDisplayInfoAt(1);
+  EXPECT_FALSE(updated_display_info2.has_touch_calibration_data());
+
+  // Make sure that SetTouchCalibrationData() is idempotent.
+  display::TouchCalibrationData::CalibrationPointPairQuad point_pair_quad_2 =
+      point_pair_quad;
+  point_pair_quad_2[1] =
+      std::make_pair(gfx::Point(950, 50), gfx::Point(975, 53));
+  display::TouchCalibrationData touch_data_2(point_pair_quad_2,
+                                             bounds_at_calibration);
+  display_manager()->SetTouchCalibrationData(
+      display_info2.id(), point_pair_quad_2, bounds_at_calibration);
+
+  updated_display_info2 = GetDisplayInfoAt(1);
+  EXPECT_TRUE(updated_display_info2.has_touch_calibration_data());
+  EXPECT_EQ(touch_data_2, updated_display_info2.GetTouchCalibrationData());
+
+  display_manager()->SetTouchCalibrationData(
+      display_info2.id(), point_pair_quad, bounds_at_calibration);
+  EXPECT_TRUE(updated_display_info2.has_touch_calibration_data());
+  EXPECT_EQ(touch_data_2, updated_display_info2.GetTouchCalibrationData());
+
+  // Recreate a new 2nd display. It won't apply the touhc calibration data
+  // because the new display has a different ID.
+  UpdateDisplay("0+0-500x500");
+  UpdateDisplay("0+0-500x500,0+501-400x400");
+  EXPECT_FALSE(GetDisplayInfoAt(1).has_touch_calibration_data());
+
+  // Recreate the displays with the same ID.  It should apply the touch
+  // calibration associated data.
+  UpdateDisplay("0+0-500x500");
+  std::vector<display::ManagedDisplayInfo> display_info_list;
+  display_info_list.push_back(display_info1);
+  display_info_list.push_back(display_info2);
+  display_manager()->OnNativeDisplaysChanged(display_info_list);
+  updated_display_info2 = GetDisplayInfoAt(1);
+
+  EXPECT_FALSE(updated_display_info2.has_touch_calibration_data());
+  EXPECT_EQ(touch_data, updated_display_info2.GetTouchCalibrationData());
+}
+#endif  // defined(OS_CHROMEOS)
+
 #if !defined(OS_WIN)
 // Disabled on windows because of http://crbug.com/650326.
 TEST_P(DisplayManagerTest, TestDeviceScaleOnlyChange) {
@@ -1471,7 +1546,7 @@
   const gfx::Insets dummy_overscan_insets;
   display_manager()->RegisterDisplayProperty(
       display_id, display::Display::ROTATE_0, 1.0f, &dummy_overscan_insets,
-      gfx::Size(), 1.0f, ui::ColorCalibrationProfile());
+      gfx::Size(), 1.0f, ui::ColorCalibrationProfile(), nullptr);
 
   // Setup the display modes with UI-scale.
   display::ManagedDisplayInfo native_display_info =
@@ -2429,9 +2504,9 @@
 
 TEST_P(DisplayManagerTest, CheckInitializationOfRotationProperty) {
   int64_t id = display_manager()->GetDisplayAt(0).id();
-  display_manager()->RegisterDisplayProperty(id, display::Display::ROTATE_90,
-                                             1.0f, nullptr, gfx::Size(), 1.0f,
-                                             ui::COLOR_PROFILE_STANDARD);
+  display_manager()->RegisterDisplayProperty(
+      id, display::Display::ROTATE_90, 1.0f, nullptr, gfx::Size(), 1.0f,
+      ui::COLOR_PROFILE_STANDARD, nullptr);
 
   const display::ManagedDisplayInfo& info =
       display_manager()->GetDisplayInfo(id);
diff --git a/ash/system/chromeos/power/tablet_power_button_controller.cc b/ash/system/chromeos/power/tablet_power_button_controller.cc
index 66841d16..f66247e 100644
--- a/ash/system/chromeos/power/tablet_power_button_controller.cc
+++ b/ash/system/chromeos/power/tablet_power_button_controller.cc
@@ -68,6 +68,7 @@
     LockStateController* controller)
     : tick_clock_(new base::DefaultTickClock()),
       last_resume_time_(base::TimeTicks()),
+      force_off_on_button_up_(true),
       controller_(controller),
       weak_ptr_factory_(this) {
   chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver(
@@ -92,36 +93,28 @@
 void TabletPowerButtonController::OnPowerButtonEvent(
     bool down,
     const base::TimeTicks& timestamp) {
-  // When the system resumes in response to the power button being pressed,
-  // Chrome receives powerd's SuspendDone signal and notification that the
-  // backlight has been turned back on before seeing the power button events
-  // that woke the system. Ignore events just after resuming to ensure that we
-  // don't turn the screen off in response to the events.
-  //
-  // TODO(warx): pressing power button should also StartShutdownTimer() in this
-  // case. Reorganize the code to support that.
-  if (timestamp - last_resume_time_ <=
-      base::TimeDelta::FromMilliseconds(kIgnorePowerButtonAfterResumeMs)) {
-    // If backlights are forced off, stop forcing off because resuming system
-    // doesn't handle this.
-    if (down && backlights_forced_off_)
-      SetDisplayForcedOff(false);
-    return;
-  }
-
   if (down) {
+    force_off_on_button_up_ = true;
+    // When the system resumes in response to the power button being pressed,
+    // Chrome receives powerd's SuspendDone signal and notification that the
+    // backlight has been turned back on before seeing the power button events
+    // that woke the system. Avoid forcing off display just after resuming to
+    // ensure that we don't turn the display off in response to the events.
+    if (timestamp - last_resume_time_ <=
+        base::TimeDelta::FromMilliseconds(kIgnorePowerButtonAfterResumeMs)) {
+      force_off_on_button_up_ = false;
+    }
     screen_off_when_power_button_down_ = brightness_level_is_zero_;
     SetDisplayForcedOff(false);
     StartShutdownTimer();
   } else {
     if (shutdown_timer_.IsRunning()) {
       shutdown_timer_.Stop();
-      if (!screen_off_when_power_button_down_) {
+      if (!screen_off_when_power_button_down_ && force_off_on_button_up_) {
         SetDisplayForcedOff(true);
         LockScreenIfRequired();
       }
     }
-    screen_off_when_power_button_down_ = false;
 
     // When power button is released, cancel shutdown animation whenever it is
     // still cancellable.
diff --git a/ash/system/chromeos/power/tablet_power_button_controller.h b/ash/system/chromeos/power/tablet_power_button_controller.h
index f59749d..59c606d 100644
--- a/ash/system/chromeos/power/tablet_power_button_controller.h
+++ b/ash/system/chromeos/power/tablet_power_button_controller.h
@@ -111,6 +111,9 @@
   // updated in SuspendDone().
   base::TimeTicks last_resume_time_;
 
+  // True if power button released should force off display.
+  bool force_off_on_button_up_;
+
   // Started when the tablet power button is pressed and stopped when it's
   // released. Runs OnShutdownTimeout() to start shutdown.
   base::OneShotTimer shutdown_timer_;
diff --git a/ash/system/chromeos/power/tablet_power_button_controller_unittest.cc b/ash/system/chromeos/power/tablet_power_button_controller_unittest.cc
index 4acd482..8e087229 100644
--- a/ash/system/chromeos/power/tablet_power_button_controller_unittest.cc
+++ b/ash/system/chromeos/power/tablet_power_button_controller_unittest.cc
@@ -228,19 +228,23 @@
   power_manager_client_->SendSuspendDone();
   power_manager_client_->SendBrightnessChanged(kNonZeroBrightness, false);
 
-  // Send the power button event after a short delay and check that it is
-  // ignored.
+  // Send the power button event after a short delay and check that backlights
+  // are not forced off.
   tick_clock_->Advance(base::TimeDelta::FromMilliseconds(500));
   power_manager_client_->SendPowerButtonEvent(true, tick_clock_->NowTicks());
+  EXPECT_TRUE(test_api_->ShutdownTimerIsRunning());
   power_manager_client_->SendPowerButtonEvent(false, tick_clock_->NowTicks());
+  EXPECT_FALSE(test_api_->ShutdownTimerIsRunning());
   EXPECT_FALSE(GetBacklightsForcedOff());
 
-  // Send the power button event after a longer delay and check that it is not
-  // ignored.
+  // Send the power button event after a longer delay and check that backlights
+  // are forced off.
   tick_clock_->Advance(base::TimeDelta::FromMilliseconds(1600));
   power_manager_client_->SendPowerButtonEvent(true, tick_clock_->NowTicks());
+  EXPECT_TRUE(test_api_->ShutdownTimerIsRunning());
   power_manager_client_->SendPowerButtonEvent(false, tick_clock_->NowTicks());
   power_manager_client_->SendBrightnessChanged(0, false);
+  EXPECT_FALSE(test_api_->ShutdownTimerIsRunning());
   EXPECT_TRUE(GetBacklightsForcedOff());
 }
 
@@ -258,20 +262,24 @@
   // brightness.
   power_manager_client_->SendSuspendDone();
 
-  // Send the power button event after a short delay and check that it is
-  // ignored. But if backlights are forced off, stop forcing off.
+  // Send the power button event after a short delay and check that backlights
+  // are not forced off.
   tick_clock_->Advance(base::TimeDelta::FromMilliseconds(500));
   power_manager_client_->SendPowerButtonEvent(true, tick_clock_->NowTicks());
   power_manager_client_->SendBrightnessChanged(kNonZeroBrightness, false);
+  EXPECT_TRUE(test_api_->ShutdownTimerIsRunning());
   power_manager_client_->SendPowerButtonEvent(false, tick_clock_->NowTicks());
+  EXPECT_FALSE(test_api_->ShutdownTimerIsRunning());
   EXPECT_FALSE(GetBacklightsForcedOff());
 
-  // Send the power button event after a longer delay and check that it is not
-  // ignored.
+  // Send the power button event after a longer delay and check that backlights
+  // are forced off.
   tick_clock_->Advance(base::TimeDelta::FromMilliseconds(1600));
   power_manager_client_->SendPowerButtonEvent(true, tick_clock_->NowTicks());
+  EXPECT_TRUE(test_api_->ShutdownTimerIsRunning());
   power_manager_client_->SendPowerButtonEvent(false, tick_clock_->NowTicks());
   power_manager_client_->SendBrightnessChanged(0, false);
+  EXPECT_FALSE(test_api_->ShutdownTimerIsRunning());
   EXPECT_TRUE(GetBacklightsForcedOff());
 }
 
diff --git a/cc/blink/web_display_item_list_impl.cc b/cc/blink/web_display_item_list_impl.cc
index b8cca0cd5..b94d1464 100644
--- a/cc/blink/web_display_item_list_impl.cc
+++ b/cc/blink/web_display_item_list_impl.cc
@@ -70,10 +70,9 @@
   display_item_list_->CreateAndAppendPairedEndItem<cc::EndClipDisplayItem>();
 }
 
-void WebDisplayItemListImpl::appendClipPathItem(
-    const SkPath& clip_path,
-    SkRegion::Op clip_op,
-    bool antialias) {
+void WebDisplayItemListImpl::appendClipPathItem(const SkPath& clip_path,
+                                                SkClipOp clip_op,
+                                                bool antialias) {
   display_item_list_->CreateAndAppendPairedBeginItem<cc::ClipPathDisplayItem>(
       clip_path, clip_op, antialias);
 }
diff --git a/cc/blink/web_display_item_list_impl.h b/cc/blink/web_display_item_list_impl.h
index a72d2bc8..9ef08b0e9 100644
--- a/cc/blink/web_display_item_list_impl.h
+++ b/cc/blink/web_display_item_list_impl.h
@@ -12,7 +12,7 @@
 #include "third_party/WebKit/public/platform/WebDisplayItemList.h"
 #include "third_party/WebKit/public/platform/WebVector.h"
 #include "third_party/skia/include/core/SkBlendMode.h"
-#include "third_party/skia/include/core/SkRegion.h"
+#include "third_party/skia/include/core/SkClipOp.h"
 #include "ui/gfx/geometry/point_f.h"
 
 class SkColorFilter;
@@ -48,7 +48,7 @@
       const blink::WebVector<SkRRect>& rounded_clip_rects) override;
   void appendEndClipItem() override;
   void appendClipPathItem(const SkPath& clip_path,
-                          SkRegion::Op clip_op,
+                          SkClipOp clip_op,
                           bool antialias) override;
   void appendEndClipPathItem() override;
   void appendFloatClipItem(const blink::WebFloatRect& clip_rect) override;
diff --git a/cc/ipc/BUILD.gn b/cc/ipc/BUILD.gn
index 01c83e3c..fab163da 100644
--- a/cc/ipc/BUILD.gn
+++ b/cc/ipc/BUILD.gn
@@ -54,6 +54,7 @@
     "selection.mojom",
     "shared_quad_state.mojom",
     "surface_id.mojom",
+    "surface_reference.mojom",
     "surface_sequence.mojom",
     "transferable_resource.mojom",
   ]
@@ -111,6 +112,7 @@
     "selection_struct_traits.h",
     "shared_quad_state_struct_traits.h",
     "surface_id_struct_traits.h",
+    "surface_reference_struct_traits.h",
     "surface_sequence_struct_traits.h",
     "transferable_resource_struct_traits.cc",
     "transferable_resource_struct_traits.h",
diff --git a/cc/ipc/local_frame_id_struct_traits.h b/cc/ipc/local_frame_id_struct_traits.h
index 514bd4c..4dd01d6 100644
--- a/cc/ipc/local_frame_id_struct_traits.h
+++ b/cc/ipc/local_frame_id_struct_traits.h
@@ -24,12 +24,8 @@
 
   static bool Read(cc::mojom::LocalFrameIdDataView data,
                    cc::LocalFrameId* out) {
-    base::UnguessableToken nonce;
-    if (!data.ReadNonce(&nonce))
-      return false;
-
-    *out = cc::LocalFrameId(data.local_id(), nonce);
-    return true;
+    out->local_id_ = data.local_id();
+    return data.ReadNonce(&out->nonce_);
   }
 };
 
diff --git a/cc/ipc/struct_traits_unittest.cc b/cc/ipc/struct_traits_unittest.cc
index a952b11..b7fa588 100644
--- a/cc/ipc/struct_traits_unittest.cc
+++ b/cc/ipc/struct_traits_unittest.cc
@@ -95,6 +95,12 @@
     callback.Run(s);
   }
 
+  void EchoSurfaceReference(
+      const SurfaceReference& s,
+      const EchoSurfaceReferenceCallback& callback) override {
+    callback.Run(s);
+  }
+
   void EchoSurfaceSequence(
       const SurfaceSequence& s,
       const EchoSurfaceSequenceCallback& callback) override {
@@ -779,6 +785,22 @@
   EXPECT_EQ(local_frame_id, output.local_frame_id());
 }
 
+TEST_F(StructTraitsTest, SurfaceReference) {
+  const SurfaceId parent_id(
+      FrameSinkId(2016, 1234),
+      LocalFrameId(0xfbadbeef, base::UnguessableToken::Deserialize(123, 456)));
+  const SurfaceId child_id(
+      FrameSinkId(1111, 9999),
+      LocalFrameId(0xabcdabcd, base::UnguessableToken::Deserialize(333, 333)));
+  const SurfaceReference input(parent_id, child_id);
+
+  mojom::TraitsTestServicePtr proxy = GetTraitsTestProxy();
+  SurfaceReference output;
+  proxy->EchoSurfaceReference(input, &output);
+  EXPECT_EQ(parent_id, output.parent_id());
+  EXPECT_EQ(child_id, output.child_id());
+}
+
 TEST_F(StructTraitsTest, SurfaceSequence) {
   const FrameSinkId frame_sink_id(2016, 1234);
   const uint32_t sequence = 0xfbadbeef;
diff --git a/cc/ipc/surface_id_struct_traits.h b/cc/ipc/surface_id_struct_traits.h
index 6f66c9f..6a9bee3 100644
--- a/cc/ipc/surface_id_struct_traits.h
+++ b/cc/ipc/surface_id_struct_traits.h
@@ -9,6 +9,7 @@
 #include "cc/ipc/local_frame_id_struct_traits.h"
 #include "cc/ipc/surface_id.mojom-shared.h"
 #include "cc/surfaces/frame_sink_id.h"
+#include "cc/surfaces/local_frame_id.h"
 #include "cc/surfaces/surface_id.h"
 
 namespace mojo {
@@ -24,16 +25,8 @@
   }
 
   static bool Read(cc::mojom::SurfaceIdDataView data, cc::SurfaceId* out) {
-    cc::FrameSinkId frame_sink_id;
-    if (!data.ReadFrameSinkId(&frame_sink_id))
-      return false;
-
-    cc::LocalFrameId local_frame_id;
-    if (!data.ReadLocalFrameId(&local_frame_id))
-      return false;
-
-    *out = cc::SurfaceId(frame_sink_id, local_frame_id);
-    return true;
+    return data.ReadFrameSinkId(&out->frame_sink_id_) &&
+           data.ReadLocalFrameId(&out->local_frame_id_);
   }
 };
 
diff --git a/cc/ipc/surface_reference.mojom b/cc/ipc/surface_reference.mojom
new file mode 100644
index 0000000..ecdd7ed
--- /dev/null
+++ b/cc/ipc/surface_reference.mojom
@@ -0,0 +1,12 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module cc.mojom;
+
+import "cc/ipc/surface_id.mojom";
+
+struct SurfaceReference {
+  SurfaceId parent_id;
+  SurfaceId child_id;
+};
diff --git a/cc/ipc/surface_reference.typemap b/cc/ipc/surface_reference.typemap
new file mode 100644
index 0000000..cb35490
--- /dev/null
+++ b/cc/ipc/surface_reference.typemap
@@ -0,0 +1,11 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+mojom = "//cc/ipc/surface_reference.mojom"
+public_headers = [ "//cc/surfaces/surface_reference.h" ]
+traits_headers = [ "//cc/ipc/surface_reference_struct_traits.h" ]
+deps = [
+  "//cc/ipc:struct_traits",
+]
+type_mappings = [ "cc.mojom.SurfaceReference=cc::SurfaceReference" ]
diff --git a/cc/ipc/surface_reference_struct_traits.h b/cc/ipc/surface_reference_struct_traits.h
new file mode 100644
index 0000000..209f1b3
--- /dev/null
+++ b/cc/ipc/surface_reference_struct_traits.h
@@ -0,0 +1,34 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_IPC_SURFACE_REFERENCE_STRUCT_TRAITS_H_
+#define CC_IPC_SURFACE_REFERENCE_STRUCT_TRAITS_H_
+
+#include "cc/ipc/surface_id_struct_traits.h"
+#include "cc/ipc/surface_reference.mojom-shared.h"
+#include "cc/surfaces/surface_id.h"
+#include "cc/surfaces/surface_reference.h"
+
+namespace mojo {
+
+template <>
+struct StructTraits<cc::mojom::SurfaceReferenceDataView, cc::SurfaceReference> {
+  static const cc::SurfaceId& parent_id(const cc::SurfaceReference& ref) {
+    return ref.parent_id();
+  }
+
+  static const cc::SurfaceId& child_id(const cc::SurfaceReference& ref) {
+    return ref.child_id();
+  }
+
+  static bool Read(cc::mojom::SurfaceReferenceDataView data,
+                   cc::SurfaceReference* out) {
+    return data.ReadParentId(&out->parent_id_) &&
+           data.ReadChildId(&out->child_id_);
+  }
+};
+
+}  // namespace mojo
+
+#endif  // CC_IPC_SURFACE_REFERENCE_STRUCT_TRAITS_H_
diff --git a/cc/ipc/traits_test_service.mojom b/cc/ipc/traits_test_service.mojom
index b94ce2f..7948f121 100644
--- a/cc/ipc/traits_test_service.mojom
+++ b/cc/ipc/traits_test_service.mojom
@@ -16,6 +16,7 @@
 import "cc/ipc/selection.mojom";
 import "cc/ipc/shared_quad_state.mojom";
 import "cc/ipc/surface_id.mojom";
+import "cc/ipc/surface_reference.mojom";
 import "cc/ipc/surface_sequence.mojom";
 import "cc/ipc/transferable_resource.mojom";
 
@@ -57,6 +58,9 @@
   EchoSurfaceId(SurfaceId s) => (SurfaceId pass);
 
   [Sync]
+  EchoSurfaceReference(SurfaceReference r) => (SurfaceReference pass);
+
+  [Sync]
   EchoSurfaceSequence(SurfaceSequence s) => (SurfaceSequence pass);
 
   [Sync]
diff --git a/cc/ipc/typemaps.gni b/cc/ipc/typemaps.gni
index 7e3e36e..d64c203 100644
--- a/cc/ipc/typemaps.gni
+++ b/cc/ipc/typemaps.gni
@@ -16,6 +16,7 @@
   "//cc/ipc/selection.typemap",
   "//cc/ipc/shared_quad_state.typemap",
   "//cc/ipc/surface_id.typemap",
+  "//cc/ipc/surface_reference.typemap",
   "//cc/ipc/surface_sequence.typemap",
   "//cc/ipc/transferable_resource.typemap",
 ]
diff --git a/cc/output/software_renderer.cc b/cc/output/software_renderer.cc
index 72ce0df..c18e4de 100644
--- a/cc/output/software_renderer.cc
+++ b/cc/output/software_renderer.cc
@@ -145,7 +145,7 @@
   // Skia applies the current matrix to clip rects so we reset it temporary.
   SkMatrix current_matrix = current_canvas_->getTotalMatrix();
   current_canvas_->resetMatrix();
-  current_canvas_->clipRect(gfx::RectToSkRect(rect), SkRegion::kReplace_Op);
+  current_canvas_->clipRect(gfx::RectToSkRect(rect), kReplace_SkClipOp);
   current_canvas_->setMatrix(current_matrix);
 }
 
@@ -257,8 +257,7 @@
     QuadFToSkPoints(local_draw_region, clip_points);
     draw_region_clip_path.addPoly(clip_points, 4, true);
 
-    current_canvas_->clipPath(draw_region_clip_path, SkRegion::kIntersect_Op,
-                              false);
+    current_canvas_->clipPath(draw_region_clip_path);
   }
 
   switch (quad->material) {
diff --git a/cc/playback/clip_display_item.cc b/cc/playback/clip_display_item.cc
index 906706a..c35eb22e 100644
--- a/cc/playback/clip_display_item.cc
+++ b/cc/playback/clip_display_item.cc
@@ -65,15 +65,12 @@
 void ClipDisplayItem::Raster(SkCanvas* canvas,
                              SkPicture::AbortCallback* callback) const {
   canvas->save();
-  canvas->clipRect(gfx::RectToSkRect(clip_rect_), SkRegion::kIntersect_Op,
-                   antialias_);
+  canvas->clipRect(gfx::RectToSkRect(clip_rect_), antialias_);
   for (size_t i = 0; i < rounded_clip_rects_.size(); ++i) {
     if (rounded_clip_rects_[i].isRect()) {
-      canvas->clipRect(rounded_clip_rects_[i].rect(), SkRegion::kIntersect_Op,
-                       antialias_);
+      canvas->clipRect(rounded_clip_rects_[i].rect(), antialias_);
     } else {
-      canvas->clipRRect(rounded_clip_rects_[i], SkRegion::kIntersect_Op,
-                        antialias_);
+      canvas->clipRRect(rounded_clip_rects_[i], antialias_);
     }
   }
 }
diff --git a/cc/playback/clip_path_display_item.cc b/cc/playback/clip_path_display_item.cc
index 64f3f957..e0542b220 100644
--- a/cc/playback/clip_path_display_item.cc
+++ b/cc/playback/clip_path_display_item.cc
@@ -16,7 +16,7 @@
 namespace cc {
 
 ClipPathDisplayItem::ClipPathDisplayItem(const SkPath& clip_path,
-                                         SkRegion::Op clip_op,
+                                         SkClipOp clip_op,
                                          bool antialias) {
   SetNew(clip_path, clip_op, antialias);
 }
@@ -25,7 +25,7 @@
   DCHECK_EQ(proto::DisplayItem::Type_ClipPath, proto.type());
 
   const proto::ClipPathDisplayItem& details = proto.clip_path_item();
-  SkRegion::Op clip_op = SkRegionOpFromProto(details.clip_op());
+  SkClipOp clip_op = SkClipOpFromProto(details.clip_op());
   bool antialias = details.antialias();
 
   SkPath clip_path;
@@ -42,7 +42,7 @@
 }
 
 void ClipPathDisplayItem::SetNew(const SkPath& clip_path,
-                                 SkRegion::Op clip_op,
+                                 SkClipOp clip_op,
                                  bool antialias) {
   clip_path_ = clip_path;
   clip_op_ = clip_op;
@@ -53,7 +53,7 @@
   proto->set_type(proto::DisplayItem::Type_ClipPath);
 
   proto::ClipPathDisplayItem* details = proto->mutable_clip_path_item();
-  details->set_clip_op(SkRegionOpToProto(clip_op_));
+  details->set_clip_op(SkClipOpToProto(clip_op_));
   details->set_antialias(antialias_);
 
   // Just use skia's serialization method for the SkPath for now.
diff --git a/cc/playback/clip_path_display_item.h b/cc/playback/clip_path_display_item.h
index ea8ab17..e1eccd82 100644
--- a/cc/playback/clip_path_display_item.h
+++ b/cc/playback/clip_path_display_item.h
@@ -12,8 +12,8 @@
 #include "base/memory/ptr_util.h"
 #include "cc/base/cc_export.h"
 #include "cc/playback/display_item.h"
+#include "third_party/skia/include/core/SkClipOp.h"
 #include "third_party/skia/include/core/SkPath.h"
-#include "third_party/skia/include/core/SkRegion.h"
 
 class SkCanvas;
 
@@ -21,7 +21,7 @@
 
 class CC_EXPORT ClipPathDisplayItem : public DisplayItem {
  public:
-  ClipPathDisplayItem(const SkPath& path, SkRegion::Op clip_op, bool antialias);
+  ClipPathDisplayItem(const SkPath& path, SkClipOp clip_op, bool antialias);
   explicit ClipPathDisplayItem(const proto::DisplayItem& proto);
   ~ClipPathDisplayItem() override;
 
@@ -35,10 +35,10 @@
   int ApproximateOpCount() const { return 1; }
 
  private:
-  void SetNew(const SkPath& path, SkRegion::Op clip_op, bool antialias);
+  void SetNew(const SkPath& path, SkClipOp clip_op, bool antialias);
 
   SkPath clip_path_;
-  SkRegion::Op clip_op_;
+  SkClipOp clip_op_;
   bool antialias_;
 };
 
diff --git a/cc/playback/display_item_list_unittest.cc b/cc/playback/display_item_list_unittest.cc
index 6f84642a7..329c33a 100644
--- a/cc/playback/display_item_list_unittest.cc
+++ b/cc/playback/display_item_list_unittest.cc
@@ -201,7 +201,7 @@
   SkPath path;
   path.addCircle(5.f, 5.f, 2.f, SkPath::Direction::kCW_Direction);
   list->CreateAndAppendPairedBeginItem<ClipPathDisplayItem>(
-      path, SkRegion::Op::kReplace_Op, false);
+      path, kReplace_SkClipOp, false);
 
   // Build the second DrawingDisplayItem.
   AppendSecondSerializationTestPicture(list, layer_size);
diff --git a/cc/playback/raster_source.cc b/cc/playback/raster_source.cc
index 9e5e946..4ad28c3c 100644
--- a/cc/playback/raster_source.cc
+++ b/cc/playback/raster_source.cc
@@ -172,7 +172,7 @@
     // Use clipRegion to bypass CTM because the rects are device rects.
     SkRegion interest_region;
     interest_region.setRect(interest_rect);
-    canvas->clipRegion(interest_region, SkRegion::kDifference_Op);
+    canvas->clipRegion(interest_region, kDifference_SkClipOp);
     canvas->clear(DebugColors::MissingResizeInvalidations());
     canvas->restore();
   }
diff --git a/cc/proto/BUILD.gn b/cc/proto/BUILD.gn
index d03e00a..23472fd8 100644
--- a/cc/proto/BUILD.gn
+++ b/cc/proto/BUILD.gn
@@ -48,6 +48,7 @@
     "scroll_offset.proto",
     "size.proto",
     "sizef.proto",
+    "skclipop.proto",
     "skregion.proto",
     "skrrect.proto",
     "skxfermode.proto",
diff --git a/cc/proto/display_item.proto b/cc/proto/display_item.proto
index 2b7db4b3..d558203 100644
--- a/cc/proto/display_item.proto
+++ b/cc/proto/display_item.proto
@@ -4,7 +4,7 @@
 
 syntax = "proto2";
 
-import "skregion.proto";
+import "skclipop.proto";
 import "skrrect.proto";
 import "skxfermode.proto";
 import "rect.proto";
@@ -61,7 +61,7 @@
 }
 
 message ClipPathDisplayItem {
-  optional cc.proto.SkRegion.Op clip_op = 1;
+  optional cc.proto.SkClipOp.Op clip_op = 1;
   optional bool antialias = 2;
   optional bytes clip_path = 3; /* SkPath */
 }
diff --git a/cc/proto/skclipop.proto b/cc/proto/skclipop.proto
new file mode 100644
index 0000000..fe2da302
--- /dev/null
+++ b/cc/proto/skclipop.proto
@@ -0,0 +1,20 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package cc.proto;
+
+message SkClipOp {
+  enum Op {
+    DIFFERENCE_ = 0;  // On Windows, winuser.h defines DIFFERENCE.
+    INTERSECT = 1;
+    UNION = 2;
+    XOR = 3;
+    REVERSE_DIFFERENCE = 4;
+    REPLACE = 5;
+  }
+}
diff --git a/cc/proto/skia_conversions.cc b/cc/proto/skia_conversions.cc
index 21ce808..fcf28b4 100644
--- a/cc/proto/skia_conversions.cc
+++ b/cc/proto/skia_conversions.cc
@@ -6,6 +6,7 @@
 
 #include "base/logging.h"
 #include "cc/proto/gfx_conversions.h"
+#include "cc/proto/skclipop.pb.h"
 #include "cc/proto/skrrect.pb.h"
 #include "third_party/skia/include/core/SkRRect.h"
 #include "ui/gfx/skia_util.h"
@@ -25,40 +26,40 @@
 
 }  // namespace
 
-SkRegion::Op SkRegionOpFromProto(proto::SkRegion::Op op) {
+SkClipOp SkClipOpFromProto(proto::SkClipOp::Op op) {
   switch (op) {
-    case proto::SkRegion::DIFFERENCE_:
-      return SkRegion::Op::kDifference_Op;
-    case proto::SkRegion::INTERSECT:
-      return SkRegion::Op::kIntersect_Op;
-    case proto::SkRegion::UNION:
-      return SkRegion::Op::kUnion_Op;
-    case proto::SkRegion::XOR:
-      return SkRegion::Op::kXOR_Op;
-    case proto::SkRegion::REVERSE_DIFFERENCE:
-      return SkRegion::Op::kReverseDifference_Op;
-    case proto::SkRegion::REPLACE:
-      return SkRegion::Op::kReplace_Op;
+    case proto::SkClipOp::DIFFERENCE_:
+      return kDifference_SkClipOp;
+    case proto::SkClipOp::INTERSECT:
+      return kIntersect_SkClipOp;
+    case proto::SkClipOp::UNION:
+      return kUnion_SkClipOp;
+    case proto::SkClipOp::XOR:
+      return kXOR_SkClipOp;
+    case proto::SkClipOp::REVERSE_DIFFERENCE:
+      return kReverseDifference_SkClipOp;
+    case proto::SkClipOp::REPLACE:
+      return kReplace_SkClipOp;
   }
-  return SkRegion::Op::kDifference_Op;
+  return kDifference_SkClipOp;
 }
 
-proto::SkRegion::Op SkRegionOpToProto(SkRegion::Op op) {
+proto::SkClipOp::Op SkClipOpToProto(SkClipOp op) {
   switch (op) {
-    case SkRegion::Op::kDifference_Op:
-      return proto::SkRegion::DIFFERENCE_;
-    case SkRegion::Op::kIntersect_Op:
-      return proto::SkRegion::INTERSECT;
-    case SkRegion::Op::kUnion_Op:
-      return proto::SkRegion::UNION;
-    case SkRegion::Op::kXOR_Op:
-      return proto::SkRegion::XOR;
-    case SkRegion::Op::kReverseDifference_Op:
-      return proto::SkRegion::REVERSE_DIFFERENCE;
-    case SkRegion::Op::kReplace_Op:
-      return proto::SkRegion::REPLACE;
+    case kDifference_SkClipOp:
+      return proto::SkClipOp::DIFFERENCE_;
+    case kIntersect_SkClipOp:
+      return proto::SkClipOp::INTERSECT;
+    case kUnion_SkClipOp:
+      return proto::SkClipOp::UNION;
+    case kXOR_SkClipOp:
+      return proto::SkClipOp::XOR;
+    case kReverseDifference_SkClipOp:
+      return proto::SkClipOp::REVERSE_DIFFERENCE;
+    case kReplace_SkClipOp:
+      return proto::SkClipOp::REPLACE;
   }
-  return proto::SkRegion::DIFFERENCE_;
+  return proto::SkClipOp::DIFFERENCE_;
 }
 
 SkBlendMode SkXfermodeModeFromProto(proto::SkXfermode::Mode mode) {
diff --git a/cc/proto/skia_conversions.h b/cc/proto/skia_conversions.h
index 402fa4e..e92e1aeb 100644
--- a/cc/proto/skia_conversions.h
+++ b/cc/proto/skia_conversions.h
@@ -6,12 +6,11 @@
 #define CC_PROTO_SKIA_CONVERSIONS_H_
 
 #include "cc/base/cc_export.h"
-#include "cc/proto/skregion.pb.h"
+#include "cc/proto/skclipop.pb.h"
 #include "cc/proto/skxfermode.pb.h"
 #include "third_party/skia/include/core/SkBlendMode.h"
-#include "third_party/skia/include/core/SkRegion.h"
+#include "third_party/skia/include/core/SkClipOp.h"
 
-class SkRegion;
 class SkRRect;
 
 namespace cc {
@@ -22,8 +21,8 @@
 
 // TODO(dtrainor): Move these to a class and make them static
 // (crbug.com/548432).
-CC_EXPORT SkRegion::Op SkRegionOpFromProto(proto::SkRegion::Op op);
-CC_EXPORT proto::SkRegion::Op SkRegionOpToProto(SkRegion::Op op);
+CC_EXPORT SkClipOp SkClipOpFromProto(proto::SkClipOp::Op op);
+CC_EXPORT proto::SkClipOp::Op SkClipOpToProto(SkClipOp op);
 
 CC_EXPORT SkBlendMode SkXfermodeModeFromProto(proto::SkXfermode::Mode mode);
 CC_EXPORT proto::SkXfermode::Mode SkXfermodeModeToProto(SkBlendMode mode);
diff --git a/cc/proto/skia_conversions_unittest.cc b/cc/proto/skia_conversions_unittest.cc
index fac61a4..7e3b6d0 100644
--- a/cc/proto/skia_conversions_unittest.cc
+++ b/cc/proto/skia_conversions_unittest.cc
@@ -11,16 +11,22 @@
 #include "cc/proto/skxfermode.pb.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkBlendMode.h"
+#include "third_party/skia/include/core/SkClipOp.h"
 #include "third_party/skia/include/core/SkRRect.h"
-#include "third_party/skia/include/core/SkRegion.h"
 
 namespace cc {
 namespace {
 
-TEST(SkiaProtoConversionsTest, SerializeDeserializeSkRegionOp) {
-  for (size_t i = 0; i < SkRegion::Op::kLastOp; i++) {
-    SkRegion::Op op = static_cast<SkRegion::Op>(i);
-    EXPECT_EQ(op, SkRegionOpFromProto(SkRegionOpToProto(op)));
+TEST(SkiaProtoConversionsTest, SerializeDeserializeSkClipOp) {
+  // explicitly list the clipops, as this list will be reduced overtime
+  // as skia constricts the valid ops for clipping
+  // https://bugs.chromium.org/p/skia/issues/detail?id=3191
+  const SkClipOp ops[] = {
+      kDifference_SkClipOp, kIntersect_SkClipOp,         kUnion_SkClipOp,
+      kXOR_SkClipOp,        kReverseDifference_SkClipOp, kReplace_SkClipOp,
+  };
+  for (SkClipOp op : ops) {
+    EXPECT_EQ(op, SkClipOpFromProto(SkClipOpToProto(op)));
   }
 }
 
diff --git a/cc/surfaces/BUILD.gn b/cc/surfaces/BUILD.gn
index fb3140a..284f1a1 100644
--- a/cc/surfaces/BUILD.gn
+++ b/cc/surfaces/BUILD.gn
@@ -12,6 +12,8 @@
     "local_frame_id.h",
     "surface_id.cc",
     "surface_id.h",
+    "surface_reference.cc",
+    "surface_reference.h",
     "surface_sequence.h",
     "surface_sequence_generator.cc",
     "surface_sequence_generator.h",
@@ -19,6 +21,7 @@
 
   deps = [
     "//base",
+    "//mojo/public/cpp/bindings:struct_traits",
   ]
 }
 
diff --git a/cc/surfaces/DEPS b/cc/surfaces/DEPS
new file mode 100644
index 0000000..7502d902
--- /dev/null
+++ b/cc/surfaces/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+mojo/public/cpp/bindings/struct_traits.h",
+]
diff --git a/cc/surfaces/local_frame_id.h b/cc/surfaces/local_frame_id.h
index ea0ec038..7773b964 100644
--- a/cc/surfaces/local_frame_id.h
+++ b/cc/surfaces/local_frame_id.h
@@ -13,8 +13,12 @@
 
 #include "base/hash.h"
 #include "base/unguessable_token.h"
+#include "mojo/public/cpp/bindings/struct_traits.h"
 
 namespace cc {
+namespace mojom {
+class LocalFrameIdDataView;
+}
 
 class LocalFrameId {
  public:
@@ -53,6 +57,8 @@
   std::string ToString() const;
 
  private:
+  friend struct mojo::StructTraits<mojom::LocalFrameIdDataView, LocalFrameId>;
+
   uint32_t local_id_;
   base::UnguessableToken nonce_;
 };
diff --git a/cc/surfaces/surface_id.h b/cc/surfaces/surface_id.h
index 6df5009..fe21851 100644
--- a/cc/surfaces/surface_id.h
+++ b/cc/surfaces/surface_id.h
@@ -14,8 +14,12 @@
 #include "base/hash.h"
 #include "cc/surfaces/frame_sink_id.h"
 #include "cc/surfaces/local_frame_id.h"
+#include "mojo/public/cpp/bindings/struct_traits.h"
 
 namespace cc {
+namespace mojom {
+class SurfaceIdDataView;
+}
 
 class SurfaceId {
  public:
@@ -62,6 +66,8 @@
   }
 
  private:
+  friend struct mojo::StructTraits<mojom::SurfaceIdDataView, SurfaceId>;
+
   // See SurfaceIdAllocator::GenerateId.
   FrameSinkId frame_sink_id_;
   LocalFrameId local_frame_id_;
diff --git a/cc/surfaces/surface_reference.cc b/cc/surfaces/surface_reference.cc
new file mode 100644
index 0000000..5570fbfd
--- /dev/null
+++ b/cc/surfaces/surface_reference.cc
@@ -0,0 +1,27 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/surfaces/surface_reference.h"
+
+#include "base/strings/stringprintf.h"
+
+namespace cc {
+
+SurfaceReference::SurfaceReference() = default;
+
+SurfaceReference::SurfaceReference(const SurfaceId& parent_id,
+                                   const SurfaceId& child_id)
+    : parent_id_(parent_id), child_id_(child_id) {}
+
+SurfaceReference::SurfaceReference(const SurfaceReference& other) = default;
+
+SurfaceReference::~SurfaceReference() = default;
+
+std::string SurfaceReference::ToString() const {
+  return base::StringPrintf("parent=%s, child=%s",
+                            parent_id_.ToString().c_str(),
+                            child_id_.ToString().c_str());
+}
+
+}  // namespace cc
diff --git a/cc/surfaces/surface_reference.h b/cc/surfaces/surface_reference.h
new file mode 100644
index 0000000..ac3bb77
--- /dev/null
+++ b/cc/surfaces/surface_reference.h
@@ -0,0 +1,65 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_SURFACES_SURFACE_REFERENCE_H_
+#define CC_SURFACES_SURFACE_REFERENCE_H_
+
+#include <string>
+
+#include "base/hash.h"
+#include "cc/surfaces/surface_id.h"
+#include "mojo/public/cpp/bindings/struct_traits.h"
+
+namespace cc {
+namespace mojom {
+class SurfaceReferenceDataView;
+}
+
+// Hold a reference from an embedding (parent) to embedded (child) surface.
+class SurfaceReference {
+ public:
+  SurfaceReference();
+  SurfaceReference(const SurfaceId& parent_id, const SurfaceId& child_id);
+  SurfaceReference(const SurfaceReference& other);
+
+  ~SurfaceReference();
+
+  const SurfaceId& parent_id() const { return parent_id_; }
+  const SurfaceId& child_id() const { return child_id_; }
+
+  size_t hash() const {
+    return base::HashInts(static_cast<uint64_t>(parent_id_.hash()),
+                          static_cast<uint64_t>(child_id_.hash()));
+  }
+
+  bool operator==(const SurfaceReference& other) const {
+    return parent_id_ == other.parent_id_ && child_id_ == other.child_id_;
+  }
+
+  bool operator!=(const SurfaceReference& other) const {
+    return !(*this == other);
+  }
+
+  bool operator<(const SurfaceReference& other) const {
+    return std::tie(parent_id_, child_id_) <
+           std::tie(other.parent_id_, other.child_id_);
+  }
+
+  std::string ToString() const;
+
+ private:
+  friend struct mojo::StructTraits<mojom::SurfaceReferenceDataView,
+                                   SurfaceReference>;
+
+  SurfaceId parent_id_;
+  SurfaceId child_id_;
+};
+
+struct SurfaceReferenceHash {
+  size_t operator()(const SurfaceReference& ref) const { return ref.hash(); }
+};
+
+}  // namespace cc
+
+#endif  // CC_SURFACES_SURFACE_REFERENCE_H_
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/UrlConstants.java b/chrome/android/java/src/org/chromium/chrome/browser/UrlConstants.java
index 0923b5a..b4ed7c9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/UrlConstants.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/UrlConstants.java
@@ -10,6 +10,7 @@
 public class UrlConstants {
     public static final String CHROME_SCHEME = "chrome://";
     public static final String CHROME_NATIVE_SCHEME = "chrome-native://";
+    public static final String CONTENT_SCHEME = "content://";
     public static final String CUSTOM_TAB_SCHEME = "customtab";
     public static final String DOCUMENT_SCHEME = "document";
     public static final String FILE_SCHEME = "file://";
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegate.java
index 59d4743b..529b7d6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegate.java
@@ -92,6 +92,7 @@
             boolean isChromeScheme = url.startsWith(UrlConstants.CHROME_SCHEME)
                     || url.startsWith(UrlConstants.CHROME_NATIVE_SCHEME);
             boolean isFileScheme = url.startsWith(UrlConstants.FILE_SCHEME);
+            boolean isContentScheme = url.startsWith(UrlConstants.CONTENT_SCHEME);
             boolean shouldShowIconRow = !mActivity.isTablet()
                     || mActivity.getWindow().getDecorView().getWidth()
                             < DeviceFormFactor.getMinimumTabletWidthPx(mActivity);
@@ -156,15 +157,18 @@
                     !currentTab.isNativePage() && currentTab.getWebContents() != null);
 
             // Hide 'Add to homescreen' for the following:
-            // 1.) chrome:// pages - Android doesn't know how to direct those URLs.
-            // 2.) incognito pages - To avoid problems where users create shortcuts in incognito
-            //                       mode and then open the webapp in regular mode.
-            // 3.) file:// - After API 24, file: URIs are not supported in VIEW intents and thus
-            //               can not be added to the homescreen.
-            // 4.) If creating shortcuts it not supported by the current home screen.
+            // * chrome:// pages - Android doesn't know how to direct those URLs.
+            // * incognito pages - To avoid problems where users create shortcuts in incognito
+            //                      mode and then open the webapp in regular mode.
+            // * file:// - After API 24, file: URIs are not supported in VIEW intents and thus
+            //             can not be added to the homescreen.
+            // * content:// - Accessing external content URIs requires the calling app to grant
+            //                access to the resource via FLAG_GRANT_READ_URI_PERMISSION, and that
+            //                is not persisted when adding to the homescreen.
+            // * If creating shortcuts it not supported by the current home screen.
             MenuItem homescreenItem = menu.findItem(R.id.add_to_homescreen_id);
             boolean homescreenItemVisible = ShortcutHelper.isAddToHomeIntentSupported(mActivity)
-                    && !isChromeScheme && !isFileScheme && !isIncognito;
+                    && !isChromeScheme && !isFileScheme && !isContentScheme && !isIncognito;
             if (homescreenItemVisible) {
                 homescreenItem.setTitle(AppBannerManager.getHomescreenLanguageOption());
             }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/Stack.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/Stack.java
index 8634b1a..3ffe7ed 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/Stack.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/Stack.java
@@ -1171,8 +1171,10 @@
                 float maxScreen = tab0ScreenAfter;
                 for (int i = pinch0TabIndex; i <= pinch1TabIndex; i++) {
                     float screenBefore = approxScreen(mStackTabs[i], oldScrollTarget);
-                    float t = (screenBefore - tab0ScreenBefore)
-                            / (tab1ScreenBefore - tab0ScreenBefore);
+                    float t = (tab1ScreenBefore == tab0ScreenBefore)
+                            ? 1
+                            : ((screenBefore - tab0ScreenBefore)
+                                      / (tab1ScreenBefore - tab0ScreenBefore));
                     float screenAfter = (1 - t) * tab0ScreenAfter + t * tab1ScreenAfter;
                     screenAfter = Math.max(minScreen, screenAfter);
                     screenAfter = Math.min(maxScreen, screenAfter);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/document/ChromeLauncherActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/document/ChromeLauncherActivity.java
index 891fccd..54e7779 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/document/ChromeLauncherActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/document/ChromeLauncherActivity.java
@@ -55,6 +55,7 @@
 import org.chromium.chrome.browser.util.UrlUtilities;
 import org.chromium.chrome.browser.webapps.ActivityAssigner;
 import org.chromium.chrome.browser.webapps.WebappLauncherActivity;
+import org.chromium.ui.widget.Toast;
 
 import java.lang.ref.WeakReference;
 import java.net.URI;
@@ -455,7 +456,9 @@
             newIntent.addFlags(Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS);
         }
         Uri uri = newIntent.getData();
+        boolean isContentScheme = false;
         if (uri != null && "content".equals(uri.getScheme())) {
+            isContentScheme = true;
             newIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
         }
         if (mIsInLegacyMultiInstanceMode) {
@@ -470,6 +473,14 @@
         StrictMode.allowThreadDiskWrites();
         try {
             startActivity(newIntent);
+        } catch (SecurityException ex) {
+            if (isContentScheme) {
+                Toast.makeText(
+                        this, R.string.external_app_restricted_access_error,
+                        Toast.LENGTH_LONG).show();
+            } else {
+                throw ex;
+            }
         } finally {
             StrictMode.setThreadPolicy(oldPolicy);
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/gsa/ContextReporter.java b/chrome/android/java/src/org/chromium/chrome/browser/gsa/ContextReporter.java
index c40337f..394e0f4c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/gsa/ContextReporter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/gsa/ContextReporter.java
@@ -212,7 +212,7 @@
      * Records an appropriate status via UMA given the current sync status.
      */
     public static void reportSyncStatus(@Nullable ProfileSyncService syncService) {
-        if (syncService == null || !syncService.isBackendInitialized()) {
+        if (syncService == null || !syncService.isEngineInitialized()) {
             reportStatus(STATUS_SYNC_NOT_INITIALIZED);
         } else if (!syncService.getActiveDataTypes().contains(ModelType.TYPED_URLS)) {
             reportStatus(STATUS_SYNC_NOT_SYNCING_URLS);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/precache/PrecacheLauncher.java b/chrome/android/java/src/org/chromium/chrome/browser/precache/PrecacheLauncher.java
index d9c8f252..4c2703f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/precache/PrecacheLauncher.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/precache/PrecacheLauncher.java
@@ -146,7 +146,7 @@
                 if (mListener == null && sync != null) {
                     mListener = new ProfileSyncService.SyncStateChangedListener() {
                         public void syncStateChanged() {
-                            if (sync.isBackendInitialized()) {
+                            if (sync.isEngineInitialized()) {
                                 mSyncInitialized = true;
                                 updateEnabledSync(context);
                             }
@@ -156,7 +156,7 @@
                 }
 
                 if (mListener != null) {
-                    // Call the listener once, in case the sync backend is already initialized.
+                    // Call the listener once, in case the sync engine is already initialized.
                     mListener.syncStateChanged();
                 }
                 Log.v(TAG, "updateEnabled complete");
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/MainPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/MainPreferences.java
index fe60ed16..e79b434 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/MainPreferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/MainPreferences.java
@@ -123,9 +123,8 @@
         ProfileSyncService syncService = ProfileSyncService.get();
 
         if (AndroidSyncSettings.isSyncEnabled(getActivity().getApplicationContext())
-                  && syncService.isBackendInitialized()
-                  && !syncService.isUsingSecondaryPassphrase()
-                  && ChromeFeatureList.isEnabled(VIEW_PASSWORDS)) {
+                && syncService.isEngineInitialized() && !syncService.isUsingSecondaryPassphrase()
+                && ChromeFeatureList.isEnabled(VIEW_PASSWORDS)) {
             passwordsPref.setKey(PREF_MANAGE_ACCOUNT_LINK);
             passwordsPref.setTitle(R.string.redirect_to_passwords_text);
             passwordsPref.setSummary(R.string.redirect_to_passwords_link);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/GmsCoreSyncListener.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/GmsCoreSyncListener.java
index 4d3642ac..5c02bea 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/GmsCoreSyncListener.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/GmsCoreSyncListener.java
@@ -26,7 +26,7 @@
     @VisibleForTesting
     public void syncStateChanged() {
         ProfileSyncService syncService = ProfileSyncService.get();
-        boolean passphraseSet = syncService.isBackendInitialized()
+        boolean passphraseSet = syncService.isEngineInitialized()
                 && syncService.isUsingSecondaryPassphrase() && syncService.isCryptographerReady();
         if (passphraseSet && !mGmsCoreInformed) {
             byte[] key = syncService.getCustomPassphraseKey();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/ProfileSyncService.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/ProfileSyncService.java
index 19619979..7c1890c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/ProfileSyncService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/ProfileSyncService.java
@@ -4,14 +4,15 @@
 
 package org.chromium.chrome.browser.sync;
 
+import org.json.JSONArray;
+import org.json.JSONException;
+
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.SuppressFBWarnings;
 import org.chromium.components.sync.ModelType;
 import org.chromium.components.sync.PassphraseType;
-import org.json.JSONArray;
-import org.json.JSONException;
 
 import java.util.HashSet;
 import java.util.List;
@@ -159,15 +160,14 @@
     }
 
     /**
-     * Returns the actual passphrase type being used for encryption.
-     * The sync backend must be running (isBackendInitialized() returns true) before
-     * calling this function.
+     * Returns the actual passphrase type being used for encryption. The sync engine must be
+     * running (isEngineInitialized() returns true) before calling this function.
      * <p/>
      * This method should only be used if you want to know the raw value. For checking whether
      * we should ask the user for a passphrase, use isPassphraseRequiredForDecryption().
      */
     public PassphraseType getPassphraseType() {
-        assert isBackendInitialized();
+        assert isEngineInitialized();
         int passphraseType = nativeGetPassphraseType(mNativeProfileSyncServiceAndroid);
         return PassphraseType.fromInternalValue(passphraseType);
     }
@@ -176,7 +176,7 @@
      * Returns true if the current explicit passphrase time is defined.
      */
     public boolean hasExplicitPassphraseTime() {
-        assert isBackendInitialized();
+        assert isEngineInitialized();
         return nativeHasExplicitPassphraseTime(mNativeProfileSyncServiceAndroid);
     }
 
@@ -184,22 +184,22 @@
      * Returns the current explicit passphrase time in milliseconds since epoch.
      */
     public long getExplicitPassphraseTime() {
-        assert isBackendInitialized();
+        assert isEngineInitialized();
         return nativeGetExplicitPassphraseTime(mNativeProfileSyncServiceAndroid);
     }
 
     public String getSyncEnterGooglePassphraseBodyWithDateText() {
-        assert isBackendInitialized();
+        assert isEngineInitialized();
         return nativeGetSyncEnterGooglePassphraseBodyWithDateText(mNativeProfileSyncServiceAndroid);
     }
 
     public String getSyncEnterCustomPassphraseBodyWithDateText() {
-        assert isBackendInitialized();
+        assert isEngineInitialized();
         return nativeGetSyncEnterCustomPassphraseBodyWithDateText(mNativeProfileSyncServiceAndroid);
     }
 
     public String getCurrentSignedInAccountText() {
-        assert isBackendInitialized();
+        assert isEngineInitialized();
         return nativeGetCurrentSignedInAccountText(mNativeProfileSyncServiceAndroid);
     }
 
@@ -208,13 +208,13 @@
     }
 
     /**
-     * Checks if sync is currently set to use a custom passphrase. The sync backend must be running
-     * (isBackendInitialized() returns true) before calling this function.
+     * Checks if sync is currently set to use a custom passphrase. The sync engine must be running
+     * (isEngineInitialized() returns true) before calling this function.
      *
      * @return true if sync is using a custom passphrase.
      */
     public boolean isUsingSecondaryPassphrase() {
-        assert isBackendInitialized();
+        assert isEngineInitialized();
         return nativeIsUsingSecondaryPassphrase(mNativeProfileSyncServiceAndroid);
     }
 
@@ -230,17 +230,17 @@
      * @return true if we need a passphrase.
      */
     public boolean isPassphraseRequiredForDecryption() {
-        assert isBackendInitialized();
+        assert isEngineInitialized();
         return nativeIsPassphraseRequiredForDecryption(mNativeProfileSyncServiceAndroid);
     }
 
     /**
-     * Checks if the sync backend is running.
+     * Checks if the sync engine is running.
      *
      * @return true if sync is initialized/running.
      */
-    public boolean isBackendInitialized() {
-        return nativeIsBackendInitialized(mNativeProfileSyncServiceAndroid);
+    public boolean isEngineInitialized() {
+        return nativeIsEngineInitialized(mNativeProfileSyncServiceAndroid);
     }
 
     /**
@@ -250,7 +250,7 @@
      * be encrypted.
      */
     public boolean isEncryptEverythingAllowed() {
-        assert isBackendInitialized();
+        assert isEngineInitialized();
         return nativeIsEncryptEverythingAllowed(mNativeProfileSyncServiceAndroid);
     }
 
@@ -260,7 +260,7 @@
      * @return true if all data types are encrypted, false if only passwords are encrypted.
      */
     public boolean isEncryptEverythingEnabled() {
-        assert isBackendInitialized();
+        assert isEngineInitialized();
         return nativeIsEncryptEverythingEnabled(mNativeProfileSyncServiceAndroid);
     }
 
@@ -269,22 +269,22 @@
      * completed and setPreferredDataTypes() is invoked.
      */
     public void enableEncryptEverything() {
-        assert isBackendInitialized();
+        assert isEngineInitialized();
         nativeEnableEncryptEverything(mNativeProfileSyncServiceAndroid);
     }
 
     public void setEncryptionPassphrase(String passphrase) {
-        assert isBackendInitialized();
+        assert isEngineInitialized();
         nativeSetEncryptionPassphrase(mNativeProfileSyncServiceAndroid, passphrase);
     }
 
     public boolean isCryptographerReady() {
-        assert isBackendInitialized();
+        assert isEngineInitialized();
         return nativeIsCryptographerReady(mNativeProfileSyncServiceAndroid);
     }
 
     public boolean setDecryptionPassphrase(String passphrase) {
-        assert isBackendInitialized();
+        assert isEngineInitialized();
         return nativeSetDecryptionPassphrase(mNativeProfileSyncServiceAndroid, passphrase);
     }
 
@@ -547,7 +547,7 @@
     private native String nativeQuerySyncStatusSummary(long nativeProfileSyncServiceAndroid);
     private native int nativeGetAuthError(long nativeProfileSyncServiceAndroid);
     private native int nativeGetProtocolErrorClientAction(long nativeProfileSyncServiceAndroid);
-    private native boolean nativeIsBackendInitialized(long nativeProfileSyncServiceAndroid);
+    private native boolean nativeIsEngineInitialized(long nativeProfileSyncServiceAndroid);
     private native boolean nativeIsEncryptEverythingAllowed(long nativeProfileSyncServiceAndroid);
     private native boolean nativeIsEncryptEverythingEnabled(long nativeProfileSyncServiceAndroid);
     private native void nativeEnableEncryptEverything(long nativeProfileSyncServiceAndroid);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncController.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncController.java
index 357724e..cd70dd3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncController.java
@@ -212,7 +212,7 @@
      * @return Whether sync is enabled to sync urls or open tabs with a non custom passphrase.
      */
     public boolean isSyncingUrlsWithKeystorePassphrase() {
-        return mProfileSyncService.isBackendInitialized()
+        return mProfileSyncService.isEngineInitialized()
                 && mProfileSyncService.getPreferredDataTypes().contains(ModelType.TYPED_URLS)
                 && mProfileSyncService.getPassphraseType().equals(
                            PassphraseType.KEYSTORE_PASSPHRASE);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncNotificationController.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncNotificationController.java
index a5761b7c..1f37729e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncNotificationController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncNotificationController.java
@@ -68,7 +68,7 @@
         if (shouldSyncAuthErrorBeShown()) {
             message = mProfileSyncService.getAuthError().getMessage();
             intent = createSettingsIntent();
-        } else if (mProfileSyncService.isBackendInitialized()
+        } else if (mProfileSyncService.isEngineInitialized()
                 && mProfileSyncService.isPassphraseRequiredForDecryption()) {
             if (mProfileSyncService.isPassphrasePrompted()) {
                 return;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseActivity.java
index c85d41a7..fab506f3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseActivity.java
@@ -64,7 +64,7 @@
         }
 
         if (!isShowingDialog(FRAGMENT_PASSPHRASE)) {
-            if (ProfileSyncService.get().isBackendInitialized()) {
+            if (ProfileSyncService.get().isEngineInitialized()) {
                 displayPassphraseDialog();
             } else {
                 addSyncStateChangedListener();
@@ -88,7 +88,7 @@
         mSyncStateChangedListener = new ProfileSyncService.SyncStateChangedListener() {
             @Override
             public void syncStateChanged() {
-                if (ProfileSyncService.get().isBackendInitialized()) {
+                if (ProfileSyncService.get().isEngineInitialized()) {
                     removeSyncStateChangedListener();
                     displayPassphraseDialog();
                 }
@@ -109,7 +109,7 @@
     }
 
     private void displayPassphraseDialog() {
-        assert ProfileSyncService.get().isBackendInitialized();
+        assert ProfileSyncService.get().isEngineInitialized();
         FragmentTransaction ft = getFragmentManager().beginTransaction();
         ft.addToBackStack(null);
         PassphraseDialogFragment.newInstance(null).show(ft, FRAGMENT_PASSPHRASE);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/SyncCustomizationFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/SyncCustomizationFragment.java
index eefa711..70e06f5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/SyncCustomizationFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/SyncCustomizationFragment.java
@@ -112,7 +112,7 @@
     public static final String ARGUMENT_ACCOUNT = "account";
 
     private ChromeSwitchPreference mSyncSwitchPreference;
-    private boolean mIsBackendInitialized;
+    private boolean mIsEngineInitialized;
     private boolean mIsPassphraseRequired;
 
     @VisibleForTesting
@@ -152,9 +152,9 @@
             LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
         mProfileSyncService = ProfileSyncService.get();
         assert mProfileSyncService != null;
-        mIsBackendInitialized = mProfileSyncService.isBackendInitialized();
+        mIsEngineInitialized = mProfileSyncService.isEngineInitialized();
         mIsPassphraseRequired =
-                mIsBackendInitialized && mProfileSyncService.isPassphraseRequiredForDecryption();
+                mIsEngineInitialized && mProfileSyncService.isPassphraseRequiredForDecryption();
 
         getActivity().setTitle(R.string.sign_in_sync);
 
@@ -281,9 +281,9 @@
         // account preference displays the correct signed in account.
         mSyncedAccountPreference.update();
 
-        mIsBackendInitialized = mProfileSyncService.isBackendInitialized();
+        mIsEngineInitialized = mProfileSyncService.isEngineInitialized();
         mIsPassphraseRequired =
-                mIsBackendInitialized && mProfileSyncService.isPassphraseRequiredForDecryption();
+                mIsEngineInitialized && mProfileSyncService.isPassphraseRequiredForDecryption();
         // This prevents sync from actually syncing until the dialog is closed.
         mProfileSyncService.setSetupInProgress(true);
         mProfileSyncService.addSyncStateChangedListener(this);
@@ -359,17 +359,17 @@
     /**
      * Update the encryption state.
      *
-     * If sync's backend is initialized, the button is enabled and the dialog will present the
+     * If sync's engine is initialized, the button is enabled and the dialog will present the
      * valid encryption options for the user. Otherwise, any encryption dialogs will be closed
-     * and the button will be disabled because the backend is needed in order to know and
+     * and the button will be disabled because the engine is needed in order to know and
      * modify the encryption state.
      */
     private void updateEncryptionState() {
         boolean isSyncEnabled = mSyncSwitchPreference.isChecked();
-        boolean isBackendInitialized = mProfileSyncService.isBackendInitialized();
-        mSyncEncryption.setEnabled(isSyncEnabled && isBackendInitialized);
+        boolean isEngineInitialized = mProfileSyncService.isEngineInitialized();
+        mSyncEncryption.setEnabled(isSyncEnabled && isEngineInitialized);
         mSyncEncryption.setSummary(null);
-        if (!isBackendInitialized) {
+        if (!isEngineInitialized) {
             // If sync is not initialized, encryption state is unavailable and can't be changed.
             // Leave the button disabled and the summary empty. Additionally, close the dialogs in
             // case they were open when a stop and clear comes.
@@ -454,7 +454,7 @@
     }
 
     private void configureEncryption(String passphrase) {
-        if (mProfileSyncService.isBackendInitialized()) {
+        if (mProfileSyncService.isEngineInitialized()) {
             mProfileSyncService.enableEncryptEverything();
             mProfileSyncService.setEncryptionPassphrase(passphrase);
             // Configure the current set of data types - this tells the sync engine to
@@ -486,8 +486,8 @@
      */
     @Override
     public boolean onPassphraseEntered(String passphrase) {
-        if (!mProfileSyncService.isBackendInitialized()) {
-            // If the backend was shut down since the dialog was opened, do nothing.
+        if (!mProfileSyncService.isEngineInitialized()) {
+            // If the engine was shut down since the dialog was opened, do nothing.
             return false;
         }
         return handleDecryption(passphrase);
@@ -505,8 +505,8 @@
      */
     @Override
     public void onPassphraseCreated(String passphrase) {
-        if (!mProfileSyncService.isBackendInitialized()) {
-            // If the backend was shut down since the dialog was opened, do nothing.
+        if (!mProfileSyncService.isEngineInitialized()) {
+            // If the engine was shut down since the dialog was opened, do nothing.
             return;
         }
         configureEncryption(passphrase);
@@ -517,8 +517,8 @@
      */
     @Override
     public void onPassphraseTypeSelected(PassphraseType type) {
-        if (!mProfileSyncService.isBackendInitialized()) {
-            // If the backend was shut down since the dialog was opened, do nothing.
+        if (!mProfileSyncService.isEngineInitialized()) {
+            // If the engine was shut down since the dialog was opened, do nothing.
             return;
         }
 
@@ -542,7 +542,7 @@
             // roughly the same time. See http://b/5983282
             return false;
         }
-        if (preference == mSyncEncryption && mProfileSyncService.isBackendInitialized()) {
+        if (preference == mSyncEncryption && mProfileSyncService.isEngineInitialized()) {
             if (mProfileSyncService.isPassphraseRequiredForDecryption()) {
                 displayPassphraseDialog();
             } else {
@@ -587,12 +587,12 @@
      *
      * If sync is on, load the prefs from native. Otherwise, all data types are disabled and
      * checked. Note that the Password data type will be shown as disabled and unchecked between
-     * sync being turned on and the backend initialization completing.
+     * sync being turned on and the engine initialization completing.
      */
     private void updateDataTypeState() {
         boolean isSyncEnabled = mSyncSwitchPreference.isChecked();
         boolean syncEverything = mSyncEverything.isChecked();
-        boolean passwordSyncConfigurable = mProfileSyncService.isBackendInitialized()
+        boolean passwordSyncConfigurable = mProfileSyncService.isEngineInitialized()
                 && mProfileSyncService.isCryptographerReady();
         Set<Integer> syncTypes = mProfileSyncService.getPreferredDataTypes();
         boolean syncAutofill = syncTypes.contains(ModelType.AUTOFILL);
@@ -749,18 +749,18 @@
      * Listen to sync state changes.
      *
      * If the user has just turned on sync, this listener is needed in order to enable
-     * the encryption settings once the backend has initialized.
+     * the encryption settings once the engine has initialized.
      */
     @Override
     public void syncStateChanged() {
-        boolean wasSyncInitialized = mIsBackendInitialized;
+        boolean wasSyncInitialized = mIsEngineInitialized;
         boolean wasPassphraseRequired = mIsPassphraseRequired;
-        mIsBackendInitialized = mProfileSyncService.isBackendInitialized();
+        mIsEngineInitialized = mProfileSyncService.isEngineInitialized();
         mIsPassphraseRequired =
-                mIsBackendInitialized && mProfileSyncService.isPassphraseRequiredForDecryption();
-        if (mIsBackendInitialized != wasSyncInitialized
+                mIsEngineInitialized && mProfileSyncService.isPassphraseRequiredForDecryption();
+        if (mIsEngineInitialized != wasSyncInitialized
                 || mIsPassphraseRequired != wasPassphraseRequired) {
-            // Update all because Password syncability is also affected by the backend.
+            // Update all because Password syncability is also affected by the engine.
             updateSyncStateFromSwitch();
         } else {
             updateSyncErrorCard();
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index 3a20e40f..d4421ba6 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -1879,6 +1879,9 @@
       <message name="IDS_EXTERNAL_APP_LEAVE_INCOGNITO_WARNING_TITLE" desc="Title for incognito external intent. [CHAR-LIMIT=24]">
         Warning
       </message>
+      <message name="IDS_EXTERNAL_APP_RESTRICTED_ACCESS_ERROR" desc="A message shown to the user if Chrome receives a file view request to something Chrome does not have access to view.">
+        Chrome does not have access to the requested resource.
+      </message>
 
       <!-- Messages for remote media playback (casting) -->
       <message name="IDS_CAST_CASTING_VIDEO" desc="AtHome text to tell user which screen casting is happening. [CHAR LIMIT=40]">
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/PersonalDataManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/PersonalDataManagerTest.java
index 7483487..b5b603e8 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/PersonalDataManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/PersonalDataManagerTest.java
@@ -151,7 +151,8 @@
         assertEquals("Visa", storedCard.getName());
         assertEquals("10", storedCard.getMonth());
         assertEquals("4012888888881881", storedCard.getNumber());
-        assertEquals("Visa\u00a0\u22ef1881", storedCard.getObfuscatedNumber());
+        assertEquals("Visa\u0020\u0020\u2022\u2006\u2022\u2006\u2022\u2006\u2022\u20061881",
+                storedCard.getObfuscatedNumber());
         assertNotNull(mHelper.getCreditCard(cardTwoGUID));
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPaymentAppAndCardsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPaymentAppAndCardsTest.java
index b103a7a..94d6b6b0 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPaymentAppAndCardsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPaymentAppAndCardsTest.java
@@ -100,9 +100,13 @@
         if (instrumentPresence == HAVE_INSTRUMENTS) {
             assertEquals("Test Pay", getPaymentInstrumentLabel(i++));
         }
-        // \u00A0\u22EF is a non-breaking space followed by a midline ellipsis.
-        assertEquals("Visa\u00A0\u22EF1111\nJon Doe", getPaymentInstrumentLabel(i++));
-        assertEquals("MasterCard\u00A0\u22EF5454\nJon Doe\nBilling address required",
+        // \u0020\...\u2006 is four dots ellipsis.
+        assertEquals(
+                "Visa\u0020\u0020\u2022\u2006\u2022\u2006\u2022\u2006\u2022\u20061111\nJon Doe",
+                getPaymentInstrumentLabel(i++));
+        assertEquals(
+                "MasterCard\u0020\u0020\u2022\u2006\u2022\u2006\u2022\u2006\u2022\u20065454\n"
+                + "Jon Doe\nBilling address required",
                 getPaymentInstrumentLabel(i++));
 
         // Check the output of the selected instrument.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/precache/PrecacheLauncherTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/precache/PrecacheLauncherTest.java
index e46233c8..3f414e2a 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/precache/PrecacheLauncherTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/precache/PrecacheLauncherTest.java
@@ -50,19 +50,19 @@
     }
 
     private static class StubProfileSyncService extends ProfileSyncService {
-        private boolean mSyncInitialized = false;
+        private boolean mEngineInitialized = false;
 
         public StubProfileSyncService() {
             super();
         }
 
         @Override
-        public boolean isBackendInitialized() {
-            return mSyncInitialized;
+        public boolean isEngineInitialized() {
+            return mEngineInitialized;
         }
 
-        public void setSyncInitialized(boolean syncInitialized) {
-            mSyncInitialized = syncInitialized;
+        public void setEngineInitialized(boolean engineInitialized) {
+            mEngineInitialized = engineInitialized;
             syncStateChanged();
         }
     }
@@ -85,7 +85,7 @@
         ContextUtils.initApplicationContext(getTargetContext().getApplicationContext());
 
         // This is a PrecacheLauncher with a stubbed out nativeShouldRun so we can change that on
-        // the fly without needing to set up a sync backend.
+        // the fly without needing to set up a sync engine.
         mLauncher = new PrecacheLauncherUnderTest();
 
         mPrecacheTaskScheduler = new MockPrecacheTaskScheduler();
@@ -104,7 +104,7 @@
         ThreadUtils.runOnUiThreadBlocking(new Runnable() {
             @Override
             public void run() {
-                // The StubProfileSyncService stubs out isBackendInitialized so we can change that
+                // The StubProfileSyncService stubs out isEngineInitialized so we can change that
                 // on the fly.
                 mSync = new StubProfileSyncService();
                 ProfileSyncService.overrideForTests(mSync);
@@ -131,7 +131,7 @@
                              FailureReason.NATIVE_SHOULD_RUN_IS_FALSE),
                 failureReasons());
 
-        setSyncInitialized(true);
+        setEngineInitialized(true);
         assertEquals(false, isPrecachingEnabled());
         assertEquals(EnumSet.of(FailureReason.NATIVE_SHOULD_RUN_IS_FALSE), failureReasons());
     }
@@ -149,7 +149,7 @@
                 failureReasons());
 
         mLauncher.setShouldRun(true);
-        setSyncInitialized(true);
+        setEngineInitialized(true);
         assertEquals(true, isPrecachingEnabled());
         assertEquals(EnumSet.noneOf(FailureReason.class), failureReasons());
     }
@@ -157,7 +157,7 @@
     @SmallTest
     @Feature({"Precache"})
     public void testUpdateEnabled_Disabled_ThenEnabled() {
-        setSyncInitialized(true);
+        setEngineInitialized(true);
         mLauncher.updateEnabled(getTargetContext());
         waitUntilUiThreadIdle();
 
@@ -173,7 +173,7 @@
     @Feature({"Precache"})
     public void testUpdateEnabled_Enabled_ThenDisabled() {
         mLauncher.setShouldRun(true);
-        setSyncInitialized(true);
+        setEngineInitialized(true);
         mLauncher.updateEnabled(getTargetContext());
         waitUntilUiThreadIdle();
 
@@ -225,12 +225,12 @@
         });
     }
 
-    /** Pretend the sync backend is initialized or not. */
-    private void setSyncInitialized(final boolean syncInitialized) {
+    /** Pretend the sync engine is initialized or not. */
+    private void setEngineInitialized(final boolean syncInitialized) {
         ThreadUtils.runOnUiThreadBlocking(new Runnable() {
             @Override
             public void run() {
-                mSync.setSyncInitialized(syncInitialized);
+                mSync.setEngineInitialized(syncInitialized);
             }
         });
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/PasswordViewingTypeTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/PasswordViewingTypeTest.java
index ffa2c43..6d3108d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/PasswordViewingTypeTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/PasswordViewingTypeTest.java
@@ -89,7 +89,7 @@
             }
 
             @Override
-            public boolean isBackendInitialized() {
+            public boolean isEngineInitialized() {
                 return true;
             }
         }
@@ -133,7 +133,7 @@
         ThreadUtils.runOnUiThreadBlocking(new Runnable() {
             @Override
             public void run() {
-                assertTrue(ProfileSyncService.get().isBackendInitialized());
+                assertTrue(ProfileSyncService.get().isEngineInitialized());
                 assertFalse(ProfileSyncService.get().isUsingSecondaryPassphrase());
             }
         });
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/FakeProfileSyncService.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/FakeProfileSyncService.java
index 622097c1..f6177b6 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/FakeProfileSyncService.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/FakeProfileSyncService.java
@@ -10,20 +10,19 @@
  * Only what has been needed for tests so far has been faked.
  */
 public class FakeProfileSyncService extends ProfileSyncService {
-
-    private boolean mBackendInitialized;
+    private boolean mEngineInitialized;
 
     public FakeProfileSyncService() {
         super();
     }
 
     @Override
-    public boolean isBackendInitialized() {
-        return mBackendInitialized;
+    public boolean isEngineInitialized() {
+        return mEngineInitialized;
     }
 
-    public void setSyncInitialized(boolean syncInitialized) {
-        mBackendInitialized = syncInitialized;
+    public void setEngineInitialized(boolean engineInitialized) {
+        mEngineInitialized = engineInitialized;
     }
 
     @Override
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ui/PassphraseActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ui/PassphraseActivityTest.java
index 73ad036..8e1847d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ui/PassphraseActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ui/PassphraseActivityTest.java
@@ -74,7 +74,7 @@
                 getInstrumentation().callActivityOnSaveInstanceState(activity, bundle);
                 // Fake sync's backend finishing its initialization.
                 FakeProfileSyncService pss = (FakeProfileSyncService) ProfileSyncService.get();
-                pss.setSyncInitialized(true);
+                pss.setEngineInitialized(true);
                 pss.syncStateChanged();
             }
         });
diff --git a/chrome/android/sync_shell/javatests/src/org/chromium/chrome/browser/sync/FirstRunTest.java b/chrome/android/sync_shell/javatests/src/org/chromium/chrome/browser/sync/FirstRunTest.java
index 3a40d64b..fcfcd9d5 100644
--- a/chrome/android/sync_shell/javatests/src/org/chromium/chrome/browser/sync/FirstRunTest.java
+++ b/chrome/android/sync_shell/javatests/src/org/chromium/chrome/browser/sync/FirstRunTest.java
@@ -72,7 +72,7 @@
         // User should be signed in and the sync backend should initialize, but sync should not
         // become fully active until the settings page is closed.
         assertEquals(testAccount, SigninTestUtil.getCurrentAccount());
-        SyncTestUtil.waitForBackendInitialized();
+        SyncTestUtil.waitForEngineInitialized();
         assertFalse(SyncTestUtil.isSyncActive());
 
         // Close the settings fragment.
diff --git a/chrome/android/sync_shell/javatests/src/org/chromium/chrome/browser/sync/SyncCustomizationFragmentTest.java b/chrome/android/sync_shell/javatests/src/org/chromium/chrome/browser/sync/SyncCustomizationFragmentTest.java
index 138ca32..44f5a9b9 100644
--- a/chrome/android/sync_shell/javatests/src/org/chromium/chrome/browser/sync/SyncCustomizationFragmentTest.java
+++ b/chrome/android/sync_shell/javatests/src/org/chromium/chrome/browser/sync/SyncCustomizationFragmentTest.java
@@ -139,7 +139,7 @@
      */
     @SmallTest
     @Feature({"Sync"})
-    public void testOpeningSettingsDoesntStartBackend() throws Exception {
+    public void testOpeningSettingsDoesntStartEngine() throws Exception {
         setUpTestAccountAndSignIn();
         stopSync();
         startSyncCustomizationFragment();
@@ -147,7 +147,7 @@
             @Override
             public void run() {
                 assertFalse(mProfileSyncService.isSyncRequested());
-                assertFalse(mProfileSyncService.isBackendInitialized());
+                assertFalse(mProfileSyncService.isEngineInitialized());
             }
         });
     }
@@ -160,7 +160,7 @@
         SyncCustomizationFragment fragment = startSyncCustomizationFragment();
         assertDefaultSyncOffState(fragment);
         togglePreference(getSyncSwitch(fragment));
-        SyncTestUtil.waitForBackendInitialized();
+        SyncTestUtil.waitForEngineInitialized();
         assertDefaultSyncOnState(fragment);
     }
 
diff --git a/chrome/app/mash/chrome_mash_content_browser_manifest_overlay.json b/chrome/app/mash/chrome_mash_content_browser_manifest_overlay.json
index d4ebfec..cc3774c 100644
--- a/chrome/app/mash/chrome_mash_content_browser_manifest_overlay.json
+++ b/chrome/app/mash/chrome_mash_content_browser_manifest_overlay.json
@@ -12,7 +12,7 @@
         "ash": [
           "app_list::mojom::AppListPresenter"
         ],
-        "ime:ime_driver": [ "ui::mojom::IMEDriver" ]
+        "ime:ime_driver": []
       },
       "requires": {
         "accessibility_autoclick": [ "ash:autoclick" ],
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 5ddcfae..05594e9 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -2821,8 +2821,6 @@
       "obsolete_system/obsolete_system_win.cc",
       "pdf/pdf_extension_util.cc",
       "pdf/pdf_extension_util.h",
-      "power/process_power_collector.cc",
-      "power/process_power_collector.h",
       "power_usage_monitor/power_usage_monitor.cc",
       "power_usage_monitor/power_usage_monitor.h",
       "process_singleton_modal_dialog_lock.cc",
diff --git a/chrome/browser/android/offline_pages/prerendering_loader.cc b/chrome/browser/android/offline_pages/prerendering_loader.cc
index c2b85f6..fb1c4bc 100644
--- a/chrome/browser/android/offline_pages/prerendering_loader.cc
+++ b/chrome/browser/android/offline_pages/prerendering_loader.cc
@@ -34,22 +34,43 @@
 Offliner::RequestStatus ClassifyFinalStatus(
     prerender::FinalStatus final_status) {
   switch (final_status) {
-
-    // Identify aborted/canceled operations
+    // Identify aborted/canceled operations.
 
     case prerender::FINAL_STATUS_CANCELLED:
     // TODO(dougarnett): Reconsider if/when get better granularity (642768)
     case prerender::FINAL_STATUS_UNSUPPORTED_SCHEME:
       return Offliner::PRERENDERING_CANCELED;
 
-    // Identify non-retryable failues.
+    // Identify non-retryable failures. These are a hard type failures
+    // associated with the page and so are expected to occur again if retried.
 
     case prerender::FINAL_STATUS_SAFE_BROWSING:
     case prerender::FINAL_STATUS_CREATING_AUDIO_STREAM:
     case prerender::FINAL_STATUS_JAVASCRIPT_ALERT:
+    case prerender::FINAL_STATUS_CREATE_NEW_WINDOW:
+    case prerender::FINAL_STATUS_INVALID_HTTP_METHOD:
+    case prerender::FINAL_STATUS_OPEN_URL:
       return Offliner::RequestStatus::PRERENDERING_FAILED_NO_RETRY;
 
+    // Identify failures that indicate we should stop further processing
+    // for now. These may be current resource issues or app closing.
+
+    case prerender::FINAL_STATUS_MEMORY_LIMIT_EXCEEDED:
+    case prerender::FINAL_STATUS_RATE_LIMIT_EXCEEDED:
+    case prerender::FINAL_STATUS_RENDERER_CRASHED:
+    case prerender::FINAL_STATUS_TOO_MANY_PROCESSES:
+    case prerender::FINAL_STATUS_TIMED_OUT:
+    case prerender::FINAL_STATUS_APP_TERMINATING:
+    case prerender::FINAL_STATUS_PROFILE_DESTROYED:
+      return Offliner::RequestStatus::PRERENDERING_FAILED_NO_NEXT;
+
     // Otherwise, assume retryable failure.
+
+    case prerender::FINAL_STATUS_NEW_NAVIGATION_ENTRY:
+    case prerender::FINAL_STATUS_CACHE_OR_HISTORY_CLEARED:
+    case prerender::FINAL_STATUS_SSL_ERROR:
+    case prerender::FINAL_STATUS_SSL_CLIENT_CERTIFICATE_REQUESTED:
+    case prerender::FINAL_STATUS_WINDOW_PRINT:
     default:
       return Offliner::RequestStatus::PRERENDERING_FAILED;
   }
diff --git a/chrome/browser/android/offline_pages/prerendering_loader_unittest.cc b/chrome/browser/android/offline_pages/prerendering_loader_unittest.cc
index ab5c694..e51baa5 100644
--- a/chrome/browser/android/offline_pages/prerendering_loader_unittest.cc
+++ b/chrome/browser/android/offline_pages/prerendering_loader_unittest.cc
@@ -243,7 +243,7 @@
 
 TEST_F(PrerenderingLoaderTest, LoadPageLoadFailedNoContent) {
   test_adapter()->Configure(nullptr /* web_contents */,
-                            prerender::FINAL_STATUS_MEMORY_LIMIT_EXCEEDED);
+                            prerender::FINAL_STATUS_CACHE_OR_HISTORY_CLEARED);
   GURL gurl("http://testit.sea");
   EXPECT_TRUE(loader()->IsIdle());
   EXPECT_TRUE(loader()->LoadPage(
@@ -290,6 +290,31 @@
   PumpLoop();
 }
 
+TEST_F(PrerenderingLoaderTest, LoadPageLoadFailedNoNext) {
+  test_adapter()->Configure(nullptr /* web_contents */,
+                            prerender::FINAL_STATUS_MEMORY_LIMIT_EXCEEDED);
+  GURL gurl("http://testit.sea");
+  EXPECT_TRUE(loader()->IsIdle());
+  EXPECT_TRUE(loader()->LoadPage(
+      gurl,
+      base::Bind(&PrerenderingLoaderTest::OnLoadDone, base::Unretained(this))));
+  EXPECT_FALSE(loader()->IsIdle());
+  EXPECT_FALSE(loader()->IsLoaded());
+
+  test_adapter()->GetObserver()->OnPrerenderDomContentLoaded();
+  PumpLoop();
+  EXPECT_TRUE(loader()->IsIdle());
+  EXPECT_TRUE(callback_called());
+  // We did not provide any WebContents for the callback so expect did not load.
+  // FinalStatus is non-next failure.
+  EXPECT_EQ(Offliner::RequestStatus::PRERENDERING_FAILED_NO_NEXT,
+            callback_load_status());
+
+  // Stopped event causes no harm.
+  test_adapter()->GetObserver()->OnPrerenderStop();
+  PumpLoop();
+}
+
 TEST_F(PrerenderingLoaderTest, LoadPageLoadCanceled) {
   test_adapter()->Configure(nullptr /* web_contents */,
                             prerender::FINAL_STATUS_CANCELLED);
diff --git a/chrome/browser/browsing_data/browsing_data_remover.cc b/chrome/browser/browsing_data/browsing_data_remover.cc
index aa079bed..3ed79a4 100644
--- a/chrome/browser/browsing_data/browsing_data_remover.cc
+++ b/chrome/browser/browsing_data/browsing_data_remover.cc
@@ -65,8 +65,6 @@
 #include "components/ntp_snippets/content_suggestions_service.h"
 #include "components/omnibox/browser/omnibox_pref_names.h"
 #include "components/password_manager/core/browser/password_store.h"
-#include "components/power/origin_power_map.h"
-#include "components/power/origin_power_map_factory.h"
 #include "components/prefs/pref_service.h"
 #include "components/previews/core/previews_ui_service.h"
 #include "components/search_engines/template_url_service.h"
@@ -557,13 +555,6 @@
     }
 #endif
 
-    // The power consumption history by origin contains details of websites
-    // that were visited.
-    power::OriginPowerMap* origin_power_map =
-        power::OriginPowerMapFactory::GetForBrowserContext(profile_);
-    if (origin_power_map)
-      origin_power_map->ClearOriginMap(nullable_filter);
-
     // Need to clear the host cache and accumulated speculative data, as it also
     // reveals some history. We have no mechanism to track when these items were
     // created, so we'll not honor the time range.
diff --git a/chrome/browser/browsing_data/browsing_data_remover_factory.cc b/chrome/browser/browsing_data/browsing_data_remover_factory.cc
index ccf45fc2..1cb6b634 100644
--- a/chrome/browser/browsing_data/browsing_data_remover_factory.cc
+++ b/chrome/browser/browsing_data/browsing_data_remover_factory.cc
@@ -19,7 +19,6 @@
 #include "chrome/browser/sessions/tab_restore_service_factory.h"
 #include "chrome/browser/web_data_service_factory.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
-#include "components/power/origin_power_map_factory.h"
 #include "content/public/browser/browser_context.h"
 #include "extensions/features/features.h"
 
@@ -59,7 +58,6 @@
   DependsOn(HistoryServiceFactory::GetInstance());
   DependsOn(HostContentSettingsMapFactory::GetInstance());
   DependsOn(PasswordStoreFactory::GetInstance());
-  DependsOn(power::OriginPowerMapFactory::GetInstance());
   DependsOn(prerender::PrerenderManagerFactory::GetInstance());
   DependsOn(TabRestoreServiceFactory::GetInstance());
   DependsOn(TemplateURLServiceFactory::GetInstance());
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index 1bcc099..ed2ddd5 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -72,7 +72,6 @@
 #include "chrome/browser/net/crl_set_fetcher.h"
 #include "chrome/browser/performance_monitor/performance_monitor.h"
 #include "chrome/browser/plugins/plugin_prefs.h"
-#include "chrome/browser/power/process_power_collector.h"
 #include "chrome/browser/prefs/chrome_command_line_pref_store.h"
 #include "chrome/browser/prefs/chrome_pref_service_factory.h"
 #include "chrome/browser/prefs/incognito_mode_prefs.h"
@@ -1914,13 +1913,13 @@
   run_message_loop_ = started;
   browser_creator_.reset();
 
-#if !defined(OS_LINUX) || defined(OS_CHROMEOS)  // http://crbug.com/426393
+#if !defined(OS_LINUX) || defined(OS_CHROMEOS)
+  // Collects power-related UMA stats for Windows, Mac, and ChromeOS.
+  // Linux is not supported (crbug.com/426393).
   power_usage_monitor_.reset(new PowerUsageMonitor());
   power_usage_monitor_->Start();
 #endif  // !defined(OS_LINUX) || defined(OS_CHROMEOS)
 
-  process_power_collector_.reset(new ProcessPowerCollector);
-  process_power_collector_->Initialize();
 #endif  // !defined(OS_ANDROID)
 
   PostBrowserStart();
@@ -2011,10 +2010,6 @@
   // Disarm the startup hang detector time bomb if it is still Arm'ed.
   startup_watcher_->Disarm();
 
-  // Remove observers attached to D-Bus clients before DbusThreadManager is
-  // shut down.
-  process_power_collector_.reset();
-
   web_usb_detector_.reset();
 
   for (size_t i = 0; i < chrome_extra_parts_.size(); ++i)
diff --git a/chrome/browser/chrome_browser_main.h b/chrome/browser/chrome_browser_main.h
index 221b612af..4bb3f19 100644
--- a/chrome/browser/chrome_browser_main.h
+++ b/chrome/browser/chrome_browser_main.h
@@ -27,7 +27,6 @@
 class ChromeBrowserMainExtraParts;
 class FieldTrialSynchronizer;
 class PrefService;
-class ProcessPowerCollector;
 class Profile;
 class StartupBrowserCreator;
 class StartupTimeBomb;
@@ -147,9 +146,6 @@
   ChromeBrowserFieldTrials browser_field_trials_;
 
 #if !defined(OS_ANDROID) && !defined(OS_IOS)
-  // A monitor for attributing power consumption to origins.
-  std::unique_ptr<ProcessPowerCollector> process_power_collector_;
-
   std::unique_ptr<WebUsbDetector> web_usb_detector_;
 #endif
 
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index aaf9540..d1250b6 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -1651,6 +1651,7 @@
     "//components/policy:generated",
     "//components/resources",
     "//components/sync",
+    "//components/variations:test_support",
     "//crypto:platform",
     "//dbus",
     "//mojo/public/cpp/system:system",
diff --git a/chrome/browser/chromeos/display/display_preferences.cc b/chrome/browser/chromeos/display/display_preferences.cc
index 563f394..8956cea 100644
--- a/chrome/browser/chromeos/display/display_preferences.cc
+++ b/chrome/browser/chromeos/display/display_preferences.cc
@@ -39,6 +39,10 @@
 const char kInsetsBottomKey[] = "insets_bottom";
 const char kInsetsRightKey[] = "insets_right";
 
+const char kTouchCalibrationWidth[] = "touch_calibration_width";
+const char kTouchCalibrationHeight[] = "touch_calibration_height";
+const char kTouchCalibrationPointPairs[] = "touch_calibration_point_pairs";
+
 // This kind of boilerplates should be done by base::JSONValueConverter but it
 // doesn't support classes like gfx::Insets for now.
 // TODO(mukai): fix base::JSONValueConverter and use it here.
@@ -66,6 +70,87 @@
   value->SetInteger(kInsetsRightKey, insets.right());
 }
 
+// Unmarshalls the string containing CalibrationPointPairQuad and populates
+// |point_pair_quad| with the unmarshalled data.
+bool ParseTouchCalibrationStringValue(
+    const std::string& str,
+    display::TouchCalibrationData::CalibrationPointPairQuad* point_pair_quad) {
+  DCHECK(point_pair_quad);
+  int x = 0, y = 0;
+  std::vector<std::string> parts = base::SplitString(
+      str, " ", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+  size_t total = point_pair_quad->size();
+  gfx::Point display_point, touch_point;
+  for (std::size_t row = 0; row < total; row++) {
+    if (!base::StringToInt(parts[row * total], &x) ||
+        !base::StringToInt(parts[row * total + 1], &y)) {
+      return false;
+    }
+    display_point.SetPoint(x, y);
+
+    if (!base::StringToInt(parts[row * total + 2], &x) ||
+        !base::StringToInt(parts[row * total + 3], &y)) {
+      return false;
+    }
+    touch_point.SetPoint(x, y);
+
+    (*point_pair_quad)[row] = std::make_pair(display_point, touch_point);
+  }
+  return true;
+}
+
+// Retrieves touch calibration associated data from the dictionary and stores
+// it in an instance of TouchCalibrationData struct.
+bool ValueToTouchData(const base::DictionaryValue& value,
+                      display::TouchCalibrationData* touch_calibration_data) {
+  display::TouchCalibrationData::CalibrationPointPairQuad* point_pair_quad =
+      &(touch_calibration_data->point_pairs);
+
+  std::string str;
+  if (!value.GetString(kTouchCalibrationPointPairs, &str))
+    return false;
+
+  if (!ParseTouchCalibrationStringValue(str, point_pair_quad))
+    return false;
+
+  int width, height;
+  if (!value.GetInteger(kTouchCalibrationWidth, &width) ||
+      !value.GetInteger(kTouchCalibrationHeight, &height)) {
+    return false;
+  }
+  touch_calibration_data->bounds = gfx::Size(width, height);
+  return true;
+}
+
+// Stores the touch calibration data into the dictionary.
+void TouchDataToValue(
+    const display::TouchCalibrationData& touch_calibration_data,
+    base::DictionaryValue* value) {
+  DCHECK(value);
+  std::string str;
+  for (std::size_t row = 0; row < touch_calibration_data.point_pairs.size();
+       row++) {
+    str +=
+        base::IntToString(touch_calibration_data.point_pairs[row].first.x()) +
+        " ";
+    str +=
+        base::IntToString(touch_calibration_data.point_pairs[row].first.y()) +
+        " ";
+    str +=
+        base::IntToString(touch_calibration_data.point_pairs[row].second.x()) +
+        " ";
+    str +=
+        base::IntToString(touch_calibration_data.point_pairs[row].second.y());
+    if (row != touch_calibration_data.point_pairs.size() - 1)
+      str += " ";
+  }
+  value->SetString(kTouchCalibrationPointPairs, str);
+  value->SetInteger(kTouchCalibrationWidth,
+                    touch_calibration_data.bounds.width());
+  value->SetInteger(kTouchCalibrationHeight,
+                    touch_calibration_data.bounds.height());
+}
+
 std::string ColorProfileToString(ui::ColorCalibrationProfile profile) {
   switch (profile) {
     case ui::COLOR_PROFILE_STANDARD:
@@ -182,17 +267,18 @@
     if (ValueToInsets(*dict_value, &insets))
       insets_to_set = &insets;
 
+    display::TouchCalibrationData calibration_data;
+    display::TouchCalibrationData* calibration_data_to_set = nullptr;
+    if (ValueToTouchData(*dict_value, &calibration_data))
+      calibration_data_to_set = &calibration_data;
+
     ui::ColorCalibrationProfile color_profile = ui::COLOR_PROFILE_STANDARD;
     std::string color_profile_name;
     if (dict_value->GetString("color_profile_name", &color_profile_name))
       color_profile = StringToColorProfile(color_profile_name);
-    GetDisplayManager()->RegisterDisplayProperty(id,
-                                                 rotation,
-                                                 ui_scale,
-                                                 insets_to_set,
-                                                 resolution_in_pixels,
-                                                 device_scale_factor,
-                                                 color_profile);
+    GetDisplayManager()->RegisterDisplayProperty(
+        id, rotation, ui_scale, insets_to_set, resolution_in_pixels,
+        device_scale_factor, color_profile, calibration_data_to_set);
   }
 }
 
@@ -283,6 +369,8 @@
       property_value->SetString(
           "color_profile_name", ColorProfileToString(info.color_profile()));
     }
+    if (info.has_touch_calibration_data())
+      TouchDataToValue(info.GetTouchCalibrationData(), property_value.get());
     pref_data->Set(base::Int64ToString(id), property_value.release());
   }
 }
@@ -401,4 +489,10 @@
   StoreDisplayPowerState(power_state);
 }
 
+bool ParseTouchCalibrationStringForTest(
+    const std::string& str,
+    display::TouchCalibrationData::CalibrationPointPairQuad* point_pair_quad) {
+  return ParseTouchCalibrationStringValue(str, point_pair_quad);
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/display/display_preferences.h b/chrome/browser/chromeos/display/display_preferences.h
index 56d3d7f..cf5e706 100644
--- a/chrome/browser/chromeos/display/display_preferences.h
+++ b/chrome/browser/chromeos/display/display_preferences.h
@@ -6,12 +6,17 @@
 #define CHROME_BROWSER_CHROMEOS_DISPLAY_DISPLAY_PREFERENCES_H_
 
 #include <stdint.h>
+#include <array>
 
 #include "third_party/cros_system_api/dbus/service_constants.h"
 #include "ui/display/display_layout.h"
 
 class PrefRegistrySimple;
 
+namespace gfx {
+class Point;
+}
+
 namespace chromeos {
 
 // Registers the prefs associated with display settings and stored
@@ -38,6 +43,13 @@
 // Stores the given |power_state| for tests.
 void StoreDisplayPowerStateForTest(DisplayPowerState power_state);
 
+// Parses the marshalled string data stored in local preferences for calibration
+// points and populates |point_pair_quad| using the unmarshalled data.
+// See TouchCalibrationData in Managed display info.
+bool ParseTouchCalibrationStringForTest(
+    const std::string& str,
+    std::array<std::pair<gfx::Point, gfx::Point>, 4>* point_pair_quad);
+
 }  // namespace chromeos
 
 #endif  // CHROME_BROWSER_CHROMEOS_DISPLAY_DISPLAY_PREFERENCES_H_
diff --git a/chrome/browser/chromeos/display/display_preferences_unittest.cc b/chrome/browser/chromeos/display/display_preferences_unittest.cc
index deb7814..65f7a90 100644
--- a/chrome/browser/chromeos/display/display_preferences_unittest.cc
+++ b/chrome/browser/chromeos/display/display_preferences_unittest.cc
@@ -51,6 +51,9 @@
 const char kOffsetKey[] = "offset";
 const char kPlacementDisplayIdKey[] = "placement.display_id";
 const char kPlacementParentDisplayIdKey[] = "placement.parent_display_id";
+const char kTouchCalibrationWidth[] = "touch_calibration_width";
+const char kTouchCalibrationHeight[] = "touch_calibration_height";
+const char kTouchCalibrationPointPairs[] = "touch_calibration_point_pairs";
 
 // The mean acceleration due to gravity on Earth in m/s^2.
 const float kMeanGravity = -9.80665f;
@@ -307,6 +310,15 @@
   EXPECT_FALSE(display::test::DisplayManagerTestApi(display_manager())
                    .SetDisplayUIScale(id2, 1.25f));
 
+  // Set touch calibration data for display |id2|.
+  display::TouchCalibrationData::CalibrationPointPairQuad point_pair_quad = {
+      {std::make_pair(gfx::Point(10, 10), gfx::Point(11, 12)),
+       std::make_pair(gfx::Point(190, 10), gfx::Point(195, 8)),
+       std::make_pair(gfx::Point(10, 90), gfx::Point(12, 94)),
+       std::make_pair(gfx::Point(190, 90), gfx::Point(189, 88))}};
+  gfx::Size touch_size(200, 150);
+  display_manager()->SetTouchCalibrationData(id2, point_pair_quad, touch_size);
+
   const base::DictionaryValue* displays =
       local_state()->GetDictionary(prefs::kSecondaryDisplays);
   const base::DictionaryValue* layout_value = nullptr;
@@ -354,6 +366,11 @@
   EXPECT_EQ(12, bottom);
   EXPECT_EQ(13, right);
 
+  std::string touch_str;
+  EXPECT_FALSE(property->GetString(kTouchCalibrationPointPairs, &touch_str));
+  EXPECT_FALSE(property->GetInteger(kTouchCalibrationWidth, &width));
+  EXPECT_FALSE(property->GetInteger(kTouchCalibrationHeight, &height));
+
   std::string color_profile;
   EXPECT_TRUE(property->GetString("color_profile_name", &color_profile));
   EXPECT_EQ("dynamic", color_profile);
@@ -369,6 +386,25 @@
   EXPECT_FALSE(property->GetInteger("insets_bottom", &bottom));
   EXPECT_FALSE(property->GetInteger("insets_right", &right));
 
+  display::TouchCalibrationData::CalibrationPointPairQuad stored_pair_quad;
+
+  EXPECT_TRUE(property->GetString(kTouchCalibrationPointPairs, &touch_str));
+  EXPECT_TRUE(ParseTouchCalibrationStringForTest(touch_str, &stored_pair_quad));
+
+  for (std::size_t row = 0; row < point_pair_quad.size(); row++) {
+    EXPECT_EQ(point_pair_quad[row].first.x(), stored_pair_quad[row].first.x());
+    EXPECT_EQ(point_pair_quad[row].first.y(), stored_pair_quad[row].first.y());
+    EXPECT_EQ(point_pair_quad[row].second.x(),
+              stored_pair_quad[row].second.x());
+    EXPECT_EQ(point_pair_quad[row].second.y(),
+              stored_pair_quad[row].second.y());
+  }
+  width = height = 0;
+  EXPECT_TRUE(property->GetInteger(kTouchCalibrationWidth, &width));
+  EXPECT_TRUE(property->GetInteger(kTouchCalibrationHeight, &height));
+  EXPECT_EQ(width, touch_size.width());
+  EXPECT_EQ(height, touch_size.height());
+
   // |id2| doesn't have the color_profile because it doesn't have 'dynamic' in
   // its available list.
   EXPECT_FALSE(property->GetString("color_profile_name", &color_profile));
@@ -473,7 +509,7 @@
   // Set new display's selected resolution.
   display_manager()->RegisterDisplayProperty(
       id2 + 1, display::Display::ROTATE_0, 1.0f, nullptr, gfx::Size(500, 400),
-      1.0f, ui::COLOR_PROFILE_STANDARD);
+      1.0f, ui::COLOR_PROFILE_STANDARD, nullptr);
 
   UpdateDisplay("200x200*2, 600x500#600x500|500x400");
 
@@ -499,7 +535,7 @@
   // Set yet another new display's selected resolution.
   display_manager()->RegisterDisplayProperty(
       id2 + 1, display::Display::ROTATE_0, 1.0f, nullptr, gfx::Size(500, 400),
-      1.0f, ui::COLOR_PROFILE_STANDARD);
+      1.0f, ui::COLOR_PROFILE_STANDARD, nullptr);
   // Disconnect 2nd display first to generate new id for external display.
   UpdateDisplay("200x200*2");
   UpdateDisplay("200x200*2, 500x400#600x500|500x400%60.0f");
diff --git a/chrome/browser/chromeos/hats/hats_finch_helper_unittest.cc b/chrome/browser/chromeos/hats/hats_finch_helper_unittest.cc
index c342a01..26f3d38 100644
--- a/chrome/browser/chromeos/hats/hats_finch_helper_unittest.cc
+++ b/chrome/browser/chromeos/hats/hats_finch_helper_unittest.cc
@@ -5,6 +5,8 @@
 #include "chrome/browser/chromeos/hats/hats_finch_helper.h"
 
 #include <map>
+#include <set>
+#include <string>
 
 #include "base/strings/string_number_conversions.h"
 #include "base/test/scoped_feature_list.h"
@@ -13,6 +15,7 @@
 #include "chrome/test/base/testing_profile.h"
 #include "components/prefs/pref_service.h"
 #include "components/variations/variations_associated_data.h"
+#include "components/variations/variations_params_manager.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -20,8 +23,7 @@
 
 namespace {
 
-const char kGroupTesting[] = "Testing";
-const std::string kTrialName(features::kHappinessTrackingSystem.name);
+const std::string kFeatureAndTrialName(features::kHappinessTrackingSystem.name);
 
 }  // namespace
 
@@ -29,18 +31,12 @@
  public:
   using ParamMap = std::map<std::string, std::string>;
 
-  HatsFinchHelperTest() : params_manager_(kTrialName, {{}}) {}
-
-  void SetUp() override {
-    scoped_feature_list_.InitAndEnableFeature(
-        features::kHappinessTrackingSystem);
-  }
+  HatsFinchHelperTest() {}
 
   void SetFinchSeedParams(ParamMap params) {
-    // Clear all the things.
-    variations::testing::ClearAllVariationParams();
-    variations::testing::ClearAllVariationIDs();
-    variations::AssociateVariationParams(kTrialName, kGroupTesting, params);
+    params_manager_.SetVariationParamsWithFeatureAssociations(
+        kFeatureAndTrialName /* trial name */, params,
+        std::set<std::string>{kFeatureAndTrialName} /*features to switch on */);
   }
 
   ParamMap CreateParamMap(std::string prob,
@@ -66,7 +62,6 @@
 
  private:
   variations::testing::VariationParamsManager params_manager_;
-  base::test::ScopedFeatureList scoped_feature_list_;
 
   DISALLOW_COPY_AND_ASSIGN(HatsFinchHelperTest);
 };
diff --git a/chrome/browser/component_updater/sw_reporter_installer_win_unittest.cc b/chrome/browser/component_updater/sw_reporter_installer_win_unittest.cc
index d8fbb38f..fdbbf14 100644
--- a/chrome/browser/component_updater/sw_reporter_installer_win_unittest.cc
+++ b/chrome/browser/component_updater/sw_reporter_installer_win_unittest.cc
@@ -23,7 +23,7 @@
 #include "base/values.h"
 #include "base/version.h"
 #include "chrome/browser/safe_browsing/srt_fetcher_win.h"
-#include "components/variations/variations_associated_data.h"
+#include "components/variations/variations_params_manager.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -137,25 +137,19 @@
 
   void CreateFeatureWithParams(
       const std::map<std::string, std::string>& params) {
-    constexpr char kFeatureName[] = "ExperimentalSwReporterEngine";
+    constexpr char kFeatureAndTrialName[] = "ExperimentalSwReporterEngine";
 
     std::map<std::string, std::string> params_with_group = params;
     params_with_group["experiment_group_for_reporting"] = kExperimentGroupName;
 
     // Assign the given variation params to the experiment group until
     // |variations_| goes out of scope when the test exits. This will also
-    // create a FieldTrial for this group.
+    // create a FieldTrial for this group and associate the params with the
+    // feature.
     variations_ = std::make_unique<variations::testing::VariationParamsManager>(
-        kFeatureName, params_with_group);
-
-    // Create a feature list containing only the field trial for this group,
-    // and enable it for the length of the test.
-    base::FieldTrial* trial = base::FieldTrialList::Find(kFeatureName);
-    ASSERT_TRUE(trial);
-    auto feature_list = std::make_unique<base::FeatureList>();
-    feature_list->RegisterFieldTrialOverride(
-        kFeatureName, base::FeatureList::OVERRIDE_ENABLE_FEATURE, trial);
-    scoped_feature_list_.InitWithFeatureList(std::move(feature_list));
+        kFeatureAndTrialName,  // trial_name
+        params_with_group,
+        std::set<std::string>{kFeatureAndTrialName});  // associated_features
   }
 
   void ExpectAttributesWithTag(const SwReporterInstallerTraits& traits,
diff --git a/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc b/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc
index 6d45ec58..07ef3ab 100644
--- a/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc
+++ b/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc
@@ -271,11 +271,34 @@
     return;
   }
 
-  helpers::DispatchOnBeforeNavigate(navigation_handle);
+  pending_on_before_navigate_event_ =
+      helpers::CreateOnBeforeNavigateEvent(navigation_handle);
+
+  // Only dispatch the onBeforeNavigate event if the associated WebContents
+  // is already added to the tab strip. Otherwise the event should be delayed
+  // and sent after the addition, to preserve the ordering of events.
+  //
+  // TODO(nasko|devlin): This check is necessary because chrome::Navigate()
+  // begins the navigation before the sending the TAB_ADDED notification, and it
+  // is used an indication of that. It would be best if instead it was known
+  // when the tab was created and immediately sent the created event instead of
+  // waiting for the later TAB_ADDED notification, but this appears to work for
+  // now.
+  if (ExtensionTabUtil::GetTabById(ExtensionTabUtil::GetTabId(web_contents()),
+                                   web_contents()->GetBrowserContext(), false,
+                                   nullptr, nullptr, nullptr, nullptr)) {
+    DispatchCachedOnBeforeNavigate();
+  }
 }
 
 void WebNavigationTabObserver::DidFinishNavigation(
     content::NavigationHandle* navigation_handle) {
+  // If there has been a DidStartNavigation call before the tab was ready to
+  // dispatch events, ensure that it is sent before processing the
+  // DidFinishNavigation.
+  // Note: This is exercised by WebNavigationApiTest.TargetBlankIncognito.
+  DispatchCachedOnBeforeNavigate();
+
   if (navigation_handle->HasCommitted() && !navigation_handle->IsErrorPage()) {
     HandleCommit(navigation_handle);
     return;
@@ -391,6 +414,17 @@
   registrar_.RemoveAll();
 }
 
+void WebNavigationTabObserver::DispatchCachedOnBeforeNavigate() {
+  if (!pending_on_before_navigate_event_)
+    return;
+
+  // EventRouter can be null in unit tests.
+  EventRouter* event_router =
+      EventRouter::Get(web_contents()->GetBrowserContext());
+  if (event_router)
+    event_router->BroadcastEvent(std::move(pending_on_before_navigate_event_));
+}
+
 void WebNavigationTabObserver::HandleCommit(
     content::NavigationHandle* navigation_handle) {
   bool is_reference_fragment_navigation = IsReferenceFragmentNavigation(
diff --git a/chrome/browser/extensions/api/web_navigation/web_navigation_api.h b/chrome/browser/extensions/api/web_navigation/web_navigation_api.h
index 47a29154..c663e68 100644
--- a/chrome/browser/extensions/api/web_navigation/web_navigation_api.h
+++ b/chrome/browser/extensions/api/web_navigation/web_navigation_api.h
@@ -71,6 +71,9 @@
                            ui::PageTransition transition) override;
   void WebContentsDestroyed() override;
 
+  // This method dispatches the already created onBeforeNavigate event.
+  void DispatchCachedOnBeforeNavigate();
+
  private:
   explicit WebNavigationTabObserver(content::WebContents* web_contents);
   friend class content::WebContentsUserData<WebNavigationTabObserver>;
@@ -95,6 +98,11 @@
   // Tracks the state of the frames we are sending events for.
   FrameNavigationState navigation_state_;
 
+  // The latest onBeforeNavigate event this frame has generated. It is stored
+  // as it might not be sent immediately, but delayed until the tab is added to
+  // the tab strip and is ready to dispatch events.
+  std::unique_ptr<Event> pending_on_before_navigate_event_;
+
   // Used for tracking registrations to redirect notifications.
   content::NotificationRegistrar registrar_;
 
diff --git a/chrome/browser/extensions/api/web_navigation/web_navigation_api_helpers.cc b/chrome/browser/extensions/api/web_navigation/web_navigation_api_helpers.cc
index 3f1e5a4..2cdcaa6f1 100644
--- a/chrome/browser/extensions/api/web_navigation/web_navigation_api_helpers.cc
+++ b/chrome/browser/extensions/api/web_navigation/web_navigation_api_helpers.cc
@@ -13,6 +13,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/time/time.h"
 #include "base/values.h"
+#include "chrome/browser/extensions/api/web_navigation/web_navigation_api.h"
 #include "chrome/browser/extensions/api/web_navigation/web_navigation_api_constants.h"
 #include "chrome/browser/extensions/extension_tab_util.h"
 #include "chrome/browser/profiles/profile.h"
@@ -61,8 +62,9 @@
 
 }  // namespace
 
-// Constructs and dispatches an onBeforeNavigate event.
-void DispatchOnBeforeNavigate(content::NavigationHandle* navigation_handle) {
+// Constructs an onBeforeNavigate event.
+std::unique_ptr<Event> CreateOnBeforeNavigateEvent(
+    content::NavigationHandle* navigation_handle) {
   GURL url(navigation_handle->GetURL());
   if (navigation_handle->IsSrcdoc())
     url = GURL(content::kAboutSrcDocURL);
@@ -81,8 +83,15 @@
       new Event(events::WEB_NAVIGATION_ON_BEFORE_NAVIGATE,
                 web_navigation::OnBeforeNavigate::kEventName,
                 web_navigation::OnBeforeNavigate::Create(details)));
-  DispatchEvent(navigation_handle->GetWebContents()->GetBrowserContext(),
-                std::move(event), url);
+
+  EventFilteringInfo info;
+  info.SetURL(navigation_handle->GetURL());
+
+  event->restrict_to_browser_context =
+      navigation_handle->GetWebContents()->GetBrowserContext();
+  event->filter_info = info;
+
+  return event;
 }
 
 // Constructs and dispatches an onCommitted or onReferenceFragmentUpdated
@@ -203,6 +212,12 @@
                 web_navigation::OnCreatedNavigationTarget::kEventName,
                 web_navigation::OnCreatedNavigationTarget::Create(details)));
   DispatchEvent(browser_context, std::move(event), target_url);
+
+  // If the target WebContents already received the onBeforeNavigate event,
+  // send it immediately after the onCreatedNavigationTarget above.
+  WebNavigationTabObserver* target_observer =
+      WebNavigationTabObserver::Get(target_web_contents);
+  target_observer->DispatchCachedOnBeforeNavigate();
 }
 
 // Constructs and dispatches an onErrorOccurred event.
diff --git a/chrome/browser/extensions/api/web_navigation/web_navigation_api_helpers.h b/chrome/browser/extensions/api/web_navigation/web_navigation_api_helpers.h
index db2832c1..5a447c92 100644
--- a/chrome/browser/extensions/api/web_navigation/web_navigation_api_helpers.h
+++ b/chrome/browser/extensions/api/web_navigation/web_navigation_api_helpers.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_EXTENSIONS_API_WEB_NAVIGATION_WEB_NAVIGATION_API_HELPERS_H_
 #define CHROME_BROWSER_EXTENSIONS_API_WEB_NAVIGATION_WEB_NAVIGATION_API_HELPERS_H_
 
+#include <memory>
 #include <string>
 
 #include "extensions/browser/extension_event_histogram_value.h"
@@ -21,6 +22,8 @@
 
 namespace extensions {
 
+struct Event;
+
 namespace web_navigation_api_helpers {
 
 // Returns the frame ID as it will be passed to the extension:
@@ -29,7 +32,8 @@
 int GetFrameId(content::RenderFrameHost* frame_host);
 
 // Create and dispatch the various events of the webNavigation API.
-void DispatchOnBeforeNavigate(content::NavigationHandle* navigation_handle);
+std::unique_ptr<Event> CreateOnBeforeNavigateEvent(
+    content::NavigationHandle* navigation_handle);
 
 void DispatchOnCommitted(events::HistogramValue histogram_value,
                          const std::string& event_name,
diff --git a/chrome/browser/extensions/extension_startup_browsertest.cc b/chrome/browser/extensions/extension_startup_browsertest.cc
index 18d84224..e2aa865 100644
--- a/chrome/browser/extensions/extension_startup_browsertest.cc
+++ b/chrome/browser/extensions/extension_startup_browsertest.cc
@@ -307,6 +307,54 @@
   TestInjection(true, true);
 }
 
+// TODO(catmullings): Remove test in future chrome release, perhaps M59.
+class DeprecatedLoadComponentExtensionSwitchBrowserTest
+    : public ExtensionBrowserTest {
+ public:
+  DeprecatedLoadComponentExtensionSwitchBrowserTest() {}
+
+  void SetUpCommandLine(base::CommandLine* command_line) override;
+
+  ExtensionRegistry* GetExtensionRegistry() {
+    return ExtensionRegistry::Get(browser()->profile());
+  }
+};
+
+void DeprecatedLoadComponentExtensionSwitchBrowserTest::SetUpCommandLine(
+    base::CommandLine* command_line) {
+  ExtensionBrowserTest::SetUpCommandLine(command_line);
+  base::FilePath fp1(test_data_dir_.AppendASCII("app_dot_com_app/"));
+  base::FilePath fp2(test_data_dir_.AppendASCII("app/"));
+
+  command_line->AppendSwitchASCII(
+      "load-component-extension",
+      fp1.AsUTF8Unsafe() + "," + fp2.AsUTF8Unsafe());
+}
+
+// Tests that the --load-component-extension flag is not supported.
+IN_PROC_BROWSER_TEST_F(DeprecatedLoadComponentExtensionSwitchBrowserTest,
+                       DefunctLoadComponentExtensionFlag) {
+  EXPECT_TRUE(extension_service()->extensions_enabled());
+
+  // Checks that the extensions loaded with the --load-component-extension flag
+  // are not installed.
+  bool is_app_dot_com_extension_installed = false;
+  bool is_app_test_extension_installed = false;
+  for (const scoped_refptr<const extensions::Extension>& extension :
+       GetExtensionRegistry()->enabled_extensions()) {
+    if (extension->name() == "App Dot Com: The App") {
+      is_app_dot_com_extension_installed = true;
+    } else if (extension->name() == "App Test") {
+      is_app_test_extension_installed = true;
+    } else {
+      EXPECT_TRUE(
+          extensions::Manifest::IsComponentLocation(extension->location()));
+    }
+  }
+  EXPECT_FALSE(is_app_dot_com_extension_installed);
+  EXPECT_FALSE(is_app_test_extension_installed);
+}
+
 class DisableExtensionsExceptBrowserTest : public ExtensionBrowserTest {
  public:
   DisableExtensionsExceptBrowserTest() {}
diff --git a/chrome/browser/extensions/extension_system_impl.cc b/chrome/browser/extensions/extension_system_impl.cc
index 6070a2dc..d862662f 100644
--- a/chrome/browser/extensions/extension_system_impl.cc
+++ b/chrome/browser/extensions/extension_system_impl.cc
@@ -254,21 +254,6 @@
   extension_service_->component_loader()->AddDefaultComponentExtensions(
       skip_session_extensions);
 #endif
-  if (command_line->HasSwitch(switches::kLoadComponentExtension)) {
-    base::CommandLine::StringType path_list =
-        command_line->GetSwitchValueNative(switches::kLoadComponentExtension);
-    base::StringTokenizerT<base::CommandLine::StringType,
-                           base::CommandLine::StringType::const_iterator>
-        t(path_list, FILE_PATH_LITERAL(","));
-    while (t.GetNext()) {
-      // Load the component extension manifest synchronously.
-      // Blocking the UI thread is acceptable here since
-      // this flag designated for developers.
-      base::ThreadRestrictions::ScopedAllowIO allow_io;
-      extension_service_->component_loader()->AddOrReplace(
-          base::FilePath(t.token()));
-    }
-  }
 
   app_sorting_.reset(new ChromeAppSorting(profile_));
 
diff --git a/chrome/browser/power/process_power_collector.cc b/chrome/browser/power/process_power_collector.cc
deleted file mode 100644
index 9c8fc3e..0000000
--- a/chrome/browser/power/process_power_collector.cc
+++ /dev/null
@@ -1,208 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/power/process_power_collector.h"
-
-#include <utility>
-
-#include "base/memory/ptr_util.h"
-#include "base/process/process_handle.h"
-#include "base/process/process_metrics.h"
-#include "base/stl_util.h"
-#include "build/build_config.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/ui/tab_contents/tab_contents_iterator.h"
-#include "components/power/origin_power_map.h"
-#include "components/power/origin_power_map_factory.h"
-#include "content/public/browser/browser_context.h"
-#include "content/public/browser/render_process_host.h"
-#include "content/public/browser/web_contents.h"
-#include "extensions/browser/app_window/app_window.h"
-#include "extensions/browser/app_window/app_window_registry.h"
-#include "url/gurl.h"
-
-#if defined(OS_CHROMEOS)
-#include "chromeos/dbus/dbus_thread_manager.h"
-#include "chromeos/dbus/power_manager/power_supply_properties.pb.h"
-#endif
-
-namespace {
-const int kSecondsPerSample = 30;
-}
-
-ProcessPowerCollector::PerProcessData::PerProcessData(
-    std::unique_ptr<base::ProcessMetrics> metrics,
-    const GURL& origin,
-    Profile* profile)
-    : metrics_(std::move(metrics)),
-      profile_(profile),
-      last_origin_(origin),
-      last_cpu_percent_(0),
-      seen_this_cycle_(true) {}
-
-ProcessPowerCollector::PerProcessData::PerProcessData()
-    : profile_(nullptr), last_cpu_percent_(0.0), seen_this_cycle_(false) {}
-
-ProcessPowerCollector::PerProcessData::~PerProcessData() {
-}
-
-ProcessPowerCollector::ProcessPowerCollector()
-    : scale_factor_(1.0) {
-#if defined(OS_CHROMEOS)
-  chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver(
-      this);
-#endif
-}
-
-ProcessPowerCollector::~ProcessPowerCollector() {
-#if defined(OS_CHROMEOS)
-  chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->RemoveObserver(
-      this);
-#endif
-}
-
-#if defined(OS_CHROMEOS)
-void ProcessPowerCollector::PowerChanged(
-    const power_manager::PowerSupplyProperties& prop) {
-  if (prop.battery_state() ==
-      power_manager::PowerSupplyProperties::DISCHARGING) {
-    if (!timer_.IsRunning())
-      StartTimer();
-    scale_factor_ = prop.battery_discharge_rate();
-  } else {
-    timer_.Stop();
-  }
-}
-#endif
-
-void ProcessPowerCollector::Initialize() {
-  StartTimer();
-}
-
-double ProcessPowerCollector::UpdatePowerConsumptionForTesting() {
-  return UpdatePowerConsumption();
-}
-
-void ProcessPowerCollector::StartTimer() {
-  DCHECK(!timer_.IsRunning());
-  timer_.Start(FROM_HERE,
-               base::TimeDelta::FromSeconds(kSecondsPerSample),
-               this,
-               &ProcessPowerCollector::HandleUpdateTimeout);
-}
-
-// Work around VS 2015 code-gen bug, seen in Update 2 and Update 3.
-// crbug.com/640588
-#if defined(COMPILER_MSVC)
-#pragma optimize("", off)
-#endif
-double ProcessPowerCollector::UpdatePowerConsumption() {
-  double total_cpu_percent = SynchronizeProcesses();
-
-  // Invalidate the process for the next cycle.
-  for (auto& metrics : metrics_map_)
-    metrics.second->set_seen_this_cycle(false);
-
-  RecordCpuUsageByOrigin(total_cpu_percent);
-  return total_cpu_percent;
-}
-
-void ProcessPowerCollector::HandleUpdateTimeout() {
-  UpdatePowerConsumption();
-}
-#if defined(COMPILER_MSVC)
-#pragma optimize("", on)
-#endif
-
-double ProcessPowerCollector::SynchronizeProcesses() {
-  // Update all tabs.
-  for (TabContentsIterator it; !it.done(); it.Next()) {
-    content::RenderProcessHost* render_process = it->GetRenderProcessHost();
-    // Skip incognito web contents.
-    if (render_process->GetBrowserContext()->IsOffTheRecord())
-      continue;
-    UpdateProcessInMap(render_process, it->GetLastCommittedURL().GetOrigin());
-  }
-
-  // Iterate over all profiles to find all app windows to attribute all apps.
-  ProfileManager* pm = g_browser_process->profile_manager();
-  std::vector<Profile*> open_profiles = pm->GetLoadedProfiles();
-  for (Profile* profile : open_profiles) {
-    const extensions::AppWindowRegistry::AppWindowList& app_windows =
-        extensions::AppWindowRegistry::Get(profile)->app_windows();
-    for (auto* window : app_windows) {
-      content::WebContents* web_contents = window->web_contents();
-      UpdateProcessInMap(web_contents->GetRenderProcessHost(),
-                         web_contents->GetLastCommittedURL().GetOrigin());
-    }
-  }
-
-  // Remove invalid processes and sum up the cpu cycle.
-  double total_cpu_percent = 0.0;
-  ProcessMetricsMap::iterator it = metrics_map_.begin();
-  while (it != metrics_map_.end()) {
-    if (!it->second->seen_this_cycle()) {
-      metrics_map_.erase(it++);
-      continue;
-    }
-
-    total_cpu_percent += it->second->last_cpu_percent();
-    ++it;
-  }
-
-  return total_cpu_percent;
-}
-
-void ProcessPowerCollector::RecordCpuUsageByOrigin(double total_cpu_percent) {
-  DCHECK_GE(total_cpu_percent, 0);
-  if (total_cpu_percent == 0)
-    return;
-
-  for (auto& metrics : metrics_map_) {
-    double last_process_power_usage = metrics.second->last_cpu_percent();
-    last_process_power_usage *= scale_factor_ / total_cpu_percent;
-
-    const GURL& origin = metrics.second->last_origin();
-    power::OriginPowerMap* origin_power_map =
-        power::OriginPowerMapFactory::GetForBrowserContext(
-            metrics.second->profile());
-    // |origin_power_map| can be NULL, if the profile is a guest profile in
-    // Chrome OS.
-    if (origin_power_map)
-      origin_power_map->AddPowerForOrigin(origin, last_process_power_usage);
-  }
-
-  // Iterate over all profiles to let them know we've finished updating.
-  ProfileManager* pm = g_browser_process->profile_manager();
-  std::vector<Profile*> open_profiles = pm->GetLoadedProfiles();
-  for (Profile* profile : open_profiles) {
-    power::OriginPowerMap* origin_power_map =
-        power::OriginPowerMapFactory::GetForBrowserContext(profile);
-    if (origin_power_map)
-      origin_power_map->OnAllOriginsUpdated();
-  }
-}
-
-void ProcessPowerCollector::UpdateProcessInMap(
-    const content::RenderProcessHost* rph,
-    const GURL& origin) {
-  base::ProcessHandle handle = rph->GetHandle();
-  if (!base::ContainsKey(metrics_map_, handle)) {
-    metrics_map_[handle] = base::MakeUnique<PerProcessData>(
-#if defined(OS_MACOSX)
-        base::ProcessMetrics::CreateProcessMetrics(handle, nullptr),
-#else
-        base::ProcessMetrics::CreateProcessMetrics(handle),
-#endif
-        origin, Profile::FromBrowserContext(rph->GetBrowserContext()));
-  }
-
-  PerProcessData* process_data = metrics_map_[handle].get();
-  process_data->set_last_cpu_percent(std::max(0.0,
-      cpu_usage_callback_.is_null() ? process_data->metrics()->GetCPUUsage()
-                                    : cpu_usage_callback_.Run(handle)));
-  process_data->set_seen_this_cycle(true);
-}
diff --git a/chrome/browser/power/process_power_collector.h b/chrome/browser/power/process_power_collector.h
deleted file mode 100644
index c1a5a75..0000000
--- a/chrome/browser/power/process_power_collector.h
+++ /dev/null
@@ -1,152 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_POWER_PROCESS_POWER_COLLECTOR_H_
-#define CHROME_BROWSER_POWER_PROCESS_POWER_COLLECTOR_H_
-
-#include <map>
-#include <memory>
-
-#include "base/macros.h"
-#include "base/process/process_handle.h"
-#include "base/process/process_metrics.h"
-#include "base/timer/timer.h"
-#include "build/build_config.h"
-#include "components/power/origin_power_map_factory.h"
-#include "url/gurl.h"
-
-#if defined(OS_CHROMEOS)
-#include "chromeos/dbus/power_manager_client.h"
-#endif
-
-class Profile;
-
-namespace content {
-class RenderProcessHost;
-}
-
-#if defined(OS_CHROMEOS)
-namespace power_manager {
-class PowerSupplyProperties;
-}
-#endif
-
-// Manages regular updates of the profile power consumption.
-class ProcessPowerCollector
-#if defined(OS_CHROMEOS)
-    : public chromeos::PowerManagerClient::Observer
-#endif
-      {
- public:
-  class PerProcessData {
-   public:
-    PerProcessData(std::unique_ptr<base::ProcessMetrics> metrics,
-                   const GURL& origin,
-                   Profile* profile);
-    PerProcessData();
-    ~PerProcessData();
-
-    base::ProcessMetrics* metrics() const { return metrics_.get(); }
-    Profile* profile() const { return profile_; }
-    const GURL& last_origin() const { return last_origin_; }
-    int last_cpu_percent() const { return last_cpu_percent_; }
-    bool seen_this_cycle() const { return seen_this_cycle_; }
-    void set_last_cpu_percent(double new_cpu) { last_cpu_percent_ = new_cpu; }
-    void set_seen_this_cycle(bool seen) { seen_this_cycle_ = seen; }
-
-   private:
-    // |metrics_| holds the ProcessMetrics information for the given process.
-    std::unique_ptr<base::ProcessMetrics> metrics_;
-
-    // |profile| is the profile that is visiting the |last_origin_|.
-    // It is not owned by PerProcessData.
-    Profile* profile_;
-
-    // |last_origin_| is the last origin visited by the process.
-    GURL last_origin_;
-
-    // |last_cpu_percent_| is the proportion of the CPU used since the last
-    // query.
-    double last_cpu_percent_;
-
-    // |seen_this_cycle| represents if the process still exists in this cycle.
-    // If it doesn't, we erase the PerProcessData.
-    bool seen_this_cycle_;
-
-    DISALLOW_COPY_AND_ASSIGN(PerProcessData);
-  };
-
-  // A map from all process handles to a metric.
-  using ProcessMetricsMap =
-      std::map<base::ProcessHandle, std::unique_ptr<PerProcessData>>;
-
-  // A callback used to define mock CPU usage for testing.
-  using CpuUsageCallback = base::Callback<double(base::ProcessHandle)>;
-
-  // On Chrome OS, can only be initialized after the DBusThreadManager has been
-  // initialized.
-  ProcessPowerCollector();
-#if defined(OS_CHROMEOS)
-  // On Chrome OS, can only be destroyed before DBusThreadManager is.
-  ~ProcessPowerCollector() override;
-#else
-  virtual ~ProcessPowerCollector();
-#endif
-
-  void set_cpu_usage_callback_for_testing(const CpuUsageCallback& callback) {
-    cpu_usage_callback_ = callback;
-  }
-
-  ProcessMetricsMap* metrics_map_for_testing() { return &metrics_map_; }
-
-#if defined(OS_CHROMEOS)
-  // PowerManagerClient::Observer implementation:
-  void PowerChanged(const power_manager::PowerSupplyProperties& prop) override;
-#endif
-
-  // Begin periodically updating the power consumption numbers by profile.
-  void Initialize();
-
-  // Calls UpdatePowerConsumption() and returns the total CPU percent.
-  double UpdatePowerConsumptionForTesting();
-
- private:
-  // Starts the timer for updating the power consumption.
-  void StartTimer();
-
-  // Calls SynchronizerProcesses() and RecordCpuUsageByOrigin() to update the
-  // |metrics_map_| and attribute power consumption. Invoked by |timer_| and as
-  // a helper method for UpdatePowerConsumptionForTesting().
-  double UpdatePowerConsumption();
-
-  // Calls UpdatePowerConsumption(). Invoked by |timer_|.
-  void HandleUpdateTimeout();
-
-  // Synchronizes the currently active processes to the |metrics_map_| and
-  // returns the total amount of cpu usage in the cycle.
-  double SynchronizeProcesses();
-
-  // Attributes the power usage to the profiles and origins using the
-  // information from SynchronizeProcesses() given a total amount
-  // of CPU used in this cycle, |total_cpu_percent|.
-  void RecordCpuUsageByOrigin(double total_cpu_percent);
-
-  // Adds the information from a given RenderProcessHost to the |metrics_map_|
-  // for a given origin. Called by SynchronizeProcesses().
-  void UpdateProcessInMap(const content::RenderProcessHost* render_process,
-                          const GURL& origin);
-
-  ProcessMetricsMap metrics_map_;
-  base::RepeatingTimer timer_;
-
-  // Callback to use to get CPU usage if set.
-  CpuUsageCallback cpu_usage_callback_;
-
-  // The factor to scale the CPU usage by.
-  double scale_factor_;
-
-  DISALLOW_COPY_AND_ASSIGN(ProcessPowerCollector);
-};
-
-#endif  // CHROME_BROWSER_POWER_PROCESS_POWER_COLLECTOR_H_
diff --git a/chrome/browser/power/process_power_collector_unittest.cc b/chrome/browser/power/process_power_collector_unittest.cc
deleted file mode 100644
index dffe11ac..0000000
--- a/chrome/browser/power/process_power_collector_unittest.cc
+++ /dev/null
@@ -1,291 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/power/process_power_collector.h"
-
-#include "build/build_config.h"
-#include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/ui/apps/chrome_app_delegate.h"
-#include "chrome/browser/ui/browser_commands.h"
-#include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/test/base/browser_with_test_window_test.h"
-#include "chrome/test/base/testing_browser_process.h"
-#include "chrome/test/base/testing_profile_manager.h"
-#include "components/power/origin_power_map.h"
-#include "components/power/origin_power_map_factory.h"
-#include "content/public/browser/site_instance.h"
-#include "content/public/test/mock_render_process_host.h"
-#include "extensions/browser/app_window/app_window_client.h"
-#include "extensions/browser/app_window/app_window_registry.h"
-#include "extensions/browser/app_window/native_app_window.h"
-#include "extensions/browser/app_window/test_app_window_contents.h"
-#include "extensions/common/extension.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "url/gurl.h"
-
-#if defined(OS_CHROMEOS)
-#include "chromeos/dbus/power_manager/power_supply_properties.pb.h"
-#endif
-
-using power::OriginPowerMap;
-using power::OriginPowerMapFactory;
-
-class BrowserProcessPowerTest : public BrowserWithTestWindowTest {
- public:
-  BrowserProcessPowerTest() {}
-  ~BrowserProcessPowerTest() override {}
-
-  void SetUp() override {
-    BrowserWithTestWindowTest::SetUp();
-    collector.reset(new ProcessPowerCollector);
-
-#if defined(OS_CHROMEOS)
-    power_manager::PowerSupplyProperties prop;
-    prop.set_external_power(power_manager::PowerSupplyProperties::AC);
-    prop.set_battery_state(power_manager::PowerSupplyProperties::DISCHARGING);
-    prop.set_battery_percent(20.00);
-    prop.set_battery_discharge_rate(1);
-    collector->PowerChanged(prop);
-#endif
-
-    profile_manager_.reset(
-        new TestingProfileManager(TestingBrowserProcess::GetGlobal()));
-    ASSERT_TRUE(profile_manager_->SetUp());
-  }
-
-  void TearDown() override {
-    collector.reset();
-    BrowserWithTestWindowTest::TearDown();
-  }
-
-  // Mocks out CPU usage for all processes as |value| percent.
-  double ReturnCpuAsConstant(double value, base::ProcessHandle handle) {
-    return value;
-  }
-
- protected:
-  content::MockRenderProcessHost* GetProcess(Browser* browser) {
-    return static_cast<content::MockRenderProcessHost*>(
-        browser->tab_strip_model()
-            ->GetActiveWebContents()
-            ->GetRenderViewHost()
-            ->GetProcess());
-  }
-
-  std::unique_ptr<base::ProcessHandle> MakeProcessHandle(int process_id) {
-    std::unique_ptr<base::ProcessHandle> proc_handle(new base::ProcessHandle(
-#if defined(OS_WIN)
-        reinterpret_cast<HANDLE>(process_id))
-#else
-        process_id)
-#endif
-                                                         );
-    return proc_handle;
-  }
-
-  std::unique_ptr<ProcessPowerCollector> collector;
-  std::unique_ptr<TestingProfileManager> profile_manager_;
-};
-
-TEST_F(BrowserProcessPowerTest, NoSite) {
-  collector->UpdatePowerConsumptionForTesting();
-  EXPECT_EQ(0u, collector->metrics_map_for_testing()->size());
-}
-
-TEST_F(BrowserProcessPowerTest, OneSite) {
-  GURL url("http://www.google.com");
-  AddTab(browser(), url);
-  collector->UpdatePowerConsumptionForTesting();
-  ProcessPowerCollector::ProcessMetricsMap* metrics_map =
-      collector->metrics_map_for_testing();
-  EXPECT_EQ(1u, metrics_map->size());
-
-  // Create fake process numbers.
-  GetProcess(browser())->SetProcessHandle(MakeProcessHandle(1));
-
-  OriginPowerMap* origin_power_map =
-      OriginPowerMapFactory::GetForBrowserContext(profile());
-  EXPECT_EQ(0, origin_power_map->GetPowerForOrigin(url));
-
-  collector->set_cpu_usage_callback_for_testing(
-      base::Bind(&BrowserProcessPowerTest::ReturnCpuAsConstant,
-                 base::Unretained(this),
-                 5));
-  EXPECT_DOUBLE_EQ(5, collector->UpdatePowerConsumptionForTesting());
-  EXPECT_EQ(100, origin_power_map->GetPowerForOrigin(url));
-}
-
-TEST_F(BrowserProcessPowerTest, MultipleSites) {
-  Browser::CreateParams native_params(profile());
-  GURL url1("http://www.google.com");
-  GURL url2("http://www.example.com");
-  GURL url3("https://www.google.com");
-  std::unique_ptr<Browser> browser2(
-      chrome::CreateBrowserWithTestWindowForParams(&native_params));
-  std::unique_ptr<Browser> browser3(
-      chrome::CreateBrowserWithTestWindowForParams(&native_params));
-  AddTab(browser(), url1);
-  AddTab(browser2.get(), url2);
-  AddTab(browser3.get(), url3);
-
-  // Create fake process numbers.
-  GetProcess(browser())->SetProcessHandle(MakeProcessHandle(1));
-  GetProcess(browser2.get())->SetProcessHandle(MakeProcessHandle(2));
-  GetProcess(browser3.get())->SetProcessHandle(MakeProcessHandle(3));
-
-  collector->UpdatePowerConsumptionForTesting();
-  ProcessPowerCollector::ProcessMetricsMap* metrics_map =
-      collector->metrics_map_for_testing();
-  EXPECT_EQ(3u, metrics_map->size());
-
-  // Since all handlers are uninitialized, this should be 0.
-  EXPECT_DOUBLE_EQ(0, collector->UpdatePowerConsumptionForTesting());
-  OriginPowerMap* origin_power_map =
-      OriginPowerMapFactory::GetForBrowserContext(profile());
-  EXPECT_EQ(0, origin_power_map->GetPowerForOrigin(url1));
-  EXPECT_EQ(0, origin_power_map->GetPowerForOrigin(url2));
-  EXPECT_EQ(0, origin_power_map->GetPowerForOrigin(url3));
-
-  collector->set_cpu_usage_callback_for_testing(
-      base::Bind(&BrowserProcessPowerTest::ReturnCpuAsConstant,
-                 base::Unretained(this),
-                 5));
-  EXPECT_DOUBLE_EQ(15, collector->UpdatePowerConsumptionForTesting());
-  EXPECT_EQ(33, origin_power_map->GetPowerForOrigin(url1));
-  EXPECT_EQ(33, origin_power_map->GetPowerForOrigin(url2));
-  EXPECT_EQ(33, origin_power_map->GetPowerForOrigin(url3));
-
-  // Close some tabs and verify that they are removed from the metrics map.
-  chrome::CloseTab(browser2.get());
-  chrome::CloseTab(browser3.get());
-
-  collector->UpdatePowerConsumptionForTesting();
-  EXPECT_EQ(1u, metrics_map->size());
-}
-
-TEST_F(BrowserProcessPowerTest, IncognitoDoesntRecordPowerUsage) {
-  Browser::CreateParams native_params(profile()->GetOffTheRecordProfile());
-  std::unique_ptr<Browser> incognito_browser(
-      chrome::CreateBrowserWithTestWindowForParams(&native_params));
-  GURL url("http://www.google.com");
-  AddTab(browser(), url);
-
-  GURL hidden_url("http://foo.com");
-  AddTab(incognito_browser.get(), hidden_url);
-
-  // Create fake process numbers.
-  GetProcess(browser())->SetProcessHandle(MakeProcessHandle(1));
-  GetProcess(incognito_browser.get())->SetProcessHandle(MakeProcessHandle(2));
-
-  EXPECT_DOUBLE_EQ(0, collector->UpdatePowerConsumptionForTesting());
-  ProcessPowerCollector::ProcessMetricsMap* metrics_map =
-      collector->metrics_map_for_testing();
-  EXPECT_EQ(1u, metrics_map->size());
-
-  OriginPowerMap* origin_power_map =
-      OriginPowerMapFactory::GetForBrowserContext(profile());
-  EXPECT_EQ(0, origin_power_map->GetPowerForOrigin(url));
-
-  collector->set_cpu_usage_callback_for_testing(
-      base::Bind(&BrowserProcessPowerTest::ReturnCpuAsConstant,
-                 base::Unretained(this),
-                 5));
-  EXPECT_DOUBLE_EQ(5, collector->UpdatePowerConsumptionForTesting());
-
-  // Verify that the incognito data was not stored.
-  EXPECT_EQ(100, origin_power_map->GetPowerForOrigin(url));
-  EXPECT_EQ(0, origin_power_map->GetPowerForOrigin(hidden_url));
-
-  chrome::CloseTab(incognito_browser.get());
-}
-
-TEST_F(BrowserProcessPowerTest, MultipleProfilesRecordSeparately) {
-  std::unique_ptr<Profile> other_profile(CreateProfile());
-  Browser::CreateParams native_params(other_profile.get());
-  std::unique_ptr<Browser> other_user(
-      chrome::CreateBrowserWithTestWindowForParams(&native_params));
-
-  GURL url("http://www.google.com");
-  AddTab(browser(), url);
-
-  GURL hidden_url("http://foo.com");
-  AddTab(other_user.get(), hidden_url);
-
-  // Create fake process numbers.
-  GetProcess(browser())->SetProcessHandle(MakeProcessHandle(1));
-  GetProcess(other_user.get())->SetProcessHandle(MakeProcessHandle(2));
-
-  EXPECT_DOUBLE_EQ(0, collector->UpdatePowerConsumptionForTesting());
-  EXPECT_EQ(2u, collector->metrics_map_for_testing()->size());
-
-  collector->set_cpu_usage_callback_for_testing(
-      base::Bind(&BrowserProcessPowerTest::ReturnCpuAsConstant,
-                 base::Unretained(this),
-                 5));
-  EXPECT_DOUBLE_EQ(10, collector->UpdatePowerConsumptionForTesting());
-
-  // profile() should have an entry for |url| but not |hidden_url|.
-  OriginPowerMap* origin_power_map_first =
-      OriginPowerMapFactory::GetForBrowserContext(profile());
-  EXPECT_EQ(100, origin_power_map_first->GetPowerForOrigin(url));
-  EXPECT_EQ(0, origin_power_map_first->GetPowerForOrigin(hidden_url));
-
-  // |other_profile| should have an entry for |hidden_url| but not |url|.
-  OriginPowerMap* origin_power_map_second =
-      OriginPowerMapFactory::GetForBrowserContext(other_profile.get());
-  EXPECT_EQ(0, origin_power_map_second->GetPowerForOrigin(url));
-  EXPECT_EQ(100, origin_power_map_second->GetPowerForOrigin(hidden_url));
-
-  // Clean up
-  chrome::CloseTab(other_user.get());
-}
-
-TEST_F(BrowserProcessPowerTest, AppsRecordPowerUsage) {
-// Install an app (an extension*).
-#if defined(OS_WIN)
-  base::FilePath extension_path(FILE_PATH_LITERAL("c:\\foo"));
-#elif defined(OS_POSIX)
-  base::FilePath extension_path(FILE_PATH_LITERAL("/foo"));
-#endif
-  base::DictionaryValue manifest;
-  manifest.SetString("name", "Fake Name");
-  manifest.SetString("version", "1");
-  std::string error;
-  char kTestAppId[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
-  scoped_refptr<extensions::Extension> extension(
-      extensions::Extension::Create(extension_path,
-                                    extensions::Manifest::INTERNAL,
-                                    manifest,
-                                    extensions::Extension::NO_FLAGS,
-                                    kTestAppId,
-                                    &error));
-  EXPECT_TRUE(extension.get()) << error;
-
-  Profile* current_profile =
-      profile_manager_->CreateTestingProfile("Test user");
-  GURL url("chrome-extension://aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
-  extensions::AppWindow* window = new extensions::AppWindow(
-      current_profile, new ChromeAppDelegate(false), extension.get());
-  content::WebContents* web_contents(
-      content::WebContents::Create(content::WebContents::CreateParams(
-          current_profile,
-          content::SiteInstance::CreateForURL(current_profile, url))));
-  window->SetAppWindowContentsForTesting(
-      std::unique_ptr<extensions::AppWindowContents>(
-          new extensions::TestAppWindowContents(web_contents)));
-  extensions::AppWindowRegistry* app_registry =
-      extensions::AppWindowRegistry::Get(current_profile);
-  app_registry->AddAppWindow(window);
-
-  collector->set_cpu_usage_callback_for_testing(
-      base::Bind(&BrowserProcessPowerTest::ReturnCpuAsConstant,
-                 base::Unretained(this),
-                 5));
-  collector->UpdatePowerConsumptionForTesting();
-  EXPECT_EQ(1u, collector->metrics_map_for_testing()->size());
-
-  window->OnNativeClose();
-  collector->UpdatePowerConsumptionForTesting();
-  EXPECT_EQ(0u, collector->metrics_map_for_testing()->size());
-}
diff --git a/chrome/browser/supervised_user/supervised_user_service.cc b/chrome/browser/supervised_user/supervised_user_service.cc
index 1f25785..aed7f3d 100644
--- a/chrome/browser/supervised_user/supervised_user_service.cc
+++ b/chrome/browser/supervised_user/supervised_user_service.cc
@@ -723,14 +723,14 @@
 }
 
 void SupervisedUserService::FinishSetupSyncWhenReady() {
-  // If we're already waiting for the Sync backend, there's nothing to do here.
+  // If we're already waiting for the sync engine, there's nothing to do here.
   if (waiting_for_sync_initialization_)
     return;
 
-  // Continue in FinishSetupSync() once the Sync backend has been initialized.
+  // Continue in FinishSetupSync() once the sync engine has been initialized.
   browser_sync::ProfileSyncService* service =
       ProfileSyncServiceFactory::GetForProfile(profile_);
-  if (service->IsBackendInitialized()) {
+  if (service->IsEngineInitialized()) {
     FinishSetupSync();
   } else {
     service->AddObserver(this);
@@ -741,7 +741,7 @@
 void SupervisedUserService::FinishSetupSync() {
   browser_sync::ProfileSyncService* service =
       ProfileSyncServiceFactory::GetForProfile(profile_);
-  DCHECK(service->IsBackendInitialized());
+  DCHECK(service->IsEngineInitialized());
 
   // Sync nothing (except types which are set via GetPreferredDataTypes).
   bool sync_everything = false;
@@ -1263,7 +1263,7 @@
 void SupervisedUserService::OnStateChanged() {
   browser_sync::ProfileSyncService* service =
       ProfileSyncServiceFactory::GetForProfile(profile_);
-  if (waiting_for_sync_initialization_ && service->IsBackendInitialized()) {
+  if (waiting_for_sync_initialization_ && service->IsEngineInitialized()) {
     waiting_for_sync_initialization_ = false;
     service->RemoveObserver(this);
     FinishSetupSync();
diff --git a/chrome/browser/sync/profile_sync_service_android.cc b/chrome/browser/sync/profile_sync_service_android.cc
index a4d8521..66e1d79 100644
--- a/chrome/browser/sync/profile_sync_service_android.cc
+++ b/chrome/browser/sync/profile_sync_service_android.cc
@@ -172,11 +172,11 @@
   return sync_service_->IsSyncActive();
 }
 
-jboolean ProfileSyncServiceAndroid::IsBackendInitialized(
+jboolean ProfileSyncServiceAndroid::IsEngineInitialized(
     JNIEnv* env,
     const JavaParamRef<jobject>&) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  return sync_service_->IsBackendInitialized();
+  return sync_service_->IsEngineInitialized();
 }
 
 void ProfileSyncServiceAndroid::SetSetupInProgress(
diff --git a/chrome/browser/sync/profile_sync_service_android.h b/chrome/browser/sync/profile_sync_service_android.h
index 068a5b8b..332ebc2 100644
--- a/chrome/browser/sync/profile_sync_service_android.h
+++ b/chrome/browser/sync/profile_sync_service_android.h
@@ -59,9 +59,8 @@
                    const base::android::JavaParamRef<jobject>& obj);
   jboolean IsSyncActive(JNIEnv* env,
                         const base::android::JavaParamRef<jobject>& obj);
-  jboolean IsBackendInitialized(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& obj);
+  jboolean IsEngineInitialized(JNIEnv* env,
+                               const base::android::JavaParamRef<jobject>& obj);
   void SetSetupInProgress(JNIEnv* env,
                           const base::android::JavaParamRef<jobject>& obj,
                           jboolean in_progress);
diff --git a/chrome/browser/sync/sync_startup_tracker.cc b/chrome/browser/sync/sync_startup_tracker.cc
index d65454b4..720ac40 100644
--- a/chrome/browser/sync/sync_startup_tracker.cc
+++ b/chrome/browser/sync/sync_startup_tracker.cc
@@ -61,8 +61,8 @@
     return SYNC_STARTUP_ERROR;
   }
 
-  // If the sync backend has started up, notify the callback.
-  if (service->IsBackendInitialized())
+  // If the sync engine has started up, notify the callback.
+  if (service->IsEngineInitialized())
     return SYNC_STARTUP_COMPLETE;
 
   // If the sync service has some kind of error, report to the user.
@@ -76,7 +76,7 @@
     return SYNC_STARTUP_ERROR;
   }
 
-  // No error detected yet, but the sync backend hasn't started up yet, so
+  // No error detected yet, but the sync engine hasn't started up yet, so
   // we're in the pending state.
   return SYNC_STARTUP_PENDING;
 }
diff --git a/chrome/browser/sync/sync_startup_tracker_unittest.cc b/chrome/browser/sync/sync_startup_tracker_unittest.cc
index 069634a..6faa4c9 100644
--- a/chrome/browser/sync/sync_startup_tracker_unittest.cc
+++ b/chrome/browser/sync/sync_startup_tracker_unittest.cc
@@ -55,7 +55,7 @@
   void SetupNonInitializedPSS() {
     EXPECT_CALL(*mock_pss_, GetAuthError())
         .WillRepeatedly(ReturnRef(no_error_));
-    EXPECT_CALL(*mock_pss_, IsBackendInitialized())
+    EXPECT_CALL(*mock_pss_, IsEngineInitialized())
         .WillRepeatedly(Return(false));
     EXPECT_CALL(*mock_pss_, HasUnrecoverableError())
         .WillRepeatedly(Return(false));
@@ -70,7 +70,7 @@
 };
 
 TEST_F(SyncStartupTrackerTest, SyncAlreadyInitialized) {
-  EXPECT_CALL(*mock_pss_, IsBackendInitialized()).WillRepeatedly(Return(true));
+  EXPECT_CALL(*mock_pss_, IsEngineInitialized()).WillRepeatedly(Return(true));
   EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(true));
   EXPECT_CALL(observer_, SyncStartupCompleted());
   SyncStartupTracker tracker(profile_.get(), &observer_);
@@ -79,7 +79,7 @@
 TEST_F(SyncStartupTrackerTest, SyncNotSignedIn) {
   // Make sure that we get a SyncStartupFailed() callback if sync is not logged
   // in.
-  EXPECT_CALL(*mock_pss_, IsBackendInitialized()).WillRepeatedly(Return(false));
+  EXPECT_CALL(*mock_pss_, IsEngineInitialized()).WillRepeatedly(Return(false));
   EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(false));
   EXPECT_CALL(observer_, SyncStartupFailed());
   SyncStartupTracker tracker(profile_.get(), &observer_);
@@ -88,7 +88,7 @@
 TEST_F(SyncStartupTrackerTest, SyncAuthError) {
   // Make sure that we get a SyncStartupFailed() callback if sync gets an auth
   // error.
-  EXPECT_CALL(*mock_pss_, IsBackendInitialized()).WillRepeatedly(Return(false));
+  EXPECT_CALL(*mock_pss_, IsEngineInitialized()).WillRepeatedly(Return(false));
   EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(true));
   GoogleServiceAuthError error(
       GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
@@ -105,7 +105,7 @@
   SyncStartupTracker tracker(profile_.get(), &observer_);
   Mock::VerifyAndClearExpectations(&observer_);
   // Now, mark the PSS as initialized.
-  EXPECT_CALL(*mock_pss_, IsBackendInitialized()).WillRepeatedly(Return(true));
+  EXPECT_CALL(*mock_pss_, IsEngineInitialized()).WillRepeatedly(Return(true));
   EXPECT_CALL(observer_, SyncStartupCompleted());
   tracker.OnStateChanged();
 }
@@ -120,7 +120,7 @@
   Mock::VerifyAndClearExpectations(mock_pss_);
 
   // Now, mark the PSS as having an auth error.
-  EXPECT_CALL(*mock_pss_, IsBackendInitialized()).WillRepeatedly(Return(false));
+  EXPECT_CALL(*mock_pss_, IsEngineInitialized()).WillRepeatedly(Return(false));
   EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(true));
   GoogleServiceAuthError error(
       GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
@@ -139,7 +139,7 @@
   Mock::VerifyAndClearExpectations(mock_pss_);
 
   // Now, mark the PSS as having an unrecoverable error.
-  EXPECT_CALL(*mock_pss_, IsBackendInitialized()).WillRepeatedly(Return(false));
+  EXPECT_CALL(*mock_pss_, IsEngineInitialized()).WillRepeatedly(Return(false));
   EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(true));
   GoogleServiceAuthError error(
       GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
diff --git a/chrome/browser/sync/test/integration/profile_sync_service_harness.cc b/chrome/browser/sync/test/integration/profile_sync_service_harness.cc
index 6a4b4ce..4e552de4 100644
--- a/chrome/browser/sync/test/integration/profile_sync_service_harness.cc
+++ b/chrome/browser/sync/test/integration/profile_sync_service_harness.cc
@@ -49,25 +49,25 @@
              GoogleServiceAuthError::REQUEST_CANCELED;
 }
 
-class BackendInitializeChecker : public SingleClientStatusChangeChecker {
+class EngineInitializeChecker : public SingleClientStatusChangeChecker {
  public:
-  explicit BackendInitializeChecker(ProfileSyncService* service)
+  explicit EngineInitializeChecker(ProfileSyncService* service)
       : SingleClientStatusChangeChecker(service) {}
 
   bool IsExitConditionSatisfied() override {
-    if (service()->IsBackendInitialized())
+    if (service()->IsEngineInitialized())
       return true;
-    // Backend initialization is blocked by an auth error.
+    // Engine initialization is blocked by an auth error.
     if (HasAuthError(service()))
       return true;
-    // Backend initialization is blocked by a failure to fetch Oauth2 tokens.
+    // Engine initialization is blocked by a failure to fetch Oauth2 tokens.
     if (service()->IsRetryingAccessTokenFetchForTest())
       return true;
-    // Still waiting on backend initialization.
+    // Still waiting on engine initialization.
     return false;
   }
 
-  std::string GetDebugMessage() const override { return "Backend Initialize"; }
+  std::string GetDebugMessage() const override { return "Engine Initialize"; }
 };
 
 class SyncSetupChecker : public SingleClientStatusChangeChecker {
@@ -179,7 +179,7 @@
   // Now that auth is completed, request that sync actually start.
   service()->RequestStart();
 
-  if (!AwaitBackendInitialization()) {
+  if (!AwaitEngineInitialization()) {
     return false;
   }
 
@@ -246,14 +246,14 @@
   return QuiesceStatusChangeChecker(services).Wait();
 }
 
-bool ProfileSyncServiceHarness::AwaitBackendInitialization() {
-  if (!BackendInitializeChecker(service()).Wait()) {
-    LOG(ERROR) << "BackendInitializeChecker timed out.";
+bool ProfileSyncServiceHarness::AwaitEngineInitialization() {
+  if (!EngineInitializeChecker(service()).Wait()) {
+    LOG(ERROR) << "EngineInitializeChecker timed out.";
     return false;
   }
 
-  if (!service()->IsBackendInitialized()) {
-    LOG(ERROR) << "Service backend not initialized.";
+  if (!service()->IsEngineInitialized()) {
+    LOG(ERROR) << "Service engine not initialized.";
     return false;
   }
 
diff --git a/chrome/browser/sync/test/integration/profile_sync_service_harness.h b/chrome/browser/sync/test/integration/profile_sync_service_harness.h
index 20de77d..b4b82eb2 100644
--- a/chrome/browser/sync/test/integration/profile_sync_service_harness.h
+++ b/chrome/browser/sync/test/integration/profile_sync_service_harness.h
@@ -82,11 +82,11 @@
   static bool AwaitQuiescence(
       const std::vector<ProfileSyncServiceHarness*>& clients);
 
-  // Blocks the caller until the sync backend is initialized or some end state
-  // (e.g., auth error) is reached. Returns true if and only if the backend
-  // initialized successfully. See ProfileSyncService's IsBackendInitialized()
-  // method for the definition of backend initialization.
-  bool AwaitBackendInitialization();
+  // Blocks the caller until the sync engine is initialized or some end state
+  // (e.g., auth error) is reached. Returns true if and only if the engine
+  // initialized successfully. See ProfileSyncService's IsEngineInitialized()
+  // method for the definition of engine initialization.
+  bool AwaitEngineInitialization();
 
   // Blocks the caller until sync setup is complete. Returns true if and only
   // if sync setup completed successfully. See syncer::SyncService's
diff --git a/chrome/browser/sync/test/integration/single_client_supervised_user_settings_sync_test.cc b/chrome/browser/sync/test/integration/single_client_supervised_user_settings_sync_test.cc
index 01ebf63..fe45dce 100644
--- a/chrome/browser/sync/test/integration/single_client_supervised_user_settings_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_supervised_user_settings_sync_test.cc
@@ -51,7 +51,7 @@
       GoogleServiceAuthError(GoogleServiceAuthError::NONE),
       std::string("token value doesn't matter in tests"));
 
-  ASSERT_TRUE(GetClient(0)->AwaitBackendInitialization());
+  ASSERT_TRUE(GetClient(0)->AwaitEngineInitialization());
   ASSERT_TRUE(GetClient(0)->AwaitSyncSetupCompletion());
 
   // TODO(pvalenzuela): Add additional tests and some verification of sync-
diff --git a/chrome/browser/sync/test/integration/sync_errors_test.cc b/chrome/browser/sync/test/integration/sync_errors_test.cc
index 76a7723..cce9867 100644
--- a/chrome/browser/sync/test/integration/sync_errors_test.cc
+++ b/chrome/browser/sync/test/integration/sync_errors_test.cc
@@ -38,14 +38,14 @@
   std::string GetDebugMessage() const override { return "Sync Disabled"; }
 };
 
-class SyncBackendStoppedChecker : public SingleClientStatusChangeChecker {
+class SyncEngineStoppedChecker : public SingleClientStatusChangeChecker {
  public:
-  explicit SyncBackendStoppedChecker(ProfileSyncService* service)
+  explicit SyncEngineStoppedChecker(ProfileSyncService* service)
       : SingleClientStatusChangeChecker(service) {}
 
   // StatusChangeChecker implementation.
   bool IsExitConditionSatisfied() override {
-    return !service()->IsBackendInitialized();
+    return !service()->IsEngineInitialized();
   }
   std::string GetDebugMessage() const override { return "Sync stopped"; }
 };
@@ -200,7 +200,7 @@
   ASSERT_EQ(status.sync_protocol_error.error_description, description);
 }
 
-// Tests that on receiving CLIENT_DATA_OBSOLETE sync backend gets restarted and
+// Tests that on receiving CLIENT_DATA_OBSOLETE sync engine gets restarted and
 // initialized with different cache_guld.
 IN_PROC_BROWSER_TEST_F(SyncErrorTest, ClientDataObsoleteTest) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
@@ -224,12 +224,12 @@
   const BookmarkNode* node2 = AddFolder(0, 0, "title2");
   SetTitle(0, node2, "new_title2");
 
-  ASSERT_TRUE(SyncBackendStoppedChecker(GetSyncService(0)).Wait());
+  ASSERT_TRUE(SyncEngineStoppedChecker(GetSyncService(0)).Wait());
 
   // Make server return SUCCESS so that sync can initialize.
   EXPECT_TRUE(GetFakeServer()->TriggerError(sync_pb::SyncEnums::SUCCESS));
 
-  ASSERT_TRUE(GetClient(0)->AwaitBackendInitialization());
+  ASSERT_TRUE(GetClient(0)->AwaitEngineInitialization());
 
   // Ensure cache_guid changed.
   GetSyncService(0)->QueryDetailedSyncStatus(&status);
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 49f5809..f32a7b05 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -519,7 +519,6 @@
     "//components/password_manager/sync/browser",
     "//components/pdf/browser",
     "//components/policy/core/browser",
-    "//components/power",
     "//components/pref_registry",
     "//components/proximity_auth",
     "//components/proxy_config",
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
index 4112d59d..b137776 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
@@ -503,7 +503,7 @@
   gfx::ScopedCanvas scoped_canvas(canvas);
   gfx::Rect tabstrip_bounds(GetBoundsForTabStrip(browser_view()->tabstrip()));
   tabstrip_bounds.set_x(GetMirroredXForRect(tabstrip_bounds));
-  canvas->ClipRect(tabstrip_bounds, SkRegion::kDifference_Op);
+  canvas->ClipRect(tabstrip_bounds, kDifference_SkClipOp);
   const gfx::Rect separator_rect(toolbar_bounds.x(), tabstrip_bounds.bottom(),
                                  toolbar_bounds.width(), 0);
   BrowserView::Paint1pxHorizontalLine(canvas, GetToolbarTopSeparatorColor(),
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mus.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mus.cc
index fdc0a87..58cf38f8 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mus.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mus.cc
@@ -492,7 +492,7 @@
   gfx::ScopedCanvas scoped_canvas(canvas);
   gfx::Rect tabstrip_bounds(GetBoundsForTabStrip(browser_view()->tabstrip()));
   tabstrip_bounds.set_x(GetMirroredXForRect(tabstrip_bounds));
-  canvas->ClipRect(tabstrip_bounds, SkRegion::kDifference_Op);
+  canvas->ClipRect(tabstrip_bounds, kDifference_SkClipOp);
   const gfx::Rect separator_rect(toolbar_bounds.x(), tabstrip_bounds.bottom(),
                                  toolbar_bounds.width(), 0);
   BrowserView::Paint1pxHorizontalLine(canvas, GetToolbarTopSeparatorColor(),
diff --git a/chrome/browser/ui/views/frame/glass_browser_frame_view.cc b/chrome/browser/ui/views/frame/glass_browser_frame_view.cc
index e8e14d3..0ceb12d4 100644
--- a/chrome/browser/ui/views/frame/glass_browser_frame_view.cc
+++ b/chrome/browser/ui/views/frame/glass_browser_frame_view.cc
@@ -654,7 +654,7 @@
   gfx::Rect tabstrip_bounds(GetBoundsForTabStrip(browser_view()->tabstrip()));
   tabstrip_bounds.set_x(GetMirroredXForRect(tabstrip_bounds));
   canvas->sk_canvas()->clipRect(gfx::RectToSkRect(tabstrip_bounds),
-                                SkRegion::kDifference_Op);
+                                kDifference_SkClipOp);
   separator_rect.set_y(tabstrip_bounds.bottom());
   BrowserView::Paint1pxHorizontalLine(canvas, GetToolbarTopSeparatorColor(),
                                       separator_rect, true);
diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc b/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc
index 2cf1328..411913b 100644
--- a/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc
+++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc
@@ -582,7 +582,7 @@
   gfx::Rect tabstrip_bounds(GetBoundsForTabStrip(browser_view()->tabstrip()));
   tabstrip_bounds.set_x(GetMirroredXForRect(tabstrip_bounds));
   canvas->sk_canvas()->clipRect(gfx::RectToSkRect(tabstrip_bounds),
-                                SkRegion::kDifference_Op);
+                                kDifference_SkClipOp);
   separator_rect.set_y(tabstrip_bounds.bottom());
   BrowserView::Paint1pxHorizontalLine(canvas, GetToolbarTopSeparatorColor(),
                                       separator_rect, true);
diff --git a/chrome/browser/ui/views/frame/windows_10_caption_button.cc b/chrome/browser/ui/views/frame/windows_10_caption_button.cc
index 5061017..e5580b3c 100644
--- a/chrome/browser/ui/views/frame/windows_10_caption_button.cc
+++ b/chrome/browser/ui/views/frame/windows_10_caption_button.cc
@@ -176,7 +176,7 @@
       DrawRect(canvas, symbol_rect, paint);
 
       // Top right ("behind") square.
-      canvas->ClipRect(symbol_rect, SkRegion::kDifference_Op);
+      canvas->ClipRect(symbol_rect, kDifference_SkClipOp);
       symbol_rect.Offset(separation, -separation);
       DrawRect(canvas, symbol_rect, paint);
       return;
diff --git a/chrome/browser/ui/views/tabs/tab.cc b/chrome/browser/ui/views/tabs/tab.cc
index 4e894358..1f39de8 100644
--- a/chrome/browser/ui/views/tabs/tab.cc
+++ b/chrome/browser/ui/views/tabs/tab.cc
@@ -1271,7 +1271,7 @@
       use_fill_and_stroke_images ? canvas : nullptr);
   if (use_fill_and_stroke_images) {
     canvas->DrawImageInt(it->fill_image, 0, 0);
-    canvas->sk_canvas()->clipPath(clip, SkRegion::kDifference_Op, true);
+    canvas->sk_canvas()->clipPath(clip, kDifference_SkClipOp, true);
   }
   canvas->DrawImageInt(it->stroke_image, 0, 0);
 }
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc
index 69f4cda0..0fb15a9 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -398,7 +398,7 @@
     canvas->ClipPath(stroke, true);
   Op(stroke, fill, kDifference_SkPathOp, &stroke);
   if (!pressed)
-    canvas->sk_canvas()->clipPath(fill, SkRegion::kDifference_Op, true);
+    canvas->sk_canvas()->clipPath(fill, kDifference_SkClipOp, true);
   // Now draw the stroke and shadow; the stroke will always be visible, while
   // the shadow will be affected by the clip we set above.
   SkPaint paint;
@@ -1468,7 +1468,7 @@
   if (active_tab) {
     canvas->sk_canvas()->clipRect(
         gfx::RectToSkRect(active_tab->GetMirroredBounds()),
-        SkRegion::kDifference_Op);
+        kDifference_SkClipOp);
   }
   BrowserView::Paint1pxHorizontalLine(canvas, GetToolbarTopSeparatorColor(),
                                       GetLocalBounds(), true);
diff --git a/chrome/browser/ui/webui/options/sync_setup_handler.cc b/chrome/browser/ui/webui/options/sync_setup_handler.cc
index dd7f0274..8d995e3 100644
--- a/chrome/browser/ui/webui/options/sync_setup_handler.cc
+++ b/chrome/browser/ui/webui/options/sync_setup_handler.cc
@@ -431,11 +431,11 @@
   base::DictionaryValue args;
 
   const int kTimeoutSec = 30;
-  DCHECK(!backend_start_timer_);
-  backend_start_timer_.reset(new base::OneShotTimer());
-  backend_start_timer_->Start(FROM_HERE,
-                              base::TimeDelta::FromSeconds(kTimeoutSec),
-                              this, &SyncSetupHandler::DisplayTimeout);
+  DCHECK(!engine_start_timer_);
+  engine_start_timer_.reset(new base::OneShotTimer());
+  engine_start_timer_->Start(FROM_HERE,
+                             base::TimeDelta::FromSeconds(kTimeoutSec), this,
+                             &SyncSetupHandler::DisplayTimeout);
 
   web_ui()->CallJavascriptFunctionUnsafe("SyncSetupOverlay.showSyncSetupPage",
                                          page, args);
@@ -445,7 +445,7 @@
 // http://crbug.com/128692
 void SyncSetupHandler::DisplayTimeout() {
   // Stop a timer to handle timeout in waiting for checking network connection.
-  backend_start_timer_.reset();
+  engine_start_timer_.reset();
 
   // Do not listen to sync startup events.
   sync_startup_tracker_.reset();
@@ -462,7 +462,7 @@
 
 void SyncSetupHandler::SyncStartupFailed() {
   // Stop a timer to handle timeout in waiting for checking network connection.
-  backend_start_timer_.reset();
+  engine_start_timer_.reset();
 
   // Just close the sync overlay (the idea is that the base settings page will
   // display the current error.)
@@ -471,10 +471,10 @@
 
 void SyncSetupHandler::SyncStartupCompleted() {
   ProfileSyncService* service = GetSyncService();
-  DCHECK(service->IsBackendInitialized());
+  DCHECK(service->IsEngineInitialized());
 
   // Stop a timer to handle timeout in waiting for checking network connection.
-  backend_start_timer_.reset();
+  engine_start_timer_.reset();
 
   DisplayConfigureSync(false);
 }
@@ -515,7 +515,7 @@
 
   // If the sync engine has shutdown for some reason, just close the sync
   // dialog.
-  if (!service || !service->IsBackendInitialized()) {
+  if (!service || !service->IsEngineInitialized()) {
     CloseUI();
     return;
   }
@@ -528,7 +528,7 @@
 
   // Note: Data encryption will not occur until configuration is complete
   // (when the PSS receives its CONFIGURE_DONE notification from the sync
-  // backend), so the user still has a chance to cancel out of the operation
+  // engine), so the user still has a chance to cancel out of the operation
   // if (for example) some kind of passphrase error is encountered.
   if (configuration.encrypt_all)
     service->EnableEncryptEverything();
@@ -681,7 +681,7 @@
 
 void SyncSetupHandler::CloseSyncSetup() {
   // Stop a timer to handle timeout in waiting for checking network connection.
-  backend_start_timer_.reset();
+  engine_start_timer_.reset();
 
   // Clear the sync startup tracker, since the setup wizard is being closed.
   sync_startup_tracker_.reset();
@@ -698,7 +698,7 @@
             ProfileSyncService::CANCEL_DURING_CONFIGURE);
 
         // If the user clicked "Cancel" while setting up sync, disable sync
-        // because we don't want the sync backend to remain in the
+        // because we don't want the sync engine to remain in the
         // first-setup-incomplete state.
         // Note: In order to disable sync across restarts on Chrome OS,
         // we must call RequestStop(CLEAR_DATA), which suppresses sync startup
@@ -821,10 +821,10 @@
       GetProfile())->IsAuthenticated());
   ProfileSyncService* service = GetSyncService();
   DCHECK(service);
-  if (!service->IsBackendInitialized()) {
+  if (!service->IsEngineInitialized()) {
     service->RequestStart();
 
-    // See if it's even possible to bring up the sync backend - if not
+    // See if it's even possible to bring up the sync engine - if not
     // (unrecoverable error?), don't bother displaying a spinner that will be
     // immediately closed because this leads to some ugly infinite UI loop (see
     // http://crbug.com/244769).
@@ -843,8 +843,8 @@
   // longer need a SyncStartupTracker.
   sync_startup_tracker_.reset();
   configuring_sync_ = true;
-  DCHECK(service->IsBackendInitialized())
-      << "Cannot configure sync until the sync backend is initialized";
+  DCHECK(service->IsEngineInitialized())
+      << "Cannot configure sync until the sync engine is initialized";
 
   // Setup args for the sync configure screen:
   //   syncAllDataTypes: true if the user wants to sync everything
diff --git a/chrome/browser/ui/webui/options/sync_setup_handler.h b/chrome/browser/ui/webui/options/sync_setup_handler.h
index 5d3ad93..993293f3 100644
--- a/chrome/browser/ui/webui/options/sync_setup_handler.h
+++ b/chrome/browser/ui/webui/options/sync_setup_handler.h
@@ -55,7 +55,7 @@
   void OpenSyncSetup(bool creating_supervised_user);
 
   // Shows advanced configuration dialog without going through sign in dialog.
-  // Kicks the sync backend if necessary with showing spinner dialog until it
+  // Kicks the sync engine if necessary with showing spinner dialog until it
   // gets ready.
   void OpenConfigureSync();
 
@@ -65,7 +65,7 @@
  protected:
   friend class SyncSetupHandlerTest;
   FRIEND_TEST_ALL_PREFIXES(SyncSetupHandlerTest,
-                           DisplayConfigureWithBackendDisabledAndCancel);
+                           DisplayConfigureWithEngineDisabledAndCancel);
   FRIEND_TEST_ALL_PREFIXES(SyncSetupHandlerTest, HandleSetupUIWhenSyncDisabled);
   FRIEND_TEST_ALL_PREFIXES(SyncSetupHandlerTest, SelectCustomEncryption);
   FRIEND_TEST_ALL_PREFIXES(SyncSetupHandlerTest, ShowSyncSetupWhenNotSignedIn);
@@ -135,7 +135,7 @@
   // is running in the background.
   void DisplaySpinner();
 
-  // Displays an error dialog which shows timeout of starting the sync backend.
+  // Displays an error dialog which shows timeout of starting the sync engine.
   void DisplayTimeout();
 
   // Closes the associated sync settings page.
@@ -154,7 +154,7 @@
   // requires a passphrase and one hasn't been provided or it was invalid.
   void DisplayConfigureSync(bool passphrase_failed);
 
-  // Helper object used to wait for the sync backend to startup.
+  // Helper object used to wait for the sync engine to startup.
   std::unique_ptr<SyncStartupTracker> sync_startup_tracker_;
 
   // Prevents Sync from running until configuration is complete.
@@ -165,9 +165,9 @@
   // histograms in the case that the user cancels out.
   bool configuring_sync_;
 
-  // The OneShotTimer object used to timeout of starting the sync backend
+  // The OneShotTimer object used to timeout of starting the sync engine
   // service.
-  std::unique_ptr<base::OneShotTimer> backend_start_timer_;
+  std::unique_ptr<base::OneShotTimer> engine_start_timer_;
 
   DISALLOW_COPY_AND_ASSIGN(SyncSetupHandler);
 };
diff --git a/chrome/browser/ui/webui/options/sync_setup_handler_unittest.cc b/chrome/browser/ui/webui/options/sync_setup_handler_unittest.cc
index 0d0d3c3..b0f22c9 100644
--- a/chrome/browser/ui/webui/options/sync_setup_handler_unittest.cc
+++ b/chrome/browser/ui/webui/options/sync_setup_handler_unittest.cc
@@ -234,10 +234,9 @@
 
   void SetupInitializedProfileSyncService() {
     // An initialized ProfileSyncService will have already completed sync setup
-    // and will have an initialized sync backend.
+    // and will have an initialized sync engine.
     ASSERT_TRUE(mock_signin_->IsInitialized());
-    EXPECT_CALL(*mock_pss_, IsBackendInitialized())
-        .WillRepeatedly(Return(true));
+    EXPECT_CALL(*mock_pss_, IsEngineInitialized()).WillRepeatedly(Return(true));
   }
 
   void ExpectConfig() {
@@ -358,15 +357,15 @@
 
 // Verifies that the handler correctly handles a cancellation when
 // it is displaying the spinner to the user.
-TEST_F(SyncSetupHandlerTest, DisplayConfigureWithBackendDisabledAndCancel) {
+TEST_F(SyncSetupHandlerTest, DisplayConfigureWithEngineDisabledAndCancel) {
   EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(true));
   EXPECT_CALL(*mock_pss_, IsFirstSetupComplete()).WillRepeatedly(Return(false));
   error_ = GoogleServiceAuthError::AuthErrorNone();
-  EXPECT_CALL(*mock_pss_, IsBackendInitialized()).WillRepeatedly(Return(false));
+  EXPECT_CALL(*mock_pss_, IsEngineInitialized()).WillRepeatedly(Return(false));
 
-  // We're simulating a user setting up sync, which would cause the backend to
+  // We're simulating a user setting up sync, which would cause the engine to
   // kick off initialization, but not download user data types. The sync
-  // backend will try to download control data types (e.g encryption info), but
+  // engine will try to download control data types (e.g encryption info), but
   // that won't finish for this test as we're simulating cancelling while the
   // spinner is showing.
   handler_->HandleShowSetupUI(NULL);
@@ -381,12 +380,12 @@
 // Verifies that the handler correctly transitions from showing the spinner
 // to showing a configuration page when sync setup completes successfully.
 TEST_F(SyncSetupHandlerTest,
-       DisplayConfigureWithBackendDisabledAndSyncStartupCompleted) {
+       DisplayConfigureWithEngineDisabledAndSyncStartupCompleted) {
   EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(true));
   EXPECT_CALL(*mock_pss_, IsFirstSetupComplete()).WillRepeatedly(Return(false));
   error_ = GoogleServiceAuthError::AuthErrorNone();
-  // Sync backend is stopped initially, and will start up.
-  EXPECT_CALL(*mock_pss_, IsBackendInitialized()).WillRepeatedly(Return(false));
+  // Sync engine is stopped initially, and will start up.
+  EXPECT_CALL(*mock_pss_, IsEngineInitialized()).WillRepeatedly(Return(false));
   SetDefaultExpectationsForConfigPage();
 
   handler_->OpenSyncSetup(false /* creating_supervised_user */);
@@ -403,7 +402,7 @@
   Mock::VerifyAndClearExpectations(mock_pss_);
   // Now, act as if the ProfileSyncService has started up.
   SetDefaultExpectationsForConfigPage();
-  EXPECT_CALL(*mock_pss_, IsBackendInitialized()).WillRepeatedly(Return(true));
+  EXPECT_CALL(*mock_pss_, IsEngineInitialized()).WillRepeatedly(Return(true));
   error_ = GoogleServiceAuthError::AuthErrorNone();
   EXPECT_CALL(*mock_pss_, GetAuthError()).WillRepeatedly(ReturnRef(error_));
   NotifySyncListeners();
@@ -423,17 +422,17 @@
   CheckBool(dictionary, "usePassphrase", false);
 }
 
-// Verifies the case where the user cancels after the sync backend has
+// Verifies the case where the user cancels after the sync engine has
 // initialized (meaning it already transitioned from the spinner to a proper
 // configuration page, tested by
-// DisplayConfigureWithBackendDisabledAndSigninSuccess), but before the user
-// before the user has continued on.
+// DisplayConfigureWithEngineDisabledAndSyncStartupCompleted), but before the
+// user has continued on.
 TEST_F(SyncSetupHandlerTest,
-       DisplayConfigureWithBackendDisabledAndCancelAfterSigninSuccess) {
+       DisplayConfigureWithEngineDisabledAndCancelAfterSigninSuccess) {
   EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(true));
   EXPECT_CALL(*mock_pss_, IsFirstSetupComplete()).WillRepeatedly(Return(false));
   error_ = GoogleServiceAuthError::AuthErrorNone();
-  EXPECT_CALL(*mock_pss_, IsBackendInitialized())
+  EXPECT_CALL(*mock_pss_, IsEngineInitialized())
       .WillOnce(Return(false))
       .WillRepeatedly(Return(true));
   SetDefaultExpectationsForConfigPage();
@@ -452,11 +451,11 @@
 }
 
 TEST_F(SyncSetupHandlerTest,
-       DisplayConfigureWithBackendDisabledAndSigninFailed) {
+       DisplayConfigureWithEngineDisabledAndSigninFailed) {
   EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(true));
   EXPECT_CALL(*mock_pss_, IsFirstSetupComplete()).WillRepeatedly(Return(false));
   error_ = GoogleServiceAuthError::AuthErrorNone();
-  EXPECT_CALL(*mock_pss_, IsBackendInitialized()).WillRepeatedly(Return(false));
+  EXPECT_CALL(*mock_pss_, IsEngineInitialized()).WillRepeatedly(Return(false));
 
   handler_->OpenSyncSetup(false /* creating_supervised_user */);
   const content::TestWebUI::CallData& data = *web_ui_.call_data()[0];
@@ -736,11 +735,11 @@
       .WillRepeatedly(Return(false));
   EXPECT_CALL(*mock_pss_, IsUsingSecondaryPassphrase())
       .WillRepeatedly(Return(false));
-  EXPECT_CALL(*mock_pss_, IsBackendInitialized()).WillRepeatedly(Return(false));
+  EXPECT_CALL(*mock_pss_, IsEngineInitialized()).WillRepeatedly(Return(false));
 
 #if defined(OS_CHROMEOS)
   // On ChromeOS, auth errors are ignored - instead we just try to start the
-  // sync backend (which will fail due to the auth error). This should only
+  // sync engine (which will fail due to the auth error). This should only
   // happen if the user manually navigates to chrome://settings/syncSetup -
   // clicking on the button in the UI will sign the user out rather than
   // displaying a spinner. Should be no visible UI on ChromeOS in this case.
@@ -749,7 +748,7 @@
 #else
 
   // On ChromeOS, this should display the spinner while we try to startup the
-  // sync backend, and on desktop this displays the login dialog.
+  // sync engine, and on desktop this displays the login dialog.
   handler_->OpenSyncSetup(false /* creating_supervised_user */);
 
   // Sync setup is closed when re-auth is in progress.
diff --git a/chrome/browser/ui/webui/settings/people_handler.cc b/chrome/browser/ui/webui/settings/people_handler.cc
index 317f7d43..642b7c8 100644
--- a/chrome/browser/ui/webui/settings/people_handler.cc
+++ b/chrome/browser/ui/webui/settings/people_handler.cc
@@ -315,11 +315,11 @@
   configuring_sync_ = true;
 
   const int kTimeoutSec = 30;
-  DCHECK(!backend_start_timer_);
-  backend_start_timer_.reset(new base::OneShotTimer());
-  backend_start_timer_->Start(FROM_HERE,
-                              base::TimeDelta::FromSeconds(kTimeoutSec), this,
-                              &PeopleHandler::DisplayTimeout);
+  DCHECK(!engine_start_timer_);
+  engine_start_timer_.reset(new base::OneShotTimer());
+  engine_start_timer_->Start(FROM_HERE,
+                             base::TimeDelta::FromSeconds(kTimeoutSec), this,
+                             &PeopleHandler::DisplayTimeout);
 
   CallJavascriptFunction("cr.webUIListenerCallback",
                          base::StringValue("page-status-changed"),
@@ -330,7 +330,7 @@
 // http://crbug.com/128692
 void PeopleHandler::DisplayTimeout() {
   // Stop a timer to handle timeout in waiting for checking network connection.
-  backend_start_timer_.reset();
+  engine_start_timer_.reset();
 
   // Do not listen to sync startup events.
   sync_startup_tracker_.reset();
@@ -347,7 +347,7 @@
 
 void PeopleHandler::SyncStartupFailed() {
   // Stop a timer to handle timeout in waiting for checking network connection.
-  backend_start_timer_.reset();
+  engine_start_timer_.reset();
 
   // Just close the sync overlay (the idea is that the base settings page will
   // display the current error.)
@@ -356,10 +356,10 @@
 
 void PeopleHandler::SyncStartupCompleted() {
   ProfileSyncService* service = GetSyncService();
-  DCHECK(service->IsBackendInitialized());
+  DCHECK(service->IsEngineInitialized());
 
   // Stop a timer to handle timeout in waiting for checking network connection.
-  backend_start_timer_.reset();
+  engine_start_timer_.reset();
 
   sync_startup_tracker_.reset();
 
@@ -389,7 +389,7 @@
 
   // If the sync engine has shutdown for some reason, just close the sync
   // dialog.
-  if (!service || !service->IsBackendInitialized()) {
+  if (!service || !service->IsEngineInitialized()) {
     CloseSyncSetup();
     ResolveJavascriptCallback(*callback_id, base::StringValue(kDonePageStatus));
     return;
@@ -420,7 +420,7 @@
 
   // If the sync engine has shutdown for some reason, just close the sync
   // dialog.
-  if (!service || !service->IsBackendInitialized()) {
+  if (!service || !service->IsEngineInitialized()) {
     CloseSyncSetup();
     ResolveJavascriptCallback(*callback_id, base::StringValue(kDonePageStatus));
     return;
@@ -434,7 +434,7 @@
 
   // Note: Data encryption will not occur until configuration is complete
   // (when the PSS receives its CONFIGURE_DONE notification from the sync
-  // backend), so the user still has a chance to cancel out of the operation
+  // engine), so the user still has a chance to cancel out of the operation
   // if (for example) some kind of passphrase error is encountered.
   if (configuration.encrypt_all)
     service->EnableEncryptEverything();
@@ -568,7 +568,7 @@
 
 void PeopleHandler::CloseSyncSetup() {
   // Stop a timer to handle timeout in waiting for checking network connection.
-  backend_start_timer_.reset();
+  engine_start_timer_.reset();
 
   // Clear the sync startup tracker, since the setup wizard is being closed.
   sync_startup_tracker_.reset();
@@ -590,7 +590,7 @@
             ProfileSyncService::CANCEL_DURING_CONFIGURE);
 
         // If the user clicked "Cancel" while setting up sync, disable sync
-        // because we don't want the sync backend to remain in the
+        // because we don't want the sync engine to remain in the
         // first-setup-incomplete state.
         // Note: In order to disable sync across restarts on Chrome OS,
         // we must call RequestStop(CLEAR_DATA), which suppresses sync startup
@@ -779,10 +779,10 @@
 
   ProfileSyncService* service = GetSyncService();
   DCHECK(service);
-  if (!service->IsBackendInitialized()) {
+  if (!service->IsEngineInitialized()) {
     service->RequestStart();
 
-    // See if it's even possible to bring up the sync backend - if not
+    // See if it's even possible to bring up the sync engine - if not
     // (unrecoverable error?), don't bother displaying a spinner that will be
     // immediately closed because this leads to some ugly infinite UI loop (see
     // http://crbug.com/244769).
@@ -797,8 +797,8 @@
   }
 
   configuring_sync_ = true;
-  DCHECK(service->IsBackendInitialized())
-      << "Cannot configure sync until the sync backend is initialized";
+  DCHECK(service->IsEngineInitialized())
+      << "Cannot configure sync until the sync engine is initialized";
 
   // Setup args for the sync configure screen:
   //   syncAllDataTypes: true if the user wants to sync everything
diff --git a/chrome/browser/ui/webui/settings/people_handler.h b/chrome/browser/ui/webui/settings/people_handler.h
index bf19c5c..da243e0 100644
--- a/chrome/browser/ui/webui/settings/people_handler.h
+++ b/chrome/browser/ui/webui/settings/people_handler.h
@@ -71,10 +71,10 @@
  private:
   friend class PeopleHandlerTest;
   FRIEND_TEST_ALL_PREFIXES(PeopleHandlerTest,
-                           DisplayConfigureWithBackendDisabledAndCancel);
+                           DisplayConfigureWithEngineDisabledAndCancel);
   FRIEND_TEST_ALL_PREFIXES(
       PeopleHandlerTest,
-      DisplayConfigureWithBackendDisabledAndSyncStartupCompleted);
+      DisplayConfigureWithEngineDisabledAndSyncStartupCompleted);
   FRIEND_TEST_ALL_PREFIXES(PeopleHandlerTest, HandleSetupUIWhenSyncDisabled);
   FRIEND_TEST_ALL_PREFIXES(PeopleHandlerTest, SelectCustomEncryption);
   FRIEND_TEST_ALL_PREFIXES(PeopleHandlerTest, ShowSyncSetupWhenNotSignedIn);
@@ -160,7 +160,7 @@
   // is running in the background.
   void DisplaySpinner();
 
-  // Displays an error dialog which shows timeout of starting the sync backend.
+  // Displays an error dialog which shows timeout of starting the sync engine.
   void DisplayTimeout();
 
   // Closes the associated sync settings page.
@@ -178,7 +178,7 @@
   // Weak pointer.
   Profile* profile_;
 
-  // Helper object used to wait for the sync backend to startup.
+  // Helper object used to wait for the sync engine to startup.
   std::unique_ptr<SyncStartupTracker> sync_startup_tracker_;
 
   // Prevents Sync from running until configuration is complete.
@@ -189,9 +189,9 @@
   // histograms in the case that the user cancels out.
   bool configuring_sync_;
 
-  // The OneShotTimer object used to timeout of starting the sync backend
+  // The OneShotTimer object used to timeout of starting the sync engine
   // service.
-  std::unique_ptr<base::OneShotTimer> backend_start_timer_;
+  std::unique_ptr<base::OneShotTimer> engine_start_timer_;
 
   // Used to listen for pref changes to allow or disallow signin.
   PrefChangeRegistrar profile_pref_registrar_;
diff --git a/chrome/browser/ui/webui/settings/people_handler_unittest.cc b/chrome/browser/ui/webui/settings/people_handler_unittest.cc
index c0087b2..273f55bf 100644
--- a/chrome/browser/ui/webui/settings/people_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/people_handler_unittest.cc
@@ -230,10 +230,9 @@
 
   void SetupInitializedProfileSyncService() {
     // An initialized ProfileSyncService will have already completed sync setup
-    // and will have an initialized sync backend.
+    // and will have an initialized sync engine.
     ASSERT_TRUE(mock_signin_->IsInitialized());
-    EXPECT_CALL(*mock_pss_, IsBackendInitialized())
-        .WillRepeatedly(Return(true));
+    EXPECT_CALL(*mock_pss_, IsEngineInitialized()).WillRepeatedly(Return(true));
   }
 
   void ExpectPageStatusResponse(const std::string& expected_status) {
@@ -366,15 +365,15 @@
 
 // Verifies that the handler correctly handles a cancellation when
 // it is displaying the spinner to the user.
-TEST_F(PeopleHandlerTest, DisplayConfigureWithBackendDisabledAndCancel) {
+TEST_F(PeopleHandlerTest, DisplayConfigureWithEngineDisabledAndCancel) {
   EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(true));
   EXPECT_CALL(*mock_pss_, IsFirstSetupComplete()).WillRepeatedly(Return(false));
   error_ = GoogleServiceAuthError::AuthErrorNone();
-  EXPECT_CALL(*mock_pss_, IsBackendInitialized()).WillRepeatedly(Return(false));
+  EXPECT_CALL(*mock_pss_, IsEngineInitialized()).WillRepeatedly(Return(false));
 
-  // We're simulating a user setting up sync, which would cause the backend to
+  // We're simulating a user setting up sync, which would cause the engine to
   // kick off initialization, but not download user data types. The sync
-  // backend will try to download control data types (e.g encryption info), but
+  // engine will try to download control data types (e.g encryption info), but
   // that won't finish for this test as we're simulating cancelling while the
   // spinner is showing.
   handler_->HandleShowSetupUI(NULL);
@@ -389,12 +388,12 @@
 // Verifies that the handler correctly transitions from showing the spinner
 // to showing a configuration page when sync setup completes successfully.
 TEST_F(PeopleHandlerTest,
-       DisplayConfigureWithBackendDisabledAndSyncStartupCompleted) {
+       DisplayConfigureWithEngineDisabledAndSyncStartupCompleted) {
   EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(true));
   EXPECT_CALL(*mock_pss_, IsFirstSetupComplete()).WillRepeatedly(Return(false));
   error_ = GoogleServiceAuthError::AuthErrorNone();
-  // Sync backend is stopped initially, and will start up.
-  EXPECT_CALL(*mock_pss_, IsBackendInitialized()).WillRepeatedly(Return(false));
+  // Sync engine is stopped initially, and will start up.
+  EXPECT_CALL(*mock_pss_, IsEngineInitialized()).WillRepeatedly(Return(false));
   SetDefaultExpectationsForConfigPage();
 
   handler_->OpenSyncSetup(false /* creating_supervised_user */);
@@ -405,7 +404,7 @@
   Mock::VerifyAndClearExpectations(mock_pss_);
   // Now, act as if the ProfileSyncService has started up.
   SetDefaultExpectationsForConfigPage();
-  EXPECT_CALL(*mock_pss_, IsBackendInitialized()).WillRepeatedly(Return(true));
+  EXPECT_CALL(*mock_pss_, IsEngineInitialized()).WillRepeatedly(Return(true));
   error_ = GoogleServiceAuthError::AuthErrorNone();
   EXPECT_CALL(*mock_pss_, GetAuthError()).WillRepeatedly(ReturnRef(error_));
   handler_->SyncStartupCompleted();
@@ -419,17 +418,17 @@
   CheckBool(dictionary, "passphraseRequired", false);
 }
 
-// Verifies the case where the user cancels after the sync backend has
+// Verifies the case where the user cancels after the sync engine has
 // initialized (meaning it already transitioned from the spinner to a proper
 // configuration page, tested by
-// DisplayConfigureWithBackendDisabledAndSigninSuccess), but before the user
-// before the user has continued on.
+// DisplayConfigureWithEngineDisabledAndSyncStartupCompleted), but before the
+// user has continued on.
 TEST_F(PeopleHandlerTest,
-       DisplayConfigureWithBackendDisabledAndCancelAfterSigninSuccess) {
+       DisplayConfigureWithEngineDisabledAndCancelAfterSigninSuccess) {
   EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(true));
   EXPECT_CALL(*mock_pss_, IsFirstSetupComplete()).WillRepeatedly(Return(false));
   error_ = GoogleServiceAuthError::AuthErrorNone();
-  EXPECT_CALL(*mock_pss_, IsBackendInitialized())
+  EXPECT_CALL(*mock_pss_, IsEngineInitialized())
       .WillOnce(Return(false))
       .WillRepeatedly(Return(true));
   SetDefaultExpectationsForConfigPage();
@@ -447,12 +446,11 @@
                 profile_)->current_login_ui());
 }
 
-TEST_F(PeopleHandlerTest,
-       DisplayConfigureWithBackendDisabledAndSigninFailed) {
+TEST_F(PeopleHandlerTest, DisplayConfigureWithEngineDisabledAndSigninFailed) {
   EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(true));
   EXPECT_CALL(*mock_pss_, IsFirstSetupComplete()).WillRepeatedly(Return(false));
   error_ = GoogleServiceAuthError::AuthErrorNone();
-  EXPECT_CALL(*mock_pss_, IsBackendInitialized()).WillRepeatedly(Return(false));
+  EXPECT_CALL(*mock_pss_, IsEngineInitialized()).WillRepeatedly(Return(false));
 
   handler_->OpenSyncSetup(false /* creating_supervised_user */);
   ExpectPageStatusChanged(PeopleHandler::kSpinnerPageStatus);
@@ -721,11 +719,11 @@
       .WillRepeatedly(Return(false));
   EXPECT_CALL(*mock_pss_, IsUsingSecondaryPassphrase())
       .WillRepeatedly(Return(false));
-  EXPECT_CALL(*mock_pss_, IsBackendInitialized()).WillRepeatedly(Return(false));
+  EXPECT_CALL(*mock_pss_, IsEngineInitialized()).WillRepeatedly(Return(false));
 
 #if defined(OS_CHROMEOS)
   // On ChromeOS, auth errors are ignored - instead we just try to start the
-  // sync backend (which will fail due to the auth error). This should only
+  // sync engine (which will fail due to the auth error). This should only
   // happen if the user manually navigates to chrome://settings/syncSetup -
   // clicking on the button in the UI will sign the user out rather than
   // displaying a spinner. Should be no visible UI on ChromeOS in this case.
@@ -734,7 +732,7 @@
 #else
 
   // On ChromeOS, this should display the spinner while we try to startup the
-  // sync backend, and on desktop this displays the login dialog.
+  // sync engine, and on desktop this displays the login dialog.
   handler_->OpenSyncSetup(false /* creating_supervised_user */);
 
   // Sync setup is closed when re-auth is in progress.
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index dfd072d..f3ef99f5 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -580,9 +580,6 @@
 // See http://crbug.com/31395.
 const char kKioskModePrinting[]             = "kiosk-printing";
 
-// Comma-separated list of directories with component extensions to load.
-const char kLoadComponentExtension[]        = "load-component-extension";
-
 // Loads an extension from the specified directory.
 const char kLoadExtension[]                 = "load-extension";
 
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index 41d5fa6..69bec54 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -171,7 +171,6 @@
 extern const char kKeepAliveForTest[];
 extern const char kKioskMode[];
 extern const char kKioskModePrinting[];
-extern const char kLoadComponentExtension[];
 extern const char kLoadExtension[];
 extern const char kLoadMediaRouterComponentExtension[];
 extern const char kMakeDefaultBrowser[];
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index af7bdf0e..36bf0ae 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -3459,6 +3459,7 @@
     "//components/sync:test_support_driver",
     "//components/sync:test_support_model",
     "//components/sync_sessions:test_support",
+    "//components/variations:test_support",
     "//content/public/app:both",
     "//content/test:test_support",
     "//crypto:platform",
@@ -3592,7 +3593,6 @@
       "../browser/net/firefox_proxy_settings_unittest.cc",
       "../browser/permissions/permission_request_manager_unittest.cc",
       "../browser/platform_util_unittest.cc",
-      "../browser/power/process_power_collector_unittest.cc",
       "../browser/power_usage_monitor/power_usage_monitor_unittest.cc",
       "../browser/process_singleton_posix_unittest.cc",
       "../browser/profile_resetter/profile_resetter_unittest.cc",
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/sync/SyncTestUtil.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/sync/SyncTestUtil.java
index 75fba1c..9660c2f7 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/sync/SyncTestUtil.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/sync/SyncTestUtil.java
@@ -11,15 +11,16 @@
 
 import junit.framework.Assert;
 
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
 import org.chromium.base.ThreadUtils;
 import org.chromium.chrome.browser.invalidation.InvalidationServiceFactory;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.sync.ProfileSyncService;
 import org.chromium.content.browser.test.util.Criteria;
 import org.chromium.content.browser.test.util.CriteriaHelper;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
 
 import java.util.ArrayList;
 import java.util.Iterator;
@@ -76,14 +77,14 @@
     }
 
     /**
-     * Waits for sync's backend to be initialized.
+     * Waits for sync's engine to be initialized.
      */
-    public static void waitForBackendInitialized() throws InterruptedException {
+    public static void waitForEngineInitialized() throws InterruptedException {
         CriteriaHelper.pollUiThread(
-                new Criteria("Timed out waiting for sync's backend to initialize.") {
+                new Criteria("Timed out waiting for sync's engine to initialize.") {
                     @Override
                     public boolean isSatisfied() {
-                        return ProfileSyncService.get().isBackendInitialized();
+                        return ProfileSyncService.get().isEngineInitialized();
                     }
                 },
                 TIMEOUT_MS, INTERVAL_MS);
diff --git a/chrome/test/media_router/BUILD.gn b/chrome/test/media_router/BUILD.gn
index 15df18e..e86de86 100644
--- a/chrome/test/media_router/BUILD.gn
+++ b/chrome/test/media_router/BUILD.gn
@@ -2,6 +2,24 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+group("media_router_perf_tests") {
+  testonly = true
+  deps = [
+    ":test_extension_resource_files",
+  ]
+  data_deps = [
+    "//chrome:chrome",
+  ]
+  data = [
+    "$root_out_dir/mr_extension/release/",
+    "internal/",
+    "telemetry/",
+    "../../../third_party/catapult/",
+    "../../../tools/perf/",
+    "../../../tools/variations/fieldtrial_util.py",
+  ]
+}
+
 group("media_router_tests") {
   testonly = true
   data_deps = [
diff --git a/components/BUILD.gn b/components/BUILD.gn
index d518aea..82a929a 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -193,7 +193,6 @@
       "//components/offline_pages/content/background_loader:unit_tests",
       "//components/packed_ct_ev_whitelist:unit_tests",
       "//components/password_manager/content/browser:unit_tests",
-      "//components/power:unit_tests",
       "//components/precache/content:unit_tests",
       "//components/safe_browsing_db:unit_tests",
       "//components/safe_json:unit_tests",
diff --git a/components/arc/intent_helper/page_transition_util.h b/components/arc/intent_helper/page_transition_util.h
index d2834af..20b66fa1 100644
--- a/components/arc/intent_helper/page_transition_util.h
+++ b/components/arc/intent_helper/page_transition_util.h
@@ -15,7 +15,6 @@
                             bool allow_client_redirect);
 
 // Removes |mask| bits from |page_transition|.
-// TODO(djacobo): Move this to ui/base/page_transition_types.cc.
 ui::PageTransition MaskOutPageTransition(ui::PageTransition page_transition,
                                          ui::PageTransition mask);
 
diff --git a/components/autofill/core/browser/autofill_experiments.cc b/components/autofill/core/browser/autofill_experiments.cc
index e066d854f..6ecc864b 100644
--- a/components/autofill/core/browser/autofill_experiments.cc
+++ b/components/autofill/core/browser/autofill_experiments.cc
@@ -108,9 +108,9 @@
   // Users who have enabled a passphrase have chosen to not make their sync
   // information accessible to Google. Since upload makes credit card data
   // available to other Google systems, disable it for passphrase users.
-  // We can't determine the passphrase state until the sync backend is
+  // We can't determine the passphrase state until the sync engine is
   // initialized so disable upload if sync is not yet available.
-  if (!sync_service->IsBackendInitialized() ||
+  if (!sync_service->IsEngineInitialized() ||
       sync_service->IsUsingSecondaryPassphrase()) {
     return false;
   }
diff --git a/components/autofill/core/browser/autofill_manager_unittest.cc b/components/autofill/core/browser/autofill_manager_unittest.cc
index 5000161..eacfe7b1 100644
--- a/components/autofill/core/browser/autofill_manager_unittest.cc
+++ b/components/autofill/core/browser/autofill_manager_unittest.cc
@@ -72,6 +72,13 @@
 
 const int kDefaultPageID = 137;
 
+const std::string kUTF8MidlineEllipsis =
+    "  "
+    "\xE2\x80\xA2\xE2\x80\x86"
+    "\xE2\x80\xA2\xE2\x80\x86"
+    "\xE2\x80\xA2\xE2\x80\x86"
+    "\xE2\x80\xA2\xE2\x80\x86";
+
 class MockAutofillClient : public TestAutofillClient {
  public:
   MockAutofillClient() {}
@@ -1439,14 +1446,11 @@
 
   // Test that we sent the right values to the external delegate.
   external_delegate_->CheckSuggestions(
-      kDefaultPageID, Suggestion("Visa\xC2\xA0\xE2\x8B\xAF"
-                                 "3456",
+      kDefaultPageID, Suggestion("Visa" + kUTF8MidlineEllipsis + "3456",
                                  "04/99", kVisaCard,
                                  autofill_manager_->GetPackedCreditCardID(4)),
-      Suggestion("MasterCard\xC2\xA0\xE2\x8B\xAF"
-                 "8765",
-                 "10/98", kMasterCard,
-                 autofill_manager_->GetPackedCreditCardID(5)));
+      Suggestion("MasterCard" + kUTF8MidlineEllipsis + "8765", "10/98",
+                 kMasterCard, autofill_manager_->GetPackedCreditCardID(5)));
 }
 
 // Test that we return all credit card profile suggestions when the triggering
@@ -1464,14 +1468,11 @@
 
   // Test that we sent the right values to the external delegate.
   external_delegate_->CheckSuggestions(
-      kDefaultPageID, Suggestion("Visa\xC2\xA0\xE2\x8B\xAF"
-                                 "3456",
+      kDefaultPageID, Suggestion("Visa" + kUTF8MidlineEllipsis + "3456",
                                  "04/99", kVisaCard,
                                  autofill_manager_->GetPackedCreditCardID(4)),
-      Suggestion("MasterCard\xC2\xA0\xE2\x8B\xAF"
-                 "8765",
-                 "10/98", kMasterCard,
-                 autofill_manager_->GetPackedCreditCardID(5)));
+      Suggestion("MasterCard" + kUTF8MidlineEllipsis + "8765", "10/98",
+                 kMasterCard, autofill_manager_->GetPackedCreditCardID(5)));
 }
 
 // Test that we return all credit card profile suggestions when the triggering
@@ -1489,14 +1490,11 @@
 
   // Test that we sent the right values to the external delegate.
   external_delegate_->CheckSuggestions(
-      kDefaultPageID, Suggestion("Visa\xC2\xA0\xE2\x8B\xAF"
-                                 "3456",
+      kDefaultPageID, Suggestion("Visa" + kUTF8MidlineEllipsis + "3456",
                                  "04/99", kVisaCard,
                                  autofill_manager_->GetPackedCreditCardID(4)),
-      Suggestion("MasterCard\xC2\xA0\xE2\x8B\xAF"
-                 "8765",
-                 "10/98", kMasterCard,
-                 autofill_manager_->GetPackedCreditCardID(5)));
+      Suggestion("MasterCard" + kUTF8MidlineEllipsis + "8765", "10/98",
+                 kMasterCard, autofill_manager_->GetPackedCreditCardID(5)));
 }
 
 // Test that we return all credit card profile suggestions when the triggering
@@ -1523,8 +1521,7 @@
 
   // Test that we sent the right value to the external delegate.
   external_delegate_->CheckSuggestions(
-      kDefaultPageID, Suggestion("MasterCard\xC2\xA0\xE2\x8B\xAF"
-                                 "3123",
+      kDefaultPageID, Suggestion("MasterCard" + kUTF8MidlineEllipsis + "3123",
                                  "08/17", kMasterCard,
                                  autofill_manager_->GetPackedCreditCardID(7)));
 }
@@ -1544,8 +1541,7 @@
 
   // Test that we sent the right values to the external delegate.
   external_delegate_->CheckSuggestions(
-      kDefaultPageID, Suggestion("Visa\xC2\xA0\xE2\x8B\xAF"
-                                 "3456",
+      kDefaultPageID, Suggestion("Visa" + kUTF8MidlineEllipsis + "3456",
                                  "04/99", kVisaCard,
                                  autofill_manager_->GetPackedCreditCardID(4)));
 }
@@ -1563,15 +1559,13 @@
   GetAutofillSuggestions(form, field);
 
 #if defined(OS_ANDROID)
-  static const char* kVisaSuggestion =
-      "Visa\xC2\xA0\xE2\x8B\xAF"
-      "3456";
-  static const char* kMcSuggestion =
-      "MasterCard\xC2\xA0\xE2\x8B\xAF"
-      "8765";
+  static const std::string kVisaSuggestion =
+      "Visa" + kUTF8MidlineEllipsis + "3456";
+  static const std::string kMcSuggestion =
+      "MasterCard" + kUTF8MidlineEllipsis + "8765";
 #else
-  static const char* kVisaSuggestion = "*3456";
-  static const char* kMcSuggestion = "*8765";
+  static const std::string kVisaSuggestion = "*3456";
+  static const std::string kMcSuggestion = "*8765";
 #endif
 
   // Test that we sent the right values to the external delegate.
@@ -1662,14 +1656,11 @@
 
   // Test that we sent the right values to the external delegate.
   external_delegate_->CheckSuggestions(
-      kDefaultPageID, Suggestion("Visa\xC2\xA0\xE2\x8B\xAF"
-                                 "3456",
+      kDefaultPageID, Suggestion("Visa" + kUTF8MidlineEllipsis + "3456",
                                  "04/99", kVisaCard,
                                  autofill_manager_->GetPackedCreditCardID(4)),
-      Suggestion("MasterCard\xC2\xA0\xE2\x8B\xAF"
-                 "8765",
-                 "10/98", kMasterCard,
-                 autofill_manager_->GetPackedCreditCardID(5)));
+      Suggestion("MasterCard" + kUTF8MidlineEllipsis + "8765", "10/98",
+                 kMasterCard, autofill_manager_->GetPackedCreditCardID(5)));
 }
 
 // Test that we will eventually return the credit card signin promo when there
@@ -1753,14 +1744,11 @@
 
   // Test that we sent the right values to the external delegate.
   external_delegate_->CheckSuggestions(
-      kDefaultPageID, Suggestion("Visa\xC2\xA0\xE2\x8B\xAF"
-                                 "3456",
+      kDefaultPageID, Suggestion("Visa" + kUTF8MidlineEllipsis + "3456",
                                  "04/99", kVisaCard,
                                  autofill_manager_->GetPackedCreditCardID(4)),
-      Suggestion("MasterCard\xC2\xA0\xE2\x8B\xAF"
-                 "8765",
-                 "10/98", kMasterCard,
-                 autofill_manager_->GetPackedCreditCardID(5)));
+      Suggestion("MasterCard" + kUTF8MidlineEllipsis + "8765", "10/98",
+                 kMasterCard, autofill_manager_->GetPackedCreditCardID(5)));
 }
 
 // Test that we return credit card suggestions for secure pages that have a
@@ -1780,14 +1768,11 @@
 
   // Test that we sent the right values to the external delegate.
   external_delegate_->CheckSuggestions(
-      kDefaultPageID, Suggestion("Visa\xC2\xA0\xE2\x8B\xAF"
-                                 "3456",
+      kDefaultPageID, Suggestion("Visa" + kUTF8MidlineEllipsis + "3456",
                                  "04/99", kVisaCard,
                                  autofill_manager_->GetPackedCreditCardID(4)),
-      Suggestion("MasterCard\xC2\xA0\xE2\x8B\xAF"
-                 "8765",
-                 "10/98", kMasterCard,
-                 autofill_manager_->GetPackedCreditCardID(5)));
+      Suggestion("MasterCard" + kUTF8MidlineEllipsis + "8765", "10/98",
+                 kMasterCard, autofill_manager_->GetPackedCreditCardID(5)));
 }
 
 // Test that we return all credit card suggestions in the case that two cards
@@ -1814,18 +1799,13 @@
 
   // Test that we sent the right values to the external delegate.
   external_delegate_->CheckSuggestions(
-      kDefaultPageID, Suggestion("Visa\xC2\xA0\xE2\x8B\xAF"
-                                 "3456",
+      kDefaultPageID, Suggestion("Visa" + kUTF8MidlineEllipsis + "3456",
                                  "04/99", kVisaCard,
                                  autofill_manager_->GetPackedCreditCardID(4)),
-      Suggestion("MasterCard\xC2\xA0\xE2\x8B\xAF"
-                 "8765",
-                 "10/98", kMasterCard,
-                 autofill_manager_->GetPackedCreditCardID(5)),
-      Suggestion("MasterCard\xC2\xA0\xE2\x8B\xAF"
-                 "3456",
-                 "05/99", kMasterCard,
-                 autofill_manager_->GetPackedCreditCardID(7)));
+      Suggestion("MasterCard" + kUTF8MidlineEllipsis + "8765", "10/98",
+                 kMasterCard, autofill_manager_->GetPackedCreditCardID(5)),
+      Suggestion("MasterCard" + kUTF8MidlineEllipsis + "3456", "05/99",
+                 kMasterCard, autofill_manager_->GetPackedCreditCardID(7)));
 }
 
 // Test that we return profile and credit card suggestions for combined forms.
@@ -1851,14 +1831,11 @@
 
   // Test that we sent the credit card suggestions to the external delegate.
   external_delegate_->CheckSuggestions(
-      kPageID2, Suggestion("Visa\xC2\xA0\xE2\x8B\xAF"
-                           "3456",
+      kPageID2, Suggestion("Visa" + kUTF8MidlineEllipsis + "3456",
                            "04/99", kVisaCard,
                            autofill_manager_->GetPackedCreditCardID(4)),
-      Suggestion("MasterCard\xC2\xA0\xE2\x8B\xAF"
-                 "8765",
-                 "10/98", kMasterCard,
-                 autofill_manager_->GetPackedCreditCardID(5)));
+      Suggestion("MasterCard" + kUTF8MidlineEllipsis + "8765", "10/98",
+                 kMasterCard, autofill_manager_->GetPackedCreditCardID(5)));
 }
 
 // Test that for non-https forms with both address and credit card fields, we
@@ -4401,8 +4378,7 @@
   GetAutofillSuggestions(form, number_field);
 
   external_delegate_->CheckSuggestions(
-      kDefaultPageID, Suggestion("Visa\xC2\xA0\xE2\x8B\xAF"
-                                 "3456",
+      kDefaultPageID, Suggestion("Visa" + kUTF8MidlineEllipsis + "3456",
                                  "04/99", kVisaCard,
                                  autofill_manager_->GetPackedCreditCardID(4)));
 }
@@ -5186,11 +5162,10 @@
   GetAutofillSuggestions(form, field);
 
 #if defined(OS_ANDROID)
-  static const char* kVisaSuggestion =
-      "Visa\xC2\xA0\xE2\x8B\xAF"
-      "3456";
+  static const std::string kVisaSuggestion =
+      "Visa" + kUTF8MidlineEllipsis + "3456";
 #else
-  static const char* kVisaSuggestion = "*3456";
+  static const std::string kVisaSuggestion = "*3456";
 #endif
 
   external_delegate_->CheckSuggestions(
diff --git a/components/autofill/core/browser/credit_card.cc b/components/autofill/core/browser/credit_card.cc
index 376ce1b..2bc1ca4 100644
--- a/components/autofill/core/browser/credit_card.cc
+++ b/components/autofill/core/browser/credit_card.cc
@@ -39,12 +39,15 @@
 
 namespace autofill {
 
-const base::char16 kMidlineEllipsis[] = { 0x22ef, 0 };
+const base::char16 kMidlineEllipsis[] = { 0x0020, 0x0020,
+                                          0x2022, 0x2006,
+                                          0x2022, 0x2006,
+                                          0x2022, 0x2006,
+                                          0x2022, 0x2006, 0 };
 
 namespace {
 
 const base::char16 kCreditCardObfuscationSymbol = '*';
-const base::char16 kNonBreakingSpace[] = { 0x00a0, 0 };
 
 bool ConvertYear(const base::string16& year, int* num) {
   // If the |year| is empty, clear the stored value.
@@ -499,8 +502,7 @@
     return type;
 
   // TODO(estade): i18n?
-  return type + base::string16(kNonBreakingSpace) +
-         base::string16(kMidlineEllipsis) + digits;
+  return type + base::string16(kMidlineEllipsis) + digits;
 }
 
 base::string16 CreditCard::AbbreviatedExpirationDateForDisplay() const {
diff --git a/components/autofill/core/browser/credit_card_unittest.cc b/components/autofill/core/browser/credit_card_unittest.cc
index d0a535e..a0cb0c63 100644
--- a/components/autofill/core/browser/credit_card_unittest.cc
+++ b/components/autofill/core/browser/credit_card_unittest.cc
@@ -59,6 +59,13 @@
   "3056 9309 0259 04aa", /* non-digit characters */
 };
 
+const std::string kUTF8MidlineEllipsis =
+    "  "
+    "\xE2\x80\xA2\xE2\x80\x86"
+    "\xE2\x80\xA2\xE2\x80\x86"
+    "\xE2\x80\xA2\xE2\x80\x86"
+    "\xE2\x80\xA2\xE2\x80\x86";
+
 }  // namespace
 
 // Tests credit card summary string generation.  This test simulates a variety
@@ -93,14 +100,10 @@
   test::SetCreditCardInfo(
       &credit_card2, "John Dillinger", "5105 1051 0510 5100", "", "2010");
   base::string16 summary2 = credit_card2.Label();
-  EXPECT_EQ(UTF8ToUTF16(
-                "MasterCard\xC2\xA0\xE2\x8B\xAF"
-                "5100"),
+  EXPECT_EQ(UTF8ToUTF16("MasterCard" + kUTF8MidlineEllipsis + "5100"),
             summary2);
   base::string16 obfuscated2 = credit_card2.TypeAndLastFourDigits();
-  EXPECT_EQ(UTF8ToUTF16(
-                "MasterCard\xC2\xA0\xE2\x8B\xAF"
-                "5100"),
+  EXPECT_EQ(UTF8ToUTF16("MasterCard" + kUTF8MidlineEllipsis + "5100"),
             obfuscated2);
 
   // Case 3: No year.
@@ -108,14 +111,10 @@
   test::SetCreditCardInfo(
       &credit_card3, "John Dillinger", "5105 1051 0510 5100", "01", "");
   base::string16 summary3 = credit_card3.Label();
-  EXPECT_EQ(UTF8ToUTF16(
-                "MasterCard\xC2\xA0\xE2\x8B\xAF"
-                "5100"),
+  EXPECT_EQ(UTF8ToUTF16("MasterCard" + kUTF8MidlineEllipsis + "5100"),
             summary3);
   base::string16 obfuscated3 = credit_card3.TypeAndLastFourDigits();
-  EXPECT_EQ(UTF8ToUTF16(
-                "MasterCard\xC2\xA0\xE2\x8B\xAF"
-                "5100"),
+  EXPECT_EQ(UTF8ToUTF16("MasterCard" + kUTF8MidlineEllipsis + "5100"),
             obfuscated3);
 
   // Case 4: Have everything.
@@ -123,14 +122,10 @@
   test::SetCreditCardInfo(
       &credit_card4, "John Dillinger", "5105 1051 0510 5100", "01", "2010");
   base::string16 summary4 = credit_card4.Label();
-  EXPECT_EQ(UTF8ToUTF16(
-                "MasterCard\xC2\xA0\xE2\x8B\xAF"
-                "5100, 01/2010"),
+  EXPECT_EQ(UTF8ToUTF16("MasterCard" + kUTF8MidlineEllipsis + "5100, 01/2010"),
             summary4);
   base::string16 obfuscated4 = credit_card4.TypeAndLastFourDigits();
-  EXPECT_EQ(UTF8ToUTF16(
-                "MasterCard\xC2\xA0\xE2\x8B\xAF"
-                "5100"),
+  EXPECT_EQ(UTF8ToUTF16("MasterCard" + kUTF8MidlineEllipsis + "5100"),
             obfuscated4);
 
   // Case 5: Very long credit card
@@ -140,14 +135,10 @@
       "John Dillinger",
       "0123456789 0123456789 0123456789 5105 1051 0510 5100", "01", "2010");
   base::string16 summary5 = credit_card5.Label();
-  EXPECT_EQ(UTF8ToUTF16(
-                "Card\xC2\xA0\xE2\x8B\xAF"
-                "5100, 01/2010"),
+  EXPECT_EQ(UTF8ToUTF16("Card" + kUTF8MidlineEllipsis + "5100, 01/2010"),
             summary5);
   base::string16 obfuscated5 = credit_card5.TypeAndLastFourDigits();
-  EXPECT_EQ(UTF8ToUTF16(
-                "Card\xC2\xA0\xE2\x8B\xAF"
-                "5100"),
+  EXPECT_EQ(UTF8ToUTF16("Card" + kUTF8MidlineEllipsis + "5100"),
             obfuscated5);
 }
 
diff --git a/components/autofill/core/browser/personal_data_manager_unittest.cc b/components/autofill/core/browser/personal_data_manager_unittest.cc
index 49bee42b..9678b18d 100644
--- a/components/autofill/core/browser/personal_data_manager_unittest.cc
+++ b/components/autofill/core/browser/personal_data_manager_unittest.cc
@@ -61,6 +61,13 @@
 
 enum UserMode { USER_MODE_NORMAL, USER_MODE_INCOGNITO };
 
+const std::string kUTF8MidlineEllipsis =
+    "  "
+    "\xE2\x80\xA2\xE2\x80\x86"
+    "\xE2\x80\xA2\xE2\x80\x86"
+    "\xE2\x80\xA2\xE2\x80\x86"
+    "\xE2\x80\xA2\xE2\x80\x86";
+
 ACTION(QuitMainMessageLoop) {
   base::MessageLoop::current()->QuitWhenIdle();
 }
@@ -3697,8 +3704,7 @@
           AutofillType(CREDIT_CARD_NUMBER),
           /* field_contents= */ base::string16());
   ASSERT_EQ(1U, suggestions.size());
-  EXPECT_EQ(UTF8ToUTF16("Amex\xC2\xA0\xE2\x8B\xAF"
-                        "8555"),
+  EXPECT_EQ(UTF8ToUTF16("Amex" + kUTF8MidlineEllipsis + "8555"),
             suggestions[0].value);
   EXPECT_EQ(ASCIIToUTF16("04/99"), suggestions[0].label);
 }
@@ -3761,17 +3767,13 @@
   suggestions = personal_data_->GetCreditCardSuggestions(
       AutofillType(CREDIT_CARD_NUMBER), /* field_contents= */ base::string16());
   ASSERT_EQ(4U, suggestions.size());
-  EXPECT_EQ(UTF8ToUTF16("Visa\xC2\xA0\xE2\x8B\xAF"
-                        "9012"),
+  EXPECT_EQ(UTF8ToUTF16("Visa" + kUTF8MidlineEllipsis + "9012"),
             suggestions[0].value);
-  EXPECT_EQ(UTF8ToUTF16("Amex\xC2\xA0\xE2\x8B\xAF"
-                        "8555"),
+  EXPECT_EQ(UTF8ToUTF16("Amex" + kUTF8MidlineEllipsis + "8555"),
             suggestions[1].value);
-  EXPECT_EQ(UTF8ToUTF16("MasterCard\xC2\xA0\xE2\x8B\xAF"
-                        "2109"),
+  EXPECT_EQ(UTF8ToUTF16("MasterCard" + kUTF8MidlineEllipsis + "2109"),
             suggestions[2].value);
-  EXPECT_EQ(UTF8ToUTF16("Visa\xC2\xA0\xE2\x8B\xAF"
-                        "2109"),
+  EXPECT_EQ(UTF8ToUTF16("Visa" + kUTF8MidlineEllipsis + "2109"),
             suggestions[3].value);
 }
 
diff --git a/components/browser_sync/profile_sync_service.cc b/components/browser_sync/profile_sync_service.cc
index aea8af1..67e30c88 100644
--- a/components/browser_sync/profile_sync_service.cc
+++ b/components/browser_sync/profile_sync_service.cc
@@ -203,7 +203,7 @@
       channel_(init_params.channel),
       blocking_pool_(init_params.blocking_pool),
       is_first_time_sync_configure_(false),
-      backend_initialized_(false),
+      engine_initialized_(false),
       sync_disabled_by_admin_(false),
       is_auth_in_progress_(false),
       signin_(std::move(init_params.signin_wrapper)),
@@ -247,7 +247,7 @@
     gaia_cookie_manager_service_->RemoveObserver(this);
   sync_prefs_.RemoveSyncPrefObserver(this);
   // Shutdown() should have been called before destruction.
-  CHECK(!backend_initialized_);
+  CHECK(!engine_initialized_);
 }
 
 bool ProfileSyncService::CanSyncStart() const {
@@ -263,8 +263,8 @@
   // against the controller impl changing to post tasks.
   startup_controller_ = base::MakeUnique<syncer::StartupController>(
       &sync_prefs_,
-      base::Bind(&ProfileSyncService::CanBackendStart, base::Unretained(this)),
-      base::Bind(&ProfileSyncService::StartUpSlowBackendComponents,
+      base::Bind(&ProfileSyncService::CanEngineStart, base::Unretained(this)),
+      base::Bind(&ProfileSyncService::StartUpSlowEngineComponents,
                  weak_factory_.GetWeakPtr()));
   std::unique_ptr<sync_sessions::LocalSessionEventRouter> router(
       sync_client_->GetSyncSessionsClient()->GetLocalSessionEventRouter());
@@ -391,8 +391,8 @@
     return;
   }
 
-  if (backend_)
-    backend_->StartSyncingWithServer();
+  if (engine_)
+    engine_->StartSyncingWithServer();
 }
 
 void ProfileSyncService::RegisterAuthNotifications() {
@@ -499,8 +499,8 @@
   return !IsFirstSetupComplete();
 }
 
-void ProfileSyncService::InitializeBackend(bool delete_stale_data) {
-  if (!backend_) {
+void ProfileSyncService::InitializeEngine(bool delete_stale_data) {
+  if (!engine_) {
     NOTREACHED();
     return;
   }
@@ -547,7 +547,7 @@
                  base::Unretained(network_resources_.get()),
                  url_request_context_, network_time_update_callback_);
 
-  backend_->Initialize(
+  engine_->Initialize(
       this, sync_thread_->task_runner(), GetJsEventHandler(), sync_service_url_,
       local_device_->GetSyncUserAgent(), credentials, delete_stale_data,
       enable_local_sync_backend, local_sync_backend_folder,
@@ -618,7 +618,7 @@
     NotifyObservers();
   }
 
-  if (backend_.get()) {
+  if (engine_) {
     DVLOG(1) << "A data type requested sync startup, but it looks like "
                 "something else beat it to the punch.";
     return;
@@ -627,17 +627,17 @@
   startup_controller_->OnDataTypeRequestsSyncStartup(type);
 }
 
-void ProfileSyncService::StartUpSlowBackendComponents() {
+void ProfileSyncService::StartUpSlowEngineComponents() {
   invalidation::InvalidationService* invalidator =
       sync_client_->GetInvalidationService();
 
-  backend_.reset(sync_client_->GetSyncApiComponentFactory()->CreateSyncEngine(
+  engine_.reset(sync_client_->GetSyncApiComponentFactory()->CreateSyncEngine(
       debug_identifier_, invalidator, sync_prefs_.AsWeakPtr(),
       directory_path_));
 
-  // Initialize the backend. Every time we start up a new SyncEngine, we'll want
+  // Initialize the engine. Every time we start up a new SyncEngine, we'll want
   // to start from a fresh SyncDB, so delete any old one that might be there.
-  InitializeBackend(ShouldDeleteSyncFolder());
+  InitializeEngine(ShouldDeleteSyncFolder());
 
   UpdateFirstSyncTimePref();
 
@@ -659,8 +659,8 @@
     sync_prefs_.SetSyncAuthError(false);
   }
 
-  if (HasSyncingBackend())
-    backend_->UpdateCredentials(GetCredentials());
+  if (HasSyncingEngine())
+    engine_->UpdateCredentials(GetCredentials());
   else
     startup_controller_->TryStart();
 }
@@ -732,12 +732,11 @@
 
 void ProfileSyncService::OnRefreshTokensLoaded() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  // This notification gets fired when OAuth2TokenService loads the tokens
-  // from storage.
-  // Initialize the backend if sync is enabled. If the sync token was
-  // not loaded, GetCredentials() will generate invalid credentials to
-  // cause the backend to generate an auth error (crbug.com/121755).
-  if (HasSyncingBackend()) {
+  // This notification gets fired when OAuth2TokenService loads the tokens from
+  // storage. Initialize the engine if sync is enabled. If the sync token was
+  // not loaded, GetCredentials() will generate invalid credentials to cause the
+  // engine to generate an auth error (crbug.com/121755).
+  if (HasSyncingEngine()) {
     RequestAccessToken();
   } else {
     startup_controller_->TryStart();
@@ -760,9 +759,9 @@
 }
 
 void ProfileSyncService::ShutdownImpl(syncer::ShutdownReason reason) {
-  if (!backend_) {
+  if (!engine_) {
     if (reason == syncer::ShutdownReason::DISABLE_SYNC && sync_thread_) {
-      // If the backend is already shut down when a DISABLE_SYNC happens,
+      // If the engine is already shut down when a DISABLE_SYNC happens,
       // the data directory needs to be cleaned up here.
       sync_thread_->task_runner()->PostTask(
           FROM_HERE, base::Bind(&DeleteSyncDataFolder, directory_path_));
@@ -775,16 +774,16 @@
     RemoveClientFromServer();
   }
 
-  // First, we spin down the backend to stop change processing as soon as
+  // First, we spin down the engine to stop change processing as soon as
   // possible.
   base::Time shutdown_start_time = base::Time::Now();
-  backend_->StopSyncingForShutdown();
+  engine_->StopSyncingForShutdown();
 
-  // Stop all data type controllers, if needed.  Note that until Stop
-  // completes, it is possible in theory to have a ChangeProcessor apply a
-  // change from a native model.  In that case, it will get applied to the sync
-  // database (which doesn't get destroyed until we destroy the backend below)
-  // as an unsynced change.  That will be persisted, and committed on restart.
+  // Stop all data type controllers, if needed. Note that until Stop completes,
+  // it is possible in theory to have a ChangeProcessor apply a change from a
+  // native model. In that case, it will get applied to the sync database (which
+  // doesn't get destroyed until we destroy the engine below) as an unsynced
+  // change. That will be persisted, and committed on restart.
   if (data_type_manager_) {
     if (data_type_manager_->state() != DataTypeManager::STOPPED) {
       // When aborting as part of shutdown, we should expect an aborted sync
@@ -795,13 +794,13 @@
     data_type_manager_.reset();
   }
 
-  // Shutdown the migrator before the backend to ensure it doesn't pull a null
+  // Shutdown the migrator before the engine to ensure it doesn't pull a null
   // snapshot.
   migrator_.reset();
   sync_js_controller_.AttachJsBackend(WeakHandle<syncer::JsBackend>());
 
-  backend_->Shutdown(reason);
-  backend_.reset();
+  engine_->Shutdown(reason);
+  engine_.reset();
 
   base::TimeDelta shutdown_time = base::Time::Now() - shutdown_start_time;
   UMA_HISTOGRAM_TIMES("Sync.Shutdown.BackendDestroyedTime", shutdown_time);
@@ -819,7 +818,7 @@
   // Clear various flags.
   expect_sync_configuration_aborted_ = false;
   is_auth_in_progress_ = false;
-  backend_initialized_ = false;
+  engine_initialized_ = false;
   cached_passphrase_.clear();
   encryption_pending_ = false;
   encrypt_everything_ = false;
@@ -843,8 +842,8 @@
     case KEEP_DATA:
       // TODO(maxbogue): Investigate whether this logic can/should be moved
       // into ShutdownImpl or the sync engine itself.
-      if (HasSyncingBackend()) {
-        backend_->UnregisterInvalidationIds();
+      if (HasSyncingEngine()) {
+        engine_->UnregisterInvalidationIds();
       }
       ShutdownImpl(syncer::STOP_SYNC);
       break;
@@ -866,7 +865,7 @@
 void ProfileSyncService::SetFirstSetupComplete() {
   DCHECK(thread_checker_.CalledOnValidThread());
   sync_prefs_.SetFirstSetupComplete();
-  if (IsBackendInitialized()) {
+  if (IsEngineInitialized()) {
     ReconfigureDatatypeManager();
   }
 }
@@ -941,12 +940,12 @@
 
 void ProfileSyncService::ReenableDatatype(syncer::ModelType type) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  if (!backend_initialized_ || !data_type_manager_)
+  if (!engine_initialized_ || !data_type_manager_)
     return;
   data_type_manager_->ReenableType(type);
 }
 
-void ProfileSyncService::UpdateBackendInitUMA(bool success) {
+void ProfileSyncService::UpdateEngineInitUMA(bool success) {
   is_first_time_sync_configure_ = !IsFirstSetupComplete();
 
   if (is_first_time_sync_configure_) {
@@ -955,9 +954,9 @@
     UMA_HISTOGRAM_BOOLEAN("Sync.BackendInitializeRestoreSuccess", success);
   }
 
-  base::Time on_backend_initialized_time = base::Time::Now();
+  base::Time on_engine_initialized_time = base::Time::Now();
   base::TimeDelta delta =
-      on_backend_initialized_time - startup_controller_->start_engine_time();
+      on_engine_initialized_time - startup_controller_->start_engine_time();
   if (is_first_time_sync_configure_) {
     UMA_HISTOGRAM_LONG_TIMES("Sync.BackendInitializeFirstTime", delta);
   } else {
@@ -965,21 +964,21 @@
   }
 }
 
-void ProfileSyncService::PostBackendInitialization() {
+void ProfileSyncService::PostEngineInitialization() {
   if (protocol_event_observers_.might_have_observers()) {
-    backend_->RequestBufferedProtocolEventsAndEnableForwarding();
+    engine_->RequestBufferedProtocolEventsAndEnableForwarding();
   }
 
   if (type_debug_info_observers_.might_have_observers()) {
-    backend_->EnableDirectoryTypeDebugInfoForwarding();
+    engine_->EnableDirectoryTypeDebugInfoForwarding();
   }
 
   // If we have a cached passphrase use it to decrypt/encrypt data now that the
-  // backend is initialized. We want to call this before notifying observers in
+  // engine is initialized. We want to call this before notifying observers in
   // case this operation affects the "passphrase required" status.
   ConsumeCachedPassphraseIfPossible();
 
-  // The very first time the backend initializes is effectively the first time
+  // The very first time the engine initializes is effectively the first time
   // we can say we successfully "synced".  LastSyncedTime will only be null in
   // this case, because the pref wasn't restored on StartUp.
   if (sync_prefs_.GetLastSyncedTime().is_null()) {
@@ -1007,14 +1006,14 @@
   NotifyObservers();
 }
 
-void ProfileSyncService::OnBackendInitialized(
+void ProfileSyncService::OnEngineInitialized(
     const syncer::WeakHandle<syncer::JsBackend>& js_backend,
     const syncer::WeakHandle<syncer::DataTypeDebugInfoListener>&
         debug_info_listener,
     const std::string& cache_guid,
     bool success) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  UpdateBackendInitUMA(success);
+  UpdateEngineInitUMA(success);
 
   if (!success) {
     // Something went unexpectedly wrong.  Play it safe: stop syncing at once
@@ -1030,11 +1029,11 @@
     // we get here, we will have already tried and failed to delete the
     // directory.  It would be no big deal if we tried to delete it again.
     OnInternalUnrecoverableError(FROM_HERE, "BackendInitialize failure", false,
-                                 ERROR_REASON_BACKEND_INIT_FAILURE);
+                                 ERROR_REASON_ENGINE_INIT_FAILURE);
     return;
   }
 
-  backend_initialized_ = true;
+  engine_initialized_ = true;
 
   sync_js_controller_.AttachJsBackend(js_backend);
   debug_info_listener_ = debug_info_listener;
@@ -1048,7 +1047,7 @@
   local_device_->Initialize(cache_guid, signin_scoped_device_id,
                             blocking_pool_);
 
-  PostBackendInitialization();
+  PostEngineInitialization();
 }
 
 void ProfileSyncService::OnSyncCycleCompleted() {
@@ -1139,7 +1138,7 @@
       // further needs to be done at this point.
     } else if (request_access_token_backoff_.failure_count() == 0) {
       // First time request without delay. Currently invalid token is used
-      // to initialize sync backend and we'll always end up here. We don't
+      // to initialize sync engine and we'll always end up here. We don't
       // want to delay initialization.
       request_access_token_backoff_.InformOfRequest(false);
       RequestAccessToken();
@@ -1173,13 +1172,13 @@
     syncer::PassphraseRequiredReason reason,
     const sync_pb::EncryptedData& pending_keys) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(backend_.get());
-  DCHECK(backend_->IsNigoriEnabled());
+  DCHECK(engine_);
+  DCHECK(engine_->IsNigoriEnabled());
 
   // TODO(lipalani) : add this check to other locations as well.
   if (HasUnrecoverableError()) {
     // When unrecoverable error is detected we post a task to shutdown the
-    // backend. The task might not have executed yet.
+    // engine. The task might not have executed yet.
     return;
   }
 
@@ -1251,8 +1250,8 @@
 
 void ProfileSyncService::OnMigrationNeededForTypes(syncer::ModelTypeSet types) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(backend_initialized_);
-  DCHECK(data_type_manager_.get());
+  DCHECK(engine_initialized_);
+  DCHECK(data_type_manager_);
 
   // Migrator must be valid, because we don't sync until it is created and this
   // callback originates from a sync cycle.
@@ -1339,7 +1338,8 @@
 }
 
 void ProfileSyncService::ClearAndRestartSyncForPassphraseEncryption() {
-  backend_->ClearServerData(
+  DCHECK(thread_checker_.CalledOnValidThread());
+  engine_->ClearServerData(
       base::Bind(&ProfileSyncService::OnClearServerDataDone,
                  sync_enabled_weak_factory_.GetWeakPtr()));
 }
@@ -1365,7 +1365,7 @@
   data_type_status_table_ = result.data_type_status_table;
 
   // We should have cleared our cached passphrase before we get here (in
-  // OnBackendInitialized()).
+  // OnEngineInitialized()).
   DCHECK(cached_passphrase_.empty());
 
   if (!sync_configure_start_time_.is_null()) {
@@ -1430,7 +1430,7 @@
   // This must be done before we start syncing with the server to avoid
   // sending unencrypted data up on a first time sync.
   if (encryption_pending_)
-    backend_->EnableEncryptEverything();
+    engine_->EnableEncryptEverything();
   NotifyObservers();
 
   if (migrator_.get() && migrator_->state() != BackendMigrator::IDLE) {
@@ -1461,11 +1461,11 @@
   DCHECK(thread_checker_.CalledOnValidThread());
   if (HasUnrecoverableError()) {
     return UNRECOVERABLE_ERROR;
-  } else if (!backend_) {
+  } else if (!engine_) {
     return NOT_ENABLED;
-  } else if (backend_.get() && !IsFirstSetupComplete()) {
+  } else if (engine_ && !IsFirstSetupComplete()) {
     return SETUP_INCOMPLETE;
-  } else if (backend_ && IsFirstSetupComplete() && data_type_manager_ &&
+  } else if (engine_ && IsFirstSetupComplete() && data_type_manager_ &&
              data_type_manager_->state() == DataTypeManager::STOPPED) {
     return DATATYPES_NOT_INITIALIZED;
   } else if (IsSyncActive()) {
@@ -1499,7 +1499,7 @@
   }
 }
 
-std::string ProfileSyncService::GetBackendInitializationStateString() const {
+std::string ProfileSyncService::GetEngineInitializationStateString() const {
   DCHECK(thread_checker_.CalledOnValidThread());
   return startup_controller_->GetEngineInitializationStateString();
 }
@@ -1511,8 +1511,8 @@
 
 bool ProfileSyncService::QueryDetailedSyncStatus(SyncEngine::Status* result) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  if (backend_.get() && backend_initialized_) {
-    *result = backend_->GetDetailedStatus();
+  if (engine_ && engine_initialized_) {
+    *result = engine_->GetDetailedStatus();
     return true;
   } else {
     SyncEngine::Status status;
@@ -1559,14 +1559,14 @@
 
 bool ProfileSyncService::IsSyncActive() const {
   DCHECK(thread_checker_.CalledOnValidThread());
-  return backend_initialized_ && data_type_manager_ &&
+  return engine_initialized_ && data_type_manager_ &&
          data_type_manager_->state() != DataTypeManager::STOPPED;
 }
 
 void ProfileSyncService::TriggerRefresh(const syncer::ModelTypeSet& types) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  if (backend_initialized_)
-    backend_->TriggerRefresh(types);
+  if (engine_initialized_)
+    engine_->TriggerRefresh(types);
 }
 
 bool ProfileSyncService::IsSignedIn() const {
@@ -1574,15 +1574,15 @@
   return !signin_->GetAccountIdToUse().empty();
 }
 
-bool ProfileSyncService::CanBackendStart() const {
+bool ProfileSyncService::CanEngineStart() const {
   return CanSyncStart() && oauth2_token_service_ &&
          oauth2_token_service_->RefreshTokenIsAvailable(
              signin_->GetAccountIdToUse());
 }
 
-bool ProfileSyncService::IsBackendInitialized() const {
+bool ProfileSyncService::IsEngineInitialized() const {
   DCHECK(thread_checker_.CalledOnValidThread());
-  return backend_initialized_;
+  return engine_initialized_;
 }
 
 bool ProfileSyncService::ConfigurationDone() const {
@@ -1707,7 +1707,7 @@
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(syncer::UserSelectableTypes().HasAll(chosen_types));
 
-  if (!backend_.get() && !HasUnrecoverableError()) {
+  if (!engine_ && !HasUnrecoverableError()) {
     NOTREACHED();
     return;
   }
@@ -1797,18 +1797,18 @@
 
 syncer::PassphraseType ProfileSyncService::GetPassphraseType() const {
   DCHECK(thread_checker_.CalledOnValidThread());
-  return backend_->GetPassphraseType();
+  return engine_->GetPassphraseType();
 }
 
 base::Time ProfileSyncService::GetExplicitPassphraseTime() const {
   DCHECK(thread_checker_.CalledOnValidThread());
-  return backend_->GetExplicitPassphraseTime();
+  return engine_->GetExplicitPassphraseTime();
 }
 
 bool ProfileSyncService::IsCryptographerReady(
     const syncer::BaseTransaction* trans) const {
   DCHECK(thread_checker_.CalledOnValidThread());
-  return backend_.get() && backend_->IsCryptographerReady(trans);
+  return engine_ && engine_->IsCryptographerReady(trans);
 }
 
 void ProfileSyncService::SetPlatformSyncAllowedProvider(
@@ -1835,7 +1835,7 @@
     restart = true;
     data_type_manager_.reset(
         sync_client_->GetSyncApiComponentFactory()->CreateDataTypeManager(
-            debug_info_listener_, &data_type_controllers_, this, backend_.get(),
+            debug_info_listener_, &data_type_controllers_, this, engine_.get(),
             this));
 
     // We create the migrator at the same time.
@@ -1865,8 +1865,8 @@
 
 syncer::UserShare* ProfileSyncService::GetUserShare() const {
   DCHECK(thread_checker_.CalledOnValidThread());
-  if (backend_.get() && backend_initialized_) {
-    return backend_->GetUserShare();
+  if (engine_ && engine_initialized_) {
+    return engine_->GetUserShare();
   }
   NOTREACHED();
   return nullptr;
@@ -1874,15 +1874,15 @@
 
 syncer::SyncCycleSnapshot ProfileSyncService::GetLastCycleSnapshot() const {
   DCHECK(thread_checker_.CalledOnValidThread());
-  if (backend_)
-    return backend_->GetLastCycleSnapshot();
+  if (engine_)
+    return engine_->GetLastCycleSnapshot();
   return syncer::SyncCycleSnapshot();
 }
 
 bool ProfileSyncService::HasUnsyncedItems() const {
   DCHECK(thread_checker_.CalledOnValidThread());
-  if (HasSyncingBackend() && backend_initialized_) {
-    return backend_->HasUnsyncedItems();
+  if (HasSyncingEngine() && engine_initialized_) {
+    return engine_->HasUnsyncedItems();
   }
   NOTREACHED();
   return false;
@@ -1896,8 +1896,8 @@
 void ProfileSyncService::GetModelSafeRoutingInfo(
     syncer::ModelSafeRoutingInfo* out) const {
   DCHECK(thread_checker_.CalledOnValidThread());
-  if (backend_.get() && backend_initialized_) {
-    backend_->GetModelSafeRoutingInfo(out);
+  if (engine_ && engine_initialized_) {
+    engine_->GetModelSafeRoutingInfo(out);
   } else {
     NOTREACHED();
   }
@@ -1907,7 +1907,7 @@
   DCHECK(thread_checker_.CalledOnValidThread());
   auto result = base::MakeUnique<base::ListValue>();
 
-  if (!backend_.get() || !backend_initialized_) {
+  if (!engine_ || !engine_initialized_) {
     return std::move(result);
   }
 
@@ -1916,7 +1916,7 @@
   ModelTypeSet active_types;
   ModelTypeSet passive_types;
   ModelSafeRoutingInfo routing_info;
-  backend_->GetModelSafeRoutingInfo(&routing_info);
+  engine_->GetModelSafeRoutingInfo(&routing_info);
   for (ModelSafeRoutingInfo::const_iterator it = routing_info.begin();
        it != routing_info.end(); ++it) {
     if (it->second == syncer::GROUP_PASSIVE) {
@@ -1926,7 +1926,7 @@
     }
   }
 
-  SyncEngine::Status detailed_status = backend_->GetDetailedStatus();
+  SyncEngine::Status detailed_status = engine_->GetDetailedStatus();
   ModelTypeSet& throttled_types(detailed_status.throttled_types);
   ModelTypeSet& backed_off_types(detailed_status.backed_off_types);
   ModelTypeSet registered = GetRegisteredDataTypes();
@@ -2010,13 +2010,13 @@
 }
 
 void ProfileSyncService::ConsumeCachedPassphraseIfPossible() {
-  // If no cached passphrase, or sync backend hasn't started up yet, just exit.
-  // If the backend isn't running yet, OnBackendInitialized() will call this
-  // method again after the backend starts up.
-  if (cached_passphrase_.empty() || !IsBackendInitialized())
+  // If no cached passphrase, or sync engine hasn't started up yet, just exit.
+  // If the engine isn't running yet, OnEngineInitialized() will call this
+  // method again after the engine starts up.
+  if (cached_passphrase_.empty() || !IsEngineInitialized())
     return;
 
-  // Backend is up and running, so we can consume the cached passphrase.
+  // Engine is up and running, so we can consume the cached passphrase.
   std::string passphrase = cached_passphrase_;
   cached_passphrase_.clear();
 
@@ -2063,8 +2063,8 @@
 void ProfileSyncService::SetEncryptionPassphrase(const std::string& passphrase,
                                                  PassphraseType type) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  // This should only be called when the backend has been initialized.
-  DCHECK(IsBackendInitialized());
+  // This should only be called when the engine has been initialized.
+  DCHECK(IsEngineInitialized());
   DCHECK(!(type == IMPLICIT && IsUsingSecondaryPassphrase()))
       << "Data is already encrypted using an explicit passphrase";
   DCHECK(!(type == EXPLICIT &&
@@ -2082,7 +2082,7 @@
     passphrase_required_reason_ = syncer::REASON_PASSPHRASE_NOT_REQUIRED;
     NotifyObservers();
   }
-  backend_->SetEncryptionPassphrase(passphrase, type == EXPLICIT);
+  engine_->SetEncryptionPassphrase(passphrase, type == EXPLICIT);
 }
 
 bool ProfileSyncService::SetDecryptionPassphrase(
@@ -2090,7 +2090,7 @@
   DCHECK(thread_checker_.CalledOnValidThread());
   if (IsPassphraseRequired()) {
     DVLOG(1) << "Setting passphrase for decryption.";
-    bool result = backend_->SetDecryptionPassphrase(passphrase);
+    bool result = engine_->SetDecryptionPassphrase(passphrase);
     UMA_HISTOGRAM_BOOLEAN("Sync.PassphraseDecryptionSucceeded", result);
     return result;
   } else {
@@ -2107,7 +2107,7 @@
 
 void ProfileSyncService::SetEncryptEverythingAllowed(bool allowed) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(allowed || !IsBackendInitialized() || !IsEncryptEverythingEnabled());
+  DCHECK(allowed || !IsEngineInitialized() || !IsEncryptEverythingEnabled());
   encrypt_everything_allowed_ = allowed;
 }
 
@@ -2115,10 +2115,10 @@
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(IsEncryptEverythingAllowed());
 
-  // Tests override IsBackendInitialized() to always return true, so we
-  // must check that instead of |backend_initialized_|.
+  // Tests override IsEngineInitialized() to always return true, so we
+  // must check that instead of |engine_initialized_|.
   // TODO(akalin): Fix the above. :/
-  DCHECK(IsBackendInitialized());
+  DCHECK(IsEngineInitialized());
   // TODO(atwilson): Persist the encryption_pending_ flag to address the various
   // problems around cancelling encryption in the background (crbug.com/119649).
   if (!encrypt_everything_)
@@ -2135,7 +2135,7 @@
 
 bool ProfileSyncService::IsEncryptEverythingEnabled() const {
   DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(backend_initialized_);
+  DCHECK(engine_initialized_);
   return encrypt_everything_ || encryption_pending_;
 }
 
@@ -2163,15 +2163,15 @@
   DCHECK(thread_checker_.CalledOnValidThread());
   if (IsSyncRequested() && !password.empty()) {
     cached_passphrase_ = password;
-    // Try to consume the passphrase we just cached. If the sync backend
+    // Try to consume the passphrase we just cached. If the sync engine
     // is not running yet, the passphrase will remain cached until the
-    // backend starts up.
+    // engine starts up.
     ConsumeCachedPassphraseIfPossible();
   }
 #if defined(OS_CHROMEOS)
   RefreshSpareBootstrapToken(password);
 #endif
-  if (!IsBackendInitialized() || GetAuthError().state() != AuthError::NONE) {
+  if (!IsEngineInitialized() || GetAuthError().state() != AuthError::NONE) {
     // Track the fact that we're still waiting for auth to complete.
     is_auth_in_progress_ = true;
   }
@@ -2191,7 +2191,7 @@
     const std::vector<gaia::ListedAccount>& signed_out_accounts,
     const GoogleServiceAuthError& error) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  if (!IsBackendInitialized())
+  if (!IsEngineInitialized())
     return;
 
   bool cookie_jar_mismatch = true;
@@ -2208,7 +2208,7 @@
 
   DVLOG(1) << "Cookie jar mismatch: " << cookie_jar_mismatch;
   DVLOG(1) << "Cookie jar empty: " << cookie_jar_empty;
-  backend_->OnCookieJarChanged(cookie_jar_mismatch, cookie_jar_empty);
+  engine_->OnCookieJarChanged(cookie_jar_mismatch, cookie_jar_empty);
 }
 
 void ProfileSyncService::AddObserver(syncer::SyncServiceObserver* observer) {
@@ -2225,8 +2225,8 @@
     ProtocolEventObserver* observer) {
   DCHECK(thread_checker_.CalledOnValidThread());
   protocol_event_observers_.AddObserver(observer);
-  if (HasSyncingBackend()) {
-    backend_->RequestBufferedProtocolEventsAndEnableForwarding();
+  if (HasSyncingEngine()) {
+    engine_->RequestBufferedProtocolEventsAndEnableForwarding();
   }
 }
 
@@ -2234,9 +2234,8 @@
     ProtocolEventObserver* observer) {
   DCHECK(thread_checker_.CalledOnValidThread());
   protocol_event_observers_.RemoveObserver(observer);
-  if (HasSyncingBackend() &&
-      !protocol_event_observers_.might_have_observers()) {
-    backend_->DisableProtocolEventForwarding();
+  if (HasSyncingEngine() && !protocol_event_observers_.might_have_observers()) {
+    engine_->DisableProtocolEventForwarding();
   }
 }
 
@@ -2245,8 +2244,8 @@
   DCHECK(thread_checker_.CalledOnValidThread());
   type_debug_info_observers_.AddObserver(type_debug_info_observer);
   if (type_debug_info_observers_.might_have_observers() &&
-      backend_initialized_) {
-    backend_->EnableDirectoryTypeDebugInfoForwarding();
+      engine_initialized_) {
+    engine_->EnableDirectoryTypeDebugInfoForwarding();
   }
 }
 
@@ -2255,8 +2254,8 @@
   DCHECK(thread_checker_.CalledOnValidThread());
   type_debug_info_observers_.RemoveObserver(type_debug_info_observer);
   if (!type_debug_info_observers_.might_have_observers() &&
-      backend_initialized_) {
-    backend_->DisableDirectoryTypeDebugInfoForwarding();
+      engine_initialized_) {
+    engine_->DisableDirectoryTypeDebugInfoForwarding();
   }
 }
 
@@ -2349,8 +2348,8 @@
   scoped_refptr<GetAllNodesRequestHelper> helper =
       new GetAllNodesRequestHelper(all_types, callback);
 
-  if (!backend_initialized_) {
-    // If there's no backend available to fulfill the request, handle it here.
+  if (!engine_initialized_) {
+    // If there's no engine available to fulfill the request, handle it here.
     for (ModelTypeSet::Iterator it = all_types.First(); it.Good(); it.Inc()) {
       helper->OnReceivedNodesForType(it.Get(),
                                      base::MakeUnique<base::ListValue>());
@@ -2442,8 +2441,8 @@
   DCHECK(thread_checker_.CalledOnValidThread());
   // If we haven't initialized yet, don't configure the DTM as it could cause
   // association to start before a Directory has even been created.
-  if (backend_initialized_) {
-    DCHECK(backend_.get());
+  if (engine_initialized_) {
+    DCHECK(engine_);
     ConfigureDataTypeManager();
   } else if (HasUnrecoverableError()) {
     // There is nothing more to configure. So inform the listeners,
@@ -2452,7 +2451,7 @@
     DVLOG(1) << "ConfigureDataTypeManager not invoked because of an "
              << "Unrecoverable error.";
   } else {
-    DVLOG(0) << "ConfigureDataTypeManager not invoked because backend is not "
+    DVLOG(0) << "ConfigureDataTypeManager not invoked because engine is not "
              << "initialized";
   }
 }
@@ -2532,8 +2531,8 @@
   network_resources_ = std::move(network_resources);
 }
 
-bool ProfileSyncService::HasSyncingBackend() const {
-  return backend_ != nullptr;
+bool ProfileSyncService::HasSyncingEngine() const {
+  return engine_ != nullptr;
 }
 
 void ProfileSyncService::UpdateFirstSyncTimePref() {
@@ -2547,10 +2546,10 @@
 
 void ProfileSyncService::FlushDirectory() const {
   DCHECK(thread_checker_.CalledOnValidThread());
-  // backend_initialized_ implies backend_ isn't null and the manager exists.
+  // engine_initialized_ implies engine_ isn't null and the manager exists.
   // If sync is not initialized yet, we fail silently.
-  if (backend_initialized_)
-    backend_->FlushDirectory();
+  if (engine_initialized_)
+    engine_->FlushDirectory();
 }
 
 base::FilePath ProfileSyncService::GetDirectoryPathForTest() const {
@@ -2569,12 +2568,12 @@
 
 void ProfileSyncService::RefreshTypesForTest(syncer::ModelTypeSet types) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  if (backend_initialized_)
-    backend_->RefreshTypesForTest(types);
+  if (engine_initialized_)
+    engine_->RefreshTypesForTest(types);
 }
 
 void ProfileSyncService::RemoveClientFromServer() const {
-  if (!backend_initialized_)
+  if (!engine_initialized_)
     return;
   const std::string cache_guid = local_device_->GetLocalSyncCacheGUID();
   std::string birthday;
@@ -2642,7 +2641,7 @@
   DCHECK(startup_controller_->IsSetupInProgress());
   startup_controller_->SetSetupInProgress(false);
 
-  if (IsBackendInitialized())
+  if (IsEngineInitialized())
     ReconfigureDatatypeManager();
   NotifyObservers();
 }
diff --git a/components/browser_sync/profile_sync_service.h b/components/browser_sync/profile_sync_service.h
index f974d5d2..ca82b84 100644
--- a/components/browser_sync/profile_sync_service.h
+++ b/components/browser_sync/profile_sync_service.h
@@ -93,11 +93,10 @@
 namespace browser_sync {
 
 // ProfileSyncService is the layer between browser subsystems like bookmarks,
-// and the sync backend.  Each subsystem is logically thought of as being
-// a sync datatype.
-//
-// Individual datatypes can, at any point, be in a variety of stages of being
-// "enabled".  Here are some specific terms for concepts used in this class:
+// and the sync engine. Each subsystem is logically thought of as being a sync
+// datatype. Individual datatypes can, at any point, be in a variety of stages
+// of being "enabled". Here are some specific terms for concepts used in this
+// class:
 //
 //   'Registered' (feature suppression for a datatype)
 //
@@ -127,7 +126,7 @@
 //      synchronized: the syncer has been instructed to querying the server
 //      for this datatype, first-time merges have finished, and there is an
 //      actively installed ChangeProcessor that listens for changes to this
-//      datatype, propagating such changes into and out of the sync backend
+//      datatype, propagating such changes into and out of the sync engine
 //      as necessary.
 //
 //      When a datatype is in the process of becoming active, it may be
@@ -282,7 +281,7 @@
   bool ConfigurationDone() const override;
   const GoogleServiceAuthError& GetAuthError() const override;
   bool HasUnrecoverableError() const override;
-  bool IsBackendInitialized() const override;
+  bool IsEngineInitialized() const override;
   sync_sessions::OpenTabsUIDelegate* GetOpenTabsUIDelegate() override;
   bool IsPassphraseRequiredForDecryption() const override;
   base::Time GetExplicitPassphraseTime() const override;
@@ -307,7 +306,7 @@
   std::string QuerySyncStatusSummaryString() override;
   bool QueryDetailedSyncStatus(syncer::SyncStatus* result) override;
   base::string16 GetLastSyncedTimeString() const override;
-  std::string GetBackendInitializationStateString() const override;
+  std::string GetEngineInitializationStateString() const override;
   syncer::SyncCycleSnapshot GetLastCycleSnapshot() const override;
   std::unique_ptr<base::Value> GetTypeStatusMap() override;
   const GURL& sync_service_url() const override;
@@ -359,7 +358,7 @@
   void OnSessionRestoreComplete();
 
   // SyncEngineHost implementation.
-  void OnBackendInitialized(
+  void OnEngineInitialized(
       const syncer::WeakHandle<syncer::JsBackend>& js_backend,
       const syncer::WeakHandle<syncer::DataTypeDebugInfoListener>&
           debug_info_listener,
@@ -416,10 +415,10 @@
   SyncStatusSummary QuerySyncStatusSummary();
 
   // Reconfigures the data type manager with the latest enabled types.
-  // Note: Does not initialize the backend if it is not already initialized.
+  // Note: Does not initialize the engine if it is not already initialized.
   // This function needs to be called only after sync has been initialized
   // (i.e.,only for reconfigurations). The reason we don't initialize the
-  // backend is because if we had encountered an unrecoverable error we don't
+  // engine is because if we had encountered an unrecoverable error we don't
   // want to startup once more.
   // This function is called by |SetSetupInProgress|.
   virtual void ReconfigureDatatypeManager();
@@ -456,7 +455,7 @@
                             const std::string& message) override;
 
   // The functions below (until ActivateDataType()) should only be
-  // called if IsBackendInitialized() is true.
+  // called if IsEngineInitialized() is true.
 
   // TODO(akalin): These two functions are used only by
   // ProfileSyncServiceHarness.  Figure out a different way to expose
@@ -484,7 +483,7 @@
 
   // Changes which data types we're going to be syncing to |preferred_types|.
   // If it is running, the DataTypeManager will be instructed to reconfigure
-  // the sync backend so that exactly these datatypes are actively synced.  See
+  // the sync engine so that exactly these datatypes are actively synced. See
   // class comment for more on what it means for a datatype to be Preferred.
   virtual void ChangePreferredDataTypes(syncer::ModelTypeSet preferred_types);
 
@@ -592,7 +591,7 @@
   enum UnrecoverableErrorReason {
     ERROR_REASON_UNSET,
     ERROR_REASON_SYNCER,
-    ERROR_REASON_BACKEND_INIT_FAILURE,
+    ERROR_REASON_ENGINE_INIT_FAILURE,
     ERROR_REASON_CONFIGURATION_RETRY,
     ERROR_REASON_CONFIGURATION_FAILURE,
     ERROR_REASON_ACTIONABLE_ERROR,
@@ -627,9 +626,9 @@
   // Helper to install and configure a data type manager.
   void ConfigureDataTypeManager();
 
-  // Shuts down the backend sync components.
+  // Shuts down the engine sync components.
   // |reason| dictates if syncing is being disabled or not, and whether
-  // to claim ownership of sync thread from backend.
+  // to claim ownership of sync thread from engine.
   void ShutdownImpl(syncer::ShutdownReason reason);
 
   // Return SyncCredentials from the OAuth2TokenService.
@@ -655,13 +654,13 @@
   // Update the last auth error and notify observers of error state.
   void UpdateAuthErrorState(const GoogleServiceAuthError& error);
 
-  // Puts the backend's sync scheduler into NORMAL mode.
+  // Puts the engine's sync scheduler into NORMAL mode.
   // Called when configuration is complete.
   void StartSyncingWithServer();
 
   // During initial signin, ProfileSyncService caches the user's signin
   // passphrase so it can be used to encrypt/decrypt data after sync starts up.
-  // This routine is invoked once the backend has started up to use the
+  // This routine is invoked once the engine has started up to use the
   // cached passphrase and clear it out when it is done.
   void ConsumeCachedPassphraseIfPossible();
 
@@ -671,12 +670,12 @@
   // token.
   void RequestAccessToken();
 
-  // Return true if backend should start from a fresh sync DB.
+  // Return true if engine should start from a fresh sync DB.
   bool ShouldDeleteSyncFolder();
 
   // If |delete_sync_data_folder| is true, then this method will delete all
   // previous "Sync Data" folders. (useful if the folder is partial/corrupt).
-  void InitializeBackend(bool delete_sync_data_folder);
+  void InitializeEngine(bool delete_sync_data_folder);
 
   // Sets the last synced time to the current time.
   void UpdateLastSyncedTime();
@@ -689,8 +688,8 @@
 
   void ClearUnrecoverableError();
 
-  // Starts up the backend sync components.
-  void StartUpSlowBackendComponents();
+  // Starts up the engine sync components.
+  virtual void StartUpSlowEngineComponents();
 
   // Collects preferred sync data types from |preference_providers_|.
   syncer::ModelTypeSet GetDataTypesFromPreferenceProviders() const;
@@ -714,24 +713,24 @@
                                     bool delete_sync_database,
                                     UnrecoverableErrorReason reason);
 
-  // Update UMA for syncing backend.
-  void UpdateBackendInitUMA(bool success);
+  // Update UMA for syncing engine.
+  void UpdateEngineInitUMA(bool success);
 
-  // Various setup following backend initialization, mostly for syncing backend.
-  void PostBackendInitialization();
+  // Various setup following engine initialization, mostly for syncing engine.
+  void PostEngineInitialization();
 
   // Whether sync has been authenticated with an account ID.
   bool IsSignedIn() const;
 
-  // The backend can only start if sync can start and has an auth token. This is
-  // different fron CanSyncStart because it represents whether the backend can
+  // The engine can only start if sync can start and has an auth token. This is
+  // different fron CanSyncStart because it represents whether the engine can
   // be started at this moment, whereas CanSyncStart represents whether sync can
   // conceptually start without further user action (acquiring a token is an
   // automatic process).
-  bool CanBackendStart() const;
+  bool CanEngineStart() const;
 
-  // True if a syncing backend exists.
-  bool HasSyncingBackend() const;
+  // True if a syncing engine exists.
+  bool HasSyncingEngine() const;
 
   // Update first sync time stored in preferences
   void UpdateFirstSyncTimePref();
@@ -756,7 +755,7 @@
   // Calls data type manager to start catch up configure.
   void BeginConfigureCatchUpBeforeClear();
 
-  // Calls sync backend to send ClearServerDataMessage to server.
+  // Calls sync engine to send ClearServerDataMessage to server.
   void ClearAndRestartSyncForPassphraseEncryption();
 
   // Restarts sync clearing directory in the process.
@@ -773,9 +772,9 @@
   // user.
   GoogleServiceAuthError last_auth_error_;
 
-  // Our asynchronous backend to communicate with sync components living on
+  // Our asynchronous engine to communicate with sync components living on
   // other threads.
-  std::unique_ptr<syncer::SyncEngine> backend_;
+  std::unique_ptr<syncer::SyncEngine> engine_;
 
   // Was the last SYNC_PASSPHRASE_REQUIRED notification sent because it
   // was required for encryption, decryption with a cached passphrase, or
@@ -799,7 +798,7 @@
   // OnConfigureDone is called.
   base::Time sync_configure_start_time_;
 
-  // Callback to update the network time; used for initializing the backend.
+  // Callback to update the network time; used for initializing the engine.
   syncer::NetworkTimeUpdateCallback network_time_update_callback_;
 
   // The path to the base directory under which sync should store its
@@ -819,7 +818,7 @@
   base::SequencedWorkerPool* blocking_pool_;
 
   // Indicates if this is the first time sync is being configured.  This value
-  // is equal to !IsFirstSetupComplete() at the time of OnBackendInitialized().
+  // is equal to !IsFirstSetupComplete() at the time of OnEngineInitialized().
   bool is_first_time_sync_configure_;
 
   // Number of UIs currently configuring the Sync service. When this number
@@ -830,15 +829,15 @@
   syncer::DataTypeController::TypeMap data_type_controllers_;
 
   // Whether the SyncEngine has been initialized.
-  bool backend_initialized_;
+  bool engine_initialized_;
 
   // Set when sync receives DISABLED_BY_ADMIN error from server. Prevents
-  // ProfileSyncService from starting backend till browser restarted or user
+  // ProfileSyncService from starting engine till browser restarted or user
   // signed out.
   bool sync_disabled_by_admin_;
 
   // Set to true if a signin has completed but we're still waiting for the
-  // backend to refresh its credentials.
+  // engine to refresh its credentials.
   bool is_auth_in_progress_;
 
   // Encapsulates user signin - used to set/get the user's authenticated
@@ -867,7 +866,7 @@
   bool expect_sync_configuration_aborted_;
 
   // Sometimes we need to temporarily hold on to a passphrase because we don't
-  // yet have a backend to send it to.  This happens during initialization as
+  // yet have a engine to send it to.  This happens during initialization as
   // we don't StartUp until we have a valid token, which happens after valid
   // credentials were provided.
   std::string cached_passphrase_;
@@ -964,7 +963,7 @@
   std::unique_ptr<base::MemoryPressureListener> memory_pressure_listener_;
 
   // Nigori state after user switching to custom passphrase, saved until
-  // transition steps complete. It will be injected into new backend after sync
+  // transition steps complete. It will be injected into new engine after sync
   // restart.
   std::unique_ptr<syncer::SyncEncryptionHandler::NigoriState>
       saved_nigori_state_;
diff --git a/components/browser_sync/profile_sync_service_mock.h b/components/browser_sync/profile_sync_service_mock.h
index 2c735276..5e8c5ca 100644
--- a/components/browser_sync/profile_sync_service_mock.h
+++ b/components/browser_sync/profile_sync_service_mock.h
@@ -34,7 +34,7 @@
   virtual ~ProfileSyncServiceMock();
 
   MOCK_METHOD4(
-      OnBackendInitialized,
+      OnEngineInitialized,
       void(const syncer::WeakHandle<syncer::JsBackend>&,
            const syncer::WeakHandle<syncer::DataTypeDebugInfoListener>&,
            const std::string&,
@@ -73,7 +73,7 @@
   MOCK_CONST_METHOD0(GetLastSyncedTimeString, base::string16());
   MOCK_CONST_METHOD0(HasUnrecoverableError, bool());
   MOCK_CONST_METHOD0(IsSyncActive, bool());
-  MOCK_CONST_METHOD0(IsBackendInitialized, bool());
+  MOCK_CONST_METHOD0(IsEngineInitialized, bool());
   MOCK_CONST_METHOD0(IsSyncRequested, bool());
   MOCK_CONST_METHOD0(waiting_for_auth, bool());
   MOCK_METHOD1(OnActionableError, void(const syncer::SyncProtocolError&));
diff --git a/components/browser_sync/profile_sync_service_startup_unittest.cc b/components/browser_sync/profile_sync_service_startup_unittest.cc
index 61192bd..1ff7cdfa 100644
--- a/components/browser_sync/profile_sync_service_startup_unittest.cc
+++ b/components/browser_sync/profile_sync_service_startup_unittest.cc
@@ -408,7 +408,7 @@
   EXPECT_CALL(observer_, OnStateChanged()).Times(AnyNumber());
   IssueTestTokens(account_id);
   sync_service_->Initialize();
-  EXPECT_TRUE(sync_service_->IsBackendInitialized());
+  EXPECT_TRUE(sync_service_->IsEngineInitialized());
   EXPECT_TRUE(sync_service_->IsSyncActive());
 
   // The service should stop when switching to managed mode.
@@ -417,7 +417,7 @@
       .WillOnce(Return(DataTypeManager::CONFIGURED));
   EXPECT_CALL(*data_type_manager, Stop()).Times(1);
   pref_service()->SetBoolean(syncer::prefs::kSyncManaged, true);
-  EXPECT_FALSE(sync_service_->IsBackendInitialized());
+  EXPECT_FALSE(sync_service_->IsEngineInitialized());
   // Note that PSS no longer references |data_type_manager| after stopping.
 
   // When switching back to unmanaged, the state should change but sync should
@@ -427,7 +427,7 @@
   EXPECT_CALL(*component_factory_, CreateDataTypeManager(_, _, _, _, _))
       .Times(0);
   pref_service()->ClearPref(syncer::prefs::kSyncManaged);
-  EXPECT_FALSE(sync_service_->IsBackendInitialized());
+  EXPECT_FALSE(sync_service_->IsEngineInitialized());
   EXPECT_FALSE(sync_service_->IsSyncActive());
 }
 
diff --git a/components/browser_sync/test_profile_sync_service.cc b/components/browser_sync/test_profile_sync_service.cc
index c7222ae5..a4bc8d08 100644
--- a/components/browser_sync/test_profile_sync_service.cc
+++ b/components/browser_sync/test_profile_sync_service.cc
@@ -30,7 +30,7 @@
 }
 
 syncer::UserShare* TestProfileSyncService::GetUserShare() const {
-  return backend_->GetUserShare();
+  return engine_->GetUserShare();
 }
 
 }  // namespace browser_sync
diff --git a/components/ntp_snippets/BUILD.gn b/components/ntp_snippets/BUILD.gn
index 84c9c8a..448fc8d66 100644
--- a/components/ntp_snippets/BUILD.gn
+++ b/components/ntp_snippets/BUILD.gn
@@ -151,7 +151,7 @@
     "//components/strings",
     "//components/sync:test_support_driver",
     "//components/sync_sessions",
-    "//components/variations",
+    "//components/variations:test_support",
     "//net:test_support",
     "//testing/gtest",
     "//third_party/icu/",
diff --git a/components/ntp_snippets/remote/ntp_snippets_fetcher_unittest.cc b/components/ntp_snippets/remote/ntp_snippets_fetcher_unittest.cc
index e5533de8bd..c02ffa4 100644
--- a/components/ntp_snippets/remote/ntp_snippets_fetcher_unittest.cc
+++ b/components/ntp_snippets/remote/ntp_snippets_fetcher_unittest.cc
@@ -25,7 +25,7 @@
 #include "components/signin/core/browser/fake_signin_manager.h"
 #include "components/signin/core/browser/test_signin_client.h"
 #include "components/variations/entropy_provider.h"
-#include "components/variations/variations_associated_data.h"
+#include "components/variations/variations_params_manager.h"
 #include "net/url_request/test_url_fetcher_factory.h"
 #include "net/url_request/url_request_test_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
diff --git a/components/ntp_snippets/remote/ntp_snippets_status_service.h b/components/ntp_snippets/remote/ntp_snippets_status_service.h
new file mode 100644
index 0000000..27380f495
--- /dev/null
+++ b/components/ntp_snippets/remote/ntp_snippets_status_service.h
@@ -0,0 +1,82 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_NTP_SNIPPETS_REMOTE_NTP_SNIPPETS_STATUS_SERVICE_H_
+#define COMPONENTS_NTP_SNIPPETS_REMOTE_NTP_SNIPPETS_STATUS_SERVICE_H_
+
+#include "base/callback.h"
+#include "base/gtest_prod_util.h"
+#include "base/scoped_observer.h"
+#include "components/prefs/pref_change_registrar.h"
+
+class PrefRegistrySimple;
+class PrefService;
+class SigninManagerBase;
+
+namespace ntp_snippets {
+
+enum class SnippetsStatus : int {
+  // Snippets are enabled and the user is signed in.
+  ENABLED_AND_SIGNED_IN,
+  // Snippets are enabled and the user is signed out (sign in is not required).
+  ENABLED_AND_SIGNED_OUT,
+  // Snippets have been disabled as part of the service configuration.
+  EXPLICITLY_DISABLED,
+  // The user is not signed in, and the service requires it to be enabled.
+  SIGNED_OUT_AND_DISABLED,
+};
+
+// Aggregates data from preferences and signin to notify the snippet service of
+// relevant changes in their states.
+class NTPSnippetsStatusService {
+ public:
+  using SnippetsStatusChangeCallback =
+      base::Callback<void(SnippetsStatus /*old_status*/,
+                          SnippetsStatus /*new_status*/)>;
+
+  NTPSnippetsStatusService(SigninManagerBase* signin_manager,
+                           PrefService* pref_service);
+
+  virtual ~NTPSnippetsStatusService();
+
+  static void RegisterProfilePrefs(PrefRegistrySimple* registry);
+
+  // Starts listening for changes from the dependencies. |callback| will be
+  // called when a significant change in state is detected.
+  void Init(const SnippetsStatusChangeCallback& callback);
+
+  // To be called when the signin state changed. Will compute the new
+  // state considering the initialisation configuration and the preferences,
+  // and notify via the registered callback if appropriate.
+  void OnSignInStateChanged();
+
+ private:
+  FRIEND_TEST_ALL_PREFIXES(NTPSnippetsStatusServiceTest,
+                           SigninStateCompatibility);
+  FRIEND_TEST_ALL_PREFIXES(NTPSnippetsStatusServiceTest, DisabledViaPref);
+
+  // Callback for the PrefChangeRegistrar.
+  void OnSnippetsEnabledChanged();
+
+  void OnStateChanged(SnippetsStatus new_snippets_status);
+
+  bool IsSignedIn() const;
+
+  SnippetsStatus GetSnippetsStatusFromDeps() const;
+
+  SnippetsStatus snippets_status_;
+  SnippetsStatusChangeCallback snippets_status_change_callback_;
+
+  bool require_signin_;
+  SigninManagerBase* signin_manager_;
+  PrefService* pref_service_;
+
+  PrefChangeRegistrar pref_change_registrar_;
+
+  DISALLOW_COPY_AND_ASSIGN(NTPSnippetsStatusService);
+};
+
+}  // namespace ntp_snippets
+
+#endif  // COMPONENTS_NTP_SNIPPETS_REMOTE_NTP_SNIPPETS_STATUS_SERVICE_H_
diff --git a/components/ntp_snippets/remote/remote_suggestions_provider_unittest.cc b/components/ntp_snippets/remote/remote_suggestions_provider_unittest.cc
index 2d77622..b593ee4 100644
--- a/components/ntp_snippets/remote/remote_suggestions_provider_unittest.cc
+++ b/components/ntp_snippets/remote/remote_suggestions_provider_unittest.cc
@@ -38,7 +38,7 @@
 #include "components/prefs/testing_pref_service.h"
 #include "components/signin/core/browser/fake_profile_oauth2_token_service.h"
 #include "components/signin/core/browser/fake_signin_manager.h"
-#include "components/variations/variations_associated_data.h"
+#include "components/variations/variations_params_manager.h"
 #include "net/url_request/test_url_fetcher_factory.h"
 #include "net/url_request/url_request_test_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
diff --git a/components/offline_pages/background/offliner.h b/components/offline_pages/background/offliner.h
index cafd0686..7238de9 100644
--- a/components/offline_pages/background/offliner.h
+++ b/components/offline_pages/background/offliner.h
@@ -44,6 +44,9 @@
     PRERENDERING_NOT_STARTED = 9,
     // Prerendering failed with hard error so should not retry the request.
     PRERENDERING_FAILED_NO_RETRY = 10,
+    // Prerendering failed with some error that suggests we should not continue
+    // processing another request at this time.
+    PRERENDERING_FAILED_NO_NEXT = 11,
     // NOTE: insert new values above this line and update histogram enum too.
     STATUS_COUNT
   };
diff --git a/components/offline_pages/background/request_coordinator.cc b/components/offline_pages/background/request_coordinator.cc
index 52983866..beb881b 100644
--- a/components/offline_pages/background/request_coordinator.cc
+++ b/components/offline_pages/background/request_coordinator.cc
@@ -888,11 +888,12 @@
     case Offliner::RequestStatus::SAVE_FAILED:
     case Offliner::RequestStatus::REQUEST_COORDINATOR_CANCELED:
     case Offliner::RequestStatus::REQUEST_COORDINATOR_TIMED_OUT:
+    case Offliner::RequestStatus::PRERENDERING_FAILED:
     case Offliner::RequestStatus::PRERENDERING_FAILED_NO_RETRY:
       return true;
     case Offliner::RequestStatus::FOREGROUND_CANCELED:
     case Offliner::RequestStatus::PRERENDERING_CANCELED:
-    case Offliner::RequestStatus::PRERENDERING_FAILED:
+    case Offliner::RequestStatus::PRERENDERING_FAILED_NO_NEXT:
       // No further processing in this service window.
       return false;
     default:
diff --git a/components/offline_pages/background/request_coordinator_event_logger.cc b/components/offline_pages/background/request_coordinator_event_logger.cc
index 1585265..a638138 100644
--- a/components/offline_pages/background/request_coordinator_event_logger.cc
+++ b/components/offline_pages/background/request_coordinator_event_logger.cc
@@ -33,6 +33,8 @@
       return "PRERENDERING_NOT_STARTED";
     case Offliner::PRERENDERING_FAILED_NO_RETRY:
       return "PRERENDERING_FAILED_NO_RETRY";
+    case Offliner::PRERENDERING_FAILED_NO_NEXT:
+      return "PRERENDERING_FAILED_NO_NEXT";
     default:
       NOTREACHED();
       return std::to_string(static_cast<int>(request_status));
diff --git a/components/offline_pages/background/request_coordinator_unittest.cc b/components/offline_pages/background/request_coordinator_unittest.cc
index c352863..5fec208 100644
--- a/components/offline_pages/background/request_coordinator_unittest.cc
+++ b/components/offline_pages/background/request_coordinator_unittest.cc
@@ -625,13 +625,12 @@
                            Offliner::RequestStatus::PRERENDERING_FAILED);
   PumpLoop();
 
-  // For retriable failure, processing should stop and scheduler callback
-  // called (so that request can be retried first next processing window).
-  EXPECT_TRUE(immediate_schedule_callback_called());
+  // For retriable failure, processing should continue to 2nd request so
+  // no scheduler callback yet.
+  EXPECT_FALSE(immediate_schedule_callback_called());
 
-  // TODO(dougarnett): Consider injecting mock RequestPicker for this test
-  // and verifying that there is no attempt to pick another request following
-  // this failure code.
+  // Busy processing 2nd request.
+  EXPECT_TRUE(is_busy());
 
   coordinator()->queue()->GetRequests(
       base::Bind(&RequestCoordinatorTest::GetRequestsDone,
@@ -642,7 +641,8 @@
   // (max number of attempts exceeded).
   EXPECT_EQ(1UL, last_requests().size());
   // Check that the observer got the notification that we failed (and the
-  // subsequent notification that the request was removed).
+  // subsequent notification that the request was removed) since we exceeded
+  // retry count.
   EXPECT_TRUE(observer().completed_called());
   EXPECT_EQ(RequestCoordinator::BackgroundSavePageResult::RETRY_COUNT_EXCEEDED,
             observer().last_status());
@@ -669,9 +669,8 @@
   // no scheduler callback yet.
   EXPECT_FALSE(immediate_schedule_callback_called());
 
-  // TODO(dougarnett): Consider injecting mock RequestPicker for this test
-  // and verifying that there is as attempt to pick another request following
-  // this non-retryable failure code.
+  // Busy processing 2nd request.
+  EXPECT_TRUE(is_busy());
 
   coordinator()->queue()->GetRequests(base::Bind(
       &RequestCoordinatorTest::GetRequestsDone, base::Unretained(this)));
@@ -686,6 +685,38 @@
             observer().last_status());
 }
 
+TEST_F(RequestCoordinatorTest, OfflinerDoneRequestFailedNoNextFailure) {
+  // Add a request to the queue, wait for callbacks to finish.
+  offline_pages::SavePageRequest request(kRequestId1, kUrl1, kClientId1,
+                                         base::Time::Now(), kUserRequested);
+  SetupForOfflinerDoneCallbackTest(&request);
+  EnableOfflinerCallback(false);
+
+  // Add second request to the queue to check handling when first fails.
+  AddRequest2();
+  PumpLoop();
+
+  // Call the OfflinerDoneCallback to simulate the request failed, wait
+  // for callbacks.
+  SendOfflinerDoneCallback(
+      request, Offliner::RequestStatus::PRERENDERING_FAILED_NO_NEXT);
+  PumpLoop();
+
+  // For no next failure, processing should not continue to 2nd request so
+  // expect scheduler callback.
+  EXPECT_TRUE(immediate_schedule_callback_called());
+
+  // Not busy for NO_NEXT failure.
+  EXPECT_FALSE(is_busy());
+
+  coordinator()->queue()->GetRequests(base::Bind(
+      &RequestCoordinatorTest::GetRequestsDone, base::Unretained(this)));
+  PumpLoop();
+
+  // Both requests still in queue.
+  EXPECT_EQ(2UL, last_requests().size());
+}
+
 TEST_F(RequestCoordinatorTest, OfflinerDoneForegroundCancel) {
   // Add a request to the queue, wait for callbacks to finish.
   offline_pages::SavePageRequest request(
diff --git a/components/power/BUILD.gn b/components/power/BUILD.gn
deleted file mode 100644
index e8c347e..0000000
--- a/components/power/BUILD.gn
+++ /dev/null
@@ -1,32 +0,0 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-static_library("power") {
-  sources = [
-    "origin_power_map.cc",
-    "origin_power_map.h",
-    "origin_power_map_factory.cc",
-    "origin_power_map_factory.h",
-  ]
-
-  deps = [
-    "//base",
-    "//components/keyed_service/content",
-    "//components/keyed_service/core",
-    "//content/public/common",
-    "//url",
-  ]
-}
-
-source_set("unit_tests") {
-  testonly = true
-  sources = [
-    "origin_power_map_unittest.cc",
-  ]
-  deps = [
-    ":power",
-    "//base",
-    "//testing/gtest",
-  ]
-}
diff --git a/components/power/DEPS b/components/power/DEPS
deleted file mode 100644
index df2301d5..0000000
--- a/components/power/DEPS
+++ /dev/null
@@ -1,4 +0,0 @@
-include_rules = [
-  "+components/keyed_service",
-  "+content/public/common",
-]
diff --git a/components/power/OWNERS b/components/power/OWNERS
deleted file mode 100644
index 76a50a4..0000000
--- a/components/power/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-derat@chromium.org
-dhnishi@chromium.org
-sivachandra@chromium.org
diff --git a/components/power/origin_power_map.cc b/components/power/origin_power_map.cc
deleted file mode 100644
index 10502e9d..0000000
--- a/components/power/origin_power_map.cc
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/power/origin_power_map.h"
-
-#include <algorithm>
-
-#include "base/logging.h"
-#include "content/public/common/url_constants.h"
-#include "url/gurl.h"
-
-namespace power {
-
-OriginPowerMap::OriginPowerMap() : total_consumed_(0.0) {
-}
-
-OriginPowerMap::~OriginPowerMap() {
-}
-
-int OriginPowerMap::GetPowerForOrigin(const GURL& url) {
-  if (!total_consumed_)
-    return 0;
-
-  OriginMap::const_iterator it = origin_map_.find(url.GetOrigin());
-  return it == origin_map_.end() ? 0 :
-      static_cast<int>(it->second * 100 / total_consumed_ + 0.5);
-}
-
-void OriginPowerMap::AddPowerForOrigin(const GURL& url, double power) {
-  DCHECK_GE(power, 0);
-  GURL origin = url.GetOrigin();
-  if (!origin.is_valid() || origin.SchemeIs(content::kChromeUIScheme))
-    return;
-
-  origin_map_[origin] += power;
-  total_consumed_ += power;
-}
-
-OriginPowerMap::PercentOriginMap OriginPowerMap::GetPercentOriginMap() {
-  OriginPowerMap::PercentOriginMap percent_map;
-
-  if (!total_consumed_)
-    return percent_map;
-
-  for (OriginMap::iterator it = origin_map_.begin(); it != origin_map_.end();
-       ++it) {
-    percent_map[it->first] =
-        static_cast<int>(it->second * 100 / total_consumed_ + 0.5);
-  }
-  return percent_map;
-}
-
-std::unique_ptr<OriginPowerMap::Subscription>
-OriginPowerMap::AddPowerConsumptionUpdatedCallback(
-    const base::Closure& callback) {
-  return callback_list_.Add(callback);
-}
-
-void OriginPowerMap::OnAllOriginsUpdated() {
-  callback_list_.Notify();
-}
-
-void OriginPowerMap::ClearOriginMap(
-    const base::Callback<bool(const GURL&)> url_filter) {
-  if (url_filter.is_null()) {
-    origin_map_.clear();
-  } else {
-    for (auto it = origin_map_.begin(); it != origin_map_.end();) {
-      auto next_it = std::next(it);
-
-      if (url_filter.Run(it->first)) {
-        total_consumed_ -= it->second;
-        origin_map_.erase(it);
-      }
-
-      it = next_it;
-    }
-  }
-
-  // Handle the empty case separately to avoid reporting nonzero power usage
-  // for zero origins in case of double rounding errors.
-  if (origin_map_.empty())
-    total_consumed_ = 0;
-}
-
-}  // namespace power
diff --git a/components/power/origin_power_map.h b/components/power/origin_power_map.h
deleted file mode 100644
index 362c023..0000000
--- a/components/power/origin_power_map.h
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_POWER_ORIGIN_POWER_MAP_H_
-#define COMPONENTS_POWER_ORIGIN_POWER_MAP_H_
-
-#include <map>
-#include <memory>
-
-#include "base/callback_list.h"
-#include "base/macros.h"
-#include "components/keyed_service/core/keyed_service.h"
-#include "url/gurl.h"
-
-namespace power {
-
-// Tracks app and website origins and how much power they are consuming while
-// running.
-class OriginPowerMap : public KeyedService {
- public:
-  typedef std::map<GURL, int> PercentOriginMap;
-  typedef base::CallbackList<void(void)>::Subscription Subscription;
-
-  OriginPowerMap();
-  ~OriginPowerMap() override;
-
-  // Returns the integer percentage usage of the total power consumed by a
-  // given URL's origin.
-  int GetPowerForOrigin(const GURL& url);
-
-  // Adds a certain amount of power consumption to a given URL's origin.
-  // |power| is a platform-specific heuristic estimating power consumption.
-  void AddPowerForOrigin(const GURL& url, double power);
-
-  // Returns a map of all origins to the integer percentage usage of power
-  // consumed.
-  PercentOriginMap GetPercentOriginMap();
-
-  // Adds a callback for the completion of a round of updates to |origin_map_|.
-  std::unique_ptr<Subscription> AddPowerConsumptionUpdatedCallback(
-      const base::Closure& callback);
-
-  // Notifies observers to let them know that the origin power map has finished
-  // updating for all origins this cycle.
-  void OnAllOriginsUpdated();
-
-  // Clears URLs out of the map. If |url_filter| is not null, only clears those
-  // URLs that are matched by it.
-  void ClearOriginMap(const base::Callback<bool(const GURL&)> url_filter);
-
- private:
-  // OriginMap maps a URL to the amount of power consumed by the URL using the
-  // same units as |total_consumed_|.
-  typedef std::map<GURL, double> OriginMap;
-  OriginMap origin_map_;
-
-  // Total amount of power consumed using units determined by
-  // the power heuristics available to the platform.
-  double total_consumed_;
-
-  base::CallbackList<void(void)> callback_list_;
-
-  DISALLOW_COPY_AND_ASSIGN(OriginPowerMap);
-};
-
-}  // namespace power
-
-#endif  // COMPONENTS_POWER_ORIGIN_POWER_MAP_H_
diff --git a/components/power/origin_power_map_factory.cc b/components/power/origin_power_map_factory.cc
deleted file mode 100644
index f1df4ff..0000000
--- a/components/power/origin_power_map_factory.cc
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/power/origin_power_map_factory.h"
-
-#include "base/memory/singleton.h"
-#include "components/keyed_service/content/browser_context_dependency_manager.h"
-#include "components/power/origin_power_map.h"
-
-namespace power {
-// static
-OriginPowerMap* OriginPowerMapFactory::GetForBrowserContext(
-    content::BrowserContext* context) {
-  return static_cast<OriginPowerMap*>(
-      GetInstance()->GetServiceForBrowserContext(context, true));
-}
-
-// static
-OriginPowerMapFactory* OriginPowerMapFactory::GetInstance() {
-  return base::Singleton<OriginPowerMapFactory>::get();
-}
-
-OriginPowerMapFactory::OriginPowerMapFactory()
-    : BrowserContextKeyedServiceFactory(
-          "OriginPowerMap",
-          BrowserContextDependencyManager::GetInstance()) {
-}
-
-OriginPowerMapFactory::~OriginPowerMapFactory() {
-}
-
-KeyedService* OriginPowerMapFactory::BuildServiceInstanceFor(
-    content::BrowserContext* context) const {
-  return new OriginPowerMap();
-}
-
-}  // namespace power
diff --git a/components/power/origin_power_map_factory.h b/components/power/origin_power_map_factory.h
deleted file mode 100644
index a21ee6b4..0000000
--- a/components/power/origin_power_map_factory.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_POWER_ORIGIN_POWER_MAP_FACTORY_H_
-#define COMPONENTS_POWER_ORIGIN_POWER_MAP_FACTORY_H_
-
-#include "base/macros.h"
-#include "base/memory/singleton.h"
-#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
-
-namespace power {
-
-class OriginPowerMap;
-
-class OriginPowerMapFactory : public BrowserContextKeyedServiceFactory {
- public:
-  static OriginPowerMap* GetForBrowserContext(content::BrowserContext* context);
-  static OriginPowerMapFactory* GetInstance();
-
- private:
-  friend struct base::DefaultSingletonTraits<OriginPowerMapFactory>;
-
-  OriginPowerMapFactory();
-  ~OriginPowerMapFactory() override;
-
-  // BrowserContextKeyedServiceFactory:
-  KeyedService* BuildServiceInstanceFor(
-      content::BrowserContext* context) const override;
-
-  DISALLOW_COPY_AND_ASSIGN(OriginPowerMapFactory);
-};
-
-}  // namespace power
-
-#endif  // COMPONENTS_POWER_ORIGIN_POWER_MAP_FACTORY_H_
diff --git a/components/power/origin_power_map_unittest.cc b/components/power/origin_power_map_unittest.cc
deleted file mode 100644
index 061f9bdd..0000000
--- a/components/power/origin_power_map_unittest.cc
+++ /dev/null
@@ -1,133 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/power/origin_power_map.h"
-
-#include <stddef.h>
-
-#include "base/bind.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace {
-
-bool HostFilter(const std::string& host, const GURL& url) {
-  return url.host() == host;
-}
-
-}  // namespace
-
-namespace power {
-
-TEST(OriginPowerMapTest, StartEmpty) {
-  OriginPowerMap origin_power_map;
-  EXPECT_EQ(size_t(0), origin_power_map.GetPercentOriginMap().size());
-}
-
-TEST(OriginPowerMapTest, AddOneOriginNotInMap) {
-  OriginPowerMap origin_power_map;
-  GURL url("http://www.google.com");
-  EXPECT_EQ(0, origin_power_map.GetPowerForOrigin(url));
-  origin_power_map.AddPowerForOrigin(url, 10);
-  EXPECT_EQ(size_t(1), origin_power_map.GetPercentOriginMap().size());
-  EXPECT_EQ(100, origin_power_map.GetPowerForOrigin(url));
-}
-
-TEST(OriginPowerMapTest, AddMultiplesOrigins) {
-  OriginPowerMap origin_power_map;
-  GURL url1("http://www.google.com");
-  EXPECT_EQ(0, origin_power_map.GetPowerForOrigin(url1));
-  origin_power_map.AddPowerForOrigin(url1, 10);
-  EXPECT_EQ(size_t(1), origin_power_map.GetPercentOriginMap().size());
-  EXPECT_EQ(100, origin_power_map.GetPowerForOrigin(url1));
-
-  GURL url2("http://www.example.com");
-  origin_power_map.AddPowerForOrigin(url2, 30);
-  EXPECT_EQ(25, origin_power_map.GetPowerForOrigin(url1));
-  EXPECT_EQ(75, origin_power_map.GetPowerForOrigin(url2));
-  origin_power_map.AddPowerForOrigin(url2, 10);
-  EXPECT_EQ(20, origin_power_map.GetPowerForOrigin(url1));
-  EXPECT_EQ(80, origin_power_map.GetPowerForOrigin(url2));
-
-  GURL url3("https://www.google.com");
-  origin_power_map.AddPowerForOrigin(url3, 50);
-  EXPECT_EQ(10, origin_power_map.GetPowerForOrigin(url1));
-  EXPECT_EQ(40, origin_power_map.GetPowerForOrigin(url2));
-  EXPECT_EQ(50, origin_power_map.GetPowerForOrigin(url3));
-}
-
-TEST(OriginPowerMapTest, PercentOriginMap) {
-  OriginPowerMap origin_power_map;
-  GURL url1("http://www.google.com");
-  GURL url2("http://www.example.com");
-  origin_power_map.AddPowerForOrigin(url1, 10);
-  origin_power_map.AddPowerForOrigin(url2, 40);
-  OriginPowerMap::PercentOriginMap origin_map =
-      origin_power_map.GetPercentOriginMap();
-  EXPECT_EQ(20, origin_map.find(url1)->second);
-  EXPECT_EQ(80, origin_map.find(url2)->second);
-}
-
-TEST(OriginPowerMapTest, EmptyPercentOriginMapWhenZeroConsumed) {
-  OriginPowerMap origin_power_map;
-  EXPECT_EQ(size_t(0), origin_power_map.GetPercentOriginMap().size());
-  GURL url("http://www.google.com");
-  origin_power_map.AddPowerForOrigin(url, 0);
-  EXPECT_EQ(size_t(0), origin_power_map.GetPercentOriginMap().size());
-}
-
-TEST(OriginPowerMapTest, ClearOriginMap) {
-  GURL url1("https://www.google.com");
-  GURL url2("https://www.chrome.com");
-  GURL url3("https://www.example.com");
-  GURL url4("http://www.chrome.com");
-  GURL url5("http://www.example.com");
-
-  // Add all 5 URLs to the map.
-  OriginPowerMap origin_power_map;
-  origin_power_map.AddPowerForOrigin(url1, 50);
-  origin_power_map.AddPowerForOrigin(url2, 20);
-  origin_power_map.AddPowerForOrigin(url3, 15);
-  origin_power_map.AddPowerForOrigin(url4, 5);
-  origin_power_map.AddPowerForOrigin(url5, 10);
-  EXPECT_DOUBLE_EQ(50, origin_power_map.GetPowerForOrigin(url1));
-  EXPECT_DOUBLE_EQ(20, origin_power_map.GetPowerForOrigin(url2));
-  EXPECT_DOUBLE_EQ(15, origin_power_map.GetPowerForOrigin(url3));
-  EXPECT_DOUBLE_EQ(5, origin_power_map.GetPowerForOrigin(url4));
-  EXPECT_DOUBLE_EQ(10, origin_power_map.GetPowerForOrigin(url5));
-
-  // Delete |url1|.
-  origin_power_map.ClearOriginMap(base::Bind(
-      static_cast<bool (*)(const GURL&, const GURL&)>(operator==), url1));
-  EXPECT_DOUBLE_EQ(0, origin_power_map.GetPowerForOrigin(url1));
-  EXPECT_DOUBLE_EQ(40, origin_power_map.GetPowerForOrigin(url2));
-  EXPECT_DOUBLE_EQ(30, origin_power_map.GetPowerForOrigin(url3));
-  EXPECT_DOUBLE_EQ(10, origin_power_map.GetPowerForOrigin(url4));
-  EXPECT_DOUBLE_EQ(20, origin_power_map.GetPowerForOrigin(url5));
-
-  // Delete every URL with the host "www.chrome.com", i.e. |url2| and |url4|.
-  origin_power_map.ClearOriginMap(base::Bind(&HostFilter, "www.chrome.com"));
-  EXPECT_DOUBLE_EQ(0, origin_power_map.GetPowerForOrigin(url1));
-  EXPECT_DOUBLE_EQ(0, origin_power_map.GetPowerForOrigin(url2));
-  EXPECT_DOUBLE_EQ(60, origin_power_map.GetPowerForOrigin(url3));
-  EXPECT_DOUBLE_EQ(0, origin_power_map.GetPowerForOrigin(url4));
-  EXPECT_DOUBLE_EQ(40, origin_power_map.GetPowerForOrigin(url5));
-
-  // Delete every URL with the host "www.example.org". There should be none.
-  origin_power_map.ClearOriginMap(base::Bind(&HostFilter, "www.example.org"));
-  EXPECT_DOUBLE_EQ(0, origin_power_map.GetPowerForOrigin(url1));
-  EXPECT_DOUBLE_EQ(0, origin_power_map.GetPowerForOrigin(url2));
-  EXPECT_DOUBLE_EQ(60, origin_power_map.GetPowerForOrigin(url3));
-  EXPECT_DOUBLE_EQ(0, origin_power_map.GetPowerForOrigin(url4));
-  EXPECT_DOUBLE_EQ(40, origin_power_map.GetPowerForOrigin(url5));
-
-  // Null callback means complete deletion.
-  origin_power_map.ClearOriginMap(base::Callback<bool(const GURL&)>());
-  EXPECT_DOUBLE_EQ(0, origin_power_map.GetPowerForOrigin(url1));
-  EXPECT_DOUBLE_EQ(0, origin_power_map.GetPowerForOrigin(url2));
-  EXPECT_DOUBLE_EQ(0, origin_power_map.GetPowerForOrigin(url3));
-  EXPECT_DOUBLE_EQ(0, origin_power_map.GetPowerForOrigin(url4));
-  EXPECT_DOUBLE_EQ(0, origin_power_map.GetPowerForOrigin(url5));
-}
-
-}  // namespace power
diff --git a/components/precache/content/precache_manager.cc b/components/precache/content/precache_manager.cc
index f4333c12..327fa2b6 100644
--- a/components/precache/content/precache_manager.cc
+++ b/components/precache/content/precache_manager.cc
@@ -99,7 +99,7 @@
 }
 
 PrecacheManager::AllowedType PrecacheManager::PrecachingAllowed() const {
-  if (!(sync_service_ && sync_service_->IsBackendInitialized()))
+  if (!(sync_service_ && sync_service_->IsEngineInitialized()))
     return AllowedType::PENDING;
 
   // SyncService delegates to SyncPrefs, which must be called on the UI thread.
@@ -182,7 +182,7 @@
     }
   } else {
     if (PrecachingAllowed() != AllowedType::PENDING) {
-      // We are not waiting on the sync backend to be initialized. The user
+      // We are not waiting on the sync engine to be initialized. The user
       // either is not in the field trial, or does not have sync enabled.
       // Pretend that precaching started, so that the PrecacheServiceLauncher
       // doesn't try to start it again.
diff --git a/components/sync/driver/about_sync_util.cc b/components/sync/driver/about_sync_util.cc
index ab8082c..3a88b59 100644
--- a/components/sync/driver/about_sync_util.cc
+++ b/components/sync/driver/about_sync_util.cc
@@ -389,7 +389,7 @@
   last_synced.SetValue(service->GetLastSyncedTimeString());
   is_setup_complete.SetValue(service->IsFirstSetupComplete());
   backend_initialization.SetValue(
-      service->GetBackendInitializationStateString());
+      service->GetEngineInitializationStateString());
   if (is_status_valid) {
     is_syncing.SetValue(full_status.syncing);
     retry_time.SetValue(GetTimeStr(full_status.retry_time,
diff --git a/components/sync/driver/fake_sync_service.cc b/components/sync/driver/fake_sync_service.cc
index 5cede64..fcc3d69c 100644
--- a/components/sync/driver/fake_sync_service.cc
+++ b/components/sync/driver/fake_sync_service.cc
@@ -92,7 +92,7 @@
   return false;
 }
 
-bool FakeSyncService::IsBackendInitialized() const {
+bool FakeSyncService::IsEngineInitialized() const {
   return false;
 }
 
@@ -166,7 +166,7 @@
   return base::string16();
 }
 
-std::string FakeSyncService::GetBackendInitializationStateString() const {
+std::string FakeSyncService::GetEngineInitializationStateString() const {
   return std::string();
 }
 
diff --git a/components/sync/driver/fake_sync_service.h b/components/sync/driver/fake_sync_service.h
index 2c4a59f..fa031a83 100644
--- a/components/sync/driver/fake_sync_service.h
+++ b/components/sync/driver/fake_sync_service.h
@@ -49,7 +49,7 @@
   bool ConfigurationDone() const override;
   const GoogleServiceAuthError& GetAuthError() const override;
   bool HasUnrecoverableError() const override;
-  bool IsBackendInitialized() const override;
+  bool IsEngineInitialized() const override;
   sync_sessions::OpenTabsUIDelegate* GetOpenTabsUIDelegate() override;
   bool IsPassphraseRequiredForDecryption() const override;
   base::Time GetExplicitPassphraseTime() const override;
@@ -69,7 +69,7 @@
   std::string QuerySyncStatusSummaryString() override;
   bool QueryDetailedSyncStatus(SyncStatus* result) override;
   base::string16 GetLastSyncedTimeString() const override;
-  std::string GetBackendInitializationStateString() const override;
+  std::string GetEngineInitializationStateString() const override;
   SyncCycleSnapshot GetLastCycleSnapshot() const override;
   std::unique_ptr<base::Value> GetTypeStatusMap() override;
   const GURL& sync_service_url() const override;
diff --git a/components/sync/driver/glue/sync_backend_host_core.h b/components/sync/driver/glue/sync_backend_host_core.h
index bdec45a..641fed6 100644
--- a/components/sync/driver/glue/sync_backend_host_core.h
+++ b/components/sync/driver/glue/sync_backend_host_core.h
@@ -291,7 +291,7 @@
   std::unique_ptr<SyncManager> sync_manager_;
 
   // Temporary holder of sync manager's initialization results. Set by
-  // OnInitializeComplete, and consumed when we pass it via OnBackendInitialized
+  // OnInitializeComplete, and consumed when we pass it via OnEngineInitialized
   // in the final state of HandleInitializationSuccessOnFrontendLoop.
   WeakHandle<JsBackend> js_backend_;
   WeakHandle<DataTypeDebugInfoListener> debug_info_listener_;
diff --git a/components/sync/driver/glue/sync_backend_host_impl.cc b/components/sync/driver/glue/sync_backend_host_impl.cc
index b276ae2..77be61b 100644
--- a/components/sync/driver/glue/sync_backend_host_impl.cc
+++ b/components/sync/driver/glue/sync_backend_host_impl.cc
@@ -562,15 +562,14 @@
   // experimental types to enable. This should be done before we inform
   // the host to ensure they're visible in the customize screen.
   AddExperimentalTypes();
-  host_->OnBackendInitialized(js_backend, debug_info_listener, cache_guid,
-                              true);
+  host_->OnEngineInitialized(js_backend, debug_info_listener, cache_guid, true);
 }
 
 void SyncBackendHostImpl::HandleInitializationFailureOnFrontendLoop() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  host_->OnBackendInitialized(WeakHandle<JsBackend>(),
-                              WeakHandle<DataTypeDebugInfoListener>(), "",
-                              false);
+  host_->OnEngineInitialized(WeakHandle<JsBackend>(),
+                             WeakHandle<DataTypeDebugInfoListener>(), "",
+                             false);
 }
 
 void SyncBackendHostImpl::HandleSyncCycleCompletedOnFrontendLoop(
diff --git a/components/sync/driver/glue/sync_backend_host_impl_unittest.cc b/components/sync/driver/glue/sync_backend_host_impl_unittest.cc
index 9572893..f615bb0 100644
--- a/components/sync/driver/glue/sync_backend_host_impl_unittest.cc
+++ b/components/sync/driver/glue/sync_backend_host_impl_unittest.cc
@@ -75,7 +75,7 @@
  public:
   virtual ~MockSyncEngineHost() {}
 
-  MOCK_METHOD4(OnBackendInitialized,
+  MOCK_METHOD4(OnEngineInitialized,
                void(const WeakHandle<JsBackend>&,
                     const WeakHandle<DataTypeDebugInfoListener>&,
                     const std::string&,
@@ -205,7 +205,7 @@
 
   // Synchronously initializes the backend.
   void InitializeBackend(bool expect_success) {
-    EXPECT_CALL(mock_host_, OnBackendInitialized(_, _, _, expect_success))
+    EXPECT_CALL(mock_host_, OnEngineInitialized(_, _, _, expect_success))
         .WillOnce(InvokeWithoutArgs(QuitMessageLoop));
     SyncEngine::HttpPostProviderFactoryGetter
         http_post_provider_factory_getter =
diff --git a/components/sync/driver/sync_service.h b/components/sync/driver/sync_service.h
index 51e40722..a07f5d5e 100644
--- a/components/sync/driver/sync_service.h
+++ b/components/sync/driver/sync_service.h
@@ -64,7 +64,7 @@
   };
 
   // Passed as an argument to RequestStop to control whether or not the sync
-  // backend should clear its data directory when it shuts down. See
+  // engine should clear its data directory when it shuts down. See
   // RequestStop for more information.
   enum SyncStopDataFate {
     KEEP_DATA,
@@ -75,7 +75,7 @@
   struct SyncTokenStatus {
     SyncTokenStatus();
 
-    // Sync server connection status reported by sync backend.
+    // Sync server connection status reported by sync engine.
     base::Time connection_status_update_time;
     ConnectionStatus connection_status;
 
@@ -147,7 +147,7 @@
   virtual bool CanSyncStart() const = 0;
 
   // Stops sync at the user's request. |data_fate| controls whether the sync
-  // backend should clear its data directory when it shuts down. Generally
+  // engine should clear its data directory when it shuts down. Generally
   // KEEP_DATA is used when the user just stops sync, and CLEAR_DATA is used
   // when they sign out of the profile entirely.
   virtual void RequestStop(SyncStopDataFate data_fate) = 0;
@@ -201,7 +201,7 @@
   virtual bool HasUnrecoverableError() const = 0;
 
   // Returns true if the SyncEngine has told us it's ready to accept changes.
-  virtual bool IsBackendInitialized() const = 0;
+  virtual bool IsEngineInitialized() const = 0;
 
   // Return the active OpenTabsUIDelegate. If open/proxy tabs is not enabled or
   // not currently syncing, returns nullptr.
@@ -217,7 +217,7 @@
   virtual base::Time GetExplicitPassphraseTime() const = 0;
 
   // Returns true if a secondary (explicit) passphrase is being used. It is not
-  // legal to call this method before the backend is initialized.
+  // legal to call this method before the engine is initialized.
   virtual bool IsUsingSecondaryPassphrase() const = 0;
 
   // Turns on encryption for all data. Callers must call OnUserChoseDatatypes()
@@ -273,16 +273,16 @@
   // Get a description of the sync status for displaying in the user interface.
   virtual std::string QuerySyncStatusSummaryString() = 0;
 
-  // Initializes a struct of status indicators with data from the backend.
-  // Returns false if the backend was not available for querying; in that case
+  // Initializes a struct of status indicators with data from the engine.
+  // Returns false if the engine was not available for querying; in that case
   // the struct will be filled with default data.
   virtual bool QueryDetailedSyncStatus(SyncStatus* result) = 0;
 
   // Returns a user-friendly string form of last synced time (in minutes).
   virtual base::string16 GetLastSyncedTimeString() const = 0;
 
-  // Returns a human readable string describing backend initialization state.
-  virtual std::string GetBackendInitializationStateString() const = 0;
+  // Returns a human readable string describing engine initialization state.
+  virtual std::string GetEngineInitializationStateString() const = 0;
 
   virtual SyncCycleSnapshot GetLastCycleSnapshot() const = 0;
 
diff --git a/components/sync/engine/fake_sync_engine.cc b/components/sync/engine/fake_sync_engine.cc
index a6ab2bb..969ea891 100644
--- a/components/sync/engine/fake_sync_engine.cc
+++ b/components/sync/engine/fake_sync_engine.cc
@@ -29,9 +29,9 @@
     const base::Closure& report_unrecoverable_error_function,
     const HttpPostProviderFactoryGetter& http_post_provider_factory_getter,
     std::unique_ptr<SyncEncryptionHandler::NigoriState> saved_nigori_state) {
-  host->OnBackendInitialized(WeakHandle<JsBackend>(),
-                             WeakHandle<DataTypeDebugInfoListener>(),
-                             kTestCacheGuid, !fail_initial_download_);
+  host->OnEngineInitialized(WeakHandle<JsBackend>(),
+                            WeakHandle<DataTypeDebugInfoListener>(),
+                            kTestCacheGuid, !fail_initial_download_);
 }
 
 void FakeSyncEngine::TriggerRefresh(const ModelTypeSet& types) {}
diff --git a/components/sync/engine/sync_engine_host.h b/components/sync/engine/sync_engine_host.h
index 55b670b..c04289b 100644
--- a/components/sync/engine/sync_engine_host.h
+++ b/components/sync/engine/sync_engine_host.h
@@ -34,20 +34,19 @@
   SyncEngineHost();
   virtual ~SyncEngineHost();
 
-  // The backend has completed initialization and it is now ready to
-  // accept and process changes.  If success is false, initialization
-  // wasn't able to be completed and should be retried.
+  // The engine has completed initialization and it is now ready to accept and
+  // process changes. If success is false, initialization wasn't able to be
+  // completed and should be retried.
   //
-  // |js_backend| is what about:sync interacts with; it's different
-  // from the 'Backend' in 'OnBackendInitialized' (unfortunately).  It
-  // is initialized only if |success| is true.
-  virtual void OnBackendInitialized(
+  // |js_backend| is what about:sync interacts with. It is initialized only if
+  // |success| is true.
+  virtual void OnEngineInitialized(
       const WeakHandle<JsBackend>& js_backend,
       const WeakHandle<DataTypeDebugInfoListener>& debug_info_listener,
       const std::string& cache_guid,
       bool success) = 0;
 
-  // The backend queried the server recently and received some updates.
+  // The engine queried the server recently and received some updates.
   virtual void OnSyncCycleCompleted() = 0;
 
   // Informs the host of some network event. These notifications are disabled by
@@ -60,7 +59,7 @@
   // Called when we receive an updated commit counter for a directory type.
   //
   // Disabled by default.  Enable by calling
-  // EnableDirectoryTypeDebugInfoForwarding() on the backend.
+  // EnableDirectoryTypeDebugInfoForwarding() on the engine.
   virtual void OnDirectoryTypeCommitCounterUpdated(
       ModelType type,
       const CommitCounters& counters) = 0;
@@ -68,7 +67,7 @@
   // Called when we receive an updated update counter for a directory type.
   //
   // Disabled by default.  Enable by calling
-  // EnableDirectoryTypeDebugInfoForwarding() on the backend.
+  // EnableDirectoryTypeDebugInfoForwarding() on the engine.
   virtual void OnDirectoryTypeUpdateCounterUpdated(
       ModelType type,
       const UpdateCounters& counters) = 0;
@@ -76,7 +75,7 @@
   // Called when we receive an updated status counter for a datatype.
   //
   // Disabled by default.  Enable by calling
-  // EnableDirectoryTypeDebugInfoForwarding() on the backend.
+  // EnableDirectoryTypeDebugInfoForwarding() on the engine.
   virtual void OnDatatypeStatusCounterUpdated(
       ModelType type,
       const StatusCounters& counters) = 0;
@@ -131,7 +130,7 @@
   //
   // |nigori_state| contains the new (post custom passphrase) encryption keys
   // and can be used to restore SyncEncryptionHandler's state across sync
-  // backend instances. See also SyncEncryptionHandlerImpl::RestoreNigori.
+  // engine instances. See also SyncEncryptionHandlerImpl::RestoreNigori.
   virtual void OnLocalSetPassphraseEncryption(
       const SyncEncryptionHandler::NigoriState& nigori_state) = 0;
 };
diff --git a/components/variations/BUILD.gn b/components/variations/BUILD.gn
index 925210b3..bc7008a 100644
--- a/components/variations/BUILD.gn
+++ b/components/variations/BUILD.gn
@@ -93,6 +93,22 @@
   }
 }
 
+static_library("test_support") {
+  testonly = true
+  sources = [
+    "variations_params_manager.cc",
+    "variations_params_manager.h",
+  ]
+
+  public_deps = [
+    ":variations",
+  ]
+
+  deps = [
+    "//base/test:test_support",
+  ]
+}
+
 source_set("unit_tests") {
   testonly = true
   sources = [
diff --git a/components/variations/variations_associated_data.cc b/components/variations/variations_associated_data.cc
index ec12245..465aed5 100644
--- a/components/variations/variations_associated_data.cc
+++ b/components/variations/variations_associated_data.cc
@@ -11,6 +11,7 @@
 #include "base/feature_list.h"
 #include "base/macros.h"
 #include "base/memory/singleton.h"
+#include "base/metrics/field_trial.h"
 #include "base/metrics/field_trial_param_associator.h"
 #include "base/strings/string_split.h"
 #include "components/variations/variations_http_header_provider.h"
@@ -19,8 +20,6 @@
 
 namespace {
 
-const char kGroupTesting[] = "Testing";
-
 // The internal singleton accessor for the map, used to keep it thread-safe.
 class GroupMapAccessor {
  public:
@@ -195,26 +194,6 @@
 // They simply wrap existing functions in this file.
 namespace testing {
 
-VariationParamsManager::VariationParamsManager(
-    const std::string& trial_name,
-    const std::map<std::string, std::string>& params) {
-  SetVariationParams(trial_name, params);
-}
-
-VariationParamsManager::~VariationParamsManager() {
-  ClearAllVariationIDs();
-  ClearAllVariationParams();
-  field_trial_list_.reset();
-}
-
-void VariationParamsManager::SetVariationParams(
-    const std::string& trial_name,
-    const std::map<std::string, std::string>& params) {
-  field_trial_list_.reset(new base::FieldTrialList(nullptr));
-  variations::AssociateVariationParams(trial_name, kGroupTesting, params);
-  base::FieldTrialList::CreateFieldTrial(trial_name, kGroupTesting);
-}
-
 void ClearAllVariationIDs() {
   GroupMapAccessor::GetInstance()->ClearAllMapsForTesting();
 }
diff --git a/components/variations/variations_associated_data.h b/components/variations/variations_associated_data.h
index 046c2b6..0734e8b 100644
--- a/components/variations/variations_associated_data.h
+++ b/components/variations/variations_associated_data.h
@@ -10,7 +10,6 @@
 #include <string>
 #include <vector>
 
-#include "base/metrics/field_trial.h"
 #include "components/variations/active_field_trials.h"
 
 // This file provides various helpers that extend the functionality around
@@ -45,7 +44,7 @@
 
 namespace base {
 struct Feature;
-}
+}  // namespace base
 
 namespace variations {
 
@@ -163,35 +162,14 @@
 // Expose some functions for testing.
 namespace testing {
 
-// Use this class as a member in your test class to set variation params for
-// your tests. You can directly set the parameters in the constructor (if they
-// are used by other members upon construction). You can change them later
-// arbitrarily many times using the SetVariationParams function. Internally, it
-// creates a FieldTrialList as a member. It works well for multiple tests of a
-// given test class, as it clears the parameters when this class is destructed.
-// Note that it clears all parameters (not just those registered here).
-class VariationParamsManager {
- public:
-  VariationParamsManager(const std::string& trial_name,
-                         const std::map<std::string, std::string>& params);
-  ~VariationParamsManager();
-
-  // Associates |params| with the given |trial_name|. It creates a new group,
-  // used only for testing. Between two calls of this function,
-  // ClearAllVariationParams() has to be called.
-  void SetVariationParams(const std::string& trial_name,
-                          const std::map<std::string, std::string>& params);
-
- private:
-  std::unique_ptr<base::FieldTrialList> field_trial_list_;
-
-  DISALLOW_COPY_AND_ASSIGN(VariationParamsManager);
-};
-
-// Clears all of the mapped associations.
+// Clears all of the mapped associations. Deprecated, try to use
+// VariationParamsManager instead as it does a lot of work for you
+// automatically.
 void ClearAllVariationIDs();
 
-// Clears all of the associated params.
+// Clears all of the associated params. Deprecated, try to use
+// VariationParamsManager instead as it does a lot of work for you
+// automatically.
 void ClearAllVariationParams();
 
 }  // namespace testing
diff --git a/components/variations/variations_params_manager.cc b/components/variations/variations_params_manager.cc
new file mode 100644
index 0000000..e4b92cc
--- /dev/null
+++ b/components/variations/variations_params_manager.cc
@@ -0,0 +1,92 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/variations/variations_params_manager.h"
+
+#include <utility>
+
+#include "base/feature_list.h"
+#include "base/metrics/field_trial.h"
+#include "base/test/scoped_feature_list.h"
+#include "components/variations/variations_associated_data.h"
+
+namespace variations {
+namespace testing {
+
+namespace {
+
+// The fixed testing group created in the provided trail when setting up params.
+const char kGroupTesting[] = "Testing";
+
+base::FieldTrial* CreateFieldTrialWithParams(
+    const std::string& trial_name,
+    const std::map<std::string, std::string>& param_values) {
+  variations::AssociateVariationParams(trial_name, kGroupTesting, param_values);
+  return base::FieldTrialList::CreateFieldTrial(trial_name, kGroupTesting);
+}
+
+}  // namespace
+
+VariationParamsManager::VariationParamsManager()
+    : field_trial_list_(new base::FieldTrialList(nullptr)),
+      scoped_feature_list_(new base::test::ScopedFeatureList()) {}
+
+VariationParamsManager::VariationParamsManager(
+    const std::string& trial_name,
+    const std::map<std::string, std::string>& param_values)
+    : VariationParamsManager() {
+  SetVariationParams(trial_name, param_values);
+}
+
+VariationParamsManager::VariationParamsManager(
+    const std::string& trial_name,
+    const std::map<std::string, std::string>& param_values,
+    const std::set<std::string>& associated_features)
+    : VariationParamsManager() {
+  SetVariationParamsWithFeatureAssociations(trial_name, param_values,
+                                            associated_features);
+}
+
+VariationParamsManager::~VariationParamsManager() {
+  ClearAllVariationIDs();
+  ClearAllVariationParams();
+}
+
+void VariationParamsManager::SetVariationParams(
+    const std::string& trial_name,
+    const std::map<std::string, std::string>& param_values) {
+  CreateFieldTrialWithParams(trial_name, param_values);
+}
+
+void VariationParamsManager::SetVariationParamsWithFeatureAssociations(
+    const std::string& trial_name,
+    const std::map<std::string, std::string>& param_values,
+    const std::set<std::string>& associated_features) {
+  base::FieldTrial* field_trial =
+      CreateFieldTrialWithParams(trial_name, param_values);
+
+  std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
+  for (const std::string& feature_name : associated_features) {
+    feature_list->RegisterFieldTrialOverride(
+              feature_name,
+              base::FeatureList::OverrideState::OVERRIDE_ENABLE_FEATURE,
+              field_trial);
+  }
+
+  scoped_feature_list_->InitWithFeatureList(std::move(feature_list));
+}
+
+void VariationParamsManager::ClearAllVariationIDs() {
+  variations::testing::ClearAllVariationIDs();
+}
+
+void VariationParamsManager::ClearAllVariationParams() {
+  variations::testing::ClearAllVariationParams();
+  // When the scoped feature list is destroyed, it puts back the original
+  // feature list that was there when InitWithFeatureList() was called.
+  scoped_feature_list_.reset(new base::test::ScopedFeatureList());
+}
+
+}  // namespace testing
+}  // namespace variations
diff --git a/components/variations/variations_params_manager.h b/components/variations/variations_params_manager.h
new file mode 100644
index 0000000..6a65e18
--- /dev/null
+++ b/components/variations/variations_params_manager.h
@@ -0,0 +1,83 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VARIATIONS_VARIATIONS_PARAMS_MANAGER_H_
+#define COMPONENTS_VARIATIONS_VARIATIONS_PARAMS_MANAGER_H_
+
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+
+#include "base/macros.h"
+#include "base/metrics/field_trial.h"
+
+namespace base {
+class FieldTrialList;
+
+namespace test {
+class ScopedFeatureList;
+}  // namespace test
+
+}  // namespace base
+
+namespace variations {
+namespace testing {
+
+// Use this class as a member in your test class to set variation params for
+// your tests. You can directly set the parameters in the constructor (if they
+// are used by other members upon construction). You can change them later
+// arbitrarily many times using the SetVariationParams function. Internally, it
+// creates a FieldTrialList as a member. It works well for multiple tests of a
+// given test class, as it clears the parameters when this class is destructed.
+// Note that it clears all parameters (not just those registered here).
+class VariationParamsManager {
+ public:
+  // Does not associate any parameters.
+  VariationParamsManager();
+  // Calls directly SetVariationParams with the provided arguments.
+  VariationParamsManager(
+      const std::string& trial_name,
+      const std::map<std::string, std::string>& param_values);
+  // Calls directly SetVariationParamsWithFeatures with the provided arguments.
+  VariationParamsManager(
+      const std::string& trial_name,
+      const std::map<std::string, std::string>& param_values,
+      const std::set<std::string>& associated_features);
+  ~VariationParamsManager();
+
+  // Associates |param_values| with the given |trial_name|. |param_values| maps
+  // parameter names to their values. The function creates a new trial group,
+  // used only for testing. Between two calls of this function,
+  // ClearAllVariationParams() has to be called.
+  void SetVariationParams(
+      const std::string& trial_name,
+      const std::map<std::string, std::string>& param_values);
+
+  // Like SetVariationParams(). |associated_features| lists names of features
+  // to be associated to the newly created trial group. As a result, all
+  // parameters from |param_values| can be accessed via any of the feature from
+  // |associated_features|.
+  void SetVariationParamsWithFeatureAssociations(
+      const std::string& trial_name,
+      const std::map<std::string, std::string>& param_values,
+      const std::set<std::string>& associated_features);
+
+  // Clears all of the mapped associations.
+  void ClearAllVariationIDs();
+
+  // Clears all of the associated params.
+  void ClearAllVariationParams();
+
+ private:
+  std::unique_ptr<base::FieldTrialList> field_trial_list_;
+  std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(VariationParamsManager);
+};
+
+}  // namespace testing
+}  // namespace variations
+
+#endif  // COMPONENTS_VARIATIONS_VARIATIONS_PARAMS_MANAGER_H_
diff --git a/content/browser/blob_storage/blob_flattener_unittest.cc b/content/browser/blob_storage/blob_flattener_unittest.cc
index b67066a..3e70dffc 100644
--- a/content/browser/blob_storage/blob_flattener_unittest.cc
+++ b/content/browser/blob_storage/blob_flattener_unittest.cc
@@ -6,11 +6,9 @@
 
 #include <memory>
 
-#include "base/bind.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
-#include "base/memory/ptr_util.h"
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
 #include "base/test/test_simple_task_runner.h"
@@ -19,7 +17,6 @@
 #include "storage/browser/blob/blob_data_handle.h"
 #include "storage/browser/blob/blob_data_item.h"
 #include "storage/browser/blob/blob_entry.h"
-#include "storage/browser/blob/blob_memory_controller.h"
 #include "storage/browser/blob/blob_storage_registry.h"
 #include "storage/browser/blob/shareable_blob_data_item.h"
 #include "storage/common/data_element.h"
@@ -28,26 +25,9 @@
 namespace storage {
 namespace {
 using base::TestSimpleTaskRunner;
-using FileCreationInfo = BlobMemoryController::FileCreationInfo;
 
 const char kType[] = "type";
 const char kDisposition[] = "";
-const size_t kTestBlobStorageIPCThresholdBytes = 20;
-const size_t kTestBlobStorageMaxSharedMemoryBytes = 50;
-
-const size_t kTestBlobStorageMaxBlobMemorySize = 400;
-const uint64_t kTestBlobStorageMaxDiskSpace = 4000;
-const uint64_t kTestBlobStorageMinFileSizeBytes = 10;
-const uint64_t kTestBlobStorageMaxFileSizeBytes = 100;
-
-void SaveBlobStatusAndFiles(BlobStatus* status_ptr,
-                            std::vector<FileCreationInfo>* files_ptr,
-                            BlobStatus status,
-                            std::vector<FileCreationInfo> files) {
-  EXPECT_FALSE(BlobStatusIsError(status));
-  *status_ptr = status;
-  std::move(files.begin(), files.end(), std::back_inserter(*files_ptr));
-}
 
 }  // namespace
 
@@ -59,15 +39,11 @@
       : fake_file_path_(base::FilePath(FILE_PATH_LITERAL("kFakePath"))) {}
   ~BlobFlattenerTest() override {}
 
-  void SetUp() override {
-    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
-    context_ = base::MakeUnique<BlobStorageContext>();
-  }
+  void SetUp() override { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); }
 
   void TearDown() override {
     base::RunLoop().RunUntilIdle();
     file_runner_->RunPendingTasks();
-    base::RunLoop().RunUntilIdle();
     ASSERT_TRUE(temp_dir_.Delete());
   }
 
@@ -90,21 +66,14 @@
     return scoped_refptr<BlobDataItem>(new BlobDataItem(std::move(element)));
   };
 
-  scoped_refptr<BlobDataItem> CreateFutureFileItem(size_t offset, size_t size) {
-    std::unique_ptr<DataElement> element(new DataElement());
-    element->SetToFilePathRange(BlobDataBuilder::GetFutureFileItemPath(0),
-                                offset, size, base::Time());
-    return scoped_refptr<BlobDataItem>(new BlobDataItem(std::move(element)));
-  };
-
   std::unique_ptr<BlobDataHandle> SetupBasicBlob(const std::string& id) {
     BlobDataBuilder builder(id);
     builder.AppendData("1", 1);
     builder.set_content_type("text/plain");
-    return context_->AddFinishedBlob(builder);
+    return context_.AddFinishedBlob(builder);
   }
 
-  BlobStorageRegistry* registry() { return context_->mutable_registry(); }
+  BlobStorageRegistry* registry() { return context_.mutable_registry(); }
 
   const ShareableBlobDataItem& GetItemInBlob(const std::string& uuid,
                                              size_t index) {
@@ -113,23 +82,12 @@
     return *entry->items()[index];
   }
 
-  void SetTestMemoryLimits() {
-    BlobStorageLimits limits;
-    limits.max_ipc_memory_size = kTestBlobStorageIPCThresholdBytes;
-    limits.max_shared_memory_size = kTestBlobStorageMaxSharedMemoryBytes;
-    limits.max_blob_in_memory_space = kTestBlobStorageMaxBlobMemorySize;
-    limits.max_blob_disk_space = kTestBlobStorageMaxDiskSpace;
-    limits.min_page_file_size = kTestBlobStorageMinFileSizeBytes;
-    limits.max_file_size = kTestBlobStorageMaxFileSizeBytes;
-    context_->mutable_memory_controller()->set_limits_for_testing(limits);
-  }
-
   base::FilePath fake_file_path_;
   base::ScopedTempDir temp_dir_;
   scoped_refptr<TestSimpleTaskRunner> file_runner_ = new TestSimpleTaskRunner();
 
   base::MessageLoop fake_io_message_loop;
-  std::unique_ptr<BlobStorageContext> context_;
+  BlobStorageContext context_;
 };
 
 TEST_F(BlobFlattenerTest, NoBlobItems) {
@@ -145,7 +103,7 @@
   EXPECT_EQ(0u, flattener.dependent_blobs.size());
   EXPECT_EQ(0u, flattener.copies.size());
   EXPECT_EQ(12u, flattener.total_size);
-  EXPECT_EQ(2u, flattener.transport_quota_needed);
+  EXPECT_EQ(2u, flattener.memory_quota_needed);
 
   ASSERT_EQ(2u, output.items().size());
   EXPECT_EQ(*CreateDataItem("hi", 2u), *output.items()[0]->item());
@@ -189,7 +147,6 @@
   const std::string kBlobUUID = "kId";
   const std::string kDataBlob = "kId2";
   const std::string kFileBlob = "kId3";
-  const std::string kPendingFileBlob = "kId4";
 
   // We have the following:
   // * data,
@@ -198,35 +155,19 @@
   // * full data blob,
   // * pending data,
 
-  context_ =
-      base::MakeUnique<BlobStorageContext>(temp_dir_.GetPath(), file_runner_);
-  SetTestMemoryLimits();
-
   std::unique_ptr<BlobDataHandle> data_blob;
   {
     BlobDataBuilder builder(kDataBlob);
     builder.AppendData("12345", 5);
     builder.set_content_type("text/plain");
-    data_blob = context_->AddFinishedBlob(builder);
+    data_blob = context_.AddFinishedBlob(builder);
   }
 
   std::unique_ptr<BlobDataHandle> file_blob;
   {
     BlobDataBuilder builder(kFileBlob);
     builder.AppendFile(fake_file_path_, 1u, 10u, base::Time::Max());
-    file_blob = context_->AddFinishedBlob(builder);
-  }
-
-  BlobStatus file_status = BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
-  std::vector<FileCreationInfo> file_handles;
-  std::unique_ptr<BlobDataHandle> future_file_blob;
-  {
-    BlobDataBuilder builder(kPendingFileBlob);
-    builder.AppendFutureFile(0u, 2u, 0);
-    builder.AppendFutureFile(2u, 5u, 0);
-    future_file_blob = context_->BuildBlob(
-        builder,
-        base::Bind(&SaveBlobStatusAndFiles, &file_status, &file_handles));
+    file_blob = context_.AddFinishedBlob(builder);
   }
 
   BlobDataBuilder builder(kBlobUUID);
@@ -236,47 +177,28 @@
   builder.AppendBlob(kDataBlob);
   builder.AppendBlob(kFileBlob, 1u, 3u);
   builder.AppendFutureData(12u);
-  builder.AppendBlob(kPendingFileBlob, 1u, 3u);
 
   BlobEntry output(kType, kDisposition);
   BlobFlattener flattener(builder, &output, registry());
   EXPECT_EQ(BlobStatus::PENDING_QUOTA, flattener.status);
 
-  EXPECT_EQ(3u, flattener.dependent_blobs.size());
-  EXPECT_EQ(32u, flattener.total_size);
-  EXPECT_EQ(14u, flattener.transport_quota_needed);
-  EXPECT_EQ(2u, flattener.copy_quota_needed);
+  EXPECT_EQ(2u, flattener.dependent_blobs.size());
+  EXPECT_EQ(29u, flattener.total_size);
+  EXPECT_EQ(16u, flattener.memory_quota_needed);
 
-  ASSERT_EQ(8u, output.items().size());
+  ASSERT_EQ(6u, output.items().size());
   EXPECT_EQ(*CreateDataItem("hi", 2u), *output.items()[0]->item());
   EXPECT_EQ(*CreateDataDescriptionItem(2u), *output.items()[1]->item());
   EXPECT_EQ(*CreateFileItem(3u, 5u), *output.items()[2]->item());
   EXPECT_EQ(GetItemInBlob(kDataBlob, 0), *output.items()[3]);
   EXPECT_EQ(*CreateFileItem(2u, 3u), *output.items()[4]->item());
   EXPECT_EQ(*CreateDataDescriptionItem(12u), *output.items()[5]->item());
-  EXPECT_EQ(*CreateFutureFileItem(1u, 1u), *output.items()[6]->item());
-  EXPECT_EQ(*CreateFutureFileItem(2u, 2u), *output.items()[7]->item());
 
-  // We're copying items at index 1, 6, and 7.
-  ASSERT_EQ(3u, flattener.copies.size());
+  // We're copying item at index 1
+  ASSERT_EQ(1u, flattener.copies.size());
   EXPECT_EQ(*flattener.copies[0].dest_item, *output.items()[1]);
   EXPECT_EQ(GetItemInBlob(kDataBlob, 0), *flattener.copies[0].source_item);
   EXPECT_EQ(1u, flattener.copies[0].source_item_offset);
-  EXPECT_EQ(*flattener.copies[1].dest_item, *output.items()[6]);
-  EXPECT_EQ(GetItemInBlob(kPendingFileBlob, 0),
-            *flattener.copies[1].source_item);
-  EXPECT_EQ(1u, flattener.copies[1].source_item_offset);
-  EXPECT_EQ(*flattener.copies[2].dest_item, *output.items()[7]);
-  EXPECT_EQ(GetItemInBlob(kPendingFileBlob, 1),
-            *flattener.copies[2].source_item);
-  EXPECT_EQ(0u, flattener.copies[2].source_item_offset);
-
-  // Clean up temp files.
-  EXPECT_TRUE(file_runner_->HasPendingTask());
-  file_runner_->RunPendingTasks();
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(BlobStatus::PENDING_TRANSPORT, file_status);
-  EXPECT_FALSE(file_handles.empty());
 }
 
 }  // namespace storage
diff --git a/content/browser/blob_storage/blob_memory_controller_unittest.cc b/content/browser/blob_storage/blob_memory_controller_unittest.cc
index ae0e796c..290a40be 100644
--- a/content/browser/blob_storage/blob_memory_controller_unittest.cc
+++ b/content/browser/blob_storage/blob_memory_controller_unittest.cc
@@ -74,7 +74,7 @@
     controller->set_limits_for_testing(limits);
   }
 
-  void SaveFileCreationInfo(std::vector<FileCreationInfo> info, bool success) {
+  void SaveFileCreationInfo(bool success, std::vector<FileCreationInfo> info) {
     file_quota_result_ = success;
     if (success) {
       files_created_.swap(info);
diff --git a/content/browser/blob_storage/blob_slice_unittest.cc b/content/browser/blob_storage/blob_slice_unittest.cc
index 97d2bd0..e7266fd3 100644
--- a/content/browser/blob_storage/blob_slice_unittest.cc
+++ b/content/browser/blob_storage/blob_slice_unittest.cc
@@ -48,16 +48,6 @@
         ShareableBlobDataItem::POPULATED_WITHOUT_QUOTA));
   };
 
-  scoped_refptr<ShareableBlobDataItem> CreateTempFileItem(size_t offset,
-                                                          size_t size) {
-    std::unique_ptr<DataElement> element(new DataElement());
-    element->SetToFilePathRange(BlobDataBuilder::GetFutureFileItemPath(0),
-                                offset, size, base::Time());
-    return scoped_refptr<ShareableBlobDataItem>(
-        new ShareableBlobDataItem(new BlobDataItem(std::move(element)),
-                                  ShareableBlobDataItem::QUOTA_NEEDED));
-  };
-
   void ExpectFirstSlice(const BlobSlice& slice,
                         scoped_refptr<ShareableBlobDataItem> source_item,
                         size_t first_item_slice_offset,
@@ -66,10 +56,8 @@
     EXPECT_EQ(first_item_slice_offset, slice.first_item_slice_offset);
 
     ASSERT_LE(1u, slice.dest_items.size());
-
-    scoped_refptr<ShareableBlobDataItem> item = slice.dest_items[0];
-    EXPECT_EQ(ShareableBlobDataItem::QUOTA_NEEDED, item->state());
-    const DataElement& dest_element = item->item()->data_element();
+    const DataElement& dest_element =
+        slice.dest_items[0]->item()->data_element();
 
     EXPECT_EQ(DataElement::TYPE_BYTES_DESCRIPTION, dest_element.type());
     EXPECT_EQ(static_cast<uint64_t>(size), dest_element.length());
@@ -83,9 +71,8 @@
     EXPECT_TRUE(slice.last_source_item);
 
     ASSERT_LE(2u, slice.dest_items.size());
-    scoped_refptr<ShareableBlobDataItem> item = slice.dest_items.back();
-    EXPECT_EQ(ShareableBlobDataItem::QUOTA_NEEDED, item->state());
-    const DataElement& dest_element = item->item()->data_element();
+    const DataElement& dest_element =
+        slice.dest_items.back()->item()->data_element();
 
     EXPECT_EQ(DataElement::TYPE_BYTES_DESCRIPTION, dest_element.type());
     EXPECT_EQ(static_cast<uint64_t>(size), dest_element.length());
@@ -95,6 +82,7 @@
 };
 
 TEST_F(BlobSliceTest, FullItem) {
+  const std::string kBlobUUID = "kId";
   const size_t kSize = 5u;
 
   BlobEntry data(kType, kDisposition);
@@ -112,6 +100,7 @@
 }
 
 TEST_F(BlobSliceTest, SliceSingleItem) {
+  const std::string kBlobUUID = "kId";
   const size_t kSize = 5u;
 
   BlobEntry data(kType, kDisposition);
@@ -126,6 +115,7 @@
 }
 
 TEST_F(BlobSliceTest, SliceSingleLastItem) {
+  const std::string kBlobUUID = "kId";
   const size_t kSize1 = 5u;
   const size_t kSize2 = 10u;
 
@@ -142,6 +132,7 @@
 }
 
 TEST_F(BlobSliceTest, SliceAcrossTwoItems) {
+  const std::string kBlobUUID = "kId";
   const size_t kSize1 = 5u;
   const size_t kSize2 = 10u;
 
@@ -159,6 +150,7 @@
 }
 
 TEST_F(BlobSliceTest, SliceFileAndLastItem) {
+  const std::string kBlobUUID = "kId";
   const size_t kSize1 = 5u;
   const size_t kSize2 = 10u;
 
@@ -178,6 +170,7 @@
 }
 
 TEST_F(BlobSliceTest, SliceAcrossLargeItem) {
+  const std::string kBlobUUID = "kId";
   const size_t kSize1 = 5u;
   const size_t kSize2 = 10u;
   const size_t kSize3 = 10u;
@@ -199,23 +192,4 @@
   EXPECT_EQ(*item2, *slice.dest_items[1]);
 }
 
-TEST_F(BlobSliceTest, SliceTempFileItem) {
-  BlobEntry data(kType, kDisposition);
-  scoped_refptr<ShareableBlobDataItem> item1 = CreateTempFileItem(1u, 10u);
-  data.AppendSharedBlobItem(item1);
-  BlobSlice slice(data, 2, 5);
-  EXPECT_EQ(0u, slice.copying_memory_size.ValueOrDie());
-  EXPECT_TRUE(slice.first_source_item);
-  EXPECT_EQ(2u, slice.first_item_slice_offset);
-  ASSERT_LE(1u, slice.dest_items.size());
-  scoped_refptr<ShareableBlobDataItem> item = slice.dest_items[0];
-  EXPECT_EQ(ShareableBlobDataItem::POPULATED_WITHOUT_QUOTA, item->state());
-
-  const DataElement& dest_element = item->item()->data_element();
-  EXPECT_EQ(DataElement::TYPE_FILE, dest_element.type());
-  EXPECT_EQ(static_cast<uint64_t>(5), dest_element.length());
-  EXPECT_EQ(*item1, *slice.first_source_item);
-  ASSERT_EQ(1u, slice.dest_items.size());
-}
-
 }  // namespace storage
diff --git a/content/browser/blob_storage/blob_storage_browsertest.cc b/content/browser/blob_storage/blob_storage_browsertest.cc
deleted file mode 100644
index fdcf7da..0000000
--- a/content/browser/blob_storage/blob_storage_browsertest.cc
+++ /dev/null
@@ -1,103 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/run_loop.h"
-#include "content/browser/blob_storage/chrome_blob_storage_context.h"
-#include "content/public/browser/browser_context.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/test/browser_test_utils.h"
-#include "content/public/test/content_browser_test.h"
-#include "content/public/test/content_browser_test_utils.h"
-#include "content/shell/browser/shell.h"
-#include "storage/browser/blob/blob_memory_controller.h"
-#include "storage/browser/blob/blob_storage_context.h"
-#include "storage/common/blob_storage/blob_storage_constants.h"
-
-namespace content {
-namespace {
-const size_t kTestBlobStorageIPCThresholdBytes = 5;
-const size_t kTestBlobStorageMaxSharedMemoryBytes = 10;
-
-const size_t kTestBlobStorageMaxBlobMemorySize = 200;
-const uint64_t kTestBlobStorageMaxDiskSpace = 3000;
-const uint64_t kTestBlobStorageMinFileSizeBytes = 20;
-const uint64_t kTestBlobStorageMaxFileSizeBytes = 50;
-}  // namespace
-
-// This browser test is aimed towards exercising the blob storage transportation
-// strategies and paging memory to disk.
-class BlobStorageBrowserTest : public ContentBrowserTest {
- public:
-  BlobStorageBrowserTest() {
-    limits_.max_ipc_memory_size = kTestBlobStorageIPCThresholdBytes;
-    limits_.max_shared_memory_size = kTestBlobStorageMaxSharedMemoryBytes;
-    limits_.max_blob_in_memory_space = kTestBlobStorageMaxBlobMemorySize;
-    limits_.max_blob_disk_space = kTestBlobStorageMaxDiskSpace;
-    limits_.min_page_file_size = kTestBlobStorageMinFileSizeBytes;
-    limits_.max_file_size = kTestBlobStorageMaxFileSizeBytes;
-  }
-
-  void SetBlobLimits() {
-    GetMemoryController()->set_limits_for_testing(limits_);
-  }
-
-  storage::BlobMemoryController* GetMemoryController() {
-    content::ChromeBlobStorageContext* blob_context =
-        ChromeBlobStorageContext::GetFor(
-            shell()->web_contents()->GetBrowserContext());
-    return blob_context->context()->mutable_memory_controller();
-  }
-
-  void SimpleTest(const GURL& test_url, bool incognito = false) {
-    // The test page will perform tests on blob storage, then navigate to either
-    // a #pass or #fail ref.
-    Shell* the_browser = incognito ? CreateOffTheRecordBrowser() : shell();
-
-    VLOG(0) << "Navigating to URL and blocking. " << test_url.spec();
-    NavigateToURLBlockUntilNavigationsComplete(the_browser, test_url, 2);
-    VLOG(0) << "Navigation done.";
-    std::string result =
-        the_browser->web_contents()->GetLastCommittedURL().ref();
-    if (result != "pass") {
-      std::string js_result;
-      ASSERT_TRUE(ExecuteScriptAndExtractString(
-          the_browser, "window.domAutomationController.send(getLog())",
-          &js_result));
-      FAIL() << "Failed: " << js_result;
-    }
-  }
-
- protected:
-  storage::BlobStorageLimits limits_;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(BlobStorageBrowserTest);
-};
-
-IN_PROC_BROWSER_TEST_F(BlobStorageBrowserTest, BlobCombinations) {
-  SetBlobLimits();
-  SimpleTest(GetTestUrl("blob_storage", "blob_creation_and_slicing.html"));
-  storage::BlobMemoryController* memory_controller = GetMemoryController();
-  // Our exact usages depend on IPC message ordering & garbage collection.
-  // Since this is basically random, we just check bounds.
-  EXPECT_LT(0u, memory_controller->memory_usage());
-  EXPECT_LT(0ul, memory_controller->disk_usage());
-  EXPECT_GT(memory_controller->disk_usage(),
-            static_cast<uint64_t>(memory_controller->memory_usage()));
-  EXPECT_GT(limits_.max_blob_in_memory_space,
-            memory_controller->memory_usage());
-  EXPECT_GT(limits_.max_blob_disk_space, memory_controller->disk_usage());
-  shell()->Close();
-
-  // Make sure we run all file / io tasks.
-  base::RunLoop().RunUntilIdle();
-  BrowserThread::GetBlockingPool()->FlushForTesting();
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_EQ(0u, memory_controller->memory_usage());
-  EXPECT_EQ(0ul, memory_controller->disk_usage());
-}
-
-}  // namespace content
diff --git a/content/browser/blob_storage/blob_storage_context_unittest.cc b/content/browser/blob_storage/blob_storage_context_unittest.cc
index 5ac28ca9..166509e7 100644
--- a/content/browser/blob_storage/blob_storage_context_unittest.cc
+++ b/content/browser/blob_storage/blob_storage_context_unittest.cc
@@ -99,18 +99,12 @@
                             std::vector<FileCreationInfo>* files_ptr,
                             BlobStatus status,
                             std::vector<FileCreationInfo> files) {
-  EXPECT_FALSE(BlobStatusIsError(status));
   *status_ptr = status;
   for (FileCreationInfo& info : files) {
     files_ptr->push_back(std::move(info));
   }
 }
 
-void IncrementNumber(size_t* number, BlobStatus status) {
-  EXPECT_EQ(BlobStatus::DONE, status);
-  *number = *number + 1;
-}
-
 }  // namespace
 
 class BlobStorageContextTest : public testing::Test {
@@ -118,16 +112,7 @@
   BlobStorageContextTest() {}
   ~BlobStorageContextTest() override {}
 
-  void SetUp() override {
-    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
-    context_ = base::MakeUnique<BlobStorageContext>();
-  }
-
-  void TearDown() override {
-    base::RunLoop().RunUntilIdle();
-    file_runner_->RunPendingTasks();
-    ASSERT_TRUE(temp_dir_.Delete());
-  }
+  void SetUp() override { context_ = base::MakeUnique<BlobStorageContext>(); }
 
   std::unique_ptr<BlobDataHandle> SetupBasicBlob(const std::string& id) {
     BlobDataBuilder builder(id);
@@ -156,8 +141,6 @@
   }
 
   std::vector<FileCreationInfo> files_;
-  base::ScopedTempDir temp_dir_;
-  scoped_refptr<TestSimpleTaskRunner> file_runner_ = new TestSimpleTaskRunner();
 
   base::MessageLoop fake_io_message_loop_;
   std::unique_ptr<BlobStorageContext> context_;
@@ -528,60 +511,6 @@
   base::RunLoop().RunUntilIdle();
 }
 
-TEST_F(BlobStorageContextTest, BuildFutureFileOnlyBlob) {
-  const std::string kId1("id1");
-  context_ =
-      base::MakeUnique<BlobStorageContext>(temp_dir_.GetPath(), file_runner_);
-  SetTestMemoryLimits();
-
-  BlobDataBuilder builder(kId1);
-  builder.set_content_type("text/plain");
-  builder.AppendFutureFile(0, 10, 0);
-
-  BlobStatus status = BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
-  std::unique_ptr<BlobDataHandle> handle = context_->BuildBlob(
-      builder, base::Bind(&SaveBlobStatusAndFiles, &status, &files_));
-
-  size_t blobs_finished = 0;
-  EXPECT_EQ(BlobStatus::PENDING_QUOTA, handle->GetBlobStatus());
-  EXPECT_EQ(BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS, status);
-  handle->RunOnConstructionComplete(
-      base::Bind(&IncrementNumber, &blobs_finished));
-  EXPECT_EQ(0u, blobs_finished);
-
-  EXPECT_TRUE(file_runner_->HasPendingTask());
-  file_runner_->RunPendingTasks();
-  EXPECT_EQ(0u, blobs_finished);
-  EXPECT_EQ(BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS, status);
-  EXPECT_EQ(BlobStatus::PENDING_QUOTA, handle->GetBlobStatus());
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_EQ(BlobStatus::PENDING_TRANSPORT, status);
-  EXPECT_EQ(BlobStatus::PENDING_TRANSPORT, handle->GetBlobStatus());
-  EXPECT_EQ(0u, blobs_finished);
-
-  ASSERT_EQ(1u, files_.size());
-
-  builder.PopulateFutureFile(0, files_[0].file_reference, base::Time::Max());
-  context_->NotifyTransportComplete(kId1);
-
-  EXPECT_EQ(BlobStatus::DONE, handle->GetBlobStatus());
-  EXPECT_EQ(0u, blobs_finished);
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(1u, blobs_finished);
-
-  builder.Clear();
-  handle.reset();
-  files_.clear();
-  base::RunLoop().RunUntilIdle();
-  // We should have file cleanup tasks.
-  EXPECT_TRUE(file_runner_->HasPendingTask());
-  file_runner_->RunPendingTasks();
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(0lu, context_->memory_controller().memory_usage());
-  EXPECT_EQ(0lu, context_->memory_controller().disk_usage());
-}
-
 TEST_F(BlobStorageContextTest, CompoundBlobs) {
   const std::string kId1("id1");
   const std::string kId2("id2");
@@ -726,171 +655,6 @@
   EXPECT_FALSE(context_->registry().HasEntry(kReferencingId));
 }
 
-namespace {
-constexpr size_t kTotalRawBlobs = 200;
-constexpr size_t kTotalSlicedBlobs = 100;
-constexpr char kTestDiskCacheData[] = "Test Blob Data";
+// TODO(michaeln): tests for the depcrecated url stuff
 
-// Appends data and data types that depend on the index. This is designed to
-// exercise all types of combinations of data, future data, files, future files,
-// and disk cache entries.
-size_t AppendDataInBuilder(BlobDataBuilder* builder,
-                           size_t index,
-                           disk_cache::Entry* cache_entry) {
-  size_t size = 0;
-  // We can't have both future data and future files, so split those up.
-  if (index % 2 != 0) {
-    builder->AppendFutureData(5u);
-    size += 5u;
-    if (index % 3 == 1) {
-      builder->AppendData("abcdefghij", 4u);
-      size += 4u;
-    }
-    if (index % 3 == 0) {
-      builder->AppendFutureData(1u);
-      size += 1u;
-    }
-  } else if (index % 3 == 0) {
-    builder->AppendFutureFile(0lu, 3lu, 0);
-    size += 3u;
-  }
-  if (index % 5 != 0) {
-    builder->AppendFile(
-        base::FilePath::FromUTF8Unsafe(base::SizeTToString(index)), 0ul, 20ul,
-        base::Time::Max());
-    size += 20u;
-  }
-  if (index % 3 != 0) {
-    scoped_refptr<BlobDataBuilder::DataHandle> disk_cache_data_handle =
-        new EmptyDataHandle();
-    builder->AppendDiskCacheEntry(disk_cache_data_handle, cache_entry,
-                                  kTestDiskCacheStreamIndex);
-    size += strlen(kTestDiskCacheData);
-  }
-  return size;
-}
-
-bool DoesBuilderHaveFutureData(size_t index) {
-  return index < kTotalRawBlobs && (index % 2 != 0 || index % 3 == 0);
-}
-
-void PopulateDataInBuilder(BlobDataBuilder* builder,
-                           size_t index,
-                           base::TaskRunner* file_runner) {
-  if (index % 2 != 0) {
-    builder->PopulateFutureData(0, "abcde", 0, 5);
-    if (index % 3 == 0) {
-      builder->PopulateFutureData(1, "z", 0, 1);
-    }
-  } else if (index % 3 == 0) {
-    scoped_refptr<ShareableFileReference> file_ref =
-        ShareableFileReference::GetOrCreate(
-            base::FilePath::FromUTF8Unsafe(
-                base::SizeTToString(index + kTotalRawBlobs)),
-            ShareableFileReference::DONT_DELETE_ON_FINAL_RELEASE, file_runner);
-    builder->PopulateFutureFile(0, file_ref, base::Time::Max());
-  }
-}
-}  // namespace
-
-TEST_F(BlobStorageContextTest, BuildBlobCombinations) {
-  const std::string kId("id");
-
-  context_ =
-      base::MakeUnique<BlobStorageContext>(temp_dir_.GetPath(), file_runner_);
-
-  SetTestMemoryLimits();
-  std::unique_ptr<disk_cache::Backend> cache = CreateInMemoryDiskCache();
-  ASSERT_TRUE(cache);
-  disk_cache::ScopedEntryPtr entry =
-      CreateDiskCacheEntry(cache.get(), "test entry", kTestDiskCacheData);
-
-  // This tests mixed blob content with both synchronous and asynchronous
-  // construction. Blobs should also be paged to disk during execution.
-  std::vector<std::unique_ptr<BlobDataBuilder>> builders;
-  std::vector<size_t> sizes;
-  for (size_t i = 0; i < kTotalRawBlobs; i++) {
-    builders.emplace_back(new BlobDataBuilder(base::SizeTToString(i)));
-    auto& builder = *builders.back();
-    size_t size = AppendDataInBuilder(&builder, i, entry.get());
-    EXPECT_NE(0u, size);
-    sizes.push_back(size);
-  }
-
-  for (size_t i = 0; i < kTotalSlicedBlobs; i++) {
-    builders.emplace_back(
-        new BlobDataBuilder(base::SizeTToString(i + kTotalRawBlobs)));
-    size_t source_size = sizes[i];
-    size_t offset = source_size == 1 ? 0 : i % (source_size - 1);
-    size_t size = (i % (source_size - offset)) + 1;
-    builders.back()->AppendBlob(base::SizeTToString(i), offset, size);
-  }
-
-  size_t total_finished_blobs = 0;
-  std::vector<std::unique_ptr<BlobDataHandle>> handles;
-  std::vector<BlobStatus> statuses;
-  std::vector<bool> populated;
-  statuses.resize(kTotalRawBlobs,
-                  BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS);
-  populated.resize(kTotalRawBlobs, false);
-  for (size_t i = 0; i < builders.size(); i++) {
-    BlobDataBuilder& builder = *builders[i];
-    builder.set_content_type("text/plain");
-    bool has_pending_memory = DoesBuilderHaveFutureData(i);
-    std::unique_ptr<BlobDataHandle> handle = context_->BuildBlob(
-        builder,
-        has_pending_memory
-            ? base::Bind(&SaveBlobStatusAndFiles, &statuses[0] + i, &files_)
-            : BlobStorageContext::TransportAllowedCallback());
-    handle->RunOnConstructionComplete(
-        base::Bind(&IncrementNumber, &total_finished_blobs));
-    handles.push_back(std::move(handle));
-  }
-  base::RunLoop().RunUntilIdle();
-
-  // We should be needing to send a page or two to disk.
-  EXPECT_TRUE(file_runner_->HasPendingTask());
-  do {
-    file_runner_->RunPendingTasks();
-    base::RunLoop().RunUntilIdle();
-    // Continue populating data for items that can fit.
-    for (size_t i = 0; i < kTotalRawBlobs; i++) {
-      BlobDataBuilder* builder = builders[i].get();
-      if (DoesBuilderHaveFutureData(i) && !populated[i] &&
-          statuses[i] == BlobStatus::PENDING_TRANSPORT) {
-        PopulateDataInBuilder(builder, i, file_runner_.get());
-        context_->NotifyTransportComplete(base::SizeTToString(i));
-        populated[i] = true;
-      }
-    }
-    base::RunLoop().RunUntilIdle();
-  } while (file_runner_->HasPendingTask());
-
-  // Check all builders with future items were signalled and populated.
-  for (size_t i = 0; i < populated.size(); i++) {
-    if (DoesBuilderHaveFutureData(i)) {
-      EXPECT_EQ(BlobStatus::PENDING_TRANSPORT, statuses[i]) << i;
-      EXPECT_TRUE(populated[i]) << i;
-    }
-  }
-  base::RunLoop().RunUntilIdle();
-
-  // We should be completely built now.
-  EXPECT_EQ(kTotalRawBlobs + kTotalSlicedBlobs, total_finished_blobs);
-  for (std::unique_ptr<BlobDataHandle>& handle : handles) {
-    EXPECT_EQ(BlobStatus::DONE, handle->GetBlobStatus());
-  }
-  handles.clear();
-  base::RunLoop().RunUntilIdle();
-  files_.clear();
-  // We should have file cleanup tasks.
-  EXPECT_TRUE(file_runner_->HasPendingTask());
-  file_runner_->RunPendingTasks();
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(0lu, context_->memory_controller().memory_usage());
-  EXPECT_EQ(0lu, context_->memory_controller().disk_usage());
-}
-
-// TODO(michaeln): tests for the deprecated url stuff
-
-}  // namespace storage
+}  // namespace content
diff --git a/content/browser/blob_storage/chrome_blob_storage_context.cc b/content/browser/blob_storage/chrome_blob_storage_context.cc
index bd02e514..533d3f1 100644
--- a/content/browser/blob_storage/chrome_blob_storage_context.cc
+++ b/content/browser/blob_storage/chrome_blob_storage_context.cc
@@ -7,14 +7,7 @@
 #include <utility>
 
 #include "base/bind.h"
-#include "base/files/file.h"
-#include "base/files/file_enumerator.h"
-#include "base/files/file_util.h"
 #include "base/guid.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/single_thread_task_runner.h"
-#include "base/task_runner.h"
-#include "base/threading/sequenced_worker_pool.h"
 #include "content/public/browser/blob_handle.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
@@ -22,35 +15,14 @@
 #include "storage/browser/blob/blob_data_handle.h"
 #include "storage/browser/blob/blob_storage_context.h"
 
-using base::FilePath;
 using base::UserDataAdapter;
 using storage::BlobStorageContext;
 
 namespace content {
 
 namespace {
-const FilePath::CharType kBlobStorageContextKeyName[] =
-    FILE_PATH_LITERAL("content_blob_storage_context");
-const FilePath::CharType kBlobStorageParentDirectory[] =
-    FILE_PATH_LITERAL("blob_storage");
 
-// Removes all folders in the parent directory except for the
-// |current_run_dir| folder. If this path is empty, then we delete all folders.
-void RemoveOldBlobStorageDirectories(FilePath blob_storage_parent,
-                                     const FilePath& current_run_dir) {
-  if (!base::DirectoryExists(blob_storage_parent)) {
-    return;
-  }
-  base::FileEnumerator enumerator(blob_storage_parent, false /* recursive */,
-                                  base::FileEnumerator::DIRECTORIES);
-  bool success = true;
-  for (FilePath name = enumerator.Next(); !name.empty();
-       name = enumerator.Next()) {
-    if (current_run_dir.empty() || name != current_run_dir)
-      success &= base::DeleteFile(name, true /* recursive */);
-  }
-  LOCAL_HISTOGRAM_BOOLEAN("Storage.Blob.CleanupSuccess", success);
-}
+const char kBlobStorageContextKeyName[] = "content_blob_storage_context";
 
 class BlobHandleImpl : public BlobHandle {
  public:
@@ -77,39 +49,11 @@
     context->SetUserData(
         kBlobStorageContextKeyName,
         new UserDataAdapter<ChromeBlobStorageContext>(blob.get()));
-
     // Check first to avoid memory leak in unittests.
-    bool io_thread_valid = BrowserThread::IsMessageLoopValid(BrowserThread::IO);
-
-    // Resolve our storage directories.
-    FilePath blob_storage_parent =
-        context->GetPath().Append(kBlobStorageParentDirectory);
-    FilePath blob_storage_dir = blob_storage_parent.Append(
-        FilePath::FromUTF8Unsafe(base::GenerateGUID()));
-
-    // Only populate the task runner if we're not off the record. This enables
-    // paging/saving blob data to disk.
-    scoped_refptr<base::TaskRunner> file_task_runner;
-
-    // If we're not incognito mode, schedule all of our file tasks to enable
-    // disk on the storage context.
-    if (!context->IsOffTheRecord() && io_thread_valid) {
-      file_task_runner =
-          BrowserThread::GetBlockingPool()->GetTaskRunnerWithShutdownBehavior(
-              base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
-      // Removes our old blob directories if they exist.
-      BrowserThread::PostAfterStartupTask(
-          FROM_HERE, file_task_runner,
-          base::Bind(&RemoveOldBlobStorageDirectories,
-                     base::Passed(&blob_storage_parent), blob_storage_dir));
-    }
-
-    if (io_thread_valid) {
+    if (BrowserThread::IsMessageLoopValid(BrowserThread::IO)) {
       BrowserThread::PostTask(
           BrowserThread::IO, FROM_HERE,
-          base::Bind(&ChromeBlobStorageContext::InitializeOnIOThread, blob,
-                     base::Passed(&blob_storage_dir),
-                     base::Passed(&file_task_runner)));
+          base::Bind(&ChromeBlobStorageContext::InitializeOnIOThread, blob));
     }
   }
 
@@ -117,12 +61,9 @@
       context, kBlobStorageContextKeyName);
 }
 
-void ChromeBlobStorageContext::InitializeOnIOThread(
-    FilePath blob_storage_dir,
-    scoped_refptr<base::TaskRunner> file_task_runner) {
+void ChromeBlobStorageContext::InitializeOnIOThread() {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  context_.reset(new BlobStorageContext(std::move(blob_storage_dir),
-                                        std::move(file_task_runner)));
+  context_.reset(new BlobStorageContext());
 }
 
 std::unique_ptr<BlobHandle> ChromeBlobStorageContext::CreateMemoryBackedBlob(
@@ -145,7 +86,7 @@
 }
 
 std::unique_ptr<BlobHandle> ChromeBlobStorageContext::CreateFileBackedBlob(
-    const FilePath& path,
+    const base::FilePath& path,
     int64_t offset,
     int64_t size,
     const base::Time& expected_modification_time) {
diff --git a/content/browser/blob_storage/chrome_blob_storage_context.h b/content/browser/blob_storage/chrome_blob_storage_context.h
index 4c7cfac..bd02cb1 100644
--- a/content/browser/blob_storage/chrome_blob_storage_context.h
+++ b/content/browser/blob_storage/chrome_blob_storage_context.h
@@ -10,14 +10,12 @@
 
 #include <memory>
 
-#include "base/files/file_path.h"
 #include "base/memory/ref_counted.h"
 #include "base/sequenced_task_runner_helpers.h"
 #include "content/common/content_export.h"
 
 namespace base {
 class FilePath;
-class TaskRunner;
 class Time;
 }
 
@@ -46,8 +44,7 @@
   static ChromeBlobStorageContext* GetFor(
       BrowserContext* browser_context);
 
-  void InitializeOnIOThread(base::FilePath blob_storage_dir,
-                            scoped_refptr<base::TaskRunner> file_task_runner);
+  void InitializeOnIOThread();
 
   storage::BlobStorageContext* context() const { return context_.get(); }
 
diff --git a/content/browser/devtools/protocol/color_picker.cc b/content/browser/devtools/protocol/color_picker.cc
index a5b1777..40e119b 100644
--- a/content/browser/devtools/protocol/color_picker.cc
+++ b/content/browser/devtools/protocol/color_picker.cc
@@ -210,7 +210,7 @@
   SkPath clip_path;
   clip_path.addOval(SkRect::MakeXYWH(padding, padding, kDiameter, kDiameter));
   clip_path.close();
-  canvas.clipPath(clip_path, SkRegion::kIntersect_Op, true);
+  canvas.clipPath(clip_path, kIntersect_SkClipOp, true);
 
   // Project pixels.
   int pixel_count = kDiameter / kPixelSize;
diff --git a/content/browser/frame_host/frame_tree_node.cc b/content/browser/frame_host/frame_tree_node.cc
index d4d0059..a67de69 100644
--- a/content/browser/frame_host/frame_tree_node.cc
+++ b/content/browser/frame_host/frame_tree_node.cc
@@ -255,8 +255,9 @@
   replication_state_.unique_name = unique_name;
 }
 
-void FrameTreeNode::SetFeaturePolicyHeader(const std::string& header) {
-  replication_state_.feature_policy_header = header;
+void FrameTreeNode::SetFeaturePolicyHeader(
+    const ParsedFeaturePolicy& parsed_header) {
+  replication_state_.feature_policy_header = parsed_header;
 }
 
 void FrameTreeNode::ResetFeaturePolicy() {
diff --git a/content/browser/frame_host/frame_tree_node.h b/content/browser/frame_host/frame_tree_node.h
index 77a0df9..6ee699e 100644
--- a/content/browser/frame_host/frame_tree_node.h
+++ b/content/browser/frame_host/frame_tree_node.h
@@ -151,7 +151,7 @@
 
   // Set the frame's feature policy from an HTTP header, clearing any existing
   // policy.
-  void SetFeaturePolicyHeader(const std::string& header);
+  void SetFeaturePolicyHeader(const ParsedFeaturePolicy& parsed_header);
 
   // Clear any feature policy associated with the frame.
   void ResetFeaturePolicy();
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index 75654d2..f71cdd0 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -1735,8 +1735,8 @@
 }
 
 void RenderFrameHostImpl::OnDidSetFeaturePolicyHeader(
-    const std::string& header) {
-  frame_tree_node()->SetFeaturePolicyHeader(header);
+    const ParsedFeaturePolicy& parsed_header) {
+  frame_tree_node()->SetFeaturePolicyHeader(parsed_header);
 }
 
 void RenderFrameHostImpl::OnDidAddContentSecurityPolicy(
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h
index bf18923..0d56287 100644
--- a/content/browser/frame_host/render_frame_host_impl.h
+++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -663,7 +663,7 @@
   void OnDidAccessInitialDocument();
   void OnDidChangeOpener(int32_t opener_routing_id);
   void OnDidChangeName(const std::string& name, const std::string& unique_name);
-  void OnDidSetFeaturePolicyHeader(const std::string& header);
+  void OnDidSetFeaturePolicyHeader(const ParsedFeaturePolicy& parsed_header);
   void OnDidAddContentSecurityPolicy(const ContentSecurityPolicyHeader& header);
   void OnEnforceInsecureRequestPolicy(blink::WebInsecureRequestPolicy policy);
   void OnUpdateToUniqueOrigin(bool is_potentially_trustworthy_unique_origin);
diff --git a/content/browser/media/capture/desktop_capture_device_aura.cc b/content/browser/media/capture/desktop_capture_device_aura.cc
index 7dafda9..acd236d 100644
--- a/content/browser/media/capture/desktop_capture_device_aura.cc
+++ b/content/browser/media/capture/desktop_capture_device_aura.cc
@@ -74,4 +74,9 @@
   core_->StopAndDeAllocate();
 }
 
+void DesktopCaptureDeviceAura::OnUtilizationReport(int frame_feedback_id,
+                                                   double utilization) {
+  core_->OnConsumerReportingUtilization(frame_feedback_id, utilization);
+}
+
 }  // namespace content
diff --git a/content/browser/media/capture/desktop_capture_device_aura.h b/content/browser/media/capture/desktop_capture_device_aura.h
index a3a9374..6eee43a 100644
--- a/content/browser/media/capture/desktop_capture_device_aura.h
+++ b/content/browser/media/capture/desktop_capture_device_aura.h
@@ -32,6 +32,7 @@
                         std::unique_ptr<Client> client) override;
   void RequestRefreshFrame() override;
   void StopAndDeAllocate() override;
+  void OnUtilizationReport(int frame_feedback_id, double utilization) override;
 
  private:
   explicit DesktopCaptureDeviceAura(const DesktopMediaID& source);
diff --git a/content/browser/media/capture/desktop_capture_device_aura_unittest.cc b/content/browser/media/capture/desktop_capture_device_aura_unittest.cc
index a5bba004..414a5fc 100644
--- a/content/browser/media/capture/desktop_capture_device_aura_unittest.cc
+++ b/content/browser/media/capture/desktop_capture_device_aura_unittest.cc
@@ -41,13 +41,14 @@
 
 class MockDeviceClient : public media::VideoCaptureDevice::Client {
  public:
-  MOCK_METHOD6(OnIncomingCapturedData,
+  MOCK_METHOD7(OnIncomingCapturedData,
                void(const uint8_t* data,
                     int length,
                     const media::VideoCaptureFormat& frame_format,
                     int rotation,
                     base::TimeTicks reference_time,
-                    base::TimeDelta tiemstamp));
+                    base::TimeDelta tiemstamp,
+                    int frame_feedback_id));
   MOCK_METHOD0(DoReserveOutputBuffer, void(void));
   MOCK_METHOD0(DoOnIncomingCapturedBuffer, void(void));
   MOCK_METHOD0(DoOnIncomingCapturedVideoFrame, void(void));
@@ -57,10 +58,10 @@
                     const std::string& reason));
 
   // Trampoline methods to workaround GMOCK problems with std::unique_ptr<>.
-  std::unique_ptr<Buffer> ReserveOutputBuffer(
-      const gfx::Size& dimensions,
-      media::VideoPixelFormat format,
-      media::VideoPixelStorage storage) override {
+  std::unique_ptr<Buffer> ReserveOutputBuffer(const gfx::Size& dimensions,
+                                              media::VideoPixelFormat format,
+                                              media::VideoPixelStorage storage,
+                                              int frame_feedback_id) override {
     EXPECT_EQ(media::PIXEL_FORMAT_I420, format);
     EXPECT_EQ(media::PIXEL_STORAGE_CPU, storage);
     DoReserveOutputBuffer();
@@ -80,7 +81,8 @@
   std::unique_ptr<Buffer> ResurrectLastOutputBuffer(
       const gfx::Size& dimensions,
       media::VideoPixelFormat format,
-      media::VideoPixelStorage storage) override {
+      media::VideoPixelStorage storage,
+      int frame_feedback_id) override {
     EXPECT_EQ(media::PIXEL_FORMAT_I420, format);
     EXPECT_EQ(media::PIXEL_STORAGE_CPU, storage);
     DoResurrectLastOutputBuffer();
diff --git a/content/browser/media/capture/desktop_capture_device_unittest.cc b/content/browser/media/capture/desktop_capture_device_unittest.cc
index da791c2e..4fe54c08 100644
--- a/content/browser/media/capture/desktop_capture_device_unittest.cc
+++ b/content/browser/media/capture/desktop_capture_device_unittest.cc
@@ -60,13 +60,14 @@
 
 class MockDeviceClient : public media::VideoCaptureDevice::Client {
  public:
-  MOCK_METHOD6(OnIncomingCapturedData,
+  MOCK_METHOD7(OnIncomingCapturedData,
                void(const uint8_t* data,
                     int length,
                     const media::VideoCaptureFormat& frame_format,
                     int rotation,
                     base::TimeTicks reference_time,
-                    base::TimeDelta timestamp));
+                    base::TimeDelta timestamp,
+                    int frame_feedback_id));
   MOCK_METHOD0(DoReserveOutputBuffer, void(void));
   MOCK_METHOD0(DoOnIncomingCapturedBuffer, void(void));
   MOCK_METHOD0(DoOnIncomingCapturedVideoFrame, void(void));
@@ -76,17 +77,17 @@
                     const std::string& reason));
 
   // Trampoline methods to workaround GMOCK problems with std::unique_ptr<>.
-  std::unique_ptr<Buffer> ReserveOutputBuffer(
-      const gfx::Size& dimensions,
-      media::VideoPixelFormat format,
-      media::VideoPixelStorage storage) override {
+  std::unique_ptr<Buffer> ReserveOutputBuffer(const gfx::Size& dimensions,
+                                              media::VideoPixelFormat format,
+                                              media::VideoPixelStorage storage,
+                                              int frame_feedback_id) override {
     EXPECT_TRUE(format == media::PIXEL_FORMAT_I420 &&
                 storage == media::PIXEL_STORAGE_CPU);
     DoReserveOutputBuffer();
     return std::unique_ptr<Buffer>();
   }
   void OnIncomingCapturedBuffer(std::unique_ptr<Buffer> buffer,
-                                const media::VideoCaptureFormat& frame_format,
+                                const media::VideoCaptureFormat& format,
                                 base::TimeTicks reference_time,
                                 base::TimeDelta timestamp) override {
     DoOnIncomingCapturedBuffer();
@@ -99,7 +100,8 @@
   std::unique_ptr<Buffer> ResurrectLastOutputBuffer(
       const gfx::Size& dimensions,
       media::VideoPixelFormat format,
-      media::VideoPixelStorage storage) override {
+      media::VideoPixelStorage storage,
+      int frame_feedback_id) override {
     EXPECT_TRUE(format == media::PIXEL_FORMAT_I420 &&
                 storage == media::PIXEL_STORAGE_CPU);
     DoResurrectLastOutputBuffer();
@@ -264,7 +266,8 @@
                  const media::VideoCaptureFormat&,
                  int,
                  base::TimeTicks,
-                 base::TimeDelta) {
+                 base::TimeDelta,
+                 int) {
     ASSERT_TRUE(output_frame_);
     ASSERT_EQ(output_frame_->stride() * output_frame_->size().height(), size);
     memcpy(output_frame_->data(), frame, size);
@@ -297,7 +300,7 @@
 
   std::unique_ptr<MockDeviceClient> client(new MockDeviceClient());
   EXPECT_CALL(*client, OnError(_, _)).Times(0);
-  EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _, _))
+  EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _, _, _))
       .WillRepeatedly(
           DoAll(SaveArg<1>(&frame_size), SaveArg<2>(&format),
                 InvokeWithoutArgs(&done_event, &base::WaitableEvent::Signal)));
@@ -334,7 +337,7 @@
 
   std::unique_ptr<MockDeviceClient> client(new MockDeviceClient());
   EXPECT_CALL(*client, OnError(_, _)).Times(0);
-  EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _, _))
+  EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _, _, _))
       .WillRepeatedly(
           DoAll(WithArg<2>(Invoke(&format_checker,
                                   &FormatChecker::ExpectAcceptableSize)),
@@ -378,7 +381,7 @@
 
   std::unique_ptr<MockDeviceClient> client(new MockDeviceClient());
   EXPECT_CALL(*client, OnError(_,_)).Times(0);
-  EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _, _))
+  EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _, _, _))
       .WillRepeatedly(
           DoAll(WithArg<2>(Invoke(&format_checker,
                                   &FormatChecker::ExpectAcceptableSize)),
@@ -426,7 +429,7 @@
 
   std::unique_ptr<MockDeviceClient> client(new MockDeviceClient());
   EXPECT_CALL(*client, OnError(_,_)).Times(0);
-  EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _, _))
+  EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _, _, _))
       .WillRepeatedly(
           DoAll(WithArg<2>(Invoke(&format_checker,
                                   &FormatChecker::ExpectAcceptableSize)),
@@ -476,7 +479,7 @@
 
   std::unique_ptr<MockDeviceClient> client(new MockDeviceClient());
   EXPECT_CALL(*client, OnError(_,_)).Times(0);
-  EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _, _))
+  EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _, _, _))
       .WillRepeatedly(
           DoAll(Invoke(this, &DesktopCaptureDeviceTest::CopyFrame),
                 SaveArg<1>(&frame_size),
@@ -523,7 +526,7 @@
 
   std::unique_ptr<MockDeviceClient> client(new MockDeviceClient());
   EXPECT_CALL(*client, OnError(_,_)).Times(0);
-  EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _, _))
+  EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _, _, _))
       .WillRepeatedly(
           DoAll(Invoke(this, &DesktopCaptureDeviceTest::CopyFrame),
                 SaveArg<1>(&frame_size),
diff --git a/content/browser/media/capture/screen_capture_device_android.cc b/content/browser/media/capture/screen_capture_device_android.cc
index d03e31c..acdd5eb 100644
--- a/content/browser/media/capture/screen_capture_device_android.cc
+++ b/content/browser/media/capture/screen_capture_device_android.cc
@@ -31,4 +31,10 @@
 void ScreenCaptureDeviceAndroid::RequestRefreshFrame() {
   core_.RequestRefreshFrame();
 }
+
+void ScreenCaptureDeviceAndroid::OnUtilizationReport(int frame_feedback_id,
+                                                     double utilization) {
+  core_.OnConsumerReportingUtilization(frame_feedback_id, utilization);
+}
+
 }  // namespace content
diff --git a/content/browser/media/capture/screen_capture_device_android.h b/content/browser/media/capture/screen_capture_device_android.h
index 09c8998..7d2d518 100644
--- a/content/browser/media/capture/screen_capture_device_android.h
+++ b/content/browser/media/capture/screen_capture_device_android.h
@@ -27,6 +27,7 @@
                         std::unique_ptr<Client> client) override;
   void StopAndDeAllocate() override;
   void RequestRefreshFrame() override;
+  void OnUtilizationReport(int frame_feedback_id, double utilization) override;
 
  private:
   media::ScreenCaptureDeviceCore core_;
diff --git a/content/browser/media/capture/screen_capture_device_android_unittest.cc b/content/browser/media/capture/screen_capture_device_android_unittest.cc
index f903f19f..1eaa60c 100644
--- a/content/browser/media/capture/screen_capture_device_android_unittest.cc
+++ b/content/browser/media/capture/screen_capture_device_android_unittest.cc
@@ -16,13 +16,14 @@
 
 class MockDeviceClient : public media::VideoCaptureDevice::Client {
  public:
-  MOCK_METHOD6(OnIncomingCapturedData,
+  MOCK_METHOD7(OnIncomingCapturedData,
                void(const uint8_t* data,
                     int length,
                     const media::VideoCaptureFormat& frame_format,
                     int rotation,
                     base::TimeTicks reference_time,
-                    base::TimeDelta tiemstamp));
+                    base::TimeDelta tiemstamp,
+                    int frame_feedback_id));
   MOCK_METHOD0(DoReserveOutputBuffer, void(void));
   MOCK_METHOD0(DoOnIncomingCapturedBuffer, void(void));
   MOCK_METHOD0(DoOnIncomingCapturedVideoFrame, void(void));
@@ -33,10 +34,10 @@
   MOCK_CONST_METHOD0(GetBufferPoolUtilization, double(void));
 
   // Trampoline methods to workaround GMOCK problems with std::unique_ptr<>.
-  std::unique_ptr<Buffer> ReserveOutputBuffer(
-      const gfx::Size& dimensions,
-      media::VideoPixelFormat format,
-      media::VideoPixelStorage storage) override {
+  std::unique_ptr<Buffer> ReserveOutputBuffer(const gfx::Size& dimensions,
+                                              media::VideoPixelFormat format,
+                                              media::VideoPixelStorage storage,
+                                              int frame_feedback_id) override {
     EXPECT_EQ(media::PIXEL_FORMAT_I420, format);
     EXPECT_EQ(media::PIXEL_STORAGE_CPU, storage);
     DoReserveOutputBuffer();
@@ -56,7 +57,8 @@
   std::unique_ptr<Buffer> ResurrectLastOutputBuffer(
       const gfx::Size& dimensions,
       media::VideoPixelFormat format,
-      media::VideoPixelStorage storage) override {
+      media::VideoPixelStorage storage,
+      int frame_feedback_id) override {
     EXPECT_EQ(media::PIXEL_FORMAT_I420, format);
     EXPECT_EQ(media::PIXEL_STORAGE_CPU, storage);
     DoResurrectLastOutputBuffer();
diff --git a/content/browser/media/capture/web_contents_video_capture_device.cc b/content/browser/media/capture/web_contents_video_capture_device.cc
index 5212e7c..d258238e 100644
--- a/content/browser/media/capture/web_contents_video_capture_device.cc
+++ b/content/browser/media/capture/web_contents_video_capture_device.cc
@@ -957,4 +957,9 @@
   core_->StopAndDeAllocate();
 }
 
+void WebContentsVideoCaptureDevice::OnUtilizationReport(int frame_feedback_id,
+                                                        double utilization) {
+  core_->OnConsumerReportingUtilization(frame_feedback_id, utilization);
+}
+
 }  // namespace content
diff --git a/content/browser/media/capture/web_contents_video_capture_device.h b/content/browser/media/capture/web_contents_video_capture_device.h
index 6063f7e..ab81c35 100644
--- a/content/browser/media/capture/web_contents_video_capture_device.h
+++ b/content/browser/media/capture/web_contents_video_capture_device.h
@@ -42,6 +42,7 @@
   void MaybeSuspend() override;
   void Resume() override;
   void StopAndDeAllocate() override;
+  void OnUtilizationReport(int frame_feedback_id, double utilization) override;
 
  private:
   WebContentsVideoCaptureDevice(
diff --git a/content/browser/media/capture/web_contents_video_capture_device_unittest.cc b/content/browser/media/capture/web_contents_video_capture_device_unittest.cc
index cc6bc3a3..3843028 100644
--- a/content/browser/media/capture/web_contents_video_capture_device_unittest.cc
+++ b/content/browser/media/capture/web_contents_video_capture_device_unittest.cc
@@ -338,36 +338,39 @@
   }
   ~StubClient() override {}
 
-  MOCK_METHOD6(OnIncomingCapturedData,
+  MOCK_METHOD7(OnIncomingCapturedData,
                void(const uint8_t* data,
                     int length,
                     const media::VideoCaptureFormat& frame_format,
                     int rotation,
                     base::TimeTicks reference_time,
-                    base::TimeDelta timestamp));
+                    base::TimeDelta timestamp,
+                    int frame_feedback_id));
 
   MOCK_METHOD0(DoOnIncomingCapturedBuffer, void(void));
 
   std::unique_ptr<media::VideoCaptureDevice::Client::Buffer>
   ReserveOutputBuffer(const gfx::Size& dimensions,
                       media::VideoPixelFormat format,
-                      media::VideoPixelStorage storage) override {
+                      media::VideoPixelStorage storage,
+                      int frame_feedback_id) override {
     CHECK_EQ(format, media::PIXEL_FORMAT_I420);
     int buffer_id_to_drop =
         media::VideoCaptureBufferPool::kInvalidId;  // Ignored.
     const int buffer_id = buffer_pool_->ReserveForProducer(
-        dimensions, format, storage, &buffer_id_to_drop);
+        dimensions, format, storage, frame_feedback_id, &buffer_id_to_drop);
     if (buffer_id == media::VideoCaptureBufferPool::kInvalidId)
       return NULL;
 
     return std::unique_ptr<media::VideoCaptureDevice::Client::Buffer>(
-        new AutoReleaseBuffer(
-            buffer_pool_, buffer_pool_->GetBufferHandle(buffer_id), buffer_id));
+        new AutoReleaseBuffer(buffer_pool_,
+                              buffer_pool_->GetBufferHandle(buffer_id),
+                              buffer_id, frame_feedback_id));
   }
 
   // Trampoline method to workaround GMOCK problems with std::unique_ptr<>.
   void OnIncomingCapturedBuffer(std::unique_ptr<Buffer> buffer,
-                                const media::VideoCaptureFormat& frame_format,
+                                const media::VideoCaptureFormat& format,
                                 base::TimeTicks reference_time,
                                 base::TimeDelta timestamp) override {
     DoOnIncomingCapturedBuffer();
@@ -404,15 +407,17 @@
   std::unique_ptr<media::VideoCaptureDevice::Client::Buffer>
   ResurrectLastOutputBuffer(const gfx::Size& dimensions,
                             media::VideoPixelFormat format,
-                            media::VideoPixelStorage storage) override {
+                            media::VideoPixelStorage storage,
+                            int frame_feedback_id) override {
     CHECK_EQ(format, media::PIXEL_FORMAT_I420);
     const int buffer_id =
         buffer_pool_->ResurrectLastForProducer(dimensions, format, storage);
     if (buffer_id == media::VideoCaptureBufferPool::kInvalidId)
       return nullptr;
     return std::unique_ptr<media::VideoCaptureDevice::Client::Buffer>(
-        new AutoReleaseBuffer(
-            buffer_pool_, buffer_pool_->GetBufferHandle(buffer_id), buffer_id));
+        new AutoReleaseBuffer(buffer_pool_,
+                              buffer_pool_->GetBufferHandle(buffer_id),
+                              buffer_id, frame_feedback_id));
   }
 
   void OnError(const tracked_objects::Location& from_here,
@@ -428,13 +433,16 @@
     AutoReleaseBuffer(
         const scoped_refptr<media::VideoCaptureBufferPool>& pool,
         std::unique_ptr<media::VideoCaptureBufferHandle> buffer_handle,
-        int buffer_id)
+        int buffer_id,
+        int frame_feedback_id)
         : id_(buffer_id),
+          frame_feedback_id_(frame_feedback_id),
           pool_(pool),
           buffer_handle_(std::move(buffer_handle)) {
       DCHECK(pool_);
     }
     int id() const override { return id_; }
+    int frame_feedback_id() const override { return frame_feedback_id_; }
     gfx::Size dimensions() const override {
       return buffer_handle_->dimensions();
     }
@@ -458,6 +466,7 @@
     ~AutoReleaseBuffer() override { pool_->RelinquishProducerReservation(id_); }
 
     const int id_;
+    const int frame_feedback_id_;
     const scoped_refptr<media::VideoCaptureBufferPool> pool_;
     const std::unique_ptr<media::VideoCaptureBufferHandle> buffer_handle_;
   };
diff --git a/content/browser/renderer_host/media/video_capture_buffer_pool_unittest.cc b/content/browser/renderer_host/media/video_capture_buffer_pool_unittest.cc
index 13d4b46b..69c0468 100644
--- a/content/browser/renderer_host/media/video_capture_buffer_pool_unittest.cc
+++ b/content/browser/renderer_host/media/video_capture_buffer_pool_unittest.cc
@@ -90,9 +90,11 @@
                     format_and_storage.pixel_storage) << " "
              << media::VideoPixelFormatToString(format_and_storage.pixel_format)
              << " " << dimensions.ToString();
+    const int arbitrary_frame_feedback_id = 0;
     const int buffer_id = pool_->ReserveForProducer(
         dimensions, format_and_storage.pixel_format,
-        format_and_storage.pixel_storage, &buffer_id_to_drop);
+        format_and_storage.pixel_storage, arbitrary_frame_feedback_id,
+        &buffer_id_to_drop);
     if (buffer_id == media::VideoCaptureBufferPool::kInvalidId)
       return std::unique_ptr<Buffer>();
     EXPECT_EQ(expected_dropped_id_, buffer_id_to_drop);
diff --git a/content/browser/renderer_host/media/video_capture_controller.cc b/content/browser/renderer_host/media/video_capture_controller.cc
index 83f264cd..5f226b6 100644
--- a/content/browser/renderer_host/media/video_capture_controller.cc
+++ b/content/browser/renderer_host/media/video_capture_controller.cc
@@ -46,40 +46,6 @@
         name, \
         (height) ? ((width) * 100) / (height) : kInfiniteRatio);
 
-class SyncTokenClientImpl : public VideoFrame::SyncTokenClient {
- public:
-  explicit SyncTokenClientImpl(display_compositor::GLHelper* gl_helper)
-      : gl_helper_(gl_helper) {}
-  ~SyncTokenClientImpl() override {}
-  void GenerateSyncToken(gpu::SyncToken* sync_token) override {
-    gl_helper_->GenerateSyncToken(sync_token);
-  }
-  void WaitSyncToken(const gpu::SyncToken& sync_token) override {
-    gl_helper_->WaitSyncToken(sync_token);
-  }
-
- private:
-  display_compositor::GLHelper* gl_helper_;
-};
-
-void ReturnVideoFrame(const scoped_refptr<VideoFrame>& video_frame,
-                      const gpu::SyncToken& sync_token) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-#if defined(OS_ANDROID)
-  NOTREACHED();
-#else
-  display_compositor::GLHelper* gl_helper =
-      ImageTransportFactory::GetInstance()->GetGLHelper();
-  // UpdateReleaseSyncToken() creates a new sync_token using |gl_helper|, so
-  // wait the given |sync_token| using |gl_helper|.
-  if (gl_helper) {
-    gl_helper->WaitSyncToken(sync_token);
-    SyncTokenClientImpl client(gl_helper);
-    video_frame->UpdateReleaseSyncToken(&client);
-  }
-#endif
-}
-
 std::unique_ptr<media::VideoCaptureJpegDecoder> CreateGpuJpegDecoder(
     const media::VideoCaptureJpegDecoder::DecodeDoneCB& decode_done_cb) {
   return base::MakeUnique<VideoCaptureGpuJpegDecoder>(decode_done_cb);
@@ -148,12 +114,10 @@
   const media::VideoCaptureParams parameters;
 
   // Buffers that are currently known to this client.
-  std::set<int> known_buffers;
+  std::vector<int> known_buffers;
 
-  // Buffers currently held by this client, and sync token callback to call when
-  // they are returned from the client.
-  typedef std::map<int, scoped_refptr<VideoFrame>> ActiveBufferMap;
-  ActiveBufferMap active_buffers;
+  // Buffers currently held by this client.
+  std::vector<int> buffers_in_use;
 
   // State of capture session, controlled by VideoCaptureManager directly. This
   // transitions to true as soon as StopSession() occurs, at which point the
@@ -172,10 +136,69 @@
   bool paused;
 };
 
+VideoCaptureController::BufferState::BufferState(
+    int buffer_id,
+    int frame_feedback_id,
+    media::VideoFrameConsumerFeedbackObserver* consumer_feedback_observer,
+    scoped_refptr<media::VideoCaptureBufferPool> buffer_pool,
+    scoped_refptr<media::VideoFrame> frame)
+    : buffer_id_(buffer_id),
+      frame_feedback_id_(frame_feedback_id),
+      consumer_feedback_observer_(consumer_feedback_observer),
+      buffer_pool_(std::move(buffer_pool)),
+      frame_(std::move(frame)),
+      max_consumer_utilization_(
+          media::VideoFrameConsumerFeedbackObserver::kNoUtilizationRecorded),
+      consumer_hold_count_(0) {}
+
+VideoCaptureController::BufferState::~BufferState() = default;
+
+VideoCaptureController::BufferState::BufferState(
+    const VideoCaptureController::BufferState& other) = default;
+
+void VideoCaptureController::BufferState::RecordConsumerUtilization(
+    double utilization) {
+  if (std::isfinite(utilization) && utilization >= 0.0) {
+    max_consumer_utilization_ =
+        std::max(max_consumer_utilization_, utilization);
+  }
+}
+
+void VideoCaptureController::BufferState::IncreaseConsumerCount() {
+  if (consumer_hold_count_ == 0)
+    buffer_pool_->HoldForConsumers(buffer_id_, 1);
+  consumer_hold_count_++;
+}
+
+void VideoCaptureController::BufferState::DecreaseConsumerCount() {
+  consumer_hold_count_--;
+  if (consumer_hold_count_ == 0) {
+    if (consumer_feedback_observer_ != nullptr &&
+        max_consumer_utilization_ !=
+            media::VideoFrameConsumerFeedbackObserver::kNoUtilizationRecorded) {
+      consumer_feedback_observer_->OnUtilizationReport(
+          frame_feedback_id_, max_consumer_utilization_);
+    }
+    buffer_pool_->RelinquishConsumerHold(buffer_id_, 1);
+    max_consumer_utilization_ =
+        media::VideoFrameConsumerFeedbackObserver::kNoUtilizationRecorded;
+  }
+}
+
+bool VideoCaptureController::BufferState::HasZeroConsumerHoldCount() {
+  return consumer_hold_count_ == 0;
+}
+
+void VideoCaptureController::BufferState::SetConsumerFeedbackObserver(
+    media::VideoFrameConsumerFeedbackObserver* consumer_feedback_observer) {
+  consumer_feedback_observer_ = consumer_feedback_observer;
+}
+
 VideoCaptureController::VideoCaptureController(int max_buffers)
     : buffer_pool_(new media::VideoCaptureBufferPoolImpl(
           base::MakeUnique<media::VideoCaptureBufferTrackerFactoryImpl>(),
           max_buffers)),
+      consumer_feedback_observer_(nullptr),
       state_(VIDEO_CAPTURE_STATE_STARTED),
       has_received_frames_(false),
       weak_ptr_factory_(this) {
@@ -187,6 +210,16 @@
   return weak_ptr_factory_.GetWeakPtr();
 }
 
+void VideoCaptureController::SetConsumerFeedbackObserver(
+    std::unique_ptr<media::VideoFrameConsumerFeedbackObserver>
+        consumer_feedback_observer) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  consumer_feedback_observer_ = std::move(consumer_feedback_observer);
+  // Update existing BufferState entries.
+  for (auto& entry : buffer_id_to_state_map_)
+    entry.second.SetConsumerFeedbackObserver(consumer_feedback_observer_.get());
+}
+
 std::unique_ptr<media::VideoCaptureDevice::Client>
 VideoCaptureController::NewDeviceClient() {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
@@ -259,9 +292,9 @@
     return kInvalidMediaCaptureSessionId;
 
   // Take back all buffers held by the |client|.
-  for (const auto& buffer : client->active_buffers)
-    buffer_pool_->RelinquishConsumerHold(buffer.first, 1);
-  client->active_buffers.clear();
+  for (const auto& buffer_id : client->buffers_in_use)
+    buffer_id_to_state_map_.at(buffer_id).DecreaseConsumerCount();
+  client->buffers_in_use.clear();
 
   int session_id = client->session_id;
   controller_clients_.remove_if(
@@ -353,42 +386,22 @@
 
   // If this buffer is not held by this client, or this client doesn't exist
   // in controller, do nothing.
-  ControllerClient::ActiveBufferMap::iterator iter;
-  if (!client || (iter = client->active_buffers.find(buffer_id)) ==
-                     client->active_buffers.end()) {
+  if (!client) {
+    NOTREACHED();
+    return;
+  }
+  auto buffers_in_use_entry_iter =
+      std::find(std::begin(client->buffers_in_use),
+                std::end(client->buffers_in_use), buffer_id);
+  if (buffers_in_use_entry_iter == std::end(client->buffers_in_use)) {
     NOTREACHED();
     return;
   }
 
-  // Set the RESOURCE_UTILIZATION to the maximum of those provided by each
-  // consumer (via separate calls to this method that refer to the same
-  // VideoFrame).  The producer of this VideoFrame may check this value, after
-  // all consumer holds are relinquished, to make quality versus performance
-  // trade-off decisions.
-  scoped_refptr<VideoFrame> frame = iter->second;
-  if (std::isfinite(consumer_resource_utilization) &&
-      consumer_resource_utilization >= 0.0) {
-    double resource_utilization = -1.0;
-    if (frame->metadata()->GetDouble(VideoFrameMetadata::RESOURCE_UTILIZATION,
-                                     &resource_utilization)) {
-      frame->metadata()->SetDouble(VideoFrameMetadata::RESOURCE_UTILIZATION,
-                                   std::max(consumer_resource_utilization,
-                                            resource_utilization));
-    } else {
-      frame->metadata()->SetDouble(VideoFrameMetadata::RESOURCE_UTILIZATION,
-                                   consumer_resource_utilization);
-    }
-  }
-
-  client->active_buffers.erase(iter);
-  buffer_pool_->RelinquishConsumerHold(buffer_id, 1);
-
-#if defined(OS_ANDROID)
-  DCHECK(!sync_token.HasData());
-#endif
-  if (sync_token.HasData())
-    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
-                            base::Bind(&ReturnVideoFrame, frame, sync_token));
+  BufferState& buffer_state = buffer_id_to_state_map_.at(buffer_id);
+  buffer_state.RecordConsumerUtilization(consumer_resource_utilization);
+  buffer_state.DecreaseConsumerCount();
+  client->buffers_in_use.erase(buffers_in_use_entry_iter);
 }
 
 const media::VideoCaptureFormat&
@@ -407,7 +420,17 @@
   const int buffer_id = buffer->id();
   DCHECK_NE(buffer_id, media::VideoCaptureBufferPool::kInvalidId);
 
-  int count = 0;
+  // Insert if not exists.
+  const auto it =
+      buffer_id_to_state_map_
+          .insert(std::make_pair(
+              buffer_id, BufferState(buffer_id, buffer->frame_feedback_id(),
+                                     consumer_feedback_observer_.get(),
+                                     buffer_pool_, frame)))
+          .first;
+  BufferState& buffer_state = it->second;
+  DCHECK(buffer_state.HasZeroConsumerHoldCount());
+
   if (state_ == VIDEO_CAPTURE_STATE_STARTED) {
     if (!frame->metadata()->HasKey(VideoFrameMetadata::FRAME_RATE)) {
       frame->metadata()->SetDouble(VideoFrameMetadata::FRAME_RATE,
@@ -436,17 +459,28 @@
         continue;
 
       // On the first use of a buffer on a client, share the memory handles.
-      const bool is_new_buffer = client->known_buffers.insert(buffer_id).second;
+      auto known_buffers_entry_iter =
+          std::find(std::begin(client->known_buffers),
+                    std::end(client->known_buffers), buffer_id);
+      bool is_new_buffer = false;
+      if (known_buffers_entry_iter == std::end(client->known_buffers)) {
+        client->known_buffers.push_back(buffer_id);
+        is_new_buffer = true;
+      }
       if (is_new_buffer)
         DoNewBufferOnIOThread(client.get(), buffer.get(), frame);
 
       client->event_handler->OnBufferReady(client->controller_id, buffer_id,
                                            frame);
-      const bool inserted =
-          client->active_buffers.insert(std::make_pair(buffer_id, frame))
-              .second;
-      DCHECK(inserted) << "Unexpected duplicate buffer: " << buffer_id;
-      count++;
+
+      auto buffers_in_use_entry_iter =
+          std::find(std::begin(client->buffers_in_use),
+                    std::end(client->buffers_in_use), buffer_id);
+      if (buffers_in_use_entry_iter == std::end(client->buffers_in_use))
+        client->buffers_in_use.push_back(buffer_id);
+      else
+        DCHECK(false) << "Unexpected duplicate buffer: " << buffer_id;
+      buffer_state.IncreaseConsumerCount();
     }
   }
 
@@ -466,8 +500,6 @@
     UMA_HISTOGRAM_COUNTS("Media.VideoCapture.FrameRate", frame_rate);
     has_received_frames_ = true;
   }
-
-  buffer_pool_->HoldForConsumers(buffer_id, count);
 }
 
 void VideoCaptureController::OnError() {
@@ -493,11 +525,17 @@
     if (client->session_closed)
       continue;
 
-    if (client->known_buffers.erase(buffer_id_to_drop)) {
+    auto known_buffers_entry_iter =
+        std::find(std::begin(client->known_buffers),
+                  std::end(client->known_buffers), buffer_id_to_drop);
+    if (known_buffers_entry_iter != std::end(client->known_buffers)) {
+      client->known_buffers.erase(known_buffers_entry_iter);
       client->event_handler->OnBufferDestroyed(client->controller_id,
                                                buffer_id_to_drop);
     }
   }
+
+  buffer_id_to_state_map_.erase(buffer_id_to_drop);
 }
 
 void VideoCaptureController::DoNewBufferOnIOThread(
diff --git a/content/browser/renderer_host/media/video_capture_controller.h b/content/browser/renderer_host/media/video_capture_controller.h
index 8aaf97c..b1a455f 100644
--- a/content/browser/renderer_host/media/video_capture_controller.h
+++ b/content/browser/renderer_host/media/video_capture_controller.h
@@ -72,6 +72,14 @@
 
   base::WeakPtr<VideoCaptureController> GetWeakPtrForIOThread();
 
+  // Factory code creating instances of VideoCaptureController may optionally
+  // set a VideoFrameConsumerFeedbackObserver. Setting the observer is done in
+  // this method separate from the constructor to allow clients to create and
+  // use instances before they can provide the observer. (This is the case with
+  // VideoCaptureManager).
+  void SetConsumerFeedbackObserver(
+      std::unique_ptr<media::VideoFrameConsumerFeedbackObserver> observer);
+
   // Return a new VideoCaptureDeviceClient to forward capture events to this
   // instance.
   std::unique_ptr<media::VideoCaptureDevice::Client> NewDeviceClient();
@@ -139,6 +147,33 @@
   struct ControllerClient;
   typedef std::list<std::unique_ptr<ControllerClient>> ControllerClients;
 
+  class BufferState {
+   public:
+    explicit BufferState(
+        int buffer_id,
+        int frame_feedback_id,
+        media::VideoFrameConsumerFeedbackObserver* consumer_feedback_observer,
+        scoped_refptr<media::VideoCaptureBufferPool> buffer_pool,
+        scoped_refptr<media::VideoFrame> frame);
+    ~BufferState();
+    BufferState(const BufferState& other);
+    void RecordConsumerUtilization(double utilization);
+    void IncreaseConsumerCount();
+    void DecreaseConsumerCount();
+    bool HasZeroConsumerHoldCount();
+    void SetConsumerFeedbackObserver(
+        media::VideoFrameConsumerFeedbackObserver* consumer_feedback_observer);
+
+   private:
+    const int buffer_id_;
+    const int frame_feedback_id_;
+    media::VideoFrameConsumerFeedbackObserver* consumer_feedback_observer_;
+    const scoped_refptr<media::VideoCaptureBufferPool> buffer_pool_;
+    const scoped_refptr<media::VideoFrame> frame_;
+    double max_consumer_utilization_;
+    int consumer_hold_count_;
+  };
+
   // Notify renderer that a new buffer has been created.
   void DoNewBufferOnIOThread(ControllerClient* client,
                              media::VideoCaptureDevice::Client::Buffer* buffer,
@@ -156,6 +191,11 @@
   // The pool of shared-memory buffers used for capturing.
   const scoped_refptr<media::VideoCaptureBufferPool> buffer_pool_;
 
+  std::unique_ptr<media::VideoFrameConsumerFeedbackObserver>
+      consumer_feedback_observer_;
+
+  std::map<int, BufferState> buffer_id_to_state_map_;
+
   // All clients served by this controller.
   ControllerClients controller_clients_;
 
diff --git a/content/browser/renderer_host/media/video_capture_controller_unittest.cc b/content/browser/renderer_host/media/video_capture_controller_unittest.cc
index 21c5fbe..2529e895 100644
--- a/content/browser/renderer_host/media/video_capture_controller_unittest.cc
+++ b/content/browser/renderer_host/media/video_capture_controller_unittest.cc
@@ -33,6 +33,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 using ::testing::_;
+using ::testing::AnyNumber;
 using ::testing::InSequence;
 using ::testing::Mock;
 using ::testing::SaveArg;
@@ -95,6 +96,13 @@
   double resource_utilization_;
 };
 
+class MockConsumerFeedbackObserver
+    : public media::VideoFrameConsumerFeedbackObserver {
+ public:
+  MOCK_METHOD2(OnUtilizationReport,
+               void(int frame_feedback_id, double utilization));
+};
+
 // Test class.
 class VideoCaptureControllerTest
     : public testing::Test,
@@ -109,6 +117,11 @@
   void SetUp() override {
     controller_.reset(new VideoCaptureController(kPoolSize));
     device_ = controller_->NewDeviceClient();
+    auto consumer_feedback_observer =
+        base::MakeUnique<MockConsumerFeedbackObserver>();
+    mock_consumer_feedback_observer_ = consumer_feedback_observer.get();
+    controller_->SetConsumerFeedbackObserver(
+        std::move(consumer_feedback_observer));
     client_a_.reset(new MockVideoCaptureControllerEventHandler(
         controller_.get()));
     client_b_.reset(new MockVideoCaptureControllerEventHandler(
@@ -135,6 +148,7 @@
   std::unique_ptr<MockVideoCaptureControllerEventHandler> client_b_;
   std::unique_ptr<VideoCaptureController> controller_;
   std::unique_ptr<media::VideoCaptureDevice::Client> device_;
+  MockConsumerFeedbackObserver* mock_consumer_feedback_observer_;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(VideoCaptureControllerTest);
@@ -284,10 +298,12 @@
   // Now, simulate an incoming captured buffer from the capture device. As a
   // side effect this will cause the first buffer to be shared with clients.
   uint8_t buffer_no = 1;
+  const int arbitrary_frame_feedback_id = 101;
   ASSERT_EQ(0.0, device_->GetBufferPoolUtilization());
   std::unique_ptr<media::VideoCaptureDevice::Client::Buffer> buffer(
       device_->ReserveOutputBuffer(capture_resolution, format,
-                                   media::PIXEL_STORAGE_CPU));
+                                   media::PIXEL_STORAGE_CPU,
+                                   arbitrary_frame_feedback_id));
   ASSERT_TRUE(buffer.get());
   ASSERT_EQ(1.0 / kPoolSize, device_->GetBufferPoolUtilization());
   memset(buffer->data(), buffer_no++, buffer->mapped_size());
@@ -316,6 +332,13 @@
       media::VideoFrameMetadata::RESOURCE_UTILIZATION));
   client_a_->resource_utilization_ = 0.5;
   client_b_->resource_utilization_ = -1.0;
+
+  // Expect VideoCaptureController to call the load observer with a
+  // resource utilization of 0.5 (the largest of all reported values).
+  EXPECT_CALL(*mock_consumer_feedback_observer_,
+              OnUtilizationReport(arbitrary_frame_feedback_id, 0.5))
+      .Times(1);
+
   video_frame->metadata()->SetTimeTicks(
       media::VideoFrameMetadata::REFERENCE_TIME, base::TimeTicks());
   device_->OnIncomingCapturedVideoFrame(std::move(buffer), video_frame);
@@ -323,32 +346,30 @@
   base::RunLoop().RunUntilIdle();
   Mock::VerifyAndClearExpectations(client_a_.get());
   Mock::VerifyAndClearExpectations(client_b_.get());
-
-  // Expect VideoCaptureController set the metadata in |video_frame| to hold a
-  // resource utilization of 0.5 (the largest of all reported values).
-  double resource_utilization_in_metadata = -1.0;
-  ASSERT_TRUE(video_frame->metadata()->GetDouble(
-      media::VideoFrameMetadata::RESOURCE_UTILIZATION,
-      &resource_utilization_in_metadata));
-  ASSERT_EQ(0.5, resource_utilization_in_metadata);
+  Mock::VerifyAndClearExpectations(mock_consumer_feedback_observer_);
 
   // Second buffer which ought to use the same shared memory buffer. In this
   // case pretend that the Buffer pointer is held by the device for a long
   // delay. This shouldn't affect anything.
+  const int arbitrary_frame_feedback_id_2 = 102;
   std::unique_ptr<media::VideoCaptureDevice::Client::Buffer> buffer2 =
       device_->ReserveOutputBuffer(capture_resolution, format,
-                                   media::PIXEL_STORAGE_CPU);
+                                   media::PIXEL_STORAGE_CPU,
+                                   arbitrary_frame_feedback_id_2);
   ASSERT_TRUE(buffer2.get());
   memset(buffer2->data(), buffer_no++, buffer2->mapped_size());
   video_frame = WrapBuffer(capture_resolution,
                            static_cast<uint8_t*>(buffer2->data()), format);
-  ASSERT_TRUE(video_frame);
-  ASSERT_FALSE(video_frame->metadata()->HasKey(
-      media::VideoFrameMetadata::RESOURCE_UTILIZATION));
   client_a_->resource_utilization_ = 0.5;
   client_b_->resource_utilization_ = 3.14;
   video_frame->metadata()->SetTimeTicks(
       media::VideoFrameMetadata::REFERENCE_TIME, base::TimeTicks());
+  // Expect VideoCaptureController to call the load observer with a
+  // resource utilization of 3.14 (the largest of all reported values).
+  EXPECT_CALL(*mock_consumer_feedback_observer_,
+              OnUtilizationReport(arbitrary_frame_feedback_id_2, 3.14))
+      .Times(1);
+
   device_->OnIncomingCapturedVideoFrame(std::move(buffer2), video_frame);
 
   // The buffer should be delivered to the clients in any order.
@@ -373,13 +394,7 @@
   base::RunLoop().RunUntilIdle();
   Mock::VerifyAndClearExpectations(client_a_.get());
   Mock::VerifyAndClearExpectations(client_b_.get());
-  // Expect VideoCaptureController set the metadata in |video_frame| to hold a
-  // resource utilization of 3.14 (the largest of all reported values).
-  resource_utilization_in_metadata = -1.0;
-  ASSERT_TRUE(video_frame->metadata()->GetDouble(
-      media::VideoFrameMetadata::RESOURCE_UTILIZATION,
-      &resource_utilization_in_metadata));
-  ASSERT_EQ(3.14, resource_utilization_in_metadata);
+  Mock::VerifyAndClearExpectations(mock_consumer_feedback_observer_);
 
   // Add a fourth client now that some buffers have come through.
   controller_->AddClient(client_b_route_2,
@@ -390,9 +405,11 @@
 
   // Third, fourth, and fifth buffers. Pretend they all arrive at the same time.
   for (int i = 0; i < kPoolSize; i++) {
+    const int arbitrary_frame_feedback_id = 200 + i;
     std::unique_ptr<media::VideoCaptureDevice::Client::Buffer> buffer =
         device_->ReserveOutputBuffer(capture_resolution, format,
-                                     media::PIXEL_STORAGE_CPU);
+                                     media::PIXEL_STORAGE_CPU,
+                                     arbitrary_frame_feedback_id);
     ASSERT_TRUE(buffer.get());
     memset(buffer->data(), buffer_no++, buffer->mapped_size());
     video_frame = WrapBuffer(capture_resolution,
@@ -405,7 +422,8 @@
   // ReserveOutputBuffer ought to fail now, because the pool is depleted.
   ASSERT_FALSE(device_
                    ->ReserveOutputBuffer(capture_resolution, format,
-                                         media::PIXEL_STORAGE_CPU)
+                                         media::PIXEL_STORAGE_CPU,
+                                         arbitrary_frame_feedback_id)
                    .get());
 
   // The new client needs to be notified of the creation of |kPoolSize| buffers;
@@ -438,7 +456,8 @@
   // Queue up another buffer.
   std::unique_ptr<media::VideoCaptureDevice::Client::Buffer> buffer3 =
       device_->ReserveOutputBuffer(capture_resolution, format,
-                                   media::PIXEL_STORAGE_CPU);
+                                   media::PIXEL_STORAGE_CPU,
+                                   arbitrary_frame_feedback_id);
   ASSERT_TRUE(buffer3.get());
   memset(buffer3->data(), buffer_no++, buffer3->mapped_size());
   video_frame = WrapBuffer(capture_resolution,
@@ -450,7 +469,8 @@
 
   std::unique_ptr<media::VideoCaptureDevice::Client::Buffer> buffer4 =
       device_->ReserveOutputBuffer(capture_resolution, format,
-                                   media::PIXEL_STORAGE_CPU);
+                                   media::PIXEL_STORAGE_CPU,
+                                   arbitrary_frame_feedback_id);
   {
     // Kill A2 via session close (posts a task to disconnect, but A2 must not
     // be sent either of these two buffers).
@@ -506,9 +526,11 @@
   base::RunLoop().RunUntilIdle();
   Mock::VerifyAndClearExpectations(client_b_.get());
 
+  const int arbitrary_frame_feedback_id = 101;
   std::unique_ptr<media::VideoCaptureDevice::Client::Buffer> buffer(
       device_->ReserveOutputBuffer(capture_resolution, media::PIXEL_FORMAT_I420,
-                                   media::PIXEL_STORAGE_CPU));
+                                   media::PIXEL_STORAGE_CPU,
+                                   arbitrary_frame_feedback_id));
   ASSERT_TRUE(buffer.get());
   scoped_refptr<media::VideoFrame> video_frame =
       WrapBuffer(capture_resolution, static_cast<uint8_t*>(buffer->data()));
@@ -543,9 +565,11 @@
   Mock::VerifyAndClearExpectations(client_a_.get());
 
   const gfx::Size dims(320, 240);
+  const int arbitrary_frame_feedback_id = 101;
   std::unique_ptr<media::VideoCaptureDevice::Client::Buffer> buffer(
       device_->ReserveOutputBuffer(dims, media::PIXEL_FORMAT_I420,
-                                   media::PIXEL_STORAGE_CPU));
+                                   media::PIXEL_STORAGE_CPU,
+                                   arbitrary_frame_feedback_id));
   ASSERT_TRUE(buffer.get());
 
   scoped_refptr<media::VideoFrame> video_frame =
diff --git a/content/browser/renderer_host/media/video_capture_manager.cc b/content/browser/renderer_host/media/video_capture_manager.cc
index e8664c1..e1830ee 100644
--- a/content/browser/renderer_host/media/video_capture_manager.cc
+++ b/content/browser/renderer_host/media/video_capture_manager.cc
@@ -47,6 +47,27 @@
 
 namespace {
 
+class VideoFrameConsumerFeedbackObserverOnTaskRunner
+    : public media::VideoFrameConsumerFeedbackObserver {
+ public:
+  VideoFrameConsumerFeedbackObserverOnTaskRunner(
+      media::VideoFrameConsumerFeedbackObserver* observer,
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+      : observer_(observer), task_runner_(std::move(task_runner)) {}
+
+  void OnUtilizationReport(int frame_feedback_id, double utilization) override {
+    task_runner_->PostTask(
+        FROM_HERE,
+        base::Bind(
+            &media::VideoFrameConsumerFeedbackObserver::OnUtilizationReport,
+            base::Unretained(observer_), frame_feedback_id, utilization));
+  }
+
+ private:
+  media::VideoFrameConsumerFeedbackObserver* const observer_;
+  const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+};
+
 // Compares two VideoCaptureFormat by checking smallest frame_size area, then
 // by _largest_ frame_rate. Used to order a VideoCaptureFormats vector so that
 // the first entry for a given resolution has the largest frame rate, as needed
@@ -126,9 +147,17 @@
 
 namespace content {
 
-// This class owns a pair VideoCaptureDevice - VideoCaptureController.
-// VideoCaptureManager owns all such pairs and is responsible for deleting the
-// instances when they are not used any longer.
+// Instances of this struct go through 3 different phases during their lifetime.
+// Phase 1: When first created (in GetOrCreateDeviceEntry()), this consists of
+// only a controller. Clients can already connect to the controller, but there
+// is no device present.
+// Phase 2: When a request to "start" the entry comes in (via
+// HandleQueuedStartRequest()), a VideoCaptureDevice::Client is created
+// via video_capture_controller()->NewDeviceClient() and is used to schedule the
+// creation and start of a VideoCaptureDevice on the Device Thread.
+// Phase 3: As soon as the creation of the VideoCaptureDevice is complete, this
+// newly created VideoCaptureDevice instance is connected to the
+// VideoCaptureController via SetConsumerFeedbackObserver().
 class VideoCaptureManager::DeviceEntry {
  public:
   DeviceEntry(MediaStreamType stream_type,
@@ -406,9 +435,11 @@
            << " serial_id = " << entry->serial_id << ".";
   entry->video_capture_controller()->OnLog(
       base::StringPrintf("Stopping device: id: %s", entry->id.c_str()));
+  entry->video_capture_controller()->SetConsumerFeedbackObserver(nullptr);
 
+  // |entry->video_capture_device| can be null if creating the device has
+  // failed.
   if (entry->video_capture_device()) {
-    // |entry->video_capture_device| can be null if creating the device fails.
     device_task_runner_->PostTask(
         FROM_HERE,
         base::Bind(&VideoCaptureManager::DoStopDeviceOnDeviceThread, this,
@@ -504,9 +535,9 @@
   DVLOG(3) << __func__;
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK_EQ(serial_id, device_start_queue_.begin()->serial_id());
+  // |device| can be null if creation failed in
+  //  DoStartDeviceCaptureOnDeviceThread.
   if (device_start_queue_.front().abort_start()) {
-    // |device| can be null if creation failed in
-    // DoStartDeviceCaptureOnDeviceThread.
     // The device is no longer wanted. Stop the device again.
     DVLOG(3) << "OnDeviceStarted but start request have been aborted.";
     media::VideoCaptureDevice* device_ptr = device.get();
@@ -521,6 +552,13 @@
     DeviceEntry* const entry = GetDeviceEntryBySerialId(serial_id);
     DCHECK(entry);
     DCHECK(!entry->video_capture_device());
+    // Passing raw pointer |device.get()| to the controller is safe,
+    // because we transfer ownership of it to |entry|. We are calling
+    // SetConsumerFeedbackObserver(nullptr) before releasing
+    // |entry->video_capture_device_| on the |device_task_runner_|.
+    entry->video_capture_controller()->SetConsumerFeedbackObserver(
+        base::MakeUnique<VideoFrameConsumerFeedbackObserverOnTaskRunner>(
+            device.get(), device_task_runner_));
     entry->SetVideoCaptureDevice(std::move(device));
 
     if (entry->stream_type == MEDIA_DESKTOP_VIDEO_CAPTURE) {
diff --git a/content/browser/service_worker/foreign_fetch_request_handler.cc b/content/browser/service_worker/foreign_fetch_request_handler.cc
index 5298f1c..ab2d134 100644
--- a/content/browser/service_worker/foreign_fetch_request_handler.cc
+++ b/content/browser/service_worker/foreign_fetch_request_handler.cc
@@ -91,7 +91,12 @@
   if (!initiated_in_secure_context)
     return;
 
-  if (ServiceWorkerUtils::IsMainResourceType(resource_type))
+  // ServiceWorkerUtils::IsMainResource doesn't consider all worker types to
+  // be main resources. This code shouldn't handle any main resource requests
+  // though, so explicitly exclude the extra worker types.
+  if (ServiceWorkerUtils::IsMainResourceType(resource_type) ||
+      resource_type == RESOURCE_TYPE_WORKER ||
+      resource_type == RESOURCE_TYPE_SERVICE_WORKER)
     return;
 
   if (request->initiator().has_value() &&
@@ -99,6 +104,17 @@
     return;
   }
 
+  ServiceWorkerProviderHost* provider_host =
+      context_wrapper->context()->GetProviderHost(process_id, provider_id);
+  if (!provider_host || !provider_host->IsContextAlive())
+    return;
+
+  base::Optional<base::TimeDelta> timeout;
+  if (provider_host->IsHostToRunningServiceWorker()) {
+    timeout = base::make_optional(
+        provider_host->running_hosted_version()->remaining_timeout());
+  }
+
   if (!context_wrapper->OriginHasForeignFetchRegistrations(
           request->url().GetOrigin())) {
     return;
@@ -110,7 +126,7 @@
       new ForeignFetchRequestHandler(
           context_wrapper, blob_storage_context->AsWeakPtr(), request_mode,
           credentials_mode, redirect_mode, resource_type, request_context_type,
-          frame_type, body));
+          frame_type, body, timeout));
   request->SetUserData(&kUserDataKey, handler.release());
 }
 
@@ -163,7 +179,7 @@
       request, network_delegate, std::string(), blob_storage_context_,
       resource_context, request_mode_, credentials_mode_, redirect_mode_,
       resource_type_, request_context_type_, frame_type_, body_,
-      ServiceWorkerFetchType::FOREIGN_FETCH, this);
+      ServiceWorkerFetchType::FOREIGN_FETCH, timeout_, this);
   job_ = job->GetWeakPtr();
   resource_context_ = resource_context;
 
@@ -184,7 +200,8 @@
     ResourceType resource_type,
     RequestContextType request_context_type,
     RequestContextFrameType frame_type,
-    scoped_refptr<ResourceRequestBodyImpl> body)
+    scoped_refptr<ResourceRequestBodyImpl> body,
+    const base::Optional<base::TimeDelta>& timeout)
     : context_(context),
       blob_storage_context_(blob_storage_context),
       resource_type_(resource_type),
@@ -194,6 +211,7 @@
       request_context_type_(request_context_type),
       frame_type_(frame_type),
       body_(body),
+      timeout_(timeout),
       weak_factory_(this) {}
 
 void ForeignFetchRequestHandler::DidFindRegistration(
diff --git a/content/browser/service_worker/foreign_fetch_request_handler.h b/content/browser/service_worker/foreign_fetch_request_handler.h
index 48cb85e..c047dc3 100644
--- a/content/browser/service_worker/foreign_fetch_request_handler.h
+++ b/content/browser/service_worker/foreign_fetch_request_handler.h
@@ -7,6 +7,7 @@
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "base/optional.h"
 #include "base/supports_user_data.h"
 #include "base/time/time.h"
 #include "content/browser/service_worker/service_worker_url_request_job.h"
@@ -94,7 +95,8 @@
       ResourceType resource_type,
       RequestContextType request_context_type,
       RequestContextFrameType frame_type,
-      scoped_refptr<ResourceRequestBodyImpl> body);
+      scoped_refptr<ResourceRequestBodyImpl> body,
+      const base::Optional<base::TimeDelta>& timeout);
 
   // Called when a ServiceWorkerRegistration has (or hasn't) been found for the
   // request being handled.
@@ -128,6 +130,7 @@
   RequestContextFrameType frame_type_;
   scoped_refptr<ResourceRequestBodyImpl> body_;
   ResourceContext* resource_context_;
+  base::Optional<base::TimeDelta> timeout_;
 
   base::WeakPtr<ServiceWorkerURLRequestJob> job_;
   scoped_refptr<ServiceWorkerVersion> target_worker_;
diff --git a/content/browser/service_worker/foreign_fetch_request_handler_unittest.cc b/content/browser/service_worker/foreign_fetch_request_handler_unittest.cc
index 8a12374b..c1410fe1 100644
--- a/content/browser/service_worker/foreign_fetch_request_handler_unittest.cc
+++ b/content/browser/service_worker/foreign_fetch_request_handler_unittest.cc
@@ -6,18 +6,24 @@
 
 #include "base/memory/ptr_util.h"
 #include "base/run_loop.h"
+#include "base/test/simple_test_tick_clock.h"
 #include "content/browser/browser_thread_impl.h"
+#include "content/browser/fileapi/mock_url_request_delegate.h"
 #include "content/browser/service_worker/embedded_worker_test_helper.h"
 #include "content/browser/service_worker/service_worker_context_core.h"
 #include "content/browser/service_worker/service_worker_registration.h"
 #include "content/browser/service_worker/service_worker_test_utils.h"
 #include "content/browser/service_worker/service_worker_version.h"
+#include "content/common/service_worker/service_worker_utils.h"
 #include "content/public/common/content_client.h"
 #include "content/public/common/origin_trial_policy.h"
 #include "content/public/test/test_browser_thread_bundle.h"
+#include "content/test/test_content_browser_client.h"
 #include "net/http/http_response_info.h"
 #include "net/test/cert_test_util.h"
 #include "net/test/test_data_directory.h"
+#include "net/url_request/url_request_context.h"
+#include "storage/browser/blob/blob_storage_context.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace content {
@@ -39,6 +45,12 @@
     0x64, 0x90, 0x08, 0x8e, 0xa8, 0xe0, 0x56, 0x3a, 0x04, 0xd0,
 };
 
+int kMockProviderId = 1;
+
+const char* kValidUrl = "https://valid.example.com/foo/bar";
+
+void EmptyCallback() {}
+
 }  // namespace
 
 class ForeignFetchRequestHandlerTest : public testing::Test {
@@ -46,6 +58,7 @@
   ForeignFetchRequestHandlerTest()
       : browser_thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP) {
     SetContentClient(&test_content_client_);
+    SetBrowserClientForTesting(&test_content_browser_client_);
   }
   ~ForeignFetchRequestHandlerTest() override {}
 
@@ -59,6 +72,35 @@
                                                   context()->AsWeakPtr());
     version_ = new ServiceWorkerVersion(registration_.get(), kResource1,
                                         kVersionId, context()->AsWeakPtr());
+
+    version_->set_foreign_fetch_scopes({kScope});
+
+    // An empty host.
+    std::unique_ptr<ServiceWorkerProviderHost> host(
+        new ServiceWorkerProviderHost(
+            helper_->mock_render_process_id(), MSG_ROUTING_NONE,
+            kMockProviderId, SERVICE_WORKER_PROVIDER_FOR_WINDOW,
+            ServiceWorkerProviderHost::FrameSecurityLevel::SECURE,
+            context()->AsWeakPtr(), nullptr));
+    host->SetDocumentUrl(GURL("https://host/scope/"));
+    provider_host_ = host->AsWeakPtr();
+    context()->AddProviderHost(std::move(host));
+
+    context()->storage()->LazyInitialize(base::Bind(&EmptyCallback));
+    base::RunLoop().RunUntilIdle();
+
+    std::vector<ServiceWorkerDatabase::ResourceRecord> records;
+    records.push_back(
+        ServiceWorkerDatabase::ResourceRecord(10, version_->script_url(), 100));
+    version_->script_cache_map()->SetResources(records);
+    version_->set_fetch_handler_existence(
+        ServiceWorkerVersion::FetchHandlerExistence::EXISTS);
+    version_->SetStatus(ServiceWorkerVersion::ACTIVATED);
+    registration_->SetActiveVersion(version_);
+    context()->storage()->StoreRegistration(
+        registration_.get(), version_.get(),
+        base::Bind(&ServiceWorkerUtils::NoOpStatusCallback));
+    base::RunLoop().RunUntilIdle();
   }
 
   void TearDown() override {
@@ -70,11 +112,22 @@
 
  protected:
   ServiceWorkerContextCore* context() const { return helper_->context(); }
+  ServiceWorkerContextWrapper* context_wrapper() const {
+    return helper_->context_wrapper();
+  }
+  ServiceWorkerProviderHost* provider_host() const {
+    return provider_host_.get();
+  }
 
   bool CheckOriginTrialToken(const ServiceWorkerVersion* const version) const {
     return ForeignFetchRequestHandler::CheckOriginTrialToken(version);
   }
 
+  base::Optional<base::TimeDelta> timeout_for_request(
+      ForeignFetchRequestHandler* handler) {
+    return handler->timeout_;
+  }
+
   ServiceWorkerVersion* version() const { return version_.get(); }
 
   static std::unique_ptr<net::HttpResponseInfo> CreateTestHttpResponseInfo() {
@@ -90,6 +143,59 @@
     return http_info;
   }
 
+  ForeignFetchRequestHandler* InitializeHandler(const std::string& url,
+                                                ResourceType resource_type,
+                                                const char* initiator) {
+    request_ = url_request_context_.CreateRequest(
+        GURL(url), net::DEFAULT_PRIORITY, &url_request_delegate_);
+    if (initiator)
+      request_->set_initiator(url::Origin(GURL(initiator)));
+    ForeignFetchRequestHandler::InitializeHandler(
+        request_.get(), context_wrapper(), &blob_storage_context_,
+        helper_->mock_render_process_id(), kMockProviderId,
+        SkipServiceWorker::NONE, FETCH_REQUEST_MODE_CORS,
+        FETCH_CREDENTIALS_MODE_OMIT, FetchRedirectMode::FOLLOW_MODE,
+        resource_type, REQUEST_CONTEXT_TYPE_FETCH,
+        REQUEST_CONTEXT_FRAME_TYPE_NONE, nullptr,
+        true /* initiated_in_secure_context */);
+
+    return ForeignFetchRequestHandler::GetHandler(request_.get());
+  }
+
+  void CreateServiceWorkerTypeProviderHost() {
+    std::unique_ptr<ServiceWorkerProviderHost> host(
+        new ServiceWorkerProviderHost(
+            helper_->mock_render_process_id(), MSG_ROUTING_NONE,
+            kMockProviderId, SERVICE_WORKER_PROVIDER_FOR_CONTROLLER,
+            ServiceWorkerProviderHost::FrameSecurityLevel::UNINITIALIZED,
+            context()->AsWeakPtr(), nullptr));
+    provider_host_ = host->AsWeakPtr();
+    context()->RemoveProviderHost(host->process_id(), host->provider_id());
+    context()->AddProviderHost(std::move(host));
+
+    scoped_refptr<ServiceWorkerRegistration> registration =
+        new ServiceWorkerRegistration(GURL("https://host/scope"), 1L,
+                                      context()->AsWeakPtr());
+    scoped_refptr<ServiceWorkerVersion> version = new ServiceWorkerVersion(
+        registration.get(), GURL("https://host/script.js"), 1L,
+        context()->AsWeakPtr());
+
+    std::vector<ServiceWorkerDatabase::ResourceRecord> records;
+    records.push_back(
+        ServiceWorkerDatabase::ResourceRecord(10, version->script_url(), 100));
+    version->script_cache_map()->SetResources(records);
+    version->set_fetch_handler_existence(
+        ServiceWorkerVersion::FetchHandlerExistence::EXISTS);
+    version->SetStatus(ServiceWorkerVersion::ACTIVATED);
+    registration->SetActiveVersion(version);
+    context()->storage()->StoreRegistration(
+        registration.get(), version.get(),
+        base::Bind(&ServiceWorkerUtils::NoOpStatusCallback));
+    base::RunLoop().RunUntilIdle();
+
+    provider_host_->running_hosted_version_ = version;
+  }
+
  private:
   class TestContentClient : public ContentClient {
    public:
@@ -116,9 +222,16 @@
   scoped_refptr<ServiceWorkerRegistration> registration_;
   scoped_refptr<ServiceWorkerVersion> version_;
   TestContentClient test_content_client_;
+  TestContentBrowserClient test_content_browser_client_;
   std::unique_ptr<EmbeddedWorkerTestHelper> helper_;
   TestBrowserThreadBundle browser_thread_bundle_;
 
+  net::URLRequestContext url_request_context_;
+  MockURLRequestDelegate url_request_delegate_;
+  base::WeakPtr<ServiceWorkerProviderHost> provider_host_;
+  storage::BlobStorageContext blob_storage_context_;
+  std::unique_ptr<net::URLRequest> request_;
+
   DISALLOW_COPY_AND_ASSIGN(ForeignFetchRequestHandlerTest);
 };
 
@@ -185,4 +298,69 @@
   EXPECT_FALSE(CheckOriginTrialToken(version()));
 }
 
+TEST_F(ForeignFetchRequestHandlerTest, InitializeHandler_Success) {
+  EXPECT_TRUE(InitializeHandler(kValidUrl, RESOURCE_TYPE_IMAGE,
+                                nullptr /* initiator */));
+}
+
+TEST_F(ForeignFetchRequestHandlerTest, InitializeHandler_WrongResourceType) {
+  EXPECT_FALSE(InitializeHandler(kValidUrl, RESOURCE_TYPE_MAIN_FRAME,
+                                 nullptr /* initiator */));
+  EXPECT_FALSE(InitializeHandler(kValidUrl, RESOURCE_TYPE_SUB_FRAME,
+                                 nullptr /* initiator */));
+  EXPECT_FALSE(InitializeHandler(kValidUrl, RESOURCE_TYPE_WORKER,
+                                 nullptr /* initiator */));
+  EXPECT_FALSE(InitializeHandler(kValidUrl, RESOURCE_TYPE_SHARED_WORKER,
+                                 nullptr /* initiator */));
+  EXPECT_FALSE(InitializeHandler(kValidUrl, RESOURCE_TYPE_SERVICE_WORKER,
+                                 nullptr /* initiator */));
+}
+
+TEST_F(ForeignFetchRequestHandlerTest, InitializeHandler_SameOriginRequest) {
+  EXPECT_FALSE(InitializeHandler(kValidUrl, RESOURCE_TYPE_IMAGE,
+                                 kValidUrl /* initiator */));
+}
+
+TEST_F(ForeignFetchRequestHandlerTest, InitializeHandler_NoRegisteredHandlers) {
+  EXPECT_FALSE(InitializeHandler("https://invalid.example.com/foo",
+                                 RESOURCE_TYPE_IMAGE, nullptr /* initiator */));
+}
+
+TEST_F(ForeignFetchRequestHandlerTest, InitializeHandler_TimeoutBehavior) {
+  ForeignFetchRequestHandler* handler =
+      InitializeHandler("https://valid.example.com/foo", RESOURCE_TYPE_IMAGE,
+                        nullptr /* initiator */);
+  ASSERT_TRUE(handler);
+
+  EXPECT_EQ(base::nullopt, timeout_for_request(handler));
+
+  CreateServiceWorkerTypeProviderHost();
+  ServiceWorkerVersion* version = provider_host()->running_hosted_version();
+
+  // Set mock clock on version to check timeout behavior.
+  base::SimpleTestTickClock* tick_clock = new base::SimpleTestTickClock();
+  tick_clock->SetNowTicks(base::TimeTicks::Now());
+  version->SetTickClockForTesting(base::WrapUnique(tick_clock));
+
+  // Make sure worker has a non-zero timeout.
+  version->StartWorker(ServiceWorkerMetrics::EventType::UNKNOWN,
+                       base::Bind(&ServiceWorkerUtils::NoOpStatusCallback));
+  base::RunLoop().RunUntilIdle();
+  version->StartRequestWithCustomTimeout(
+      ServiceWorkerMetrics::EventType::ACTIVATE,
+      base::Bind(&ServiceWorkerUtils::NoOpStatusCallback),
+      base::TimeDelta::FromSeconds(10), ServiceWorkerVersion::KILL_ON_TIMEOUT);
+
+  // Advance clock by a couple seconds.
+  tick_clock->Advance(base::TimeDelta::FromSeconds(4));
+  base::TimeDelta remaining_time = version->remaining_timeout();
+  EXPECT_EQ(base::TimeDelta::FromSeconds(6), remaining_time);
+
+  // Make sure new request only gets remaining timeout.
+  handler = InitializeHandler("https://valid.example.com/foo",
+                              RESOURCE_TYPE_IMAGE, nullptr /* initiator */);
+  ASSERT_TRUE(handler);
+  EXPECT_EQ(remaining_time, timeout_for_request(handler));
+}
+
 }  // namespace content
diff --git a/content/browser/service_worker/service_worker_browsertest.cc b/content/browser/service_worker/service_worker_browsertest.cc
index 8be954f..9b2189a 100644
--- a/content/browser/service_worker/service_worker_browsertest.cc
+++ b/content/browser/service_worker/service_worker_browsertest.cc
@@ -791,7 +791,8 @@
     version_->SetStatus(ServiceWorkerVersion::ACTIVATED);
     fetch_dispatcher_.reset(new ServiceWorkerFetchDispatcher(
         std::move(request), version_.get(), RESOURCE_TYPE_MAIN_FRAME,
-        net::NetLogWithSource(), CreatePrepareReceiver(prepare_result),
+        base::nullopt, net::NetLogWithSource(),
+        CreatePrepareReceiver(prepare_result),
         CreateResponseReceiver(done, blob_context_.get(), result)));
     fetch_dispatcher_->Run();
   }
diff --git a/content/browser/service_worker/service_worker_context_wrapper.h b/content/browser/service_worker/service_worker_context_wrapper.h
index a130f94..cadaaad 100644
--- a/content/browser/service_worker/service_worker_context_wrapper.h
+++ b/content/browser/service_worker/service_worker_context_wrapper.h
@@ -217,6 +217,7 @@
   friend class base::RefCountedThreadSafe<ServiceWorkerContextWrapper>;
   friend class EmbeddedWorkerTestHelper;
   friend class EmbeddedWorkerBrowserTest;
+  friend class ForeignFetchRequestHandler;
   friend class ServiceWorkerDispatcherHost;
   friend class ServiceWorkerInternalsUI;
   friend class ServiceWorkerNavigationHandleCore;
diff --git a/content/browser/service_worker/service_worker_controllee_request_handler.cc b/content/browser/service_worker/service_worker_controllee_request_handler.cc
index e12c224c..58e7c16 100644
--- a/content/browser/service_worker/service_worker_controllee_request_handler.cc
+++ b/content/browser/service_worker/service_worker_controllee_request_handler.cc
@@ -126,7 +126,7 @@
           blob_storage_context_, resource_context, request_mode_,
           credentials_mode_, redirect_mode_, resource_type_,
           request_context_type_, frame_type_, body_,
-          ServiceWorkerFetchType::FETCH, this));
+          ServiceWorkerFetchType::FETCH, base::nullopt, this));
   job_ = job->GetWeakPtr();
 
   resource_context_ = resource_context;
diff --git a/content/browser/service_worker/service_worker_fetch_dispatcher.cc b/content/browser/service_worker/service_worker_fetch_dispatcher.cc
index 23446d5..c899c8e 100644
--- a/content/browser/service_worker/service_worker_fetch_dispatcher.cc
+++ b/content/browser/service_worker/service_worker_fetch_dispatcher.cc
@@ -210,6 +210,7 @@
     std::unique_ptr<ServiceWorkerFetchRequest> request,
     ServiceWorkerVersion* version,
     ResourceType resource_type,
+    const base::Optional<base::TimeDelta>& timeout,
     const net::NetLogWithSource& net_log,
     const base::Closure& prepare_callback,
     const FetchCallback& fetch_callback)
@@ -219,6 +220,7 @@
       fetch_callback_(fetch_callback),
       request_(std::move(request)),
       resource_type_(resource_type),
+      timeout_(timeout),
       did_complete_(false),
       weak_factory_(this) {
   net_log_.BeginEvent(net::NetLogEventType::SERVICE_WORKER_DISPATCH_FETCH_EVENT,
@@ -297,13 +299,27 @@
   prepare_callback.Run();
 
   net_log_.BeginEvent(net::NetLogEventType::SERVICE_WORKER_FETCH_EVENT);
-  int fetch_event_id = version_->StartRequest(
-      GetEventType(),
-      base::Bind(&ServiceWorkerFetchDispatcher::DidFailToDispatch,
-                 weak_factory_.GetWeakPtr()));
-  int event_finish_id = version_->StartRequest(
-      FetchTypeToWaitUntilEventType(request_->fetch_type),
-      base::Bind(&ServiceWorkerUtils::NoOpStatusCallback));
+  int fetch_event_id;
+  int event_finish_id;
+  if (timeout_) {
+    fetch_event_id = version_->StartRequestWithCustomTimeout(
+        GetEventType(),
+        base::Bind(&ServiceWorkerFetchDispatcher::DidFailToDispatch,
+                   weak_factory_.GetWeakPtr()),
+        *timeout_, ServiceWorkerVersion::CONTINUE_ON_TIMEOUT);
+    event_finish_id = version_->StartRequestWithCustomTimeout(
+        FetchTypeToWaitUntilEventType(request_->fetch_type),
+        base::Bind(&ServiceWorkerUtils::NoOpStatusCallback), *timeout_,
+        ServiceWorkerVersion::CONTINUE_ON_TIMEOUT);
+  } else {
+    fetch_event_id = version_->StartRequest(
+        GetEventType(),
+        base::Bind(&ServiceWorkerFetchDispatcher::DidFailToDispatch,
+                   weak_factory_.GetWeakPtr()));
+    event_finish_id = version_->StartRequest(
+        FetchTypeToWaitUntilEventType(request_->fetch_type),
+        base::Bind(&ServiceWorkerUtils::NoOpStatusCallback));
+  }
 
   ResponseCallback* response_callback =
       new ResponseCallback(weak_factory_.GetWeakPtr(), version_.get());
diff --git a/content/browser/service_worker/service_worker_fetch_dispatcher.h b/content/browser/service_worker/service_worker_fetch_dispatcher.h
index c28818e..319f031 100644
--- a/content/browser/service_worker/service_worker_fetch_dispatcher.h
+++ b/content/browser/service_worker/service_worker_fetch_dispatcher.h
@@ -10,6 +10,8 @@
 #include "base/callback.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "base/time/time.h"
 #include "content/browser/service_worker/service_worker_metrics.h"
 #include "content/common/content_export.h"
 #include "content/common/service_worker/service_worker_event_dispatcher.mojom.h"
@@ -41,6 +43,7 @@
       std::unique_ptr<ServiceWorkerFetchRequest> request,
       ServiceWorkerVersion* version,
       ResourceType resource_type,
+      const base::Optional<base::TimeDelta>& timeout,
       const net::NetLogWithSource& net_log,
       const base::Closure& prepare_callback,
       const FetchCallback& fetch_callback);
@@ -81,6 +84,7 @@
   FetchCallback fetch_callback_;
   std::unique_ptr<ServiceWorkerFetchRequest> request_;
   ResourceType resource_type_;
+  base::Optional<base::TimeDelta> timeout_;
   bool did_complete_;
   mojom::URLLoaderFactoryPtr url_loader_factory_;
   std::unique_ptr<mojom::URLLoader> url_loader_;
diff --git a/content/browser/service_worker/service_worker_provider_host.h b/content/browser/service_worker/service_worker_provider_host.h
index a53f8a3a75..3cfc6ea 100644
--- a/content/browser/service_worker/service_worker_provider_host.h
+++ b/content/browser/service_worker/service_worker_provider_host.h
@@ -262,6 +262,7 @@
   void NotifyControllerLost();
 
  private:
+  friend class ForeignFetchRequestHandlerTest;
   friend class LinkHeaderServiceWorkerTest;
   friend class ServiceWorkerProviderHostTestP;
   friend class ServiceWorkerWriteToCacheJobTest;
diff --git a/content/browser/service_worker/service_worker_storage.h b/content/browser/service_worker/service_worker_storage.h
index 95c227dd..d08e5330 100644
--- a/content/browser/service_worker/service_worker_storage.h
+++ b/content/browser/service_worker/service_worker_storage.h
@@ -240,6 +240,7 @@
   void PurgeResources(const ResourceList& resources);
 
  private:
+  friend class ForeignFetchRequestHandlerTest;
   friend class ServiceWorkerDispatcherHostTest;
   friend class ServiceWorkerHandleTest;
   friend class ServiceWorkerStorageTest;
diff --git a/content/browser/service_worker/service_worker_url_request_job.cc b/content/browser/service_worker/service_worker_url_request_job.cc
index 22403a2..ff176f2 100644
--- a/content/browser/service_worker/service_worker_url_request_job.cc
+++ b/content/browser/service_worker/service_worker_url_request_job.cc
@@ -234,6 +234,7 @@
     RequestContextFrameType frame_type,
     scoped_refptr<ResourceRequestBodyImpl> body,
     ServiceWorkerFetchType fetch_type,
+    const base::Optional<base::TimeDelta>& timeout,
     Delegate* delegate)
     : net::URLRequestJob(request, network_delegate),
       delegate_(delegate),
@@ -252,6 +253,7 @@
       fall_back_required_(false),
       body_(body),
       fetch_type_(fetch_type),
+      timeout_(timeout),
       weak_factory_(this) {
   DCHECK(delegate_) << "ServiceWorkerURLRequestJob requires a delegate";
 }
@@ -852,7 +854,8 @@
 
   DCHECK(!fetch_dispatcher_);
   fetch_dispatcher_.reset(new ServiceWorkerFetchDispatcher(
-      CreateFetchRequest(), active_worker, resource_type_, request()->net_log(),
+      CreateFetchRequest(), active_worker, resource_type_, timeout_,
+      request()->net_log(),
       base::Bind(&ServiceWorkerURLRequestJob::DidPrepareFetchEvent,
                  weak_factory_.GetWeakPtr(), active_worker),
       base::Bind(&ServiceWorkerURLRequestJob::DidDispatchFetchEvent,
diff --git a/content/browser/service_worker/service_worker_url_request_job.h b/content/browser/service_worker/service_worker_url_request_job.h
index 020842dd..42b34d5d 100644
--- a/content/browser/service_worker/service_worker_url_request_job.h
+++ b/content/browser/service_worker/service_worker_url_request_job.h
@@ -14,6 +14,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
+#include "base/optional.h"
 #include "base/time/time.h"
 #include "content/browser/service_worker/embedded_worker_status.h"
 #include "content/browser/service_worker/service_worker_metrics.h"
@@ -93,6 +94,7 @@
       RequestContextFrameType frame_type,
       scoped_refptr<ResourceRequestBodyImpl> body,
       ServiceWorkerFetchType fetch_type,
+      const base::Optional<base::TimeDelta>& timeout,
       Delegate* delegate);
 
   ~ServiceWorkerURLRequestJob() override;
@@ -269,6 +271,7 @@
   scoped_refptr<ResourceRequestBodyImpl> body_;
   std::unique_ptr<storage::BlobDataHandle> request_body_blob_data_handle_;
   ServiceWorkerFetchType fetch_type_;
+  base::Optional<base::TimeDelta> timeout_;
 
   ResponseBodyType response_body_type_ = UNKNOWN;
   bool did_record_result_ = false;
diff --git a/content/browser/service_worker/service_worker_url_request_job_unittest.cc b/content/browser/service_worker/service_worker_url_request_job_unittest.cc
index 88ed182..f6cca34 100644
--- a/content/browser/service_worker/service_worker_url_request_job_unittest.cc
+++ b/content/browser/service_worker/service_worker_url_request_job_unittest.cc
@@ -15,6 +15,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
+#include "base/test/simple_test_tick_clock.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
 #include "content/browser/blob_storage/chrome_blob_storage_context.h"
@@ -83,6 +84,9 @@
   ~MockHttpProtocolHandler() override {}
 
   void set_resource_type(ResourceType type) { resource_type_ = type; }
+  void set_custom_timeout(base::Optional<base::TimeDelta> timeout) {
+    custom_timeout_ = timeout;
+  }
 
   net::URLRequestJob* MaybeCreateJob(
       net::URLRequest* request,
@@ -101,7 +105,7 @@
         resource_type_, REQUEST_CONTEXT_TYPE_HYPERLINK,
         REQUEST_CONTEXT_FRAME_TYPE_TOP_LEVEL,
         scoped_refptr<ResourceRequestBodyImpl>(), ServiceWorkerFetchType::FETCH,
-        delegate_);
+        custom_timeout_, delegate_);
     job_->ForwardToServiceWorker();
     return job_;
   }
@@ -114,6 +118,7 @@
   mutable ServiceWorkerURLRequestJob* job_;
   ServiceWorkerURLRequestJob::Delegate* delegate_;
   ResourceType resource_type_;
+  base::Optional<base::TimeDelta> custom_timeout_;
 };
 
 // Returns a BlobProtocolHandler that uses |blob_storage_context|. Caller owns
@@ -359,6 +364,19 @@
   EXPECT_EQ(std::string(), info->response_cache_storage_cache_name());
 }
 
+TEST_P(ServiceWorkerURLRequestJobTestP, CustomTimeout) {
+  version_->SetStatus(ServiceWorkerVersion::ACTIVATED);
+
+  // Set mock clock on version_ to check timeout behavior.
+  base::SimpleTestTickClock* tick_clock = new base::SimpleTestTickClock();
+  tick_clock->SetNowTicks(base::TimeTicks::Now());
+  version_->SetTickClockForTesting(base::WrapUnique(tick_clock));
+
+  http_protocol_handler_->set_custom_timeout(base::TimeDelta::FromSeconds(5));
+  TestRequest(200, "OK", std::string(), true /* expect_valid_ssl */);
+  EXPECT_EQ(base::TimeDelta::FromSeconds(5), version_->remaining_timeout());
+}
+
 class ProviderDeleteHelper : public EmbeddedWorkerTestHelper {
  public:
   ProviderDeleteHelper() : EmbeddedWorkerTestHelper(base::FilePath()) {}
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index 5e0a3e2..1681357 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -635,8 +635,35 @@
     command_line->AppendSwitchASCII(switches::kEnableBlinkFeatures,
                                     "FeaturePolicy");
   }
+
+  ParsedFeaturePolicy CreateFPHeader(const std::string& feature_name,
+                                     const std::vector<GURL>& origins) {
+    ParsedFeaturePolicy result(1);
+    result[0].feature_name = feature_name;
+    result[0].matches_all_origins = false;
+    DCHECK(!origins.empty());
+    for (const GURL& origin : origins)
+      result[0].origins.push_back(url::Origin(origin));
+    return result;
+  }
+
+  ParsedFeaturePolicy CreateFPHeaderMatchesAll(
+      const std::string& feature_name) {
+    ParsedFeaturePolicy result(1);
+    result[0].feature_name = feature_name;
+    result[0].matches_all_origins = true;
+    return result;
+  }
 };
 
+bool operator==(const FeaturePolicyParsedWhitelist& first,
+                const FeaturePolicyParsedWhitelist& second) {
+  return std::tie(first.feature_name, first.matches_all_origins,
+                  first.origins) == std::tie(second.feature_name,
+                                             second.matches_all_origins,
+                                             second.origins);
+}
+
 double GetFrameDeviceScaleFactor(const ToRenderFrameHost& adapter) {
   double device_scale_factor;
   const char kGetFrameDeviceScaleFactor[] =
@@ -8686,19 +8713,19 @@
   EXPECT_TRUE(NavigateToURL(shell(), start_url));
 
   FrameTreeNode* root = web_contents()->GetFrameTree()->root();
-  EXPECT_EQ("{\"vibrate\":[\"self\"]}",
+  EXPECT_EQ(CreateFPHeader("vibrate", {start_url.GetOrigin()}),
             root->current_replication_state().feature_policy_header);
 
   // When the main frame navigates to a page with a new policy, it should
   // overwrite the old one.
   EXPECT_TRUE(NavigateToURL(shell(), first_nav_url));
-  EXPECT_EQ("{\"vibrate\":[\"*\"]}",
+  EXPECT_EQ(CreateFPHeaderMatchesAll("vibrate"),
             root->current_replication_state().feature_policy_header);
 
   // When the main frame navigates to a page without a policy, the replicated
   // policy header should be cleared.
   EXPECT_TRUE(NavigateToURL(shell(), second_nav_url));
-  EXPECT_EQ("", root->current_replication_state().feature_policy_header);
+  EXPECT_TRUE(root->current_replication_state().feature_policy_header.empty());
 }
 
 IN_PROC_BROWSER_TEST_F(SitePerProcessFeaturePolicyBrowserTest,
@@ -8712,19 +8739,19 @@
   EXPECT_TRUE(NavigateToURL(shell(), start_url));
 
   FrameTreeNode* root = web_contents()->GetFrameTree()->root();
-  EXPECT_EQ("{\"vibrate\":[\"self\"]}",
+  EXPECT_EQ(CreateFPHeader("vibrate", {start_url.GetOrigin()}),
             root->current_replication_state().feature_policy_header);
 
   // When the main frame navigates to a page with a new policy, it should
   // overwrite the old one.
   EXPECT_TRUE(NavigateToURL(shell(), first_nav_url));
-  EXPECT_EQ("{\"vibrate\":[\"*\"]}",
+  EXPECT_EQ(CreateFPHeaderMatchesAll("vibrate"),
             root->current_replication_state().feature_policy_header);
 
   // When the main frame navigates to a page without a policy, the replicated
   // policy header should be cleared.
   EXPECT_TRUE(NavigateToURL(shell(), second_nav_url));
-  EXPECT_EQ("", root->current_replication_state().feature_policy_header);
+  EXPECT_TRUE(root->current_replication_state().feature_policy_header.empty());
 }
 
 // Test that the replicated feature policy header is correct in subframes as
@@ -8740,28 +8767,30 @@
   EXPECT_TRUE(NavigateToURL(shell(), main_url));
 
   FrameTreeNode* root = web_contents()->GetFrameTree()->root();
-  EXPECT_EQ("{\"vibrate\":[\"self\", \"http://example.com/\"]}",
+  EXPECT_EQ(CreateFPHeader("vibrate",
+                           {main_url.GetOrigin(), GURL("http://example.com/")}),
             root->current_replication_state().feature_policy_header);
   EXPECT_EQ(1UL, root->child_count());
   EXPECT_EQ(
-      "{\"vibrate\":[\"self\"]}",
+      CreateFPHeader("vibrate", {main_url.GetOrigin()}),
       root->child_at(0)->current_replication_state().feature_policy_header);
 
   // Navigate the iframe cross-site.
   NavigateFrameToURL(root->child_at(0), first_nav_url);
   EXPECT_EQ(
-      "{\"vibrate\":[\"*\"]}",
+      CreateFPHeaderMatchesAll("vibrate"),
       root->child_at(0)->current_replication_state().feature_policy_header);
 
   // Navigate the iframe to another location, this one with no policy header
   NavigateFrameToURL(root->child_at(0), second_nav_url);
-  EXPECT_EQ(
-      "", root->child_at(0)->current_replication_state().feature_policy_header);
+  EXPECT_TRUE(root->child_at(0)
+                  ->current_replication_state()
+                  .feature_policy_header.empty());
 
   // Navigate the iframe back to a page with a policy
   NavigateFrameToURL(root->child_at(0), first_nav_url);
   EXPECT_EQ(
-      "{\"vibrate\":[\"*\"]}",
+      CreateFPHeaderMatchesAll("vibrate"),
       root->child_at(0)->current_replication_state().feature_policy_header);
 }
 
diff --git a/content/child/blob_storage/blob_transport_controller.cc b/content/child/blob_storage/blob_transport_controller.cc
index 1b01792..5db46a24 100644
--- a/content/child/blob_storage/blob_transport_controller.cc
+++ b/content/child/blob_storage/blob_transport_controller.cc
@@ -90,23 +90,27 @@
   return true;
 }
 
-bool WriteSingleRequestToDisk(const BlobConsolidation* consolidation,
-                              const BlobItemBytesRequest& request,
-                              File* file) {
+base::Optional<base::Time> WriteSingleRequestToDisk(
+    const BlobConsolidation* consolidation,
+    const BlobItemBytesRequest& request,
+    File* file) {
   if (!file->IsValid())
-    return false;
+    return base::nullopt;
   int64_t seek_distance = file->Seek(
       File::FROM_BEGIN, base::checked_cast<int64_t>(request.handle_offset));
   bool seek_failed = seek_distance < 0;
   UMA_HISTOGRAM_BOOLEAN("Storage.Blob.RendererFileSeekFailed", seek_failed);
-  if (seek_failed)
-    return false;
+  if (seek_failed) {
+    return base::nullopt;
+  }
   BlobConsolidation::ReadStatus status = consolidation->VisitMemory(
       request.renderer_item_index, request.renderer_item_offset, request.size,
       base::Bind(&WriteSingleChunk, file));
   if (status != BlobConsolidation::ReadStatus::OK)
-    return false;
-  return true;
+    return base::nullopt;
+  File::Info info;
+  file->GetInfo(&info);
+  return base::make_optional(info.last_modified);
 }
 
 base::Optional<std::vector<BlobItemBytesResponse>> WriteDiskRequests(
@@ -114,6 +118,8 @@
     std::unique_ptr<std::vector<BlobItemBytesRequest>> requests,
     const std::vector<IPC::PlatformFileForTransit>& file_handles) {
   std::vector<BlobItemBytesResponse> responses;
+  std::vector<base::Time> last_modified_times;
+  last_modified_times.resize(file_handles.size());
   // We grab ownership of the file handles here. When this vector is destroyed
   // it will close the files.
   std::vector<File> files;
@@ -122,24 +128,13 @@
     files.emplace_back(IPC::PlatformFileForTransitToFile(file_handle));
   }
   for (const auto& request : *requests) {
-    if (!WriteSingleRequestToDisk(consolidation.get(), request,
-                                  &files[request.handle_index])) {
+    base::Optional<base::Time> last_modified = WriteSingleRequestToDisk(
+        consolidation.get(), request, &files[request.handle_index]);
+    if (!last_modified) {
       return base::nullopt;
     }
+    last_modified_times[request.handle_index] = last_modified.value();
   }
-  // The last modified time needs to be collected after we flush the file.
-  std::vector<base::Time> last_modified_times;
-  last_modified_times.resize(file_handles.size());
-  for (size_t i = 0; i < files.size(); ++i) {
-    auto& file = files[i];
-    if (!file.Flush())
-      return base::nullopt;
-    File::Info info;
-    if (!file.GetInfo(&info))
-      return base::nullopt;
-    last_modified_times[i] = info.last_modified;
-  }
-
   for (const auto& request : *requests) {
     responses.push_back(BlobItemBytesResponse(request.request_number));
     responses.back().time_file_modified =
diff --git a/content/common/frame_messages.h b/content/common/frame_messages.h
index 63aadc0..a925cb1 100644
--- a/content/common/frame_messages.h
+++ b/content/common/frame_messages.h
@@ -392,6 +392,12 @@
 #endif
 IPC_STRUCT_TRAITS_END()
 
+IPC_STRUCT_TRAITS_BEGIN(content::FeaturePolicyParsedWhitelist)
+  IPC_STRUCT_TRAITS_MEMBER(feature_name)
+  IPC_STRUCT_TRAITS_MEMBER(matches_all_origins)
+  IPC_STRUCT_TRAITS_MEMBER(origins)
+IPC_STRUCT_TRAITS_END()
+
 IPC_STRUCT_TRAITS_BEGIN(content::FrameReplicationState)
   IPC_STRUCT_TRAITS_MEMBER(origin)
   IPC_STRUCT_TRAITS_MEMBER(sandbox_flags)
@@ -980,8 +986,10 @@
                     std::string /* unique_name */)
 
 // Notifies the browser process that a non-empty Feature-Policy HTTP header was
-// delivered with the document being loaded into the frame.
-IPC_MESSAGE_ROUTED1(FrameHostMsg_DidSetFeaturePolicyHeader, std::string)
+// delivered with the document being loaded into the frame. |parsed_header| is
+// a list of an origin whitelist for each feature in the policy.
+IPC_MESSAGE_ROUTED1(FrameHostMsg_DidSetFeaturePolicyHeader,
+                    content::ParsedFeaturePolicy /* parsed_header */)
 
 // Notifies the browser process about a new Content Security Policy that needs
 // to be applies to the frame.  This message is sent when a frame commits
diff --git a/content/common/frame_replication_state.cc b/content/common/frame_replication_state.cc
index 239c9ab..a726525 100644
--- a/content/common/frame_replication_state.cc
+++ b/content/common/frame_replication_state.cc
@@ -3,11 +3,20 @@
 // found in the LICENSE file.
 
 #include "content/common/frame_replication_state.h"
+
 #include "third_party/WebKit/public/web/WebSandboxFlags.h"
 #include "third_party/WebKit/public/web/WebTreeScopeType.h"
 
 namespace content {
 
+FeaturePolicyParsedWhitelist::FeaturePolicyParsedWhitelist()
+    : matches_all_origins(false) {}
+
+FeaturePolicyParsedWhitelist::FeaturePolicyParsedWhitelist(
+    const FeaturePolicyParsedWhitelist& fppw) = default;
+
+FeaturePolicyParsedWhitelist::~FeaturePolicyParsedWhitelist() {}
+
 FrameReplicationState::FrameReplicationState()
     : sandbox_flags(blink::WebSandboxFlags::None),
       scope(blink::WebTreeScopeType::Document),
diff --git a/content/common/frame_replication_state.h b/content/common/frame_replication_state.h
index cc8d4780..291287f6 100644
--- a/content/common/frame_replication_state.h
+++ b/content/common/frame_replication_state.h
@@ -20,6 +20,21 @@
 
 namespace content {
 
+// This struct holds feature policy whitelist data that needs to be replicated
+// between a RenderFrame and any of its associated RenderFrameProxies. A list of
+// these form part of the FrameReplicationState below (one entry per feature).
+struct CONTENT_EXPORT FeaturePolicyParsedWhitelist {
+  FeaturePolicyParsedWhitelist();
+  FeaturePolicyParsedWhitelist(const FeaturePolicyParsedWhitelist& fppw);
+  ~FeaturePolicyParsedWhitelist();
+
+  std::string feature_name;
+  bool matches_all_origins;
+  std::vector<url::Origin> origins;
+};
+
+using ParsedFeaturePolicy = std::vector<FeaturePolicyParsedWhitelist>;
+
 // This structure holds information that needs to be replicated between a
 // RenderFrame and any of its associated RenderFrameProxies.
 struct CONTENT_EXPORT FrameReplicationState {
@@ -84,9 +99,9 @@
   // scratch.
   std::string unique_name;
 
-  // Feature policy header. May be empty if no header was sent with the
+  // Parsed feature policy header. May be empty if no header was sent with the
   // document.
-  std::string feature_policy_header;
+  ParsedFeaturePolicy feature_policy_header;
 
   // Accumulated CSP headers - gathered from http headers, <meta> elements,
   // parent frames (in case of about:blank frames).
diff --git a/content/renderer/pepper/pepper_graphics_2d_host.cc b/content/renderer/pepper/pepper_graphics_2d_host.cc
index c6cb580..23137b0 100644
--- a/content/renderer/pepper/pepper_graphics_2d_host.cc
+++ b/content/renderer/pepper/pepper_graphics_2d_host.cc
@@ -346,7 +346,7 @@
     SkAutoCanvasRestore auto_restore(canvas, true);
     SkRect image_data_rect =
         gfx::RectToSkRect(gfx::Rect(plugin_rect.origin(), image_size));
-    canvas->clipRect(image_data_rect, SkRegion::kDifference_Op);
+    canvas->clipRect(image_data_rect, kDifference_SkClipOp);
 
     SkPaint paint;
     paint.setBlendMode(SkBlendMode::kSrc);
diff --git a/content/renderer/pepper/video_decoder_shim.cc b/content/renderer/pepper/video_decoder_shim.cc
index 9d731b30..6af89b6 100644
--- a/content/renderer/pepper/video_decoder_shim.cc
+++ b/content/renderer/pepper/video_decoder_shim.cc
@@ -869,7 +869,7 @@
   DCHECK(RenderThreadImpl::current());
   DCHECK_EQ(state_, UNINITIALIZED);
 
-  if (vda_config.is_encrypted) {
+  if (vda_config.is_encrypted()) {
     NOTREACHED() << "Encrypted streams are not supported";
     return false;
   }
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index f68bb2e5..50c14548 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -881,6 +881,21 @@
   return (time_ticks - base::TimeTicks()).InSecondsF();
 }
 
+ParsedFeaturePolicy ToParsedFeaturePolicy(
+    const blink::WebParsedFeaturePolicy& web_parsed_whitelists) {
+  ParsedFeaturePolicy result;
+  for (const blink::WebFeaturePolicy::ParsedWhitelist& web_whitelist :
+       web_parsed_whitelists) {
+    FeaturePolicyParsedWhitelist whitelist;
+    whitelist.feature_name = web_whitelist.featureName.utf8();
+    whitelist.matches_all_origins = web_whitelist.matchesAllOrigins;
+    for (const blink::WebSecurityOrigin& web_origin : web_whitelist.origins)
+      whitelist.origins.push_back(web_origin);
+    result.push_back(whitelist);
+  }
+  return result;
+}
+
 }  // namespace
 
 struct RenderFrameImpl::PendingFileChooser {
@@ -3135,9 +3150,9 @@
 }
 
 void RenderFrameImpl::didSetFeaturePolicyHeader(
-    const blink::WebString& header_value) {
-  Send(new FrameHostMsg_DidSetFeaturePolicyHeader(routing_id_,
-                                                  header_value.utf8()));
+    const blink::WebParsedFeaturePolicy& parsed_header) {
+  Send(new FrameHostMsg_DidSetFeaturePolicyHeader(
+      routing_id_, ToParsedFeaturePolicy(parsed_header)));
 }
 
 void RenderFrameImpl::didAddContentSecurityPolicy(
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index e45410b1..0f30095 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -51,6 +51,7 @@
 #include "services/service_manager/public/interfaces/connector.mojom.h"
 #include "services/service_manager/public/interfaces/interface_provider.mojom.h"
 #include "third_party/WebKit/public/platform/WebEffectiveConnectionType.h"
+#include "third_party/WebKit/public/platform/WebFeaturePolicy.h"
 #include "third_party/WebKit/public/platform/WebFocusType.h"
 #include "third_party/WebKit/public/platform/WebLoadingBehaviorFlag.h"
 #include "third_party/WebKit/public/platform/WebMediaPlayer.h"
@@ -487,7 +488,8 @@
       bool is_potentially_trustworthy_unique_origin) override;
   void didChangeSandboxFlags(blink::WebFrame* child_frame,
                              blink::WebSandboxFlags flags) override;
-  void didSetFeaturePolicyHeader(const blink::WebString& header_value) override;
+  void didSetFeaturePolicyHeader(
+      const blink::WebParsedFeaturePolicy& parsed_header) override;
   void didAddContentSecurityPolicy(
       const blink::WebString& header_value,
       blink::WebContentSecurityPolicyType type,
diff --git a/content/renderer/render_frame_proxy.cc b/content/renderer/render_frame_proxy.cc
index 9131d4c..aedf2fa4 100644
--- a/content/renderer/render_frame_proxy.cc
+++ b/content/renderer/render_frame_proxy.cc
@@ -29,6 +29,7 @@
 #include "content/renderer/render_widget.h"
 #include "ipc/ipc_message_macros.h"
 #include "third_party/WebKit/public/platform/URLConversion.h"
+#include "third_party/WebKit/public/platform/WebFeaturePolicy.h"
 #include "third_party/WebKit/public/platform/WebString.h"
 #include "third_party/WebKit/public/web/WebLocalFrame.h"
 #include "third_party/WebKit/public/web/WebUserGestureIndicator.h"
@@ -47,6 +48,22 @@
 typedef std::map<blink::WebFrame*, RenderFrameProxy*> FrameMap;
 base::LazyInstance<FrameMap> g_frame_map = LAZY_INSTANCE_INITIALIZER;
 
+blink::WebParsedFeaturePolicy ToWebParsedFeaturePolicy(
+    const ParsedFeaturePolicy& parsed_whitelists) {
+  std::vector<blink::WebFeaturePolicy::ParsedWhitelist> result;
+  for (const FeaturePolicyParsedWhitelist& whitelist : parsed_whitelists) {
+    blink::WebFeaturePolicy::ParsedWhitelist web_whitelist;
+    web_whitelist.featureName =
+        blink::WebString::fromUTF8(whitelist.feature_name);
+    web_whitelist.matchesAllOrigins = whitelist.matches_all_origins;
+    std::vector<blink::WebSecurityOrigin> web_origins;
+    for (const url::Origin& origin : whitelist.origins)
+      web_origins.push_back(origin);
+    result.push_back(web_whitelist);
+  }
+  return result;
+}
+
 }  // namespace
 
 // static
@@ -224,7 +241,7 @@
   web_frame_->setReplicatedPotentiallyTrustworthyUniqueOrigin(
       state.has_potentially_trustworthy_unique_origin);
   web_frame_->setReplicatedFeaturePolicyHeader(
-      blink::WebString::fromUTF8(state.feature_policy_header));
+      ToWebParsedFeaturePolicy(state.feature_policy_header));
 
   web_frame_->resetReplicatedContentSecurityPolicy();
   for (const auto& header : state.accumulated_csp_headers)
diff --git a/content/renderer/skia_benchmarking_extension_unittest.cc b/content/renderer/skia_benchmarking_extension_unittest.cc
index 87ea6a0..b7519f1 100644
--- a/content/renderer/skia_benchmarking_extension_unittest.cc
+++ b/content/renderer/skia_benchmarking_extension_unittest.cc
@@ -53,7 +53,7 @@
 
   // Draw a trivial scene.
   benchmarking_canvas.save();
-  benchmarking_canvas.clipRect(fullRect, SkRegion::kIntersect_Op, false);
+  benchmarking_canvas.clipRect(fullRect);
   benchmarking_canvas.setMatrix(trans);
   benchmarking_canvas.drawRect(fillRect, red_paint);
   benchmarking_canvas.restore();
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 2dc8a3c..0550e19 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -547,7 +547,6 @@
     "../browser/background_sync/background_sync_browsertest.cc",
     "../browser/battery_status/battery_monitor_impl_browsertest.cc",
     "../browser/battery_status/battery_monitor_integration_browsertest.cc",
-    "../browser/blob_storage/blob_storage_browsertest.cc",
     "../browser/blob_storage/blob_url_browsertest.cc",
     "../browser/bookmarklet_browsertest.cc",
     "../browser/browser_side_navigation_browsertest.cc",
diff --git a/content/test/data/blob_storage/blob_creation_and_slicing.html b/content/test/data/blob_storage/blob_creation_and_slicing.html
deleted file mode 100644
index e3f36c0..0000000
--- a/content/test/data/blob_storage/blob_creation_and_slicing.html
+++ /dev/null
@@ -1,134 +0,0 @@
-<html>
-  <head>
-    <title>Blob Creation & Slicing</title>
-    <script type="text/javascript" src="common.js"></script>
-    <script type="text/javascript">
-// We create < 3000 bytes of data, as that is the max for the browsertest.
-var MAX_BYTES = 3000;
-
-// Number of blobs constructed with raw data.
-var NUM_RAW_BLOBS = 100;
-// Number of blobs we create by slicing the raw data blobs.
-var NUM_SLICES = 100;
-// Number of blobs we create by sandwiching sliced blobs in between new data.
-var NUM_SANDWICHES = 100;
-var totalSize = 0;
-
-var generatePsuedoRandomUint8Array = function(length) {
-  var array = new Uint8Array(length);
-  for (let i = 0; i < length; ++i) {
-    array[i] = (17 + 23 * i) & 0xFF;
-  }
-  return array;
-}
-
-var test = function() {
-  var blobs = [];
-
-  // This should cause us to go straight to file.
-  var savedToDiskDataSize = 190;
-  var savedToDiskData = generatePsuedoRandomUint8Array(savedToDiskDataSize);
-
-  // This should require multiple shared memory segments.
-  var sharedMemoryDataSize = 15;
-  var sharedMemoryData = generatePsuedoRandomUint8Array(sharedMemoryDataSize);
- 
-  var sandwichDataSize = 7;
-
-  // This should fit in IPC.
-  var ipcDataSize = 2;
-  var ipcData = generatePsuedoRandomUint8Array(ipcDataSize);
-  
-  var expectedBlobData = [];
-  for (let i = 0; i < NUM_RAW_BLOBS; ++i) {
-    let data = [];
-    if (i % 5 == 0) {
-      data.push(sharedMemoryData);
-    } else if (i % 13 == 0) {
-      data.push(savedToDiskData);
-    } else {
-      data.push(ipcData);
-    }
-    expectedBlobData.push(data[data.length - 1]);
-    let blob = new Blob(data, { content_type: "text/plain" });
-    blobs.push(blob);
-    totalSize += blob.size;
-  }
-
-  for (let i = 0; i < NUM_SLICES; ++i) {
-    let origSize;
-    let origData;
-    let rawIndex = i % NUM_RAW_BLOBS;
-    if (rawIndex % 5 == 0) {
-      origSize = sharedMemoryDataSize;
-      origData = sharedMemoryData;
-    } else if (rawIndex % 13 == 0) {
-      origSize = savedToDiskDataSize;
-      origData = savedToDiskData;
-     } else {
-      origSize = ipcDataSize;
-      origData = ipcData;
-    }
-    let blob;
-    let expectedData;
-    if (i % 2 == 0) {
-      blob = blobs[i].slice(origSize / 2);
-      expectedData = origData.slice(origSize / 2);
-    } else {
-      blob = blobs[i].slice(0, origSize / 2);
-      expectedData = origData.slice(0, origSize / 2);
-    }
-    expectedBlobData.push(expectedData);
-    blobs.push(blob);
-  }
-  
-  var getBytes = function(string) {
-    var bytes = [];
-    for (let i = 0; i < string.length; ++i) {
-      bytes.push(string.charCodeAt(i));
-    }
-    return bytes;
-  }
-
-  for (let i = 0; i < NUM_SANDWICHES; ++i) {
-    let sliceIndex = NUM_RAW_BLOBS + (i % NUM_SLICES);
-    let slicedDataSize = expectedBlobData[sliceIndex].length;
-    blobs.push(new Blob(['pre', blobs[sliceIndex], 'post'], { content_type: "text/plain" }));
-    totalSize += sandwichDataSize;
-
-    let expectedData = new Uint8Array(sandwichDataSize + slicedDataSize);
-    expectedData.set(getBytes("pre"), 0);
-    expectedData.set(expectedBlobData[sliceIndex], 3);
-    expectedData.set(getBytes("post"), 3 + slicedDataSize);
-    expectedBlobData.push(expectedData);
-  }
-  shouldBeTrue("totalSize <= MAX_BYTES");
-
-  var numRead = 0;
-  for (let i = 0; i < blobs.length; i++) {
-    (function(index, d) {
-      var reader = new FileReader();
-      reader.onloadend = function(e) {
-        if (reader.error) {
-          fail('Error when reading blob ' + index + ': ' + reader.error);
-          return;
-        }
-        numRead++;
-        debug('Finished reading blob ' + index);
-        shouldBe('event.target.result.byteLength', d.length + '');
-        shouldBe('new Uint8Array(event.target.result)',
-                 '[' + d + ']');
-        if (numRead >= blobs.length) {
-          done('Done!');
-        }
-      }
-      return reader;
-    })(i, expectedBlobData[i]).readAsArrayBuffer(blobs[i]);
-  }
-};
-    </script>
-  </head>
-  <body onLoad="test()">
-    <div id="status">Starting...<br/></div>
-  </body>
-</html>
diff --git a/content/test/data/blob_storage/common.js b/content/test/data/blob_storage/common.js
deleted file mode 100644
index e9b3cc6a..0000000
--- a/content/test/data/blob_storage/common.js
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-function debug(message) {
-  var span = document.createElement("span");
-  span.appendChild(document.createTextNode(message));
-  span.appendChild(document.createElement("br"));
-  document.getElementById('status').appendChild(span);
-}
-
-function done(message) {
-  if (document.location.hash == '#fail')
-    return;
-  if (message)
-    debug('PASS: ' + message);
-  else
-    debug('PASS');
-  document.location.hash = '#pass';
-}
-
-function fail(message) {
-  debug('FAILED: ' + message);
-  document.location.hash = '#fail';
-}
-
-function getLog() {
-  return "" + document.getElementById('status').innerHTML;
-}
-
-// The following functions are based on
-// WebKit/LayoutTests/fast/js/resources/js-test-pre.js
-// so that the tests will look similar to the existing layout tests.
-function stringify(v) {
-  if (v === 0 && 1/v < 0)
-    return "-0";
-  else return "" + v;
-}
-
-function areArraysEqual(a, b) {
-  try {
-    if (a.length !== b.length)
-      return false;
-    for (var i = 0; i < a.length; i++) {
-      if (a[i] !== b[i])
-        return false;
-    }
-  } catch (ex) {
-    return false;
-  }
-  return true;
-}
-
-function isResultCorrect(_actual, _expected)
-{
-  if (_expected === 0)
-    return _actual === _expected && (1/_actual) === (1/_expected);
-  if (_actual === _expected)
-    return true;
-  if (typeof(_expected) == "number" && isNaN(_expected))
-    return typeof(_actual) == "number" && isNaN(_actual);
-  if (Object.prototype.toString.call(_expected) ==
-      Object.prototype.toString.call([]))
-    return areArraysEqual(_actual, _expected);
-  return false;
-}
-
-function shouldBe(_a, _b)
-{
-  if (typeof _a != "string" || typeof _b != "string")
-    debug("WARN: shouldBe() expects string arguments");
-  var exception;
-  var _av;
-  try {
-    _av = eval(_a);
-  } catch (e) {
-    exception = e;
-  }
-  var _bv = eval(_b);
-
-  if (exception)
-    fail(_a + " should be " + _bv + ". Threw exception " + exception);
-  else if (isResultCorrect(_av, _bv))
-    debug(_a + " is " + _b);
-  else if (typeof(_av) == typeof(_bv))
-    fail(_a + " should be " + _bv + ". Was " + stringify(_av) + ".");
-  else
-    fail(_a + " should be " + _bv + " (of type " + typeof _bv + "). " +
-         "Was " + _av + " (of type " + typeof _av + ").");
-}
-
-function shouldBeTrue(_a) { shouldBe(_a, "true"); }
-function shouldBeFalse(_a) { shouldBe(_a, "false"); }
-function shouldBeNaN(_a) { shouldBe(_a, "NaN"); }
-function shouldBeNull(_a) { shouldBe(_a, "null"); }
-function shouldBeEqualToString(a, b) {
-  var unevaledString = '"' + b.replace(/\\/g, "\\\\").replace(/"/g, "\"") + '"';
-  shouldBe(a, unevaledString);
-}
-
-if (typeof String.prototype.startsWith !== 'function') {
-  String.prototype.startsWith = function (str) {
-    return this.indexOf(str) === 0;
-  };
-}
\ No newline at end of file
diff --git a/content/test/data/media/depth_stream_test_utilities.js b/content/test/data/media/depth_stream_test_utilities.js
index c48df6d..44c51c72 100644
--- a/content/test/data/media/depth_stream_test_utilities.js
+++ b/content/test/data/media/depth_stream_test_utilities.js
@@ -61,7 +61,7 @@
                             data[i + 1] + ", " + data[i + 2] +
                             " differ at index " + i);
     }
-    var calculated = (data[0] +
+    var calculated = (data[0] + wrap_around +
                       step * ((flip_y ? -row : row) + column)) % wrap_around;
     if (Math.abs(calculated - data[i]) > tolerance) {
       return Promise.reject(test_name + ": reference value " + data[i] +
diff --git a/content/test/gpu/generate_buildbot_json.py b/content/test/gpu/generate_buildbot_json.py
index 9e51c1d..3e6b0f6 100755
--- a/content/test/gpu/generate_buildbot_json.py
+++ b/content/test/gpu/generate_buildbot_json.py
@@ -1220,6 +1220,20 @@
       '--use-gl=angle',
     ],
   },
+  'webgl_conformance_d3d11_passthrough': {
+    'tester_configs': [
+      {
+        'fyi_only': True,
+        'os_types': ['win'],
+        'run_on_optional': True,
+      }
+    ],
+    'target_name': 'webgl_conformance',
+    'extra_browser_args': [
+      '--use-angle=d3d11',
+      '--use-passthrough-cmd-decoder',
+    ],
+  },
   'webgl2_conformance_tests': {
     'tester_configs': [
       {
diff --git a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
index 46e5e20..92b9b11 100644
--- a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
+++ b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
@@ -43,12 +43,13 @@
     # All platforms.
     self.Flaky('conformance2/query/occlusion-query.html', bug=603168)
     self.Fail('conformance2/glsl3/tricky-loop-conditions.html', bug=483282)
+    self.Fail('conformance2/rendering/depth-stencil-feedback-loop.html',
+        bug=660844) # WebGL 2.0.1
     self.Fail('conformance2/rendering/rendering-sampling-feedback-loop.html',
         bug=660844)
     self.Fail('conformance2/textures/misc/' +
         'integer-cubemap-specification-order-bug.html',
         bug=483282) # owner:cwallez, test might be buggy
-
     # Windows only.
     self.Fail('conformance2/textures/misc/tex-srgb-mipmap.html',
         ['win'], bug=634519)
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
index 82d78839..eea4e8c 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
@@ -171,9 +171,9 @@
 
   // Check for required extensions
   if (!feature_info_->feature_flags().angle_robust_client_memory ||
-      !feature_info_->feature_flags().chromium_bind_generates_resource ||
-      (feature_info_->IsWebGLContext() !=
-       feature_info_->feature_flags().angle_webgl_compatibility)) {
+      !feature_info_->feature_flags().chromium_bind_generates_resource) {
+    // TODO(geofflang): Verify that ANGLE_webgl_compatibility is enabled if this
+    // is a WebGL context (depends on crbug.com/671217).
     Destroy(true);
     return false;
   }
diff --git a/ios/chrome/browser/physical_web/physical_web_prefs_registration.cc b/ios/chrome/browser/physical_web/physical_web_prefs_registration.cc
index 8f94b9b6..7490366 100644
--- a/ios/chrome/browser/physical_web/physical_web_prefs_registration.cc
+++ b/ios/chrome/browser/physical_web/physical_web_prefs_registration.cc
@@ -5,6 +5,7 @@
 #include "ios/chrome/browser/physical_web/physical_web_prefs_registration.h"
 
 #include "components/pref_registry/pref_registry_syncable.h"
+#include "components/prefs/pref_registry_simple.h"
 #include "ios/chrome/browser/physical_web/physical_web_constants.h"
 #include "ios/chrome/browser/pref_names.h"
 
@@ -14,3 +15,8 @@
       prefs::kIosPhysicalWebEnabled, physical_web::kPhysicalWebOnboarding,
       user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
 }
+
+void RegisterPhysicalWebLocalStatePrefs(PrefRegistrySimple* registry) {
+  registry->RegisterIntegerPref(prefs::kIosPhysicalWebEnabled,
+                                physical_web::kPhysicalWebOnboarding);
+}
diff --git a/ios/chrome/browser/physical_web/physical_web_prefs_registration.h b/ios/chrome/browser/physical_web/physical_web_prefs_registration.h
index c18f4a8c..bccfa9c 100644
--- a/ios/chrome/browser/physical_web/physical_web_prefs_registration.h
+++ b/ios/chrome/browser/physical_web/physical_web_prefs_registration.h
@@ -5,12 +5,16 @@
 #ifndef IOS_CHROME_BROWSER_PHYSICAL_WEB_PHYSICAL_WEB_PREFS_REGISTRATION_H_
 #define IOS_CHROME_BROWSER_PHYSICAL_WEB_PHYSICAL_WEB_PREFS_REGISTRATION_H_
 
+class PrefRegistrySimple;
 namespace user_prefs {
 class PrefRegistrySyncable;
 }
 
-// Registers the prefs needed by physical web.
+// Registers browser state prefs needed by the Physical Web.
 void RegisterPhysicalWebBrowserStatePrefs(
     user_prefs::PrefRegistrySyncable* registry);
 
+// Registers local state prefs needed by the Physical Web.
+void RegisterPhysicalWebLocalStatePrefs(PrefRegistrySimple* registry);
+
 #endif  // IOS_CHROME_BROWSER_PHYSICAL_WEB_PHYSICAL_WEB_PREFS_REGISTRATION_H_
diff --git a/ios/chrome/browser/prefs/BUILD.gn b/ios/chrome/browser/prefs/BUILD.gn
index 5f17600..459855a6 100644
--- a/ios/chrome/browser/prefs/BUILD.gn
+++ b/ios/chrome/browser/prefs/BUILD.gn
@@ -49,6 +49,7 @@
     "//ios/chrome/browser/memory",
     "//ios/chrome/browser/metrics",
     "//ios/chrome/browser/net",
+    "//ios/chrome/browser/physical_web",
     "//ios/chrome/browser/reading_list",
     "//ios/chrome/browser/signin",
     "//ios/chrome/browser/voice:prefs",
diff --git a/ios/chrome/browser/prefs/browser_prefs.mm b/ios/chrome/browser/prefs/browser_prefs.mm
index 5bacf8d9..57f18dfb 100644
--- a/ios/chrome/browser/prefs/browser_prefs.mm
+++ b/ios/chrome/browser/prefs/browser_prefs.mm
@@ -40,6 +40,7 @@
 #import "ios/chrome/browser/metrics/ios_chrome_metrics_service_client.h"
 #include "ios/chrome/browser/net/http_server_properties_manager_factory.h"
 #include "ios/chrome/browser/notification_promo.h"
+#include "ios/chrome/browser/physical_web/physical_web_prefs_registration.h"
 #include "ios/chrome/browser/pref_names.h"
 #include "ios/chrome/browser/signin/signin_manager_factory.h"
 #include "ios/chrome/browser/voice/voice_search_prefs_registration.h"
@@ -59,6 +60,7 @@
   ssl_config::SSLConfigServiceManager::RegisterPrefs(registry);
   update_client::RegisterPrefs(registry);
   variations::VariationsService::RegisterPrefs(registry);
+  RegisterPhysicalWebLocalStatePrefs(registry);
 
   // Preferences related to the browser state manager.
   registry->RegisterStringPref(prefs::kBrowserStateLastUsed, std::string());
diff --git a/media/base/android/java/src/org/chromium/media/MediaCodecBridge.java b/media/base/android/java/src/org/chromium/media/MediaCodecBridge.java
index 4b3dce08..d33bfa43 100644
--- a/media/base/android/java/src/org/chromium/media/MediaCodecBridge.java
+++ b/media/base/android/java/src/org/chromium/media/MediaCodecBridge.java
@@ -7,6 +7,7 @@
 import android.annotation.TargetApi;
 import android.media.AudioFormat;
 import android.media.MediaCodec;
+import android.media.MediaCodec.CryptoInfo;
 import android.media.MediaCrypto;
 import android.media.MediaFormat;
 import android.os.Build;
@@ -54,6 +55,14 @@
     // We use only one output audio format (PCM16) that has 2 bytes per sample
     private static final int PCM16_BYTES_PER_SAMPLE = 2;
 
+    // The following values should be kept in sync with the media::EncryptionScheme::CipherMode
+    // enum in media/base/encryption_scheme.h
+    private static final int MEDIA_ENCRYPTION_SCHEME_CIPHER_MODE_UNENCRYPTED = 0;
+    private static final int MEDIA_ENCRYPTION_SCHEME_CIPHER_MODE_AES_CTR = 1;
+    private static final int MEDIA_ENCRYPTION_SCHEME_CIPHER_MODE_AES_CBC = 2;
+
+    private static final int MEDIA_CODEC_UNKNOWN_CIPHER_MODE = -1;
+
     // TODO(qinmin): Use MediaFormat constants when part of the public API.
     private static final String KEY_CROP_LEFT = "crop-left";
     private static final String KEY_CROP_RIGHT = "crop-right";
@@ -401,15 +410,50 @@
         }
     }
 
+    // Incoming |native| values are as defined in media/base/encryption_scheme.h. Translated values
+    // are from MediaCodec. At present, these values are in sync. Returns
+    // MEDIA_CODEC_UNKNOWN_CIPHER_MODE in the case of unknown incoming value.
+    private int translateCipherModeValue(int nativeValue) {
+        switch (nativeValue) {
+            case MEDIA_ENCRYPTION_SCHEME_CIPHER_MODE_UNENCRYPTED:
+                return MediaCodec.CRYPTO_MODE_UNENCRYPTED;
+            case MEDIA_ENCRYPTION_SCHEME_CIPHER_MODE_AES_CTR:
+                return MediaCodec.CRYPTO_MODE_AES_CTR;
+            case MEDIA_ENCRYPTION_SCHEME_CIPHER_MODE_AES_CBC:
+                return MediaCodec.CRYPTO_MODE_AES_CBC;
+            default:
+                Log.e(TAG, "Unsupported cipher mode: " + nativeValue);
+                return MEDIA_CODEC_UNKNOWN_CIPHER_MODE;
+        }
+    }
+
     @CalledByNative
-    private int queueSecureInputBuffer(
-            int index, int offset, byte[] iv, byte[] keyId, int[] numBytesOfClearData,
-            int[] numBytesOfEncryptedData, int numSubSamples, long presentationTimeUs) {
+    private int queueSecureInputBuffer(int index, int offset, byte[] iv, byte[] keyId,
+            int[] numBytesOfClearData, int[] numBytesOfEncryptedData, int numSubSamples,
+            int cipherMode, int patternEncrypt, int patternSkip, long presentationTimeUs) {
         resetLastPresentationTimeIfNeeded(presentationTimeUs);
         try {
-            MediaCodec.CryptoInfo cryptoInfo = new MediaCodec.CryptoInfo();
-            cryptoInfo.set(numSubSamples, numBytesOfClearData, numBytesOfEncryptedData,
-                    keyId, iv, MediaCodec.CRYPTO_MODE_AES_CTR);
+            cipherMode = translateCipherModeValue(cipherMode);
+            if (cipherMode == MEDIA_CODEC_UNKNOWN_CIPHER_MODE) {
+                return MEDIA_CODEC_ERROR;
+            }
+            boolean usesCbcs = cipherMode == MediaCodec.CRYPTO_MODE_AES_CBC;
+            if (usesCbcs && !MediaCodecUtil.platformSupportsCbcsEncryption()) {
+                Log.e(TAG, "Encryption scheme 'cbcs' not supported on this platform.");
+                return MEDIA_CODEC_ERROR;
+            }
+            CryptoInfo cryptoInfo = new CryptoInfo();
+            cryptoInfo.set(numSubSamples, numBytesOfClearData, numBytesOfEncryptedData, keyId, iv,
+                    cipherMode);
+            if (patternEncrypt != 0 && patternSkip != 0) {
+                if (usesCbcs) {
+                    // Above platform check ensured that setting the pattern is indeed supported.
+                    MediaCodecUtil.setPatternIfSupported(cryptoInfo, patternEncrypt, patternSkip);
+                } else {
+                    Log.e(TAG, "Pattern encryption only supported for 'cbcs' scheme (CBC mode).");
+                    return MEDIA_CODEC_ERROR;
+                }
+            }
             mMediaCodec.queueSecureInputBuffer(index, offset, cryptoInfo, presentationTimeUs, 0);
         } catch (MediaCodec.CryptoException e) {
             if (e.getErrorCode() == MediaCodec.CryptoException.ERROR_NO_KEY) {
diff --git a/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java b/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java
index 5bec035..e16cef8 100644
--- a/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java
+++ b/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java
@@ -6,6 +6,7 @@
 
 import android.annotation.TargetApi;
 import android.media.MediaCodec;
+import android.media.MediaCodec.CryptoInfo;
 import android.media.MediaCodecInfo;
 import android.media.MediaCodecList;
 import android.os.Build;
@@ -514,4 +515,27 @@
         Log.w(TAG, "HW encoder for " + mime + " is not available on this device.");
         return null;
     }
+
+    /**
+     * Returns true if and only if the platform we are running on supports the 'cbcs'
+     * encryption scheme, specifically AES CBC encryption with possibility of pattern
+     * encryption.
+     * While 'cbcs' scheme was originally implemented in N, there was a bug (in the
+     * DRM code) which means that it didn't really work properly until post-N).
+     */
+    static boolean platformSupportsCbcsEncryption() {
+        return Build.VERSION.SDK_INT > Build.VERSION_CODES.N;
+    }
+
+    /**
+     * Sets the encryption pattern value if and only if CryptoInfo.setPattern method is
+     * supported.
+     * This method was introduced in Android N. Note that if platformSupportsCbcsEncryption
+     * returns true, then this function will set the pattern.
+     */
+    static void setPatternIfSupported(CryptoInfo cryptoInfo, int encrypt, int skip) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+            cryptoInfo.setPattern(new CryptoInfo.Pattern(encrypt, skip));
+        }
+    }
 }
diff --git a/media/base/android/media_codec_bridge.cc b/media/base/android/media_codec_bridge.cc
index de8bac3..0231c007 100644
--- a/media/base/android/media_codec_bridge.cc
+++ b/media/base/android/media_codec_bridge.cc
@@ -24,12 +24,14 @@
     const std::string& key_id,
     const std::string& iv,
     const std::vector<SubsampleEntry>& subsamples,
+    const EncryptionScheme& encryption_scheme,
     base::TimeDelta presentation_time) {
   const std::vector<char> key_vec(key_id.begin(), key_id.end());
   const std::vector<char> iv_vec(iv.begin(), iv.end());
   return QueueSecureInputBuffer(index, data, data_size, key_vec, iv_vec,
                                 subsamples.empty() ? nullptr : &subsamples[0],
-                                (int)subsamples.size(), presentation_time);
+                                (int)subsamples.size(), encryption_scheme,
+                                presentation_time);
 }
 
 MediaCodecStatus MediaCodecBridge::CopyFromOutputBuffer(int index,
diff --git a/media/base/android/media_codec_bridge.h b/media/base/android/media_codec_bridge.h
index d7377366..dd31591 100644
--- a/media/base/android/media_codec_bridge.h
+++ b/media/base/android/media_codec_bridge.h
@@ -21,6 +21,7 @@
 
 namespace media {
 
+class EncryptionScheme;
 struct SubsampleEntry;
 
 // These must be in sync with MediaCodecBridge.MEDIA_CODEC_XXX constants in
@@ -101,6 +102,7 @@
       const std::string& key_id,
       const std::string& iv,
       const std::vector<SubsampleEntry>& subsamples,
+      const EncryptionScheme& encryption_scheme,
       base::TimeDelta presentation_time);
 
   // Same QueueSecureInputBuffer overriden for the use with
@@ -115,6 +117,7 @@
       const std::vector<char>& iv,
       const SubsampleEntry* subsamples,
       int subsamples_size,
+      const EncryptionScheme& encryption_scheme,
       base::TimeDelta presentation_time) = 0;
 
   // Submits an empty buffer with a EOS (END OF STREAM) flag.
diff --git a/media/base/android/media_codec_loop.cc b/media/base/android/media_codec_loop.cc
index 43acf0af..ff2d04eb 100644
--- a/media/base/android/media_codec_loop.cc
+++ b/media/base/android/media_codec_loop.cc
@@ -39,7 +39,7 @@
       subsamples(other.subsamples),
       presentation_time(other.presentation_time),
       is_eos(other.is_eos),
-      is_encrypted(other.is_encrypted) {}
+      encryption_scheme(other.encryption_scheme) {}
 
 MediaCodecLoop::InputData::~InputData() {}
 
@@ -199,14 +199,14 @@
 
   media::MediaCodecStatus status = MEDIA_CODEC_OK;
 
-  if (input_data.is_encrypted) {
+  if (input_data.encryption_scheme.is_encrypted()) {
     // Note that input_data might not have a valid memory ptr if this is a
     // re-send of a buffer that was sent before decryption keys arrived.
 
     status = media_codec_->QueueSecureInputBuffer(
         input_buffer.index, input_data.memory, input_data.length,
         input_data.key_id, input_data.iv, input_data.subsamples,
-        input_data.presentation_time);
+        input_data.encryption_scheme, input_data.presentation_time);
 
   } else {
     status = media_codec_->QueueInputBuffer(
diff --git a/media/base/android/media_codec_loop.h b/media/base/android/media_codec_loop.h
index 6413698..2ee738c 100644
--- a/media/base/android/media_codec_loop.h
+++ b/media/base/android/media_codec_loop.h
@@ -17,6 +17,7 @@
 #include "base/timer/timer.h"
 #include "media/base/android/media_codec_bridge.h"
 #include "media/base/decode_status.h"
+#include "media/base/encryption_scheme.h"
 #include "media/base/media_export.h"
 #include "media/base/subsample_entry.h"
 
@@ -128,7 +129,7 @@
     base::TimeDelta presentation_time;
 
     bool is_eos = false;
-    bool is_encrypted = false;
+    EncryptionScheme encryption_scheme;
   };
 
   // Handy enum for "no buffer".
diff --git a/media/base/android/mock_media_codec_bridge.cc b/media/base/android/mock_media_codec_bridge.cc
index 50ef75d8..79c62d6 100644
--- a/media/base/android/mock_media_codec_bridge.cc
+++ b/media/base/android/mock_media_codec_bridge.cc
@@ -4,6 +4,7 @@
 
 #include "media/base/android/mock_media_codec_bridge.h"
 
+#include "media/base/encryption_scheme.h"
 #include "media/base/subsample_entry.h"
 
 using ::testing::_;
diff --git a/media/base/android/mock_media_codec_bridge.h b/media/base/android/mock_media_codec_bridge.h
index dfb52233..bcf9bc0 100644
--- a/media/base/android/mock_media_codec_bridge.h
+++ b/media/base/android/mock_media_codec_bridge.h
@@ -24,15 +24,16 @@
   MOCK_METHOD1(GetOutputChannelCount, MediaCodecStatus(int*));
   MOCK_METHOD4(QueueInputBuffer,
                MediaCodecStatus(int, const uint8_t*, size_t, base::TimeDelta));
-  MOCK_METHOD7(QueueSecureInputBuffer,
+  MOCK_METHOD8(QueueSecureInputBuffer,
                MediaCodecStatus(int,
                                 const uint8_t*,
                                 size_t,
                                 const std::string&,
                                 const std::string&,
                                 const std::vector<SubsampleEntry>&,
+                                const EncryptionScheme& encryption_scheme,
                                 base::TimeDelta));
-  MOCK_METHOD8(QueueSecureInputBuffer,
+  MOCK_METHOD9(QueueSecureInputBuffer,
                MediaCodecStatus(int,
                                 const uint8_t*,
                                 size_t,
@@ -40,6 +41,7 @@
                                 const std::vector<char>&,
                                 const SubsampleEntry*,
                                 int,
+                                const EncryptionScheme& encryption_scheme,
                                 base::TimeDelta));
   MOCK_METHOD1(QueueEOS, void(int));
   MOCK_METHOD2(DequeueInputBuffer, MediaCodecStatus(base::TimeDelta, int*));
diff --git a/media/base/android/ndk_media_codec_bridge.cc b/media/base/android/ndk_media_codec_bridge.cc
index b7d7575..5fe8b5a9 100644
--- a/media/base/android/ndk_media_codec_bridge.cc
+++ b/media/base/android/ndk_media_codec_bridge.cc
@@ -13,6 +13,7 @@
 #include "base/logging.h"
 #include "base/native_library.h"
 #include "base/strings/string_util.h"
+#include "media/base/encryption_scheme.h"
 #include "media/base/subsample_entry.h"
 
 namespace {
@@ -136,6 +137,7 @@
     const std::vector<char>& iv,
     const SubsampleEntry* subsamples,
     int subsamples_size,
+    const EncryptionScheme& encryption_scheme,
     base::TimeDelta presentation_time) {
   if (data_size >
       base::checked_cast<size_t>(std::numeric_limits<int32_t>::max())) {
@@ -145,6 +147,9 @@
     return MEDIA_CODEC_ERROR;
   if (data && !FillInputBuffer(index, data, data_size))
     return MEDIA_CODEC_ERROR;
+  if (encryption_scheme.mode() != EncryptionScheme::CIPHER_MODE_AES_CTR ||
+      encryption_scheme.pattern().IsInEffect())
+    return MEDIA_CODEC_ERROR;
 
   int new_subsamples_size = subsamples_size == 0 ? 1 : subsamples_size;
   std::vector<size_t> clear_data, encrypted_data;
diff --git a/media/base/android/ndk_media_codec_bridge.h b/media/base/android/ndk_media_codec_bridge.h
index 0b93a1ef..38ad4b0 100644
--- a/media/base/android/ndk_media_codec_bridge.h
+++ b/media/base/android/ndk_media_codec_bridge.h
@@ -43,6 +43,7 @@
       const std::vector<char>& iv,
       const SubsampleEntry* subsamples,
       int subsamples_size,
+      const EncryptionScheme& encryption_scheme,
       base::TimeDelta presentation_time) override;
   void QueueEOS(int input_buffer_index) override;
   MediaCodecStatus DequeueInputBuffer(base::TimeDelta timeout,
diff --git a/media/base/android/sdk_media_codec_bridge.cc b/media/base/android/sdk_media_codec_bridge.cc
index aed2457..d80e727 100644
--- a/media/base/android/sdk_media_codec_bridge.cc
+++ b/media/base/android/sdk_media_codec_bridge.cc
@@ -182,6 +182,7 @@
     const std::vector<char>& iv,
     const SubsampleEntry* subsamples,
     int subsamples_size,
+    const EncryptionScheme& encryption_scheme,
     base::TimeDelta presentation_time) {
   DVLOG(3) << __func__ << index << ": " << data_size;
   if (data_size >
@@ -234,6 +235,9 @@
       Java_MediaCodecBridge_queueSecureInputBuffer(
           env, j_media_codec_.obj(), index, 0, j_iv.obj(), j_key_id.obj(),
           clear_array, cypher_array, new_subsamples_size,
+          static_cast<int>(encryption_scheme.mode()),
+          static_cast<int>(encryption_scheme.pattern().encrypt_blocks()),
+          static_cast<int>(encryption_scheme.pattern().skip_blocks()),
           presentation_time.InMicroseconds()));
 }
 
diff --git a/media/base/android/sdk_media_codec_bridge.h b/media/base/android/sdk_media_codec_bridge.h
index d1841e3..c8e854a7 100644
--- a/media/base/android/sdk_media_codec_bridge.h
+++ b/media/base/android/sdk_media_codec_bridge.h
@@ -48,6 +48,7 @@
       const std::vector<char>& iv,
       const SubsampleEntry* subsamples,
       int subsamples_size,
+      const EncryptionScheme& encryption_scheme,
       base::TimeDelta presentation_time) override;
   void QueueEOS(int input_buffer_index) override;
   MediaCodecStatus DequeueInputBuffer(base::TimeDelta timeout,
diff --git a/media/base/demuxer.h b/media/base/demuxer.h
index ae07e60..6fa71a5 100644
--- a/media/base/demuxer.h
+++ b/media/base/demuxer.h
@@ -89,7 +89,8 @@
                           bool enable_text_tracks) = 0;
 
   // Aborts any pending read operations that the demuxer is involved with; any
-  // read aborted will be aborted with a status of kAborted.
+  // read aborted will be aborted with a status of kAborted. Future reads will
+  // also be aborted until Seek() is called.
   virtual void AbortPendingReads() = 0;
 
   // Indicates that a new Seek() call is on its way. Implementations may abort
diff --git a/media/base/encryption_scheme.cc b/media/base/encryption_scheme.cc
index 70d133c6..dea08ed 100644
--- a/media/base/encryption_scheme.cc
+++ b/media/base/encryption_scheme.cc
@@ -14,6 +14,14 @@
 
 EncryptionScheme::Pattern::~Pattern() {}
 
+uint32_t EncryptionScheme::Pattern::encrypt_blocks() const {
+  return encrypt_blocks_;
+}
+
+uint32_t EncryptionScheme::Pattern::skip_blocks() const {
+  return skip_blocks_;
+}
+
 bool EncryptionScheme::Pattern::Matches(const Pattern& other) const {
   return encrypt_blocks_ == other.encrypt_blocks() &&
          skip_blocks_ == other.skip_blocks();
@@ -30,6 +38,18 @@
 
 EncryptionScheme::~EncryptionScheme() {}
 
+bool EncryptionScheme::is_encrypted() const {
+  return mode_ != CIPHER_MODE_UNENCRYPTED;
+}
+
+EncryptionScheme::CipherMode EncryptionScheme::mode() const {
+  return mode_;
+}
+
+const EncryptionScheme::Pattern& EncryptionScheme::pattern() const {
+  return pattern_;
+}
+
 bool EncryptionScheme::Matches(const EncryptionScheme& other) const {
   return mode_ == other.mode_ && pattern_.Matches(other.pattern_);
 }
diff --git a/media/base/encryption_scheme.h b/media/base/encryption_scheme.h
index 37bea67..572db86 100644
--- a/media/base/encryption_scheme.h
+++ b/media/base/encryption_scheme.h
@@ -42,8 +42,8 @@
 
     bool Matches(const Pattern& other) const;
 
-    uint32_t encrypt_blocks() const { return encrypt_blocks_; }
-    uint32_t skip_blocks() const { return skip_blocks_; }
+    uint32_t encrypt_blocks() const;
+    uint32_t skip_blocks() const;
 
     bool IsInEffect() const;
 
@@ -63,9 +63,9 @@
 
   bool Matches(const EncryptionScheme& other) const;
 
-  bool is_encrypted() const { return mode_ != CIPHER_MODE_UNENCRYPTED; }
-  CipherMode mode() const { return mode_; }
-  const Pattern& pattern() const { return pattern_; }
+  bool is_encrypted() const;
+  CipherMode mode() const;
+  const Pattern& pattern() const;
 
  private:
   CipherMode mode_ = CIPHER_MODE_UNENCRYPTED;
diff --git a/media/base/ipc/media_param_traits.cc b/media/base/ipc/media_param_traits.cc
index 6de97b32..8233f3d 100644
--- a/media/base/ipc/media_param_traits.cc
+++ b/media/base/ipc/media_param_traits.cc
@@ -8,6 +8,7 @@
 #include "ipc/ipc_message_utils.h"
 #include "media/base/audio_parameters.h"
 #include "media/base/audio_point.h"
+#include "media/base/encryption_scheme.h"
 #include "media/base/limits.h"
 #include "ui/gfx/ipc/geometry/gfx_param_traits.h"
 #include "ui/gfx/ipc/gfx_param_traits.h"
@@ -80,6 +81,74 @@
   l->append(base::StringPrintf("<AudioParameters>"));
 }
 
+template <>
+struct ParamTraits<media::EncryptionScheme::Pattern> {
+  typedef media::EncryptionScheme::Pattern param_type;
+  static void GetSize(base::PickleSizer* s, const param_type& p);
+  static void Write(base::Pickle* m, const param_type& p);
+  static bool Read(const base::Pickle* m,
+                   base::PickleIterator* iter,
+                   param_type* r);
+  static void Log(const param_type& p, std::string* l);
+};
+
+void ParamTraits<media::EncryptionScheme>::GetSize(base::PickleSizer* s,
+                                                   const param_type& p) {
+  GetParamSize(s, p.mode());
+  GetParamSize(s, p.pattern());
+}
+
+void ParamTraits<media::EncryptionScheme>::Write(base::Pickle* m,
+                                                 const param_type& p) {
+  WriteParam(m, p.mode());
+  WriteParam(m, p.pattern());
+}
+
+bool ParamTraits<media::EncryptionScheme>::Read(const base::Pickle* m,
+                                                base::PickleIterator* iter,
+                                                param_type* r) {
+  media::EncryptionScheme::CipherMode mode;
+  media::EncryptionScheme::Pattern pattern;
+  if (!ReadParam(m, iter, &mode) || !ReadParam(m, iter, &pattern))
+    return false;
+  *r = media::EncryptionScheme(mode, pattern);
+  return true;
+}
+
+void ParamTraits<media::EncryptionScheme>::Log(const param_type& p,
+                                               std::string* l) {
+  l->append(base::StringPrintf("<EncryptionScheme>"));
+}
+
+void ParamTraits<media::EncryptionScheme::Pattern>::GetSize(
+    base::PickleSizer* s,
+    const param_type& p) {
+  GetParamSize(s, p.encrypt_blocks());
+  GetParamSize(s, p.skip_blocks());
+}
+
+void ParamTraits<media::EncryptionScheme::Pattern>::Write(base::Pickle* m,
+                                                          const param_type& p) {
+  WriteParam(m, p.encrypt_blocks());
+  WriteParam(m, p.skip_blocks());
+}
+
+bool ParamTraits<media::EncryptionScheme::Pattern>::Read(
+    const base::Pickle* m,
+    base::PickleIterator* iter,
+    param_type* r) {
+  uint8_t encrypt_blocks, skip_blocks;
+  if (!ReadParam(m, iter, &encrypt_blocks) || !ReadParam(m, iter, &skip_blocks))
+    return false;
+  *r = media::EncryptionScheme::Pattern(encrypt_blocks, skip_blocks);
+  return true;
+}
+
+void ParamTraits<media::EncryptionScheme::Pattern>::Log(const param_type& p,
+                                                        std::string* l) {
+  l->append(base::StringPrintf("<Pattern>"));
+}
+
 }  // namespace IPC
 
 // Generate param traits size methods.
diff --git a/media/base/ipc/media_param_traits.h b/media/base/ipc/media_param_traits.h
index 37f8938..dcc5833 100644
--- a/media/base/ipc/media_param_traits.h
+++ b/media/base/ipc/media_param_traits.h
@@ -11,6 +11,7 @@
 
 namespace media {
 class AudioParameters;
+class EncryptionScheme;
 }
 
 namespace IPC {
@@ -26,6 +27,17 @@
   static void Log(const param_type& p, std::string* l);
 };
 
+template <>
+struct ParamTraits<media::EncryptionScheme> {
+  typedef media::EncryptionScheme param_type;
+  static void GetSize(base::PickleSizer* s, const param_type& p);
+  static void Write(base::Pickle* m, const param_type& p);
+  static bool Read(const base::Pickle* m,
+                   base::PickleIterator* iter,
+                   param_type* r);
+  static void Log(const param_type& p, std::string* l);
+};
+
 }  // namespace IPC
 
 #endif  // MEDIA_BASE_IPC_MEDIA_PARAM_TRAITS_H_
diff --git a/media/capture/content/screen_capture_device_core.cc b/media/capture/content/screen_capture_device_core.cc
index f65ea59..88513f08 100644
--- a/media/capture/content/screen_capture_device_core.cc
+++ b/media/capture/content/screen_capture_device_core.cc
@@ -113,6 +113,14 @@
   capture_machine_->Stop(base::Bind(&base::DoNothing));
 }
 
+void ScreenCaptureDeviceCore::OnConsumerReportingUtilization(
+    int frame_feedback_id,
+    double utilization) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(oracle_proxy_);
+  oracle_proxy_->OnConsumerReportingUtilization(frame_feedback_id, utilization);
+}
+
 void ScreenCaptureDeviceCore::CaptureStarted(bool success) {
   DCHECK(thread_checker_.CalledOnValidThread());
   if (!success)
diff --git a/media/capture/content/screen_capture_device_core.h b/media/capture/content/screen_capture_device_core.h
index c965043..60faf27 100644
--- a/media/capture/content/screen_capture_device_core.h
+++ b/media/capture/content/screen_capture_device_core.h
@@ -89,6 +89,8 @@
   void Suspend();
   void Resume();
   void StopAndDeAllocate();
+  void OnConsumerReportingUtilization(int frame_feedback_id,
+                                      double utilization);
 
  private:
   // Flag indicating current state.
diff --git a/media/capture/content/thread_safe_capture_oracle.cc b/media/capture/content/thread_safe_capture_oracle.cc
index c406396e..9fc8f93 100644
--- a/media/capture/content/thread_safe_capture_oracle.cc
+++ b/media/capture/content/thread_safe_capture_oracle.cc
@@ -81,6 +81,7 @@
       return false;
     }
 
+    frame_number = oracle_.next_frame_number();
     visible_size = oracle_.capture_size();
     // TODO(miu): Clients should request exact padding, instead of this
     // memory-wasting hack to make frames that are compatible with all HW
@@ -91,7 +92,7 @@
     if (event == VideoCaptureOracle::kPassiveRefreshRequest) {
       output_buffer = client_->ResurrectLastOutputBuffer(
           coded_size, params_.requested_format.pixel_format,
-          params_.requested_format.pixel_storage);
+          params_.requested_format.pixel_storage, frame_number);
       if (!output_buffer) {
         TRACE_EVENT_INSTANT0("gpu.capture", "ResurrectionFailed",
                              TRACE_EVENT_SCOPE_THREAD);
@@ -100,7 +101,7 @@
     } else {
       output_buffer = client_->ReserveOutputBuffer(
           coded_size, params_.requested_format.pixel_format,
-          params_.requested_format.pixel_storage);
+          params_.requested_format.pixel_storage, frame_number);
     }
 
     // Get the current buffer pool utilization and attenuate it: The utilization
@@ -118,7 +119,7 @@
       return false;
     }
 
-    frame_number = oracle_.RecordCapture(attenuated_utilization);
+    oracle_.RecordCapture(attenuated_utilization);
     estimated_frame_duration = oracle_.estimated_frame_duration();
   }  // End of critical section.
 
@@ -228,25 +229,15 @@
     frame->metadata()->SetTimeTicks(VideoFrameMetadata::REFERENCE_TIME,
                                     reference_time);
 
-    frame->AddDestructionObserver(
-        base::Bind(&ThreadSafeCaptureOracle::DidConsumeFrame, this,
-                   frame_number, frame->metadata()));
-
     client_->OnIncomingCapturedVideoFrame(std::move(buffer), std::move(frame));
   }
 }
 
-void ThreadSafeCaptureOracle::DidConsumeFrame(
+void ThreadSafeCaptureOracle::OnConsumerReportingUtilization(
     int frame_number,
-    const media::VideoFrameMetadata* metadata) {
-  // Note: This function may be called on any thread by the VideoFrame
-  // destructor.  |metadata| is still valid for read-access at this point.
-  double utilization = -1.0;
-  if (metadata->GetDouble(media::VideoFrameMetadata::RESOURCE_UTILIZATION,
-                          &utilization)) {
-    base::AutoLock guard(lock_);
-    oracle_.RecordConsumerFeedback(frame_number, utilization);
-  }
+    double utilization) {
+  base::AutoLock guard(lock_);
+  oracle_.RecordConsumerFeedback(frame_number, utilization);
 }
 
 }  // namespace media
diff --git a/media/capture/content/thread_safe_capture_oracle.h b/media/capture/content/thread_safe_capture_oracle.h
index 7cc554d..e35c0b4 100644
--- a/media/capture/content/thread_safe_capture_oracle.h
+++ b/media/capture/content/thread_safe_capture_oracle.h
@@ -91,6 +91,8 @@
   void ReportError(const tracked_objects::Location& from_here,
                    const std::string& reason);
 
+  void OnConsumerReportingUtilization(int frame_number, double utilization);
+
  private:
   friend class base::RefCountedThreadSafe<ThreadSafeCaptureOracle>;
   virtual ~ThreadSafeCaptureOracle();
diff --git a/media/capture/content/video_capture_oracle.cc b/media/capture/content/video_capture_oracle.cc
index e8dd752..b1dcb80 100644
--- a/media/capture/content/video_capture_oracle.cc
+++ b/media/capture/content/video_capture_oracle.cc
@@ -214,7 +214,11 @@
   return true;
 }
 
-int VideoCaptureOracle::RecordCapture(double pool_utilization) {
+int VideoCaptureOracle::next_frame_number() const {
+  return next_frame_number_;
+}
+
+void VideoCaptureOracle::RecordCapture(double pool_utilization) {
   DCHECK(std::isfinite(pool_utilization) && pool_utilization >= 0.0);
 
   smoothing_sampler_.RecordSample();
@@ -227,7 +231,7 @@
   }
 
   num_frames_pending_++;
-  return next_frame_number_++;
+  next_frame_number_++;
 }
 
 void VideoCaptureOracle::RecordWillNotCapture(double pool_utilization) {
diff --git a/media/capture/content/video_capture_oracle.h b/media/capture/content/video_capture_oracle.h
index 82898f9..8d545e2 100644
--- a/media/capture/content/video_capture_oracle.h
+++ b/media/capture/content/video_capture_oracle.h
@@ -50,13 +50,15 @@
                                     const gfx::Rect& damage_rect,
                                     base::TimeTicks event_time);
 
+  // Returns the |frame_number| to be used with CompleteCapture().
+  int next_frame_number() const;
+
   // Record and update internal state based on whether the frame capture will be
   // started.  |pool_utilization| is a value in the range 0.0 to 1.0 to indicate
   // the current buffer pool utilization relative to a sustainable maximum (not
   // the absolute maximum).  This method should only be called if the last call
-  // to ObserveEventAndDecideCapture() returned true.  The first method returns
-  // the |frame_number| to be used with CompleteCapture().
-  int RecordCapture(double pool_utilization);
+  // to ObserveEventAndDecideCapture() returned true.
+  void RecordCapture(double pool_utilization);
   void RecordWillNotCapture(double pool_utilization);
 
   // Notify of the completion of a capture, and whether it was successful.
diff --git a/media/capture/content/video_capture_oracle_unittest.cc b/media/capture/content/video_capture_oracle_unittest.cc
index dbfcae2..fcfa40c 100644
--- a/media/capture/content/video_capture_oracle_unittest.cc
+++ b/media/capture/content/video_capture_oracle_unittest.cc
@@ -83,7 +83,8 @@
     t += event_increment;
     ASSERT_TRUE(oracle.ObserveEventAndDecideCapture(
         VideoCaptureOracle::kCompositorUpdate, damage_rect, t));
-    last_frame_number = oracle.RecordCapture(0.0);
+    last_frame_number = oracle.next_frame_number();
+    oracle.RecordCapture(0.0);
     ASSERT_TRUE(oracle.CompleteCapture(last_frame_number, true, &ignored));
   }
 
@@ -94,7 +95,8 @@
       t += event_increment;
       ASSERT_TRUE(oracle.ObserveEventAndDecideCapture(
           VideoCaptureOracle::kCompositorUpdate, damage_rect, t));
-      last_frame_number = oracle.RecordCapture(0.0);
+      last_frame_number = oracle.next_frame_number();
+      oracle.RecordCapture(0.0);
     }
     for (int j = num_in_flight - 1; j >= 0; --j) {
       ASSERT_TRUE(
@@ -110,7 +112,8 @@
       t += event_increment;
       ASSERT_TRUE(oracle.ObserveEventAndDecideCapture(
           VideoCaptureOracle::kCompositorUpdate, damage_rect, t));
-      last_frame_number = oracle.RecordCapture(0.0);
+      last_frame_number = oracle.next_frame_number();
+      oracle.RecordCapture(0.0);
     }
     ASSERT_TRUE(oracle.CompleteCapture(last_frame_number, true, &ignored));
     for (int j = 1; j < num_in_flight; ++j) {
@@ -127,7 +130,8 @@
       t += event_increment;
       ASSERT_TRUE(oracle.ObserveEventAndDecideCapture(
           VideoCaptureOracle::kCompositorUpdate, damage_rect, t));
-      last_frame_number = oracle.RecordCapture(0.0);
+      last_frame_number = oracle.next_frame_number();
+      oracle.RecordCapture(0.0);
     }
     // Report the last frame as an out of order failure.
     ASSERT_FALSE(oracle.CompleteCapture(last_frame_number, false, &ignored));
@@ -178,7 +182,8 @@
     }
     ASSERT_LT(base::TimeDelta(), oracle.estimated_frame_duration());
 
-    const int frame_number = oracle.RecordCapture(0.0);
+    const int frame_number = oracle.next_frame_number();
+    oracle.RecordCapture(0.0);
 
     base::TimeTicks frame_timestamp;
     ASSERT_TRUE(oracle.CompleteCapture(frame_number, true, &frame_timestamp));
@@ -217,8 +222,9 @@
     t += vsync_interval;
     if (oracle.ObserveEventAndDecideCapture(
             VideoCaptureOracle::kCompositorUpdate, gfx::Rect(), t)) {
-      ASSERT_TRUE(
-          oracle.CompleteCapture(oracle.RecordCapture(0.0), true, &ignored));
+      const int frame_number = oracle.next_frame_number();
+      oracle.RecordCapture(0.0);
+      ASSERT_TRUE(oracle.CompleteCapture(frame_number, true, &ignored));
       did_complete_a_capture = true;
     }
   }
@@ -234,7 +240,8 @@
       break;
     }
   }
-  int frame_number = oracle.RecordCapture(0.0);
+  int frame_number = oracle.next_frame_number();
+  oracle.RecordCapture(0.0);
 
   // Stop providing the compositor events and start providing refresh request
   // events.  No overdue samplings should be recommended because of the
@@ -254,8 +261,9 @@
     t += refresh_interval;
     if (oracle.ObserveEventAndDecideCapture(
             VideoCaptureOracle::kPassiveRefreshRequest, gfx::Rect(), t)) {
-      ASSERT_TRUE(
-          oracle.CompleteCapture(oracle.RecordCapture(0.0), true, &ignored));
+      const int frame_number = oracle.next_frame_number();
+      oracle.RecordCapture(0.0);
+      ASSERT_TRUE(oracle.CompleteCapture(frame_number, true, &ignored));
       did_complete_a_capture = true;
     }
   }
@@ -270,7 +278,8 @@
       break;
     }
   }
-  frame_number = oracle.RecordCapture(0.0);
+  frame_number = oracle.next_frame_number();
+  oracle.RecordCapture(0.0);
 
   // Confirm that the oracle does not recommend sampling until the outstanding
   // "refresh" capture completes.
@@ -306,8 +315,9 @@
         VideoCaptureOracle::kCompositorUpdate, gfx::Rect(), t));
     ASSERT_EQ(Get720pSize(), oracle.capture_size());
     base::TimeTicks ignored;
-    ASSERT_TRUE(
-        oracle.CompleteCapture(oracle.RecordCapture(0.0), true, &ignored));
+    const int frame_number = oracle.next_frame_number();
+    oracle.RecordCapture(0.0);
+    ASSERT_TRUE(oracle.CompleteCapture(frame_number, true, &ignored));
   }
 
   // Now run 30 seconds of frame captures with lots of random source size
@@ -334,8 +344,9 @@
     }
 
     base::TimeTicks ignored;
-    ASSERT_TRUE(
-        oracle.CompleteCapture(oracle.RecordCapture(0.0), true, &ignored));
+    const int frame_number = oracle.next_frame_number();
+    oracle.RecordCapture(0.0);
+    ASSERT_TRUE(oracle.CompleteCapture(frame_number, true, &ignored));
   }
 }
 
@@ -370,8 +381,8 @@
         is_content_animating ? gfx::Rect(Get720pSize()) : gfx::Rect(), t));
     ASSERT_EQ(Get720pSize(), oracle.capture_size());
     const double utilization = 0.9;
-    const int frame_number =
-        oracle.RecordCapture(with_consumer_feedback ? 0.25 : utilization);
+    const int frame_number = oracle.next_frame_number();
+    oracle.RecordCapture(with_consumer_feedback ? 0.25 : utilization);
     base::TimeTicks ignored;
     ASSERT_TRUE(oracle.CompleteCapture(frame_number, true, &ignored));
     if (with_consumer_feedback)
@@ -406,8 +417,8 @@
       }
 
       const double utilization = stepped_down_size.IsEmpty() ? 1.5 : 0.9;
-      const int frame_number =
-          oracle.RecordCapture(with_consumer_feedback ? 0.25 : utilization);
+      const int frame_number = oracle.next_frame_number();
+      oracle.RecordCapture(with_consumer_feedback ? 0.25 : utilization);
       base::TimeTicks ignored;
       ASSERT_TRUE(oracle.CompleteCapture(frame_number, true, &ignored));
       if (with_consumer_feedback)
@@ -447,8 +458,8 @@
       }
 
       const double utilization = stepped_up_size.IsEmpty() ? 0.0 : 0.9;
-      const int frame_number =
-          oracle.RecordCapture(with_consumer_feedback ? 0.25 : utilization);
+      const int frame_number = oracle.next_frame_number();
+      oracle.RecordCapture(with_consumer_feedback ? 0.25 : utilization);
       base::TimeTicks ignored;
       ASSERT_TRUE(oracle.CompleteCapture(frame_number, true, &ignored));
       if (with_consumer_feedback)
@@ -494,7 +505,8 @@
       continue;
     }
     ASSERT_EQ(Get360pSize(), oracle.capture_size());
-    const int frame_number = oracle.RecordCapture(0.25);
+    const int frame_number = oracle.next_frame_number();
+    oracle.RecordCapture(0.25);
     base::TimeTicks ignored;
     ASSERT_TRUE(oracle.CompleteCapture(frame_number, true, &ignored));
   }
@@ -514,7 +526,8 @@
     ASSERT_LE(last_capture_size.width(), oracle.capture_size().width());
     ASSERT_LE(last_capture_size.height(), oracle.capture_size().height());
     last_capture_size = oracle.capture_size();
-    const int frame_number = oracle.RecordCapture(0.25);
+    const int frame_number = oracle.next_frame_number();
+    oracle.RecordCapture(0.25);
     base::TimeTicks ignored;
     ASSERT_TRUE(oracle.CompleteCapture(frame_number, true, &ignored));
   }
@@ -544,7 +557,8 @@
     }
 
     const double utilization = stepped_down_size.IsEmpty() ? 1.5 : 0.9;
-    const int frame_number = oracle.RecordCapture(utilization);
+    const int frame_number = oracle.next_frame_number();
+    oracle.RecordCapture(utilization);
     base::TimeTicks ignored;
     ASSERT_TRUE(oracle.CompleteCapture(frame_number, true, &ignored));
   }
@@ -576,7 +590,8 @@
     }
 
     const double utilization = stepped_up_size.IsEmpty() ? 0.25 : 0.9;
-    const int frame_number = oracle.RecordCapture(utilization);
+    const int frame_number = oracle.next_frame_number();
+    oracle.RecordCapture(utilization);
     base::TimeTicks ignored;
     ASSERT_TRUE(oracle.CompleteCapture(frame_number, true, &ignored));
   }
@@ -599,8 +614,9 @@
         VideoCaptureOracle::kCompositorUpdate, gfx::Rect(), t));
     ASSERT_EQ(Get720pSize(), oracle.capture_size());
     base::TimeTicks ignored;
-    ASSERT_TRUE(
-        oracle.CompleteCapture(oracle.RecordCapture(0.9), true, &ignored));
+    const int frame_number = oracle.next_frame_number();
+    oracle.RecordCapture(0.9);
+    ASSERT_TRUE(oracle.CompleteCapture(frame_number, true, &ignored));
   }
 
   // Now run 10 seconds with overload indicated.  Still, expect no capture size
@@ -611,8 +627,9 @@
         VideoCaptureOracle::kCompositorUpdate, gfx::Rect(), t));
     ASSERT_EQ(Get720pSize(), oracle.capture_size());
     base::TimeTicks ignored;
-    ASSERT_TRUE(
-        oracle.CompleteCapture(oracle.RecordCapture(2.0), true, &ignored));
+    const int frame_number = oracle.next_frame_number();
+    oracle.RecordCapture(2.0);
+    ASSERT_TRUE(oracle.CompleteCapture(frame_number, true, &ignored));
   }
 }
 
diff --git a/media/capture/video/fake_video_capture_device.cc b/media/capture/video/fake_video_capture_device.cc
index 31fe10b..8ea60bc 100644
--- a/media/capture/video/fake_video_capture_device.cc
+++ b/media/capture/video/fake_video_capture_device.cc
@@ -319,10 +319,11 @@
     base::TimeTicks expected_execution_time) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
+  const int arbitrary_frame_feedback_id = 0;
   std::unique_ptr<VideoCaptureDevice::Client::Buffer> capture_buffer(
-      client_->ReserveOutputBuffer(capture_format_.frame_size,
-                                   capture_format_.pixel_format,
-                                   capture_format_.pixel_storage));
+      client_->ReserveOutputBuffer(
+          capture_format_.frame_size, capture_format_.pixel_format,
+          capture_format_.pixel_storage, arbitrary_frame_feedback_id));
   DLOG_IF(ERROR, !capture_buffer) << "Couldn't allocate Capture Buffer";
   DCHECK(capture_buffer->data()) << "Buffer has NO backing memory";
 
diff --git a/media/capture/video/fake_video_capture_device_unittest.cc b/media/capture/video/fake_video_capture_device_unittest.cc
index 5b6fe3a..c9bbae9 100644
--- a/media/capture/video/fake_video_capture_device_unittest.cc
+++ b/media/capture/video/fake_video_capture_device_unittest.cc
@@ -37,13 +37,15 @@
 // This class is a Client::Buffer that allocates and frees the requested |size|.
 class MockBuffer : public VideoCaptureDevice::Client::Buffer {
  public:
-  MockBuffer(int buffer_id, size_t mapped_size)
+  MockBuffer(int buffer_id, int frame_feedback_id, size_t mapped_size)
       : id_(buffer_id),
+        frame_feedback_id_(frame_feedback_id),
         mapped_size_(mapped_size),
         data_(new uint8_t[mapped_size]) {}
   ~MockBuffer() override { delete[] data_; }
 
   int id() const override { return id_; }
+  int frame_feedback_id() const override { return frame_feedback_id_; }
   gfx::Size dimensions() const override { return gfx::Size(); }
   size_t mapped_size() const override { return mapped_size_; }
   void* data(int plane) override { return data_; }
@@ -57,6 +59,7 @@
 
  private:
   const int id_;
+  const int frame_feedback_id_;
   const size_t mapped_size_;
   uint8_t* const data_;
 };
@@ -76,28 +79,31 @@
                               const VideoCaptureFormat& format,
                               int rotation,
                               base::TimeTicks reference_time,
-                              base::TimeDelta timestamp) override {
+                              base::TimeDelta timestamp,
+                              int frame_feedback_id) override {
     frame_cb_.Run(format);
   }
   // Virtual methods for capturing using Client's Buffers.
-  std::unique_ptr<Buffer> ReserveOutputBuffer(
-      const gfx::Size& dimensions,
-      media::VideoPixelFormat format,
-      media::VideoPixelStorage storage) {
+  std::unique_ptr<Buffer> ReserveOutputBuffer(const gfx::Size& dimensions,
+                                              media::VideoPixelFormat format,
+                                              media::VideoPixelStorage storage,
+                                              int frame_feedback_id) override {
     EXPECT_TRUE((format == media::PIXEL_FORMAT_ARGB &&
                  storage == media::PIXEL_STORAGE_CPU));
     EXPECT_GT(dimensions.GetArea(), 0);
     const VideoCaptureFormat frame_format(dimensions, 0.0, format);
-    return base::MakeUnique<MockBuffer>(0, frame_format.ImageAllocationSize());
+    return base::MakeUnique<MockBuffer>(0, frame_feedback_id,
+                                        frame_format.ImageAllocationSize());
   }
   void OnIncomingCapturedBuffer(std::unique_ptr<Buffer> buffer,
-                                const VideoCaptureFormat& frame_format,
+                                const VideoCaptureFormat& format,
                                 base::TimeTicks reference_time,
-                                base::TimeDelta timestamp) {
-    frame_cb_.Run(frame_format);
+                                base::TimeDelta timestamp) override {
+    frame_cb_.Run(format);
   }
-  void OnIncomingCapturedVideoFrame(std::unique_ptr<Buffer> buffer,
-                                    scoped_refptr<media::VideoFrame> frame) {
+  void OnIncomingCapturedVideoFrame(
+      std::unique_ptr<Buffer> buffer,
+      scoped_refptr<media::VideoFrame> frame) override {
     VideoCaptureFormat format(frame->natural_size(), 30.0,
                               PIXEL_FORMAT_I420);
     frame_cb_.Run(format);
@@ -105,7 +111,8 @@
   std::unique_ptr<Buffer> ResurrectLastOutputBuffer(
       const gfx::Size& dimensions,
       media::VideoPixelFormat format,
-      media::VideoPixelStorage storage) {
+      media::VideoPixelStorage storage,
+      int frame_feedback_id) override {
     return std::unique_ptr<Buffer>();
   }
   double GetBufferPoolUtilization() const override { return 0.0; }
diff --git a/media/capture/video/video_capture_buffer_pool.h b/media/capture/video/video_capture_buffer_pool.h
index 4b32f0d..2d04957 100644
--- a/media/capture/video/video_capture_buffer_pool.h
+++ b/media/capture/video/video_capture_buffer_pool.h
@@ -64,6 +64,7 @@
   virtual int ReserveForProducer(const gfx::Size& dimensions,
                                  media::VideoPixelFormat format,
                                  media::VideoPixelStorage storage,
+                                 int frame_feedback_id,
                                  int* buffer_id_to_drop) = 0;
 
   // Indicate that a buffer held for the producer should be returned back to the
diff --git a/media/capture/video/video_capture_buffer_pool_impl.cc b/media/capture/video/video_capture_buffer_pool_impl.cc
index 3b5860a..7bc10e16 100644
--- a/media/capture/video/video_capture_buffer_pool_impl.cc
+++ b/media/capture/video/video_capture_buffer_pool_impl.cc
@@ -57,10 +57,11 @@
     const gfx::Size& dimensions,
     media::VideoPixelFormat format,
     media::VideoPixelStorage storage,
+    int frame_feedback_id,
     int* buffer_id_to_drop) {
   base::AutoLock lock(lock_);
   return ReserveForProducerInternal(dimensions, format, storage,
-                                    buffer_id_to_drop);
+                                    frame_feedback_id, buffer_id_to_drop);
 }
 
 void VideoCaptureBufferPoolImpl::RelinquishProducerReservation(int buffer_id) {
@@ -151,6 +152,7 @@
     const gfx::Size& dimensions,
     media::VideoPixelFormat pixel_format,
     media::VideoPixelStorage storage_type,
+    int frame_feedback_id,
     int* buffer_id_to_drop) {
   lock_.AssertAcquired();
 
@@ -177,6 +179,7 @@
         // Existing tracker is big enough and has correct format. Reuse it.
         tracker->set_dimensions(dimensions);
         tracker->set_held_by_producer(true);
+        tracker->set_frame_feedback_id(frame_feedback_id);
         return it->first;
       }
       if (tracker->max_pixel_count() > largest_size_in_pixels) {
@@ -194,6 +197,7 @@
       last_relinquished_buffer_id_ = kInvalidId;
       tracker_of_last_resort->second->set_dimensions(dimensions);
       tracker_of_last_resort->second->set_held_by_producer(true);
+      tracker_of_last_resort->second->set_frame_feedback_id(frame_feedback_id);
       return tracker_of_last_resort->first;
     }
     if (tracker_to_drop == trackers_.end()) {
@@ -219,6 +223,7 @@
   }
 
   tracker->set_held_by_producer(true);
+  tracker->set_frame_feedback_id(frame_feedback_id);
   trackers_[buffer_id] = std::move(tracker);
 
   return buffer_id;
diff --git a/media/capture/video/video_capture_buffer_pool_impl.h b/media/capture/video/video_capture_buffer_pool_impl.h
index 5f3a166..08c8475 100644
--- a/media/capture/video/video_capture_buffer_pool_impl.h
+++ b/media/capture/video/video_capture_buffer_pool_impl.h
@@ -41,6 +41,7 @@
   int ReserveForProducer(const gfx::Size& dimensions,
                          media::VideoPixelFormat format,
                          media::VideoPixelStorage storage,
+                         int frame_feedback_id,
                          int* buffer_id_to_drop) override;
   void RelinquishProducerReservation(int buffer_id) override;
   int ResurrectLastForProducer(const gfx::Size& dimensions,
@@ -57,6 +58,7 @@
   int ReserveForProducerInternal(const gfx::Size& dimensions,
                                  media::VideoPixelFormat format,
                                  media::VideoPixelStorage storage,
+                                 int frame_feedback_id,
                                  int* tracker_id_to_drop);
 
   VideoCaptureBufferTracker* GetTracker(int buffer_id);
diff --git a/media/capture/video/video_capture_buffer_tracker.h b/media/capture/video/video_capture_buffer_tracker.h
index 776f33f..3e3d7bf 100644
--- a/media/capture/video/video_capture_buffer_tracker.h
+++ b/media/capture/video/video_capture_buffer_tracker.h
@@ -23,7 +23,8 @@
   VideoCaptureBufferTracker()
       : max_pixel_count_(0),
         held_by_producer_(false),
-        consumer_hold_count_(0) {}
+        consumer_hold_count_(0),
+        frame_feedback_id_(0) {}
   virtual bool Init(const gfx::Size& dimensions,
                     media::VideoPixelFormat format,
                     media::VideoPixelStorage storage_type,
@@ -46,6 +47,8 @@
   void set_held_by_producer(bool value) { held_by_producer_ = value; }
   int consumer_hold_count() const { return consumer_hold_count_; }
   void set_consumer_hold_count(int value) { consumer_hold_count_ = value; }
+  void set_frame_feedback_id(int value) { frame_feedback_id_ = value; }
+  int frame_feedback_id() { return frame_feedback_id_; }
 
   // Returns a scoped handle to the underlying storage.
   virtual std::unique_ptr<VideoCaptureBufferHandle> GetBufferHandle() = 0;
@@ -67,6 +70,8 @@
 
   // Number of consumer processes which hold this VideoCaptureBufferTracker.
   int consumer_hold_count_;
+
+  int frame_feedback_id_;
 };
 
 }  // namespace content
diff --git a/media/capture/video/video_capture_device.h b/media/capture/video/video_capture_device.h
index 211f4d4..dc0b77f 100644
--- a/media/capture/video/video_capture_device.h
+++ b/media/capture/video/video_capture_device.h
@@ -41,7 +41,35 @@
 
 namespace media {
 
-class CAPTURE_EXPORT VideoCaptureDevice {
+class CAPTURE_EXPORT VideoFrameConsumerFeedbackObserver {
+ public:
+  virtual ~VideoFrameConsumerFeedbackObserver() {}
+
+  // During processing of a video frame, consumers may report back their
+  // utilization level to the source device. The device may use this information
+  // to adjust the rate of data it pushes out. Values are interpreted as
+  // follows:
+  // Less than 0.0 is meaningless and should be ignored.  1.0 indicates a
+  // maximum sustainable utilization.  Greater than 1.0 indicates the consumer
+  // is likely to stall or drop frames if the data volume is not reduced.
+  //
+  // Example: In a system that encodes and transmits video frames over the
+  // network, this value can be used to indicate whether sufficient CPU
+  // is available for encoding and/or sufficient bandwidth is available for
+  // transmission over the network.  The maximum of the two utilization
+  // measurements would be used as feedback.
+  //
+  // The parameter |frame_feedback_id| must match a |frame_feedback_id|
+  // previously sent out by the VideoCaptureDevice we are giving feedback about.
+  // It is used to indicate which particular frame the reported utilization
+  // corresponds to.
+  virtual void OnUtilizationReport(int frame_feedback_id, double utilization) {}
+
+  static constexpr double kNoUtilizationRecorded = -1.0;
+};
+
+class CAPTURE_EXPORT VideoCaptureDevice
+    : public VideoFrameConsumerFeedbackObserver {
  public:
 
   // Interface defining the methods that clients of VideoCapture must have. It
@@ -55,6 +83,7 @@
      public:
       virtual ~Buffer() = 0;
       virtual int id() const = 0;
+      virtual int frame_feedback_id() const = 0;
       virtual gfx::Size dimensions() const = 0;
       virtual size_t mapped_size() const = 0;
       virtual void* data(int plane) = 0;
@@ -80,12 +109,18 @@
     // first frame in the stream and the current frame; however, the time source
     // is determined by the platform's device driver and is often not the system
     // clock, or even has a drift with respect to system clock.
+    // |frame_feedback_id| is an identifier that allows clients to refer back to
+    // this particular frame when reporting consumer feedback via
+    // OnConsumerReportingUtilization(). This identifier is needed because
+    // frames are consumed asynchronously and multiple frames can be "in flight"
+    // at the same time.
     virtual void OnIncomingCapturedData(const uint8_t* data,
                                         int length,
                                         const VideoCaptureFormat& frame_format,
                                         int clockwise_rotation,
                                         base::TimeTicks reference_time,
-                                        base::TimeDelta timestamp) = 0;
+                                        base::TimeDelta timestamp,
+                                        int frame_feedback_id = 0) = 0;
 
     // Reserve an output buffer into which contents can be captured directly.
     // The returned Buffer will always be allocated with a memory size suitable
@@ -100,7 +135,8 @@
     virtual std::unique_ptr<Buffer> ReserveOutputBuffer(
         const gfx::Size& dimensions,
         VideoPixelFormat format,
-        VideoPixelStorage storage) = 0;
+        VideoPixelStorage storage,
+        int frame_feedback_id) = 0;
 
     // Captured new video data, held in |frame| or |buffer|, respectively for
     // OnIncomingCapturedVideoFrame() and  OnIncomingCapturedBuffer().
@@ -129,7 +165,8 @@
     virtual std::unique_ptr<Buffer> ResurrectLastOutputBuffer(
         const gfx::Size& dimensions,
         VideoPixelFormat format,
-        VideoPixelStorage storage) = 0;
+        VideoPixelStorage storage,
+        int new_frame_feedback_id) = 0;
 
     // An error has occurred that cannot be handled and VideoCaptureDevice must
     // be StopAndDeAllocate()-ed. |reason| is a text description of the error.
@@ -144,7 +181,7 @@
     virtual double GetBufferPoolUtilization() const = 0;
   };
 
-  virtual ~VideoCaptureDevice();
+  ~VideoCaptureDevice() override;
 
   // Prepares the video capturer for use. StopAndDeAllocate() must be called
   // before the object is deleted.
diff --git a/media/capture/video/video_capture_device_client.cc b/media/capture/video/video_capture_device_client.cc
index 3590c7aa..ab0b916 100644
--- a/media/capture/video/video_capture_device_client.cc
+++ b/media/capture/video/video_capture_device_client.cc
@@ -41,13 +41,17 @@
 // implementation to guarantee proper cleanup on destruction on our side.
 class AutoReleaseBuffer : public media::VideoCaptureDevice::Client::Buffer {
  public:
-  AutoReleaseBuffer(scoped_refptr<VideoCaptureBufferPool> pool, int buffer_id)
-      : id_(buffer_id),
-        pool_(std::move(pool)),
+  AutoReleaseBuffer(scoped_refptr<VideoCaptureBufferPool> pool,
+                    int buffer_id,
+                    int frame_feedback_id)
+      : pool_(std::move(pool)),
+        id_(buffer_id),
+        frame_feedback_id_(frame_feedback_id),
         buffer_handle_(pool_->GetBufferHandle(buffer_id)) {
     DCHECK(pool_.get());
   }
   int id() const override { return id_; }
+  int frame_feedback_id() const override { return frame_feedback_id_; }
   gfx::Size dimensions() const override { return buffer_handle_->dimensions(); }
   size_t mapped_size() const override { return buffer_handle_->mapped_size(); }
   void* data(int plane) override { return buffer_handle_->data(plane); }
@@ -66,8 +70,9 @@
  private:
   ~AutoReleaseBuffer() override { pool_->RelinquishProducerReservation(id_); }
 
-  const int id_;
   const scoped_refptr<VideoCaptureBufferPool> pool_;
+  const int id_;
+  const int frame_feedback_id_;
   const std::unique_ptr<VideoCaptureBufferHandle> buffer_handle_;
 };
 
@@ -93,7 +98,8 @@
     const VideoCaptureFormat& frame_format,
     int rotation,
     base::TimeTicks reference_time,
-    base::TimeDelta timestamp) {
+    base::TimeDelta timestamp,
+    int frame_feedback_id) {
   TRACE_EVENT0("video", "VideoCaptureDeviceClient::OnIncomingCapturedData");
   DCHECK_EQ(media::PIXEL_STORAGE_CPU, frame_format.pixel_storage);
 
@@ -115,7 +121,7 @@
 
   if (frame_format.pixel_format == media::PIXEL_FORMAT_Y16) {
     return OnIncomingCapturedY16Data(data, length, frame_format, reference_time,
-                                     timestamp);
+                                     timestamp, frame_feedback_id);
   }
 
   // |chopped_{width,height} and |new_unrotated_{width,height}| are the lowest
@@ -142,9 +148,9 @@
 
   const gfx::Size dimensions(destination_width, destination_height);
   uint8_t *y_plane_data, *u_plane_data, *v_plane_data;
-  std::unique_ptr<Buffer> buffer(
-      ReserveI420OutputBuffer(dimensions, media::PIXEL_STORAGE_CPU,
-                              &y_plane_data, &u_plane_data, &v_plane_data));
+  std::unique_ptr<Buffer> buffer(ReserveI420OutputBuffer(
+      dimensions, media::PIXEL_STORAGE_CPU, frame_feedback_id, &y_plane_data,
+      &u_plane_data, &v_plane_data));
 #if DCHECK_IS_ON()
   dropped_frame_counter_ = buffer.get() ? 0 : dropped_frame_counter_ + 1;
   if (dropped_frame_counter_ >= kMaxDroppedFrames)
@@ -266,7 +272,8 @@
 VideoCaptureDeviceClient::ReserveOutputBuffer(
     const gfx::Size& frame_size,
     media::VideoPixelFormat pixel_format,
-    media::VideoPixelStorage pixel_storage) {
+    media::VideoPixelStorage pixel_storage,
+    int frame_feedback_id) {
   DCHECK_GT(frame_size.width(), 0);
   DCHECK_GT(frame_size.height(), 0);
   DCHECK(IsFormatSupported(pixel_format));
@@ -274,14 +281,15 @@
   // TODO(mcasas): For PIXEL_STORAGE_GPUMEMORYBUFFER, find a way to indicate if
   // it's a ShMem GMB or a DmaBuf GMB.
   int buffer_id_to_drop = VideoCaptureBufferPool::kInvalidId;
-  const int buffer_id = buffer_pool_->ReserveForProducer(
-      frame_size, pixel_format, pixel_storage, &buffer_id_to_drop);
+  const int buffer_id =
+      buffer_pool_->ReserveForProducer(frame_size, pixel_format, pixel_storage,
+                                       frame_feedback_id, &buffer_id_to_drop);
   if (buffer_id_to_drop != VideoCaptureBufferPool::kInvalidId)
     receiver_->OnBufferDestroyed(buffer_id_to_drop);
   if (buffer_id == VideoCaptureBufferPool::kInvalidId)
     return nullptr;
   return base::WrapUnique<Buffer>(
-      new AutoReleaseBuffer(buffer_pool_, buffer_id));
+      new AutoReleaseBuffer(buffer_pool_, buffer_id, frame_feedback_id));
 }
 
 void VideoCaptureDeviceClient::OnIncomingCapturedBuffer(
@@ -324,13 +332,14 @@
 VideoCaptureDeviceClient::ResurrectLastOutputBuffer(
     const gfx::Size& dimensions,
     media::VideoPixelFormat format,
-    media::VideoPixelStorage storage) {
+    media::VideoPixelStorage storage,
+    int new_frame_feedback_id) {
   const int buffer_id =
       buffer_pool_->ResurrectLastForProducer(dimensions, format, storage);
   if (buffer_id == VideoCaptureBufferPool::kInvalidId)
     return nullptr;
   return base::WrapUnique<Buffer>(
-      new AutoReleaseBuffer(buffer_pool_, buffer_id));
+      new AutoReleaseBuffer(buffer_pool_, buffer_id, new_frame_feedback_id));
 }
 
 void VideoCaptureDeviceClient::OnError(
@@ -358,6 +367,7 @@
 VideoCaptureDeviceClient::ReserveI420OutputBuffer(
     const gfx::Size& dimensions,
     media::VideoPixelStorage storage,
+    int frame_feedback_id,
     uint8_t** y_plane_data,
     uint8_t** u_plane_data,
     uint8_t** v_plane_data) {
@@ -366,8 +376,8 @@
   DCHECK(dimensions.width());
 
   const media::VideoPixelFormat format = media::PIXEL_FORMAT_I420;
-  std::unique_ptr<Buffer> buffer(
-      ReserveOutputBuffer(dimensions, media::PIXEL_FORMAT_I420, storage));
+  std::unique_ptr<Buffer> buffer(ReserveOutputBuffer(
+      dimensions, media::PIXEL_FORMAT_I420, storage, frame_feedback_id));
   if (!buffer)
     return std::unique_ptr<Buffer>();
   // TODO(emircan): See http://crbug.com/521068, move this pointer
@@ -387,10 +397,11 @@
     int length,
     const VideoCaptureFormat& frame_format,
     base::TimeTicks reference_time,
-    base::TimeDelta timestamp) {
-  std::unique_ptr<Buffer> buffer(ReserveOutputBuffer(frame_format.frame_size,
-                                                     media::PIXEL_FORMAT_Y16,
-                                                     media::PIXEL_STORAGE_CPU));
+    base::TimeDelta timestamp,
+    int frame_feedback_id) {
+  std::unique_ptr<Buffer> buffer(
+      ReserveOutputBuffer(frame_format.frame_size, media::PIXEL_FORMAT_Y16,
+                          media::PIXEL_STORAGE_CPU, frame_feedback_id));
   // The input |length| can be greater than the required buffer size because of
   // paddings and/or alignments, but it cannot be smaller.
   DCHECK_GE(static_cast<size_t>(length), frame_format.ImageAllocationSize());
diff --git a/media/capture/video/video_capture_device_client.h b/media/capture/video/video_capture_device_client.h
index 9a36f9d..0c85539 100644
--- a/media/capture/video/video_capture_device_client.h
+++ b/media/capture/video/video_capture_device_client.h
@@ -55,13 +55,14 @@
                               const media::VideoCaptureFormat& frame_format,
                               int rotation,
                               base::TimeTicks reference_time,
-                              base::TimeDelta timestamp) override;
-  std::unique_ptr<Buffer> ReserveOutputBuffer(
-      const gfx::Size& dimensions,
-      media::VideoPixelFormat format,
-      media::VideoPixelStorage storage) override;
+                              base::TimeDelta timestamp,
+                              int frame_feedback_id = 0) override;
+  std::unique_ptr<Buffer> ReserveOutputBuffer(const gfx::Size& dimensions,
+                                              media::VideoPixelFormat format,
+                                              media::VideoPixelStorage storage,
+                                              int frame_feedback_id) override;
   void OnIncomingCapturedBuffer(std::unique_ptr<Buffer> buffer,
-                                const media::VideoCaptureFormat& frame_format,
+                                const VideoCaptureFormat& format,
                                 base::TimeTicks reference_time,
                                 base::TimeDelta timestamp) override;
   void OnIncomingCapturedVideoFrame(
@@ -70,7 +71,8 @@
   std::unique_ptr<Buffer> ResurrectLastOutputBuffer(
       const gfx::Size& dimensions,
       media::VideoPixelFormat format,
-      media::VideoPixelStorage storage) override;
+      media::VideoPixelStorage storage,
+      int new_frame_feedback_id) override;
   void OnError(const tracked_objects::Location& from_here,
                const std::string& reason) override;
   void OnLog(const std::string& message) override;
@@ -90,6 +92,7 @@
   std::unique_ptr<Buffer> ReserveI420OutputBuffer(
       const gfx::Size& dimensions,
       media::VideoPixelStorage storage,
+      int frame_feedback_id,
       uint8_t** y_plane_data,
       uint8_t** u_plane_data,
       uint8_t** v_plane_data);
@@ -99,7 +102,8 @@
                                  int length,
                                  const VideoCaptureFormat& frame_format,
                                  base::TimeTicks reference_time,
-                                 base::TimeDelta timestamp);
+                                 base::TimeDelta timestamp,
+                                 int frame_feedback_id);
 
   // The receiver to which we post events.
   const std::unique_ptr<VideoFrameReceiver> receiver_;
diff --git a/media/capture/video/video_capture_device_unittest.cc b/media/capture/video/video_capture_device_unittest.cc
index 20e7a06..b3289d21 100644
--- a/media/capture/video/video_capture_device_unittest.cc
+++ b/media/capture/video/video_capture_device_unittest.cc
@@ -113,23 +113,24 @@
                               const VideoCaptureFormat& format,
                               int rotation,
                               base::TimeTicks reference_time,
-                              base::TimeDelta timestamp) override {
+                              base::TimeDelta timestamp,
+                              int frame_feedback_id) override {
     ASSERT_GT(length, 0);
     ASSERT_TRUE(data);
     main_thread_->PostTask(FROM_HERE, base::Bind(frame_cb_, format));
   }
 
   // Trampoline methods to workaround GMOCK problems with std::unique_ptr<>.
-  std::unique_ptr<Buffer> ReserveOutputBuffer(
-      const gfx::Size& dimensions,
-      media::VideoPixelFormat format,
-      media::VideoPixelStorage storage) override {
+  std::unique_ptr<Buffer> ReserveOutputBuffer(const gfx::Size& dimensions,
+                                              media::VideoPixelFormat format,
+                                              media::VideoPixelStorage storage,
+                                              int frame_feedback_id) override {
     DoReserveOutputBuffer();
     NOTREACHED() << "This should never be called";
     return std::unique_ptr<Buffer>();
   }
   void OnIncomingCapturedBuffer(std::unique_ptr<Buffer> buffer,
-                                const VideoCaptureFormat& frame_format,
+                                const VideoCaptureFormat& format,
                                 base::TimeTicks reference_time,
                                 base::TimeDelta timestamp) override {
     DoOnIncomingCapturedBuffer();
@@ -141,7 +142,8 @@
   std::unique_ptr<Buffer> ResurrectLastOutputBuffer(
       const gfx::Size& dimensions,
       media::VideoPixelFormat format,
-      media::VideoPixelStorage storage) {
+      media::VideoPixelStorage storage,
+      int frame_feedback_id) {
     DoResurrectLastOutputBuffer();
     NOTREACHED() << "This should never be called";
     return std::unique_ptr<Buffer>();
diff --git a/media/capture/video/video_frame_receiver.h b/media/capture/video/video_frame_receiver.h
index b2af10c6..e772f33 100644
--- a/media/capture/video/video_frame_receiver.h
+++ b/media/capture/video/video_frame_receiver.h
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifndef MEDIA_CAPTURE_VIDEO_VIDEO_FRAME_RECEIVER_H_
+#define MEDIA_CAPTURE_VIDEO_VIDEO_FRAME_RECEIVER_H_
+
 #include "media/capture/capture_export.h"
 #include "media/capture/video/video_capture_device.h"
 
@@ -22,3 +25,5 @@
 };
 
 }  // namespace media
+
+#endif  // MEDIA_CAPTURE_VIDEO_VIDEO_FRAME_RECEIVER_H_
diff --git a/media/filters/android/media_codec_audio_decoder.cc b/media/filters/android/media_codec_audio_decoder.cc
index 79744ea..a38e57a 100644
--- a/media/filters/android/media_codec_audio_decoder.cc
+++ b/media/filters/android/media_codec_audio_decoder.cc
@@ -275,10 +275,10 @@
     input_data.length = decoder_buffer->data_size();
     const DecryptConfig* decrypt_config = decoder_buffer->decrypt_config();
     if (decrypt_config && decrypt_config->is_encrypted()) {
-      input_data.is_encrypted = true;
       input_data.key_id = decrypt_config->key_id();
       input_data.iv = decrypt_config->iv();
       input_data.subsamples = decrypt_config->subsamples();
+      input_data.encryption_scheme = config_.encryption_scheme();
     }
     input_data.presentation_time = decoder_buffer->timestamp();
   }
diff --git a/media/filters/decoder_stream_traits.cc b/media/filters/decoder_stream_traits.cc
index deaedddc..ec6c799 100644
--- a/media/filters/decoder_stream_traits.cc
+++ b/media/filters/decoder_stream_traits.cc
@@ -13,10 +13,38 @@
 
 namespace media {
 
+// Audio decoder stream traits implementation.
+
+// static
 std::string DecoderStreamTraits<DemuxerStream::AUDIO>::ToString() {
   return "audio";
 }
 
+// static
+bool DecoderStreamTraits<DemuxerStream::AUDIO>::NeedsBitstreamConversion(
+    DecoderType* decoder) {
+  return decoder->NeedsBitstreamConversion();
+}
+
+// static
+void DecoderStreamTraits<DemuxerStream::AUDIO>::ReportStatistics(
+    const StatisticsCB& statistics_cb,
+    int bytes_decoded) {
+  PipelineStatistics statistics;
+  statistics.audio_bytes_decoded = bytes_decoded;
+  statistics_cb.Run(statistics);
+}
+
+// static
+scoped_refptr<DecoderStreamTraits<DemuxerStream::AUDIO>::OutputType>
+    DecoderStreamTraits<DemuxerStream::AUDIO>::CreateEOSOutput() {
+  return OutputType::CreateEOSBuffer();
+}
+
+DecoderStreamTraits<DemuxerStream::AUDIO>::DecoderStreamTraits(
+    const scoped_refptr<MediaLog>& media_log)
+    : media_log_(media_log) {}
+
 void DecoderStreamTraits<DemuxerStream::AUDIO>::InitializeDecoder(
     DecoderType* decoder,
     DemuxerStream* stream,
@@ -28,62 +56,6 @@
                       output_cb);
 }
 
-bool DecoderStreamTraits<DemuxerStream::AUDIO>::NeedsBitstreamConversion(
-    DecoderType* decoder) {
-  return decoder->NeedsBitstreamConversion();
-}
-
-void DecoderStreamTraits<DemuxerStream::AUDIO>::ReportStatistics(
-    const StatisticsCB& statistics_cb,
-    int bytes_decoded) {
-  PipelineStatistics statistics;
-  statistics.audio_bytes_decoded = bytes_decoded;
-  statistics_cb.Run(statistics);
-}
-
-scoped_refptr<DecoderStreamTraits<DemuxerStream::AUDIO>::OutputType>
-    DecoderStreamTraits<DemuxerStream::AUDIO>::CreateEOSOutput() {
-  return OutputType::CreateEOSBuffer();
-}
-
-std::string DecoderStreamTraits<DemuxerStream::VIDEO>::ToString() {
-  return "video";
-}
-
-void DecoderStreamTraits<DemuxerStream::VIDEO>::InitializeDecoder(
-    DecoderType* decoder,
-    DemuxerStream* stream,
-    CdmContext* cdm_context,
-    const InitCB& init_cb,
-    const OutputCB& output_cb) {
-  DCHECK(stream->video_decoder_config().IsValidConfig());
-  decoder->Initialize(stream->video_decoder_config(),
-                      stream->liveness() == DemuxerStream::LIVENESS_LIVE,
-                      cdm_context, init_cb, output_cb);
-}
-
-bool DecoderStreamTraits<DemuxerStream::VIDEO>::NeedsBitstreamConversion(
-    DecoderType* decoder) {
-  return decoder->NeedsBitstreamConversion();
-}
-
-void DecoderStreamTraits<DemuxerStream::VIDEO>::ReportStatistics(
-    const StatisticsCB& statistics_cb,
-    int bytes_decoded) {
-  PipelineStatistics statistics;
-  statistics.video_bytes_decoded = bytes_decoded;
-  statistics_cb.Run(statistics);
-}
-
-scoped_refptr<DecoderStreamTraits<DemuxerStream::VIDEO>::OutputType>
-    DecoderStreamTraits<DemuxerStream::VIDEO>::CreateEOSOutput() {
-  return OutputType::CreateEOSFrame();
-}
-
-DecoderStreamTraits<DemuxerStream::AUDIO>::DecoderStreamTraits(
-    const scoped_refptr<MediaLog>& media_log)
-    : media_log_(media_log) {}
-
 void DecoderStreamTraits<DemuxerStream::AUDIO>::OnStreamReset(
     DemuxerStream* stream) {
   DCHECK(stream);
@@ -103,4 +75,44 @@
   audio_ts_validator_->RecordOutputDuration(buffer);
 }
 
+// Video decoder stream traits implementation.
+
+// static
+std::string DecoderStreamTraits<DemuxerStream::VIDEO>::ToString() {
+  return "video";
+}
+
+// static
+bool DecoderStreamTraits<DemuxerStream::VIDEO>::NeedsBitstreamConversion(
+    DecoderType* decoder) {
+  return decoder->NeedsBitstreamConversion();
+}
+
+// static
+void DecoderStreamTraits<DemuxerStream::VIDEO>::ReportStatistics(
+    const StatisticsCB& statistics_cb,
+    int bytes_decoded) {
+  PipelineStatistics statistics;
+  statistics.video_bytes_decoded = bytes_decoded;
+  statistics_cb.Run(statistics);
+}
+
+// static
+scoped_refptr<DecoderStreamTraits<DemuxerStream::VIDEO>::OutputType>
+    DecoderStreamTraits<DemuxerStream::VIDEO>::CreateEOSOutput() {
+  return OutputType::CreateEOSFrame();
+}
+
+void DecoderStreamTraits<DemuxerStream::VIDEO>::InitializeDecoder(
+    DecoderType* decoder,
+    DemuxerStream* stream,
+    CdmContext* cdm_context,
+    const InitCB& init_cb,
+    const OutputCB& output_cb) {
+  DCHECK(stream->video_decoder_config().IsValidConfig());
+  decoder->Initialize(stream->video_decoder_config(),
+                      stream->liveness() == DemuxerStream::LIVENESS_LIVE,
+                      cdm_context, init_cb, output_cb);
+}
+
 }  // namespace media
diff --git a/media/filters/decoder_stream_traits.h b/media/filters/decoder_stream_traits.h
index 46311cc7..63951c1 100644
--- a/media/filters/decoder_stream_traits.h
+++ b/media/filters/decoder_stream_traits.h
@@ -34,21 +34,22 @@
   typedef base::Callback<void(bool success)> InitCB;
   typedef base::Callback<void(const scoped_refptr<OutputType>&)> OutputCB;
 
+  static std::string ToString();
+  static bool NeedsBitstreamConversion(DecoderType* decoder);
+  static void ReportStatistics(const StatisticsCB& statistics_cb,
+                               int bytes_decoded);
+  static scoped_refptr<OutputType> CreateEOSOutput();
+
   explicit DecoderStreamTraits(const scoped_refptr<MediaLog>& media_log);
 
-  static std::string ToString();
   void InitializeDecoder(DecoderType* decoder,
                          DemuxerStream* stream,
                          CdmContext* cdm_context,
                          const InitCB& init_cb,
                          const OutputCB& output_cb);
-  static bool NeedsBitstreamConversion(DecoderType* decoder);
   void OnDecode(const scoped_refptr<DecoderBuffer>& buffer);
   void OnDecodeDone(const scoped_refptr<OutputType>& buffer);
   void OnStreamReset(DemuxerStream* stream);
-  static void ReportStatistics(const StatisticsCB& statistics_cb,
-                               int bytes_decoded);
-  static scoped_refptr<OutputType> CreateEOSOutput();
 
  private:
   // Validates encoded timestamps match decoded output duration. MEDIA_LOG warns
@@ -68,21 +69,22 @@
   typedef base::Callback<void(bool success)> InitCB;
   typedef base::Callback<void(const scoped_refptr<OutputType>&)> OutputCB;
 
+  static std::string ToString();
+  static bool NeedsBitstreamConversion(DecoderType* decoder);
+  static void ReportStatistics(const StatisticsCB& statistics_cb,
+                               int bytes_decoded);
+  static scoped_refptr<OutputType> CreateEOSOutput();
+
   explicit DecoderStreamTraits(const scoped_refptr<MediaLog>& media_log) {}
 
-  static std::string ToString();
   void InitializeDecoder(DecoderType* decoder,
                          DemuxerStream* stream,
                          CdmContext* cdm_context,
                          const InitCB& init_cb,
                          const OutputCB& output_cb);
-  static bool NeedsBitstreamConversion(DecoderType* decoder);
   void OnDecode(const scoped_refptr<DecoderBuffer>& buffer) {}
   void OnDecodeDone(const scoped_refptr<OutputType>& buffer) {}
   void OnStreamReset(DemuxerStream* stream) {}
-  static void ReportStatistics(const StatisticsCB& statistics_cb,
-                               int bytes_decoded);
-  static scoped_refptr<OutputType> CreateEOSOutput();
 };
 
 }  // namespace media
diff --git a/media/filters/ffmpeg_demuxer.cc b/media/filters/ffmpeg_demuxer.cc
index 093e4f5..eab193e 100644
--- a/media/filters/ffmpeg_demuxer.cc
+++ b/media/filters/ffmpeg_demuxer.cc
@@ -275,6 +275,7 @@
       video_rotation_(VIDEO_ROTATION_0),
       is_enabled_(true),
       waiting_for_keyframe_(false),
+      aborted_(false),
       fixup_negative_timestamps_(false) {
   DCHECK(demuxer_);
 
@@ -591,9 +592,11 @@
   end_of_stream_ = false;
   last_packet_timestamp_ = kNoTimestamp;
   last_packet_duration_ = kNoTimestamp;
+  aborted_ = false;
 }
 
 void FFmpegDemuxerStream::Abort() {
+  aborted_ = true;
   if (!read_cb_.is_null())
     base::ResetAndReturn(&read_cb_).Run(DemuxerStream::kAborted, nullptr);
 }
@@ -641,6 +644,11 @@
     return;
   }
 
+  if (aborted_) {
+    base::ResetAndReturn(&read_cb_).Run(kAborted, nullptr);
+    return;
+  }
+
   SatisfyPendingRead();
 }
 
diff --git a/media/filters/ffmpeg_demuxer.h b/media/filters/ffmpeg_demuxer.h
index 6c065de..8b9320ed 100644
--- a/media/filters/ffmpeg_demuxer.h
+++ b/media/filters/ffmpeg_demuxer.h
@@ -185,6 +185,7 @@
   VideoRotation video_rotation_;
   bool is_enabled_;
   bool waiting_for_keyframe_;
+  bool aborted_;
 
   DecoderBufferQueue buffer_queue_;
   ReadCB read_cb_;
diff --git a/media/filters/ffmpeg_demuxer_unittest.cc b/media/filters/ffmpeg_demuxer_unittest.cc
index 4225530a..eda72002 100644
--- a/media/filters/ffmpeg_demuxer_unittest.cc
+++ b/media/filters/ffmpeg_demuxer_unittest.cc
@@ -437,6 +437,10 @@
   demuxer_->AbortPendingReads();
   base::RunLoop().Run();
 
+  // Additional reads should also be aborted (until a Seek()).
+  audio->Read(NewReadCB(FROM_HERE, 29, 0, true, DemuxerStream::kAborted));
+  base::RunLoop().Run();
+
   // Ensure blocking thread has completed outstanding work.
   demuxer_->Stop();
   EXPECT_EQ(format_context()->pb->eof_reached, 0);
diff --git a/media/filters/gpu_video_decoder.cc b/media/filters/gpu_video_decoder.cc
index 54fa866..6a4bcad 100644
--- a/media/filters/gpu_video_decoder.cc
+++ b/media/filters/gpu_video_decoder.cc
@@ -357,8 +357,8 @@
   VideoDecodeAccelerator::Config vda_config;
   vda_config.profile = config_.profile();
   vda_config.cdm_id = cdm_id_;
-  vda_config.is_encrypted = config_.is_encrypted();
   vda_config.surface_id = surface_id;
+  vda_config.encryption_scheme = config_.encryption_scheme();
   vda_config.is_deferred_initialization_allowed = true;
   vda_config.initial_expected_coded_size = config_.coded_size();
 
diff --git a/media/gpu/android_video_decode_accelerator.cc b/media/gpu/android_video_decode_accelerator.cc
index b02f45e..abf3776 100644
--- a/media/gpu/android_video_decode_accelerator.cc
+++ b/media/gpu/android_video_decode_accelerator.cc
@@ -333,7 +333,7 @@
   // We signaled that we support deferred initialization, so see if the client
   // does also.
   deferred_initialization_pending_ = config.is_deferred_initialization_allowed;
-  if (config_.is_encrypted && !deferred_initialization_pending_) {
+  if (config_.is_encrypted() && !deferred_initialization_pending_) {
     DLOG(ERROR) << "Deferred initialization must be used for encrypted streams";
     return false;
   }
@@ -374,7 +374,7 @@
     return false;
 
   // If we are encrypted, then we aren't able to create the codec yet.
-  if (config_.is_encrypted) {
+  if (config_.is_encrypted()) {
     InitializeCdm();
     return true;
   }
@@ -504,7 +504,7 @@
   } else {
     status = media_codec_->QueueSecureInputBuffer(
         input_buf_index, memory, bitstream_buffer.size(), key_id, iv,
-        subsamples, presentation_timestamp);
+        subsamples, config_.encryption_scheme, presentation_timestamp);
   }
 
   DVLOG(2) << __func__
@@ -1452,8 +1452,8 @@
     const {
   // Prevent MediaCodec from using its internal software decoders when we have
   // more secure and up to date versions in the renderer process.
-  return !config_.is_encrypted && (codec_config_->codec == kCodecVP8 ||
-                                   codec_config_->codec == kCodecVP9);
+  return !config_.is_encrypted() && (codec_config_->codec == kCodecVP8 ||
+                                     codec_config_->codec == kCodecVP9);
 }
 
 bool AndroidVideoDecodeAccelerator::UpdateSurface() {
diff --git a/media/gpu/dxva_video_decode_accelerator_win.cc b/media/gpu/dxva_video_decode_accelerator_win.cc
index 935cd72..6af7501 100644
--- a/media/gpu/dxva_video_decode_accelerator_win.cc
+++ b/media/gpu/dxva_video_decode_accelerator_win.cc
@@ -536,7 +536,7 @@
     return false;
   }
 
-  if (config.is_encrypted) {
+  if (config.is_encrypted()) {
     NOTREACHED() << "Encrypted streams are not supported for this VDA";
     return false;
   }
diff --git a/media/gpu/fake_video_decode_accelerator.cc b/media/gpu/fake_video_decode_accelerator.cc
index 533cabf..f4386c6a 100644
--- a/media/gpu/fake_video_decode_accelerator.cc
+++ b/media/gpu/fake_video_decode_accelerator.cc
@@ -49,7 +49,7 @@
     LOG(ERROR) << "unknown codec profile";
     return false;
   }
-  if (config.is_encrypted) {
+  if (config.is_encrypted()) {
     NOTREACHED() << "encrypted streams are not supported";
     return false;
   }
diff --git a/media/gpu/ipc/common/media_param_traits_macros.h b/media/gpu/ipc/common/media_param_traits_macros.h
index e650f05..6904202d 100644
--- a/media/gpu/ipc/common/media_param_traits_macros.h
+++ b/media/gpu/ipc/common/media_param_traits_macros.h
@@ -7,7 +7,7 @@
 
 #include "gpu/config/gpu_info.h"
 #include "ipc/ipc_message_macros.h"
-#include "media/base/ipc/media_param_traits_macros.h"
+#include "media/base/ipc/media_param_traits.h"
 #include "media/gpu/ipc/common/create_video_encoder_params.h"
 #include "media/video/jpeg_decode_accelerator.h"
 #include "media/video/video_decode_accelerator.h"
@@ -21,7 +21,7 @@
 
 IPC_STRUCT_TRAITS_BEGIN(media::VideoDecodeAccelerator::Config)
   IPC_STRUCT_TRAITS_MEMBER(profile)
-  IPC_STRUCT_TRAITS_MEMBER(is_encrypted)
+  IPC_STRUCT_TRAITS_MEMBER(encryption_scheme)
   IPC_STRUCT_TRAITS_MEMBER(cdm_id)
   IPC_STRUCT_TRAITS_MEMBER(is_deferred_initialization_allowed)
   IPC_STRUCT_TRAITS_MEMBER(surface_id)
diff --git a/media/gpu/ipc/service/gpu_video_decode_accelerator.cc b/media/gpu/ipc/service/gpu_video_decode_accelerator.cc
index d6d18b1..cb0b3c8c 100644
--- a/media/gpu/ipc/service/gpu_video_decode_accelerator.cc
+++ b/media/gpu/ipc/service/gpu_video_decode_accelerator.cc
@@ -371,7 +371,7 @@
       vda_factory->CreateVDA(this, config, gpu_workarounds, gpu_preferences);
   if (!video_decode_accelerator_) {
     LOG(ERROR) << "HW video decode not available for profile " << config.profile
-               << (config.is_encrypted ? " with encryption" : "");
+               << (config.is_encrypted() ? " with encryption" : "");
     return false;
   }
 
diff --git a/media/gpu/v4l2_slice_video_decode_accelerator.cc b/media/gpu/v4l2_slice_video_decode_accelerator.cc
index 029d92cf..f825ca4b 100644
--- a/media/gpu/v4l2_slice_video_decode_accelerator.cc
+++ b/media/gpu/v4l2_slice_video_decode_accelerator.cc
@@ -518,7 +518,7 @@
   DCHECK(child_task_runner_->BelongsToCurrentThread());
   DCHECK_EQ(state_, kUninitialized);
 
-  if (config.is_encrypted) {
+  if (config.is_encrypted()) {
     NOTREACHED() << "Encrypted streams are not supported for this VDA";
     return false;
   }
diff --git a/media/gpu/v4l2_video_decode_accelerator.cc b/media/gpu/v4l2_video_decode_accelerator.cc
index 15408f1d..0df948c 100644
--- a/media/gpu/v4l2_video_decode_accelerator.cc
+++ b/media/gpu/v4l2_video_decode_accelerator.cc
@@ -205,7 +205,7 @@
   DCHECK(child_task_runner_->BelongsToCurrentThread());
   DCHECK_EQ(decoder_state_, kUninitialized);
 
-  if (config.is_encrypted) {
+  if (config.is_encrypted()) {
     NOTREACHED() << "Encrypted streams are not supported for this VDA";
     return false;
   }
diff --git a/media/gpu/vaapi_video_decode_accelerator.cc b/media/gpu/vaapi_video_decode_accelerator.cc
index 372a257..755e9ab 100644
--- a/media/gpu/vaapi_video_decode_accelerator.cc
+++ b/media/gpu/vaapi_video_decode_accelerator.cc
@@ -333,7 +333,7 @@
                                              Client* client) {
   DCHECK(task_runner_->BelongsToCurrentThread());
 
-  if (config.is_encrypted) {
+  if (config.is_encrypted()) {
     NOTREACHED() << "Encrypted streams are not supported for this VDA";
     return false;
   }
diff --git a/media/gpu/vt_video_decode_accelerator_mac.cc b/media/gpu/vt_video_decode_accelerator_mac.cc
index f79b2da9..59cb2ce 100644
--- a/media/gpu/vt_video_decode_accelerator_mac.cc
+++ b/media/gpu/vt_video_decode_accelerator_mac.cc
@@ -306,7 +306,7 @@
     return false;
   }
 
-  if (config.is_encrypted) {
+  if (config.is_encrypted()) {
     NOTREACHED() << "Encrypted streams are not supported for this VDA";
     return false;
   }
diff --git a/media/mojo/clients/BUILD.gn b/media/mojo/clients/BUILD.gn
index 98d2bcd..e1d9bdf 100644
--- a/media/mojo/clients/BUILD.gn
+++ b/media/mojo/clients/BUILD.gn
@@ -11,7 +11,7 @@
     # TODO(xhwang): Only allow //media/mojo:media_mojo_unittests
     "//media/mojo:*",
 
-    # TODO(xhwang): Only allow //media/mojo/services:media_mojo_shell_unittests
+    # TODO(xhwang): Only allow //media/mojo/services:media_service_unittests
     "//media/mojo/services:*",
 
     "//media/test/*",
diff --git a/media/mojo/services/BUILD.gn b/media/mojo/services/BUILD.gn
index dd57bde..a27ce33 100644
--- a/media/mojo/services/BUILD.gn
+++ b/media/mojo/services/BUILD.gn
@@ -150,11 +150,11 @@
   ]
 }
 
-test("media_mojo_shell_unittests") {
+test("media_service_unittests") {
   testonly = true
 
   sources = [
-    "media_mojo_unittest.cc",
+    "media_service_unittest.cc",
   ]
 
   deps = [
@@ -181,7 +181,7 @@
 }
 
 service_manifest("test_manifest") {
-  name = "media_mojo_shell_unittests"
+  name = "media_service_unittests"
   source = "test_manifest.json"
 }
 
diff --git a/media/mojo/services/media_mojo_unittest.cc b/media/mojo/services/media_service_unittest.cc
similarity index 99%
rename from media/mojo/services/media_mojo_unittest.cc
rename to media/mojo/services/media_service_unittest.cc
index ed5e770..c3156ad 100644
--- a/media/mojo/services/media_mojo_unittest.cc
+++ b/media/mojo/services/media_service_unittest.cc
@@ -68,7 +68,7 @@
 class MediaServiceTest : public service_manager::test::ServiceTest {
  public:
   MediaServiceTest()
-      : ServiceTest("media_mojo_shell_unittests"),
+      : ServiceTest("media_service_unittests"),
         renderer_client_binding_(&renderer_client_),
         video_stream_(DemuxerStream::VIDEO) {}
   ~MediaServiceTest() override {}
diff --git a/media/mojo/services/test_manifest.json b/media/mojo/services/test_manifest.json
index 27063eb..1ff56985 100644
--- a/media/mojo/services/test_manifest.json
+++ b/media/mojo/services/test_manifest.json
@@ -1,6 +1,6 @@
 {
-  "name": "media_mojo_shell_unittests",
-  "display_name": "Media Mojo Shell Unittests",
+  "name": "media_service_unittests",
+  "display_name": "Media Service Unittests",
   "interface_provider_specs": {
     "service_manager:connector": {
       "requires": {
diff --git a/media/video/video_decode_accelerator.cc b/media/video/video_decode_accelerator.cc
index 399607c..b2b8a53 100644
--- a/media/video/video_decode_accelerator.cc
+++ b/media/video/video_decode_accelerator.cc
@@ -20,7 +20,7 @@
 std::string VideoDecodeAccelerator::Config::AsHumanReadableString() const {
   std::ostringstream s;
   s << "profile: " << GetProfileName(profile) << " encrypted? "
-    << (is_encrypted ? "true" : "false");
+    << (is_encrypted() ? "true" : "false");
   return s.str();
 }
 
diff --git a/media/video/video_decode_accelerator.h b/media/video/video_decode_accelerator.h
index c14050c6..462c08b 100644
--- a/media/video/video_decode_accelerator.h
+++ b/media/video/video_decode_accelerator.h
@@ -14,6 +14,7 @@
 #include "base/memory/weak_ptr.h"
 #include "media/base/bitstream_buffer.h"
 #include "media/base/cdm_context.h"
+#include "media/base/encryption_scheme.h"
 #include "media/base/surface_manager.h"
 #include "media/base/video_decoder_config.h"
 #include "media/video/picture.h"
@@ -127,12 +128,13 @@
     ~Config();
 
     std::string AsHumanReadableString() const;
+    bool is_encrypted() const { return encryption_scheme.is_encrypted(); }
 
     // The video codec and profile.
     VideoCodecProfile profile = VIDEO_CODEC_PROFILE_UNKNOWN;
 
-    // Whether the stream is encrypted.
-    bool is_encrypted = false;
+    // Whether the stream is encrypted, and, if so, the scheme used.
+    EncryptionScheme encryption_scheme;
 
     // The CDM that the VDA should use to decode encrypted streams. Must be
     // set to a valid ID if |is_encrypted|.
diff --git a/net/http/transport_security_state_static.json b/net/http/transport_security_state_static.json
index 367ef139..b38cbd2 100644
--- a/net/http/transport_security_state_static.json
+++ b/net/http/transport_security_state_static.json
@@ -8783,7 +8783,9 @@
     { "name": "winebid.com", "include_subdomains": true, "mode": "force-https" },
 
     // Entries submitted through hstspreload.appspot.com in Chrome 51 or later.
-    // START OF BULK ADDITIONS
+    // These entries are subject to the continued requirements documented at
+    // https://hstspreload.org/#continued-requirements
+    // START OF BULK ENTRIES
     { "name": "050media.nl", "include_subdomains": true, "mode": "force-https" },
     { "name": "0x.sk", "include_subdomains": true, "mode": "force-https" },
     { "name": "0x44.net", "include_subdomains": true, "mode": "force-https" },
@@ -18746,11 +18748,11 @@
     { "name": "rijk-catering.nl", "include_subdomains": true, "mode": "force-https" },
     { "name": "rvsbevestigingen.nl", "include_subdomains": true, "mode": "force-https" },
     { "name": "yhb.io", "include_subdomains": true, "mode": "force-https" },
-    // END OF BULK ADDITIONS
+    // END OF BULK ENTRIES
 
-    // Manual additions in Chrome 51 or later that do not belong in a
+    // Manual additions and changes in Chrome 51 or later that do not belong in a
     // special section above.
-    // START OF MANUAL ADDITIONS
+    // START OF MANUAL ENTRIES
     { "name": "bodhi.fedoraproject.org", "include_subdomains": true, "mode": "force-https" },
     { "name": "communityblog.fedoraproject.org", "include_subdomains": true, "mode": "force-https" },
     { "name": "keys.fedoraproject.org", "include_subdomains": true, "mode": "force-https" },
@@ -18799,7 +18801,7 @@
     { "name": "www.amazon.nl", "include_subdomains": true, "mode": "force-https" },
     { "name": "crypto.is", "include_subdomains": true, "mode": "force-https", "expect_ct": true, "expect_ct_report_uri": "https://clients3.google.com/ct_upload" },
     { "name": "ritter.vg", "expect_ct": true, "expect_ct_report_uri": "https://clients3.google.com/ct_upload", "expect_staple": true, "expect_staple_report_uri": "https://asac.casa/expectstaple.jsp" },
-    // END OF MANUAL ADDITIONS
+    // END OF MANUAL ENTRIES
 
     // To avoid trailing comma changes from showing up in diffs, we place a
     // single entry at the end.
diff --git a/net/http2/decoder/decode_buffer.cc b/net/http2/decoder/decode_buffer.cc
new file mode 100644
index 0000000..15235bae
--- /dev/null
+++ b/net/http2/decoder/decode_buffer.cc
@@ -0,0 +1,90 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/decode_buffer.h"
+
+namespace net {
+
+bool DecodeBuffer::SlowDecodeUnsignedInt(uint32_t field_size,
+                                         uint32_t field_offset,
+                                         uint32_t* decode_offset,
+                                         uint32_t* value) {
+  DCHECK_LT(0u, field_size);
+  DCHECK_LE(field_size, 4u);
+  DCHECK(decode_offset != nullptr);
+  DCHECK_LE(field_offset, *decode_offset);
+  const uint32_t next_field_offset = field_offset + field_size;
+  if (*decode_offset == field_offset) {
+    // Starting to decode field. It is possible we will reach this point
+    // twice, once when we've just exhausted the input, and once when
+    // resuming decoding with a new input buffer.
+    // Clear the field; we do NOT assume that the caller has done so
+    // previously.
+    *value = 0;
+  } else if (*decode_offset >= next_field_offset) {
+    // We already decoded this field.
+    return true;
+  }
+  do {
+    if (Empty()) {
+      return false;  // Not done decoding.
+    }
+    *value = *value << 8 | DecodeUInt8();
+    (*decode_offset)++;
+  } while (*decode_offset < next_field_offset);
+  return true;
+}
+
+bool DecodeBuffer::SlowDecodeUInt8(uint32_t field_offset,
+                                   uint32_t* decode_offset,
+                                   uint8_t* value) {
+  uint32_t tmp = *value;
+  const bool done = SlowDecodeUnsignedInt(1 /* field_size */, field_offset,
+                                          decode_offset, &tmp);
+  *value = tmp & 0xff;
+  DCHECK_EQ(tmp, *value);
+  return done;
+}
+
+bool DecodeBuffer::SlowDecodeUInt16(uint32_t field_offset,
+                                    uint32_t* decode_offset,
+                                    uint16_t* value) {
+  uint32_t tmp = *value;
+  const bool done = SlowDecodeUnsignedInt(2 /* field_size */, field_offset,
+                                          decode_offset, &tmp);
+  *value = tmp & 0xffff;
+  DCHECK_EQ(tmp, *value);
+  return done;
+}
+
+bool DecodeBuffer::SlowDecodeUInt24(uint32_t field_offset,
+                                    uint32_t* decode_offset,
+                                    uint32_t* value) {
+  uint32_t tmp = *value;
+  const bool done = SlowDecodeUnsignedInt(3 /* field_size */, field_offset,
+                                          decode_offset, &tmp);
+  *value = tmp & 0xffffff;
+  DCHECK_EQ(tmp, *value);
+  return done;
+}
+
+bool DecodeBuffer::SlowDecodeUInt31(uint32_t field_offset,
+                                    uint32_t* decode_offset,
+                                    uint32_t* value) {
+  uint32_t tmp = *value;
+  const bool done = SlowDecodeUnsignedInt(4 /* field_size */, field_offset,
+                                          decode_offset, &tmp);
+  *value = tmp & 0x7fffffff;
+  DCHECK_EQ(tmp & 0x7fffffff, *value);
+  return done;
+}
+
+bool DecodeBuffer::SlowDecodeUInt32(uint32_t field_offset,
+                                    uint32_t* decode_offset,
+                                    uint32_t* value) {
+  return SlowDecodeUnsignedInt(4 /* field_size */, field_offset, decode_offset,
+                               value);
+}
+
+}  // namespace net
diff --git a/net/http2/decoder/decode_buffer.h b/net/http2/decoder/decode_buffer.h
new file mode 100644
index 0000000..56f695f4
--- /dev/null
+++ b/net/http2/decoder/decode_buffer.h
@@ -0,0 +1,289 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_DECODE_BUFFER_H_
+#define NET_HTTP2_DECODER_DECODE_BUFFER_H_
+
+// DecodeBuffer provides primitives for decoding various integer types found
+// in HTTP/2 frames.
+// DecodeBuffer wraps a byte array from which we can read and decode serialized
+// HTTP/2 frames, or parts thereof. DecodeBuffer is intended only for stack
+// allocation, where the caller is typically going to use the DecodeBuffer
+// instance as part of decoding the entire buffer before returning to its own
+// caller. Only the concrete Slow* methods are defined in the cc file,
+// all other methods are defined in this header file to enable inlining.
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+
+namespace net {
+class DecodeBufferSubset;
+
+class NET_EXPORT_PRIVATE DecodeBuffer {
+ public:
+  DecodeBuffer(const char* buffer, size_t len)
+      : buffer_(buffer), cursor_(buffer), beyond_(buffer + len) {
+    DCHECK_NE(buffer, nullptr);
+    DCHECK_LE(len, MaxDecodeBufferLength());
+  }
+  explicit DecodeBuffer(base::StringPiece s)
+      : DecodeBuffer(s.data(), s.size()) {}
+  // Constructor for character arrays, typically in tests. For example:
+  //    const char input[] = { 0x11 };
+  //    DecodeBuffer b(input);
+  template <size_t N>
+  explicit DecodeBuffer(const char (&buf)[N]) : DecodeBuffer(buf, N) {}
+
+  bool Empty() const { return cursor_ >= beyond_; }
+  bool HasData() const { return cursor_ < beyond_; }
+  size_t Remaining() const {
+    DCHECK_LE(cursor_, beyond_);
+    return beyond_ - cursor_;
+  }
+  size_t Offset() const { return cursor_ - buffer_; }
+  size_t FullSize() const { return beyond_ - buffer_; }
+
+  // Returns the minimum of the number of bytes remaining in this DecodeBuffer
+  // and |length|, in support of determining how much of some structure/payload
+  // is in this DecodeBuffer.
+  size_t MinLengthRemaining(size_t length) const {
+    return std::min(length, Remaining());
+  }
+
+  // For string decoding, returns a pointer to the next byte/char to be decoded.
+  const char* cursor() const { return cursor_; }
+  // Advances the cursor (pointer to the next byte/char to be decoded).
+  void AdvanceCursor(size_t amount) {
+    DCHECK_LE(amount, Remaining());  // Need at least that much remaining.
+    DCHECK_EQ(subset_, nullptr) << "Access via subset only when present.";
+    cursor_ += amount;
+  }
+
+  // Only call methods starting "Decode" when there is enough input remaining.
+  char DecodeChar() {
+    DCHECK_LE(1u, Remaining());  // Need at least one byte remaining.
+    DCHECK_EQ(subset_, nullptr) << "Access via subset only when present.";
+    return *cursor_++;
+  }
+
+  uint8_t DecodeUInt8() { return static_cast<uint8_t>(DecodeChar()); }
+
+  uint16_t DecodeUInt16() {
+    DCHECK_LE(2u, Remaining());
+    const uint8_t b1 = DecodeUInt8();
+    const uint8_t b2 = DecodeUInt8();
+    // Note that chars are automatically promoted to ints during arithmetic,
+    // so the b1 << 8 doesn't end up as zero before being or-ed with b2.
+    // And the left-shift operator has higher precedence than the or operator.
+    return b1 << 8 | b2;
+  }
+
+  uint32_t DecodeUInt24() {
+    DCHECK_LE(3u, Remaining());
+    const uint8_t b1 = DecodeUInt8();
+    const uint8_t b2 = DecodeUInt8();
+    const uint8_t b3 = DecodeUInt8();
+    return b1 << 16 | b2 << 8 | b3;
+  }
+
+  // For 31-bit unsigned integers, where the 32nd bit is reserved for future
+  // use (i.e. the high-bit of the first byte of the encoding); examples:
+  // the Stream Id in a frame header or the Window Size Increment in a
+  // WINDOW_UPDATE frame.
+  uint32_t DecodeUInt31() {
+    DCHECK_LE(4u, Remaining());
+    const uint8_t b1 = DecodeUInt8() & 0x7f;  // Mask out the high order bit.
+    const uint8_t b2 = DecodeUInt8();
+    const uint8_t b3 = DecodeUInt8();
+    const uint8_t b4 = DecodeUInt8();
+    return b1 << 24 | b2 << 16 | b3 << 8 | b4;
+  }
+
+  uint32_t DecodeUInt32() {
+    DCHECK_LE(4u, Remaining());
+    const uint8_t b1 = DecodeUInt8();
+    const uint8_t b2 = DecodeUInt8();
+    const uint8_t b3 = DecodeUInt8();
+    const uint8_t b4 = DecodeUInt8();
+    return b1 << 24 | b2 << 16 | b3 << 8 | b4;
+  }
+
+  // SlowDecode* routines are used for decoding a multi-field structure when
+  // there may not be enough bytes in the buffer to decode the entirety of the
+  // structure.
+
+  // Read as much of an unsigned int field of an encoded structure as possible,
+  // keeping track via decode_offset of our position in the encoded structure.
+  // Returns true if the field has been fully decoded.
+  // |field_size| is the number of bytes of the encoding of the field (usually
+  // a compile time fixed value).
+  // |field_offset| is the offset of the first byte of the encoding of the field
+  // within the encoding of that structure (usually a compile time fixed value).
+  // |*decode_offset| is the offset of the byte to be decoded next.
+  // |*value| is the storage for the decoded value, and is used for storing
+  // partially decoded values; if some, but not all, bytes of the encoding are
+  // available then this method will return having stored the decoded bytes into
+  // *value.
+  bool SlowDecodeUnsignedInt(uint32_t field_size,
+                             uint32_t field_offset,
+                             uint32_t* decode_offset,
+                             uint32_t* value);
+
+  // Like SlowDecodeUnsignedInt, but specifically for 8-bit unsigned integers.
+  // Obviously a byte can't be split (on our byte addressable machines), but
+  // a larger structure containing such a field might be.
+  bool SlowDecodeUInt8(uint32_t field_offset,
+                       uint32_t* decode_offset,
+                       uint8_t* value);
+
+  // Like SlowDecodeUnsignedInt, but specifically for 16-bit unsigned integers.
+  bool SlowDecodeUInt16(uint32_t field_offset,
+                        uint32_t* decode_offset,
+                        uint16_t* value);
+
+  // Like SlowDecodeUnsignedInt, but specifically for 24-bit unsigned integers.
+  bool SlowDecodeUInt24(uint32_t field_offset,
+                        uint32_t* decode_offset,
+                        uint32_t* value);
+
+  // Like SlowDecodeUnsignedInt, but specifically for 31-bit unsigned integers.
+  // (same definition as for DecodeUInt31).
+  bool SlowDecodeUInt31(uint32_t field_offset,
+                        uint32_t* decode_offset,
+                        uint32_t* value);
+
+  // Like SlowDecodeUnsignedInt, but specifically for 31-bit unsigned integers.
+  bool SlowDecodeUInt32(uint32_t field_offset,
+                        uint32_t* decode_offset,
+                        uint32_t* value);
+
+  // Decodes an enum value, where the size (in bytes) of the encoding must be
+  // stated explicitly. It is assumed that under the covers enums are really
+  // just integers, and that we can static_cast them to and from uint32.
+  template <typename E>
+  bool SlowDecodeEnum(uint32_t field_size,
+                      uint32_t field_offset,
+                      uint32_t* decode_offset,
+                      E* value) {
+    uint32_t tmp = static_cast<uint32_t>(*value);
+    const bool done =
+        SlowDecodeUnsignedInt(field_size, field_offset, decode_offset, &tmp);
+    *value = static_cast<E>(tmp);
+    DCHECK_EQ(tmp, static_cast<uint32_t>(*value));
+    return done;
+  }
+
+  // We assume the decode buffers will typically be modest in size (i.e. a few
+  // K).
+  // Let's make sure during testing that we don't go very high, with 32MB
+  // selected rather arbitrarily.
+  static constexpr size_t MaxDecodeBufferLength() { return 1 << 25; }
+
+ protected:
+#ifndef NDEBUG
+  // These are part of validating during tests that there is at most one
+  // DecodeBufferSubset instance at a time for any DecodeBuffer instance.
+  void set_subset_of_base(DecodeBuffer* base,
+                          const DecodeBufferSubset* subset) {
+    DCHECK_EQ(this, subset);
+    base->set_subset(subset);
+  }
+  void clear_subset_of_base(DecodeBuffer* base,
+                            const DecodeBufferSubset* subset) {
+    DCHECK_EQ(this, subset);
+    base->clear_subset(subset);
+  }
+#endif
+
+ private:
+#ifndef NDEBUG
+  void set_subset(const DecodeBufferSubset* subset) {
+    DCHECK(subset != nullptr);
+    DCHECK_EQ(subset_, nullptr) << "There is already a subset";
+    subset_ = subset;
+  }
+  void clear_subset(const DecodeBufferSubset* subset) {
+    DCHECK(subset != nullptr);
+    DCHECK_EQ(subset_, subset);
+    subset_ = nullptr;
+  }
+#endif
+
+  // Prevent heap allocation of DecodeBuffer.
+  static void* operator new(size_t s);
+  static void* operator new[](size_t s);
+  static void operator delete(void* p);
+  static void operator delete[](void* p);
+
+  const char* const buffer_;
+  const char* cursor_;
+  const char* const beyond_;
+  const DecodeBufferSubset* subset_ = nullptr;  // Used for DCHECKs.
+
+  DISALLOW_COPY_AND_ASSIGN(DecodeBuffer);
+};
+
+// DecodeBufferSubset is used when decoding a known sized chunk of data, which
+// starts at base->cursor(), and continues for subset_len, which may be
+// entirely in |base|, or may extend beyond it (hence the MinLengthRemaining
+// in the constructor).
+// There are two benefits to using DecodeBufferSubset: it ensures that the
+// cursor of |base| is advanced when the subset's destructor runs, and it
+// ensures that the consumer of the subset can't go beyond the subset which
+// it is intended to decode.
+// There must be only a single DecodeBufferSubset at a time for a base
+// DecodeBuffer, though they can be nested (i.e. a DecodeBufferSubset's
+// base may itself be a DecodeBufferSubset). This avoids the AdvanceCursor
+// being called erroneously.
+class DecodeBufferSubset : public DecodeBuffer {
+ public:
+  DecodeBufferSubset(DecodeBuffer* base, size_t subset_len)
+      : DecodeBuffer(base->cursor(), base->MinLengthRemaining(subset_len)),
+#ifndef NDEBUG
+        start_base_offset_(base->Offset()),
+        max_base_offset_(start_base_offset_ + FullSize()),
+#endif
+        base_buffer_(base) {
+#ifndef NDEBUG
+    DCHECK_LE(max_base_offset_, base->FullSize());
+    set_subset_of_base(base_buffer_, this);
+#endif
+  }
+
+  ~DecodeBufferSubset() {
+    size_t offset = Offset();
+#ifndef NDEBUG
+    clear_subset_of_base(base_buffer_, this);
+    DCHECK_LE(Offset(), FullSize());
+    DCHECK_EQ(start_base_offset_, base_buffer_->Offset())
+        << "The base buffer was modified";
+    DCHECK_LE(offset, FullSize());
+    DCHECK_LE(start_base_offset_ + offset, base_buffer_->FullSize());
+#endif
+    base_buffer_->AdvanceCursor(offset);
+#ifndef NDEBUG
+    DCHECK_GE(max_base_offset_, base_buffer_->Offset());
+#endif
+  }
+
+ private:
+#ifndef NDEBUG
+  const size_t start_base_offset_;  // Used for DCHECKs.
+  const size_t max_base_offset_;    // Used for DCHECKs.
+#endif
+  DecodeBuffer* const base_buffer_;
+
+  DISALLOW_COPY_AND_ASSIGN(DecodeBufferSubset);
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_DECODE_BUFFER_H_
diff --git a/net/http2/decoder/decode_buffer_test.cc b/net/http2/decoder/decode_buffer_test.cc
new file mode 100644
index 0000000..35a9fbb
--- /dev/null
+++ b/net/http2/decoder/decode_buffer_test.cc
@@ -0,0 +1,406 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/decode_buffer.h"
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/strings/string_piece.h"
+#include "net/http2/tools/http2_random.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::StringPiece;
+using std::string;
+
+namespace net {
+namespace test {
+
+enum class TestEnumClass32 {
+  kValue1 = 1,
+  kValue99 = 99,
+  kValue1M = 1000000,
+};
+
+enum class TestEnumClass8 {
+  kValue1 = 1,
+  kValue2 = 1,
+  kValue99 = 99,
+  kValue255 = 255,
+};
+
+enum TestEnum8 {
+  kMaskLo = 0x01,
+  kMaskHi = 0x80,
+};
+
+struct TestStruct {
+  uint8_t f1;
+  uint16_t f2;
+  uint32_t f3;  // Decoded as a uint24
+  uint32_t f4;
+  uint32_t f5;  // Decoded as if uint31
+  TestEnumClass32 f6;
+  TestEnumClass8 f7;
+  TestEnum8 f8;
+};
+
+const size_t kF1Offset = 0;
+const size_t kF2Offset = 1;
+const size_t kF3Offset = 3;
+const size_t kF4Offset = 6;
+const size_t kF5Offset = 10;
+const size_t kF6Offset = 14;
+const size_t kF7Offset = 18;
+const size_t kF8Offset = 19;
+
+class DecodeBufferTest : public ::testing::Test {
+ public:
+  DecodeBufferTest() {}
+
+ protected:
+  // Double checks the call fn(f).
+  template <typename T>
+  bool SlowDecodeField(DecodeBuffer* b,
+                       size_t field_size,
+                       size_t field_offset,
+                       const base::Callback<bool(DecodeBuffer*)>& fn,
+                       T* f) {
+    VLOG(2) << "Remaining: " << b->Remaining();
+    VLOG(2) << "field_size: " << field_size;
+    VLOG(2) << "field_offset: " << field_offset;
+    VLOG(2) << "decode_offset_: " << decode_offset_;
+    EXPECT_GE(decode_offset_, field_offset);
+    bool had_data = b->HasData();
+    VLOG(2) << "had_data: " << had_data;
+    uint32_t old = static_cast<uint32_t>(*f);
+    VLOG(2) << "old: " << old;
+    size_t old_decode_offset = decode_offset_;
+    bool done = fn.Run(b);
+    VLOG(2) << "done: " << done;
+    if (old_decode_offset == decode_offset_) {
+      // Didn't do any decoding (may have no input, or may have already
+      // decoded this field).
+      if (done) {
+        EXPECT_LE(field_offset + field_size, decode_offset_);
+        // Shouldn't have modified already decoded field.
+        EXPECT_EQ(old, static_cast<uint32_t>(*f));
+      } else {
+        EXPECT_TRUE(!had_data);
+      }
+    } else {
+      // Did some decoding.
+      EXPECT_TRUE(had_data);
+      EXPECT_LT(old_decode_offset, decode_offset_);
+      if (done) {
+        EXPECT_EQ(field_offset + field_size, decode_offset_);
+      } else {
+        EXPECT_GT(field_offset + field_size, decode_offset_);
+      }
+    }
+    VLOG(2) << "---------------------------------------";
+    return done;
+  }
+
+  bool decode_f1(TestStruct* p, DecodeBuffer* db) {
+    return db->SlowDecodeUInt8(kF1Offset, &decode_offset_, &p->f1);
+  }
+  bool decode_f2(TestStruct* p, DecodeBuffer* db) {
+    return db->SlowDecodeUInt16(kF2Offset, &decode_offset_, &p->f2);
+  }
+  bool decode_f3(TestStruct* p, DecodeBuffer* db) {
+    return db->SlowDecodeUInt24(kF3Offset, &decode_offset_, &p->f3);
+  }
+  bool decode_f4(TestStruct* p, DecodeBuffer* db) {
+    return db->SlowDecodeUInt32(kF4Offset, &decode_offset_, &p->f4);
+  }
+  bool decode_f5(TestStruct* p, DecodeBuffer* db) {
+    return db->SlowDecodeUInt31(kF5Offset, &decode_offset_, &p->f5);
+  }
+  bool decode_f6(TestStruct* p, DecodeBuffer* db) {
+    return db->SlowDecodeEnum(4, kF6Offset, &decode_offset_, &p->f6);
+  }
+  bool decode_f7(TestStruct* p, DecodeBuffer* db) {
+    return db->SlowDecodeEnum(1, kF7Offset, &decode_offset_, &p->f7);
+  }
+  bool decode_f8(TestStruct* p, DecodeBuffer* db) {
+    return db->SlowDecodeEnum(1, kF8Offset, &decode_offset_, &p->f8);
+  }
+
+  void SlowDecodeTestStruct(StringPiece input, TestStruct* p) {
+    VLOG(2) << "############################################################";
+    EXPECT_LE(10u, input.size());
+    decode_offset_ = 0;
+    while (input.size() > 0) {
+      size_t size = input.size();
+      // Sometimes check that zero length input is OK.
+      auto r = random_.Next();
+      if (r % 100 == 0) {
+        size = 0;
+      } else if (size > 1) {
+        auto r = random_.Next();
+        size = (r % size) + 1;
+      }
+      VLOG(2) << "================= input size " << size;
+      DecodeBuffer b(input.data(), size);
+      size_t old_decode_offset = decode_offset_;
+      if (SlowDecodeField(&b, 1, kF1Offset,
+                          base::Bind(&DecodeBufferTest::decode_f1,
+                                     base::Unretained(this), p),
+                          &p->f1) &&
+          SlowDecodeField(&b, 2, kF2Offset,
+                          base::Bind(&DecodeBufferTest::decode_f2,
+                                     base::Unretained(this), p),
+                          &p->f2) &&
+          SlowDecodeField(&b, 3, kF3Offset,
+                          base::Bind(&DecodeBufferTest::decode_f3,
+                                     base::Unretained(this), p),
+                          &p->f3) &&
+          SlowDecodeField(&b, 4, kF4Offset,
+                          base::Bind(&DecodeBufferTest::decode_f4,
+                                     base::Unretained(this), p),
+                          &p->f4) &&
+          SlowDecodeField(&b, 4, kF5Offset,
+                          base::Bind(&DecodeBufferTest::decode_f5,
+                                     base::Unretained(this), p),
+                          &p->f5) &&
+          SlowDecodeField(&b, 4, kF6Offset,
+                          base::Bind(&DecodeBufferTest::decode_f6,
+                                     base::Unretained(this), p),
+                          &p->f6) &&
+          SlowDecodeField(&b, 1, kF7Offset,
+                          base::Bind(&DecodeBufferTest::decode_f7,
+                                     base::Unretained(this), p),
+                          &p->f7) &&
+          SlowDecodeField(&b, 1, kF8Offset,
+                          base::Bind(&DecodeBufferTest::decode_f8,
+                                     base::Unretained(this), p),
+                          &p->f8)) {
+        EXPECT_TRUE(b.Empty());
+        EXPECT_EQ(size, input.size());
+        EXPECT_EQ(input.size(), b.Offset());  // All input consumed.
+        return;
+      }
+      EXPECT_EQ(old_decode_offset + size, decode_offset_);
+      EXPECT_TRUE(b.Empty());
+      EXPECT_EQ(size, b.Offset());    // All input consumed.
+      EXPECT_LT(size, input.size());  // More remains.
+      input = StringPiece(input.data() + size, input.size() - size);
+    }
+    ADD_FAILURE() << "Ran out of input! decode_offset_ = " << decode_offset_;
+  }
+
+  Http2Random random_;
+  uint32_t decode_offset_;
+};
+
+TEST_F(DecodeBufferTest, DecodesFixedInts) {
+  const char data[] = "\x01\x12\x23\x34\x45\x56\x67\x78\x89\x9a";
+  DecodeBuffer b1(data, strlen(data));
+  EXPECT_EQ(1, b1.DecodeUInt8());
+  EXPECT_EQ(0x1223u, b1.DecodeUInt16());
+  EXPECT_EQ(0x344556u, b1.DecodeUInt24());
+  EXPECT_EQ(0x6778899Au, b1.DecodeUInt32());
+
+  DecodeBuffer b2(data, strlen(data));
+  uint8_t b;
+  decode_offset_ = 0;
+  EXPECT_TRUE(b2.SlowDecodeUInt8(0, &decode_offset_, &b));
+  EXPECT_EQ(1, b);
+  uint16_t s;
+  decode_offset_ = 0;
+  EXPECT_TRUE(b2.SlowDecodeUInt16(0, &decode_offset_, &s));
+  EXPECT_EQ(0x1223, s);
+  uint32_t i;
+  decode_offset_ = 0;
+  EXPECT_TRUE(b2.SlowDecodeUInt24(0, &decode_offset_, &i));
+  //  EXPECT_EQ(0x344556, b1.DecodeUInt24());
+  //  EXPECT_EQ(0x6778899a, b1.DecodeUInt32());
+}
+
+// Decode the structure many times, where we'll pass different partitions
+// into DecodeSlowly.
+TEST_F(DecodeBufferTest, SlowDecodeTestStruct) {
+  // clang-format off
+  const char data[] = {
+    0x12u,                       // f1
+    0x23u, 0x34u,                // f2
+    0x45u, 0x56u, 0x67u,         // f3
+    0x78u, 0x89u, 0x9au, 0xabu,  // f4
+    0xfeu, 0xedu, 0xdcu, 0xcbu,  // f5 (high-bit will be cleared.)
+    0x00u, 0x0fu, 0x42u, 0x40u,  // f6 (kValue1M)
+    0x63u,                       // f7 (kValue99)
+    0x81u,                       // f8 (kMaskLo | kMaskHi)
+  };
+  // clang-format on
+  StringPiece input(data, sizeof data);
+  for (int i = 0; i < 200; ++i) {
+    TestStruct ts;
+    // Init the struct to random garbage.
+    ts.f1 = random_.Rand8();
+    ts.f2 = random_.Rand16();
+    ts.f3 = random_.Rand32();
+    ts.f4 = random_.Rand32();
+    // Ensure high-bit is set.
+    ts.f5 = 0x80000000 | random_.Rand32();  // Ensure high-bit is set.
+    ts.f6 = static_cast<TestEnumClass32>(random_.Rand32());
+    ts.f7 = static_cast<TestEnumClass8>(random_.Rand8());
+    ts.f8 = static_cast<TestEnum8>(random_.Rand8());
+    SlowDecodeTestStruct(input, &ts);
+    ASSERT_EQ(0x12u, ts.f1);
+    ASSERT_EQ(0x2334u, ts.f2);
+    ASSERT_EQ(0x455667u, ts.f3);
+    ASSERT_EQ(0x78899AABu, ts.f4);
+    ASSERT_EQ(0x7EEDDCCBu, ts.f5);
+    ASSERT_EQ(TestEnumClass32::kValue1M, ts.f6);
+    ASSERT_EQ(TestEnumClass8::kValue99, ts.f7);
+    ASSERT_EQ(kMaskLo | kMaskHi, ts.f8);
+  }
+}
+
+// Make sure that DecodeBuffer is not copying input, just pointing into
+// provided input buffer.
+TEST_F(DecodeBufferTest, HasNotCopiedInput) {
+  const char data[] = "ab";
+  DecodeBuffer b1(data, 2);
+
+  EXPECT_EQ(2u, b1.Remaining());
+  EXPECT_EQ(0u, b1.Offset());
+  EXPECT_FALSE(b1.Empty());
+  EXPECT_EQ(data, b1.cursor());  // cursor points to input buffer
+  EXPECT_TRUE(b1.HasData());
+
+  b1.AdvanceCursor(1);
+
+  EXPECT_EQ(1u, b1.Remaining());
+  EXPECT_EQ(1u, b1.Offset());
+  EXPECT_FALSE(b1.Empty());
+  EXPECT_EQ(&data[1], b1.cursor());
+  EXPECT_TRUE(b1.HasData());
+
+  b1.AdvanceCursor(1);
+
+  EXPECT_EQ(0u, b1.Remaining());
+  EXPECT_EQ(2u, b1.Offset());
+  EXPECT_TRUE(b1.Empty());
+  EXPECT_EQ(&data[2], b1.cursor());
+  EXPECT_FALSE(b1.HasData());
+
+  DecodeBuffer b2(data, 0);
+
+  EXPECT_EQ(0u, b2.Remaining());
+  EXPECT_EQ(0u, b2.Offset());
+  EXPECT_TRUE(b2.Empty());
+  EXPECT_EQ(data, b2.cursor());
+  EXPECT_FALSE(b2.HasData());
+}
+
+// DecodeBufferSubset can't go beyond the end of the base buffer.
+TEST_F(DecodeBufferTest, DecodeBufferSubsetLimited) {
+  const char data[] = "abc";
+  DecodeBuffer base(data, 3);
+  base.AdvanceCursor(1);
+  DecodeBufferSubset subset(&base, 100);
+  EXPECT_EQ(2u, subset.FullSize());
+}
+
+// DecodeBufferSubset advances the cursor of its base upon destruction.
+TEST_F(DecodeBufferTest, DecodeBufferSubsetAdvancesCursor) {
+  const char data[] = "abc";
+  const size_t size = sizeof(data) - 1;
+  EXPECT_EQ(3u, size);
+  DecodeBuffer base(data, size);
+  {
+    // First no change to the cursor.
+    DecodeBufferSubset subset(&base, size + 100);
+    EXPECT_EQ(size, subset.FullSize());
+    EXPECT_EQ(base.FullSize(), subset.FullSize());
+    EXPECT_EQ(0u, subset.Offset());
+  }
+  EXPECT_EQ(0u, base.Offset());
+  EXPECT_EQ(size, base.Remaining());
+}
+
+// Make sure that DecodeBuffer ctor complains about bad args.
+#if GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
+TEST(DecodeBufferDeathTest, NonNullBufferRequired) {
+  EXPECT_DEBUG_DEATH({ DecodeBuffer b(nullptr, 3); }, "nullptr");
+}
+
+// Make sure that DecodeBuffer ctor complains about bad args.
+TEST(DecodeBufferDeathTest, ModestBufferSizeRequired) {
+  EXPECT_DEBUG_DEATH(
+      {
+        const char data[] = "abc";
+        size_t len = 0;
+        DecodeBuffer b(data, ~len);
+      },
+      "Max.*Length");
+}
+
+// Make sure that DecodeBuffer detects advance beyond end, in debug mode.
+TEST(DecodeBufferDeathTest, LimitedAdvance) {
+  {
+    // Advance right up to end is OK.
+    const char data[] = "abc";
+    DecodeBuffer b(data, 3);
+    b.AdvanceCursor(3);  // OK
+    EXPECT_TRUE(b.Empty());
+  }
+  EXPECT_DEBUG_DEATH(
+      {
+        // Going beyond is not OK.
+        const char data[] = "abc";
+        DecodeBuffer b(data, 3);
+        b.AdvanceCursor(4);
+      },
+      "4 vs. 3");
+}
+
+// Make sure that DecodeBuffer detects decode beyond end, in debug mode.
+TEST(DecodeBufferDeathTest, DecodeUInt8PastEnd) {
+  const char data[] = {0x12, 0x23};
+  DecodeBuffer b(data, sizeof data);
+  EXPECT_EQ(2u, b.FullSize());
+  EXPECT_EQ(0x1223, b.DecodeUInt16());
+  EXPECT_DEBUG_DEATH({ b.DecodeUInt8(); }, "1 vs. 0");
+}
+
+// Make sure that DecodeBuffer detects decode beyond end, in debug mode.
+TEST(DecodeBufferDeathTest, DecodeUInt16OverEnd) {
+  const char data[] = {0x12, 0x23, 0x34};
+  DecodeBuffer b(data, sizeof data);
+  EXPECT_EQ(3u, b.FullSize());
+  EXPECT_EQ(0x1223, b.DecodeUInt16());
+  EXPECT_DEBUG_DEATH({ b.DecodeUInt16(); }, "2 vs. 1");
+}
+
+// Make sure that DecodeBuffer doesn't agree with having two subsets.
+TEST(DecodeBufferSubsetDeathTest, TwoSubsets) {
+  const char data[] = "abc";
+  DecodeBuffer base(data, 3);
+  DecodeBufferSubset subset1(&base, 1);
+  EXPECT_DEBUG_DEATH({ DecodeBufferSubset subset2(&base, 1); },
+                     "There is already a subset");
+}
+
+// Make sure that DecodeBufferSubset notices when the base's cursor has moved.
+TEST(DecodeBufferSubsetDeathTest, BaseCursorAdvanced) {
+  const char data[] = "abc";
+  DecodeBuffer base(data, 3);
+  base.AdvanceCursor(1);
+  EXPECT_DEBUG_DEATH(
+      {
+        DecodeBufferSubset subset1(&base, 2);
+        base.AdvanceCursor(1);
+      },
+      "Access via subset only when present");
+}
+#endif  // GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
+
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/decoder/decode_http2_structures.cc b/net/http2/decoder/decode_http2_structures.cc
new file mode 100644
index 0000000..cd419e3
--- /dev/null
+++ b/net/http2/decoder/decode_http2_structures.cc
@@ -0,0 +1,355 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/decode_http2_structures.h"
+
+#include <string.h>
+
+#include "base/logging.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/http2_constants.h"
+
+namespace net {
+
+// Http2FrameHeader decoding:
+
+void DoDecode(Http2FrameHeader* out, DecodeBuffer* b) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  DCHECK_LE(Http2FrameHeader::EncodedSize(), b->Remaining());
+  out->payload_length = b->DecodeUInt24();
+  out->type = static_cast<Http2FrameType>(b->DecodeUInt8());
+  out->flags = static_cast<Http2FrameFlag>(b->DecodeUInt8());
+  out->stream_id = b->DecodeUInt31();
+}
+
+bool MaybeDecode(Http2FrameHeader* out, DecodeBuffer* b) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  if (b->Remaining() >= Http2FrameHeader::EncodedSize()) {
+    DoDecode(out, b);
+    return true;
+  }
+  return false;
+}
+
+bool SlowDecode(Http2FrameHeader* out, DecodeBuffer* b, uint32_t* offset) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  DCHECK_NE(nullptr, offset);
+  DCHECK_GT(Http2FrameHeader::EncodedSize(), *offset);
+  if (b->SlowDecodeUInt24(0 /* field_offset */, offset, &out->payload_length) &&
+      b->SlowDecodeEnum(1 /* field_size */, 3 /* field_offset */, offset,
+                        &out->type) &&
+      b->SlowDecodeEnum(1 /* field_size */, 4 /* field_offset */, offset,
+                        &out->flags) &&
+      b->SlowDecodeUInt31(5 /* field_offset */, offset, &out->stream_id)) {
+    DCHECK_EQ(Http2FrameHeader::EncodedSize(), *offset);
+    return true;
+  }
+  DCHECK_GT(Http2FrameHeader::EncodedSize(), *offset);
+  return false;
+}
+
+// Http2PriorityFields decoding:
+
+void DoDecode(Http2PriorityFields* out, DecodeBuffer* b) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  DCHECK_LE(Http2PriorityFields::EncodedSize(), b->Remaining());
+  uint32_t stream_id_and_flag = b->DecodeUInt32();
+  out->stream_dependency = stream_id_and_flag & StreamIdMask();
+  if (out->stream_dependency == stream_id_and_flag) {
+    out->is_exclusive = false;
+  } else {
+    out->is_exclusive = true;
+  }
+  // Note that chars are automatically promoted to ints during arithmetic,
+  // so 255 + 1 doesn't end up as zero.
+  out->weight = b->DecodeUInt8() + 1;
+}
+
+bool MaybeDecode(Http2PriorityFields* out, DecodeBuffer* b) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  if (b->Remaining() >= Http2PriorityFields::EncodedSize()) {
+    DoDecode(out, b);
+    return true;
+  }
+  return false;
+}
+
+bool SlowDecode(Http2PriorityFields* out, DecodeBuffer* b, uint32_t* offset) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  DCHECK_NE(nullptr, offset);
+  DCHECK_GT(Http2PriorityFields::EncodedSize(), *offset);
+  const uint32_t start_offset = *offset;
+  if (b->SlowDecodeUInt32(0 /* field_offset */, offset,
+                          &out->stream_dependency) &&
+      b->SlowDecodeUnsignedInt(1,  // field_size
+                               4,  // field_offset
+                               offset, &out->weight)) {
+    DCHECK_EQ(Http2PriorityFields::EncodedSize(), *offset);
+    if (start_offset < *offset) {
+      // First time here. Extract is_exclusive from stream_dependency.
+      const uint32_t stream_id_only = out->stream_dependency & StreamIdMask();
+      if (out->stream_dependency != stream_id_only) {
+        out->stream_dependency = stream_id_only;
+        out->is_exclusive = true;
+      } else {
+        out->is_exclusive = false;
+      }
+      // Need to add one to the weight field because the encoding is 0-255, but
+      // interpreted as 1-256.
+      ++(out->weight);
+    }
+    return true;
+  }
+  DCHECK_GT(Http2PriorityFields::EncodedSize(), *offset);
+  return false;
+}
+
+// Http2RstStreamFields decoding:
+
+void DoDecode(Http2RstStreamFields* out, DecodeBuffer* b) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  DCHECK_LE(Http2RstStreamFields::EncodedSize(), b->Remaining());
+  out->error_code = static_cast<Http2ErrorCode>(b->DecodeUInt32());
+}
+
+bool MaybeDecode(Http2RstStreamFields* out, DecodeBuffer* b) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  if (b->Remaining() >= Http2RstStreamFields::EncodedSize()) {
+    DoDecode(out, b);
+    return true;
+  }
+  return false;
+}
+
+bool SlowDecode(Http2RstStreamFields* out, DecodeBuffer* b, uint32_t* offset) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  DCHECK_NE(nullptr, offset);
+  DCHECK_GT(Http2RstStreamFields::EncodedSize(), *offset);
+
+  if (b->SlowDecodeEnum(4 /* field_size */, 0 /* field_offset */, offset,
+                        &out->error_code)) {
+    DCHECK_EQ(Http2RstStreamFields::EncodedSize(), *offset);
+    return true;
+  }
+  DCHECK_GT(Http2RstStreamFields::EncodedSize(), *offset);
+  return false;
+}
+
+// Http2SettingFields decoding:
+
+void DoDecode(Http2SettingFields* out, DecodeBuffer* b) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  DCHECK_LE(Http2SettingFields::EncodedSize(), b->Remaining());
+  out->parameter = static_cast<Http2SettingsParameter>(b->DecodeUInt16());
+  out->value = b->DecodeUInt32();
+}
+
+bool MaybeDecode(Http2SettingFields* out, DecodeBuffer* b) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  if (b->Remaining() >= Http2SettingFields::EncodedSize()) {
+    DoDecode(out, b);
+    return true;
+  }
+  return false;
+}
+
+bool SlowDecode(Http2SettingFields* out, DecodeBuffer* b, uint32_t* offset) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  DCHECK_NE(nullptr, offset);
+  DCHECK_LT(*offset, Http2SettingFields::EncodedSize());
+
+  if (b->SlowDecodeEnum(2 /* field_size */, 0 /* field_offset */, offset,
+                        &out->parameter) &&
+      b->SlowDecodeUInt32(2 /* field_offset */, offset, &out->value)) {
+    DCHECK_EQ(Http2SettingFields::EncodedSize(), *offset);
+    return true;
+  }
+  DCHECK_LT(*offset, Http2SettingFields::EncodedSize());
+  return false;
+}
+
+// Http2PushPromiseFields decoding:
+
+void DoDecode(Http2PushPromiseFields* out, DecodeBuffer* b) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  DCHECK_LE(Http2PushPromiseFields::EncodedSize(), b->Remaining());
+  out->promised_stream_id = b->DecodeUInt31();
+}
+
+bool MaybeDecode(Http2PushPromiseFields* out, DecodeBuffer* b) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  if (b->Remaining() >= Http2PushPromiseFields::EncodedSize()) {
+    DoDecode(out, b);
+    return true;
+  }
+  return false;
+}
+
+bool SlowDecode(Http2PushPromiseFields* out,
+                DecodeBuffer* b,
+                uint32_t* offset) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  DCHECK_NE(nullptr, offset);
+  DCHECK_LT(*offset, Http2PushPromiseFields::EncodedSize());
+  if (b->SlowDecodeUInt31(0 /* field_offset */, offset,
+                          &out->promised_stream_id)) {
+    DCHECK_EQ(Http2PushPromiseFields::EncodedSize(), *offset);
+    return true;
+  }
+  DCHECK_LT(*offset, Http2PushPromiseFields::EncodedSize());
+  return false;
+}
+
+// Http2PingFields decoding:
+
+void DoDecode(Http2PingFields* out, DecodeBuffer* b) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  DCHECK_LE(Http2PingFields::EncodedSize(), b->Remaining());
+  memcpy(out->opaque_data, b->cursor(), Http2PingFields::EncodedSize());
+  b->AdvanceCursor(Http2PingFields::EncodedSize());
+}
+
+bool MaybeDecode(Http2PingFields* out, DecodeBuffer* b) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  if (b->Remaining() >= Http2PingFields::EncodedSize()) {
+    DoDecode(out, b);
+    return true;
+  }
+  return false;
+}
+
+bool SlowDecode(Http2PingFields* out, DecodeBuffer* b, uint32_t* offset) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  DCHECK_NE(nullptr, offset);
+  DCHECK_LT(*offset, Http2PingFields::EncodedSize());
+  while (*offset < Http2PingFields::EncodedSize()) {
+    if (b->Empty()) {
+      return false;
+    }
+    out->opaque_data[(*offset)++] = b->DecodeUInt8();
+  }
+  return true;
+}
+
+// Http2GoAwayFields decoding:
+
+void DoDecode(Http2GoAwayFields* out, DecodeBuffer* b) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  DCHECK_LE(Http2GoAwayFields::EncodedSize(), b->Remaining());
+  out->last_stream_id = b->DecodeUInt31();
+  out->error_code = static_cast<Http2ErrorCode>(b->DecodeUInt32());
+}
+
+bool MaybeDecode(Http2GoAwayFields* out, DecodeBuffer* b) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  if (b->Remaining() >= Http2GoAwayFields::EncodedSize()) {
+    DoDecode(out, b);
+    return true;
+  }
+  return false;
+}
+
+bool SlowDecode(Http2GoAwayFields* out, DecodeBuffer* b, uint32_t* offset) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  DCHECK_NE(nullptr, offset);
+  DCHECK_LT(*offset, Http2GoAwayFields::EncodedSize());
+  if (b->SlowDecodeUInt31(0 /* field_offset */, offset, &out->last_stream_id) &&
+      b->SlowDecodeEnum(4 /* field_size */, 4 /* field_offset */, offset,
+                        &out->error_code)) {
+    DCHECK_EQ(Http2GoAwayFields::EncodedSize(), *offset);
+    return true;
+  }
+  DCHECK_LT(*offset, Http2GoAwayFields::EncodedSize());
+  return false;
+}
+
+// Http2WindowUpdateFields decoding:
+
+void DoDecode(Http2WindowUpdateFields* out, DecodeBuffer* b) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  DCHECK_LE(Http2WindowUpdateFields::EncodedSize(), b->Remaining());
+  out->window_size_increment = b->DecodeUInt31();
+}
+
+bool MaybeDecode(Http2WindowUpdateFields* out, DecodeBuffer* b) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  if (b->Remaining() >= Http2WindowUpdateFields::EncodedSize()) {
+    DoDecode(out, b);
+    return true;
+  }
+  return false;
+}
+
+bool SlowDecode(Http2WindowUpdateFields* out,
+                DecodeBuffer* b,
+                uint32_t* offset) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  DCHECK_NE(nullptr, offset);
+  DCHECK_LT(*offset, Http2WindowUpdateFields::EncodedSize());
+  if (b->SlowDecodeUInt31(0 /* field_offset */, offset,
+                          &out->window_size_increment)) {
+    DCHECK_EQ(Http2WindowUpdateFields::EncodedSize(), *offset);
+    return true;
+  }
+  DCHECK_LT(*offset, Http2WindowUpdateFields::EncodedSize());
+  return false;
+}
+
+// Http2AltSvcFields decoding:
+
+void DoDecode(Http2AltSvcFields* out, DecodeBuffer* b) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  DCHECK_LE(Http2AltSvcFields::EncodedSize(), b->Remaining());
+  out->origin_length = b->DecodeUInt16();
+}
+
+bool MaybeDecode(Http2AltSvcFields* out, DecodeBuffer* b) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  if (b->Remaining() >= Http2AltSvcFields::EncodedSize()) {
+    DoDecode(out, b);
+    return true;
+  }
+  return false;
+}
+
+bool SlowDecode(Http2AltSvcFields* out, DecodeBuffer* b, uint32_t* offset) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  DCHECK_NE(nullptr, offset);
+  DCHECK_LT(*offset, Http2AltSvcFields::EncodedSize());
+  if (b->SlowDecodeUInt16(0 /* field_offset */, offset, &out->origin_length)) {
+    DCHECK_EQ(Http2AltSvcFields::EncodedSize(), *offset);
+    return true;
+  }
+  DCHECK_LT(*offset, Http2AltSvcFields::EncodedSize());
+  return false;
+}
+
+}  // namespace net
diff --git a/net/http2/decoder/decode_http2_structures.h b/net/http2/decoder/decode_http2_structures.h
new file mode 100644
index 0000000..7cee46f
--- /dev/null
+++ b/net/http2/decoder/decode_http2_structures.h
@@ -0,0 +1,94 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_DECODE_HTTP2_STRUCTURES_H_
+#define NET_HTTP2_DECODER_DECODE_HTTP2_STRUCTURES_H_
+
+// Provides functions for decoding the fixed size structures in the HTTP/2 spec.
+
+// TODO(jamessynge): Consider whether the value of the SlowDecode methods is
+// worth their complexity; in particular, dropping back to buffering at most
+// 9 bytes (the largest fixed size structure) may actually be more efficient
+// than using the SlowDecode methods, or at least worth the complexity
+// reduction.
+// See http2_structure_decoder.h et al for an experiment in removing all except
+// DoDecode.
+
+#include "net/base/net_export.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+
+// DoDecode(STRUCTURE* out, DecodeBuffer* b) decodes the structure from start
+// to end, advancing the cursor by STRUCTURE::EncodedSize(). The decoder buffer
+// must be large enough (i.e. b->Remaining() >= STRUCTURE::EncodedSize()).
+
+NET_EXPORT_PRIVATE void DoDecode(Http2FrameHeader* out, DecodeBuffer* b);
+NET_EXPORT_PRIVATE void DoDecode(Http2PriorityFields* out, DecodeBuffer* b);
+NET_EXPORT_PRIVATE void DoDecode(Http2RstStreamFields* out, DecodeBuffer* b);
+NET_EXPORT_PRIVATE void DoDecode(Http2SettingFields* out, DecodeBuffer* b);
+NET_EXPORT_PRIVATE void DoDecode(Http2PushPromiseFields* out, DecodeBuffer* b);
+NET_EXPORT_PRIVATE void DoDecode(Http2PingFields* out, DecodeBuffer* b);
+NET_EXPORT_PRIVATE void DoDecode(Http2GoAwayFields* out, DecodeBuffer* b);
+NET_EXPORT_PRIVATE void DoDecode(Http2WindowUpdateFields* out, DecodeBuffer* b);
+NET_EXPORT_PRIVATE void DoDecode(Http2AltSvcFields* out, DecodeBuffer* b);
+
+// MaybeDecode(STRUCTURE* out, DecodeBuffer* b) decodes the structure from
+// start to end if the decoder buffer is large enough, advancing the cursor
+// by STRUCTURE::EncodedSize(), then returns true.
+// If the decode buffer isn't large enough, does nothing and returns false.
+// The buffer is large enough if b->Remaining() >= STRUCTURE::EncodedSize().
+
+NET_EXPORT_PRIVATE bool MaybeDecode(Http2FrameHeader* out, DecodeBuffer* b);
+NET_EXPORT_PRIVATE bool MaybeDecode(Http2PriorityFields* out, DecodeBuffer* b);
+NET_EXPORT_PRIVATE bool MaybeDecode(Http2RstStreamFields* out, DecodeBuffer* b);
+NET_EXPORT_PRIVATE bool MaybeDecode(Http2SettingFields* out, DecodeBuffer* b);
+NET_EXPORT_PRIVATE bool MaybeDecode(Http2PushPromiseFields* out,
+                                    DecodeBuffer* b);
+NET_EXPORT_PRIVATE bool MaybeDecode(Http2PingFields* out, DecodeBuffer* b);
+NET_EXPORT_PRIVATE bool MaybeDecode(Http2GoAwayFields* out, DecodeBuffer* b);
+NET_EXPORT_PRIVATE bool MaybeDecode(Http2WindowUpdateFields* out,
+                                    DecodeBuffer* b);
+NET_EXPORT_PRIVATE bool MaybeDecode(Http2AltSvcFields* out, DecodeBuffer* b);
+
+// SlowDecode(STRUCTURE* out, DecodeBuffer* b, uint32_t* offset) provides
+// incremental decoding of a structure, supporting cases where the structure
+// is split across multiple input buffers. *offset represents the offset within
+// the encoding of the structure, in the range [0, STRUCTURE::EncodedSize()].
+// Returns true when it is able to completely decode the structure, false
+// before that. Updates *offset to record the progress decoding the structure;
+// if false is returned, then b->Remaining() == 0 when SlowDecode returns.
+
+NET_EXPORT_PRIVATE bool SlowDecode(Http2FrameHeader* out,
+                                   DecodeBuffer* b,
+                                   uint32_t* offset);
+NET_EXPORT_PRIVATE bool SlowDecode(Http2PriorityFields* out,
+                                   DecodeBuffer* b,
+                                   uint32_t* offset);
+NET_EXPORT_PRIVATE bool SlowDecode(Http2RstStreamFields* out,
+                                   DecodeBuffer* b,
+                                   uint32_t* offset);
+NET_EXPORT_PRIVATE bool SlowDecode(Http2SettingFields* out,
+                                   DecodeBuffer* b,
+                                   uint32_t* offset);
+NET_EXPORT_PRIVATE bool SlowDecode(Http2PushPromiseFields* out,
+                                   DecodeBuffer* b,
+                                   uint32_t* offset);
+NET_EXPORT_PRIVATE bool SlowDecode(Http2PingFields* out,
+                                   DecodeBuffer* b,
+                                   uint32_t* offset);
+NET_EXPORT_PRIVATE bool SlowDecode(Http2GoAwayFields* out,
+                                   DecodeBuffer* b,
+                                   uint32_t* offset);
+NET_EXPORT_PRIVATE bool SlowDecode(Http2WindowUpdateFields* out,
+                                   DecodeBuffer* b,
+                                   uint32_t* offset);
+NET_EXPORT_PRIVATE bool SlowDecode(Http2AltSvcFields* out,
+                                   DecodeBuffer* b,
+                                   uint32_t* offset);
+
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_DECODE_HTTP2_STRUCTURES_H_
diff --git a/net/http2/decoder/decode_http2_structures_test.cc b/net/http2/decoder/decode_http2_structures_test.cc
new file mode 100644
index 0000000..4b09e7a
--- /dev/null
+++ b/net/http2/decoder/decode_http2_structures_test.cc
@@ -0,0 +1,542 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/decode_http2_structures.h"
+
+// Tests decoding all of the fixed size HTTP/2 structures (i.e. those defined
+// in net/http2/http2_structures.h).
+
+// TODO(jamessynge): Combine tests of DoDecode, MaybeDecode, SlowDecode and
+// Http2StructureDecoder test using gUnit's support for tests parameterized
+// by type.
+
+#include <stddef.h>
+#include <string>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/logging.h"
+#include "base/strings/string_piece.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures_test_util.h"
+#include "net/http2/tools/http2_frame_builder.h"
+#include "net/http2/tools/random_decoder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::AssertionFailure;
+using ::testing::AssertionResult;
+using ::testing::AssertionSuccess;
+using base::StringPiece;
+using std::string;
+
+namespace net {
+namespace test {
+namespace {
+
+template <class S>
+string SerializeStructure(const S& s) {
+  Http2FrameBuilder fb;
+  fb.Append(s);
+  EXPECT_EQ(S::EncodedSize(), fb.size());
+  return fb.buffer();
+}
+
+template <class S>
+class StructureDecoderTest : public RandomDecoderTest {
+ protected:
+  typedef S Structure;
+
+  StructureDecoderTest() {
+    // IF the test adds more data after the encoded structure, stop as
+    // soon as the structure is decoded.
+    stop_decode_on_done_ = true;
+  }
+
+  // Reset the decoding to the start of the structure, and overwrite the
+  // current contents of |structure_|, in to which we'll decode the buffer.
+  DecodeStatus StartDecoding(DecodeBuffer* b) override {
+    decode_offset_ = 0;
+    Randomize(&structure_);
+    return ResumeDecoding(b);
+  }
+
+  DecodeStatus ResumeDecoding(DecodeBuffer* b) override {
+    // If we're at the start...
+    if (decode_offset_ == 0) {
+      const uint32_t start_offset = b->Offset();
+      const char* const start_cursor = b->cursor();
+      // ... attempt to decode the entire structure.
+      if (MaybeDecode(&structure_, b)) {
+        ++fast_decode_count_;
+        EXPECT_EQ(S::EncodedSize(), b->Offset() - start_offset);
+
+        if (!HasFailure()) {
+          // Success. Confirm that SlowDecode produces the same result.
+          DecodeBuffer b2(start_cursor, b->Offset() - start_offset);
+          S second;
+          Randomize(&second);
+          uint32_t second_offset = 0;
+          EXPECT_TRUE(SlowDecode(&second, &b2, &second_offset));
+          EXPECT_EQ(S::EncodedSize(), second_offset);
+          EXPECT_EQ(structure_, second);
+        }
+
+        // Test can't easily tell if MaybeDecode or SlowDecode is used, so
+        // update decode_offset_ as if SlowDecode had been used to completely
+        // decode.
+        decode_offset_ = S::EncodedSize();
+        return DecodeStatus::kDecodeDone;
+      }
+    }
+
+    // We didn't have enough in the first buffer to decode everything, so we'll
+    // reach here multiple times until we've completely decoded the structure.
+    if (SlowDecode(&structure_, b, &decode_offset_)) {
+      ++slow_decode_count_;
+      EXPECT_EQ(S::EncodedSize(), decode_offset_);
+      return DecodeStatus::kDecodeDone;
+    }
+
+    // Drained the input buffer, but not yet done.
+    EXPECT_TRUE(b->Empty());
+    EXPECT_GT(S::EncodedSize(), decode_offset_);
+
+    return DecodeStatus::kDecodeInProgress;
+  }
+
+  // Set the fields of |*p| to random values.
+  void Randomize(S* p) { ::net::test::Randomize(p, RandomPtr()); }
+
+  AssertionResult ValidatorForDecodeLeadingStructure(const S* expected,
+                                                     const DecodeBuffer& db,
+                                                     DecodeStatus status) {
+    if (expected != nullptr && *expected != structure_) {
+      return AssertionFailure()
+             << "Expected structs to be equal\nExpected: " << *expected
+             << "\n  Actual: " << structure_;
+    }
+    return AssertionSuccess();
+  }
+
+  // Fully decodes the Structure at the start of data, and confirms it matches
+  // *expected (if provided).
+  void DecodeLeadingStructure(const S* expected, StringPiece data) {
+    ASSERT_LE(S::EncodedSize(), data.size());
+    DecodeBuffer original(data);
+
+    // The validator is called after each of the several times that the input
+    // DecodeBuffer is decoded, each with a different segmentation of the input.
+    // Validate that structure_ matches the expected value, if provided.
+    Validator validator =
+        base::Bind(&StructureDecoderTest::ValidatorForDecodeLeadingStructure,
+                   base::Unretained(this), expected);
+
+    // First validate that decoding is done and that we've advanced the cursor
+    // the expected amount.
+    Validator wrapped_validator =
+        ValidateDoneAndOffset(S::EncodedSize(), validator);
+
+    // Decode several times, with several segmentations of the input buffer.
+    fast_decode_count_ = 0;
+    slow_decode_count_ = 0;
+    EXPECT_TRUE(DecodeAndValidateSeveralWays(
+        &original, false /*return_non_zero_on_first*/, wrapped_validator));
+
+    if (!HasFailure()) {
+      EXPECT_EQ(S::EncodedSize(), decode_offset_);
+      EXPECT_EQ(S::EncodedSize(), original.Offset());
+      EXPECT_LT(0u, fast_decode_count_);
+      EXPECT_LT(0u, slow_decode_count_);
+      if (expected != nullptr) {
+        DVLOG(1) << "DecodeLeadingStructure expected: " << *expected;
+        DVLOG(1) << "DecodeLeadingStructure   actual: " << structure_;
+        EXPECT_EQ(*expected, structure_);
+      }
+    }
+  }
+
+  template <size_t N>
+  void DecodeLeadingStructure(const char (&data)[N]) {
+    DecodeLeadingStructure(nullptr, StringPiece(data, N));
+  }
+
+  // Encode the structure |in_s| into bytes, then decode the bytes
+  // and validate that the decoder produced the same field values.
+  void EncodeThenDecode(const S& in_s) {
+    string bytes = SerializeStructure(in_s);
+    EXPECT_EQ(S::EncodedSize(), bytes.size());
+    DecodeLeadingStructure(&in_s, bytes);
+  }
+
+  // Generate
+  void TestDecodingRandomizedStructures(size_t count) {
+    for (size_t i = 0; i < count && !HasFailure(); ++i) {
+      Structure input;
+      Randomize(&input);
+      EncodeThenDecode(input);
+    }
+  }
+
+  uint32_t decode_offset_ = 0;
+  S structure_;
+  size_t fast_decode_count_ = 0;
+  size_t slow_decode_count_ = 0;
+};
+
+class FrameHeaderDecoderTest : public StructureDecoderTest<Http2FrameHeader> {};
+
+TEST_F(FrameHeaderDecoderTest, DecodesLiteral) {
+  {
+    // Realistic input.
+    const char kData[] = {
+        0x00, 0x00, 0x05,        // Payload length: 5
+        0x01,                    // Frame type: HEADERS
+        0x08,                    // Flags: PADDED
+        0x00, 0x00, 0x00, 0x01,  // Stream ID: 1
+        0x04,                    // Padding length: 4
+        0x00, 0x00, 0x00, 0x00,  // Padding bytes
+    };
+    DecodeLeadingStructure(kData);
+    if (!HasFailure()) {
+      EXPECT_EQ(5u, structure_.payload_length);
+      EXPECT_EQ(Http2FrameType::HEADERS, structure_.type);
+      EXPECT_EQ(Http2FrameFlag::FLAG_PADDED, structure_.flags);
+      EXPECT_EQ(1u, structure_.stream_id);
+    }
+  }
+  {
+    // Unlikely input.
+    const char kData[] = {
+        0xffu, 0xffu, 0xffu,         // Payload length: uint24 max
+        0xffu,                       // Frame type: Unknown
+        0xffu,                       // Flags: Unknown/All
+        0xffu, 0xffu, 0xffu, 0xffu,  // Stream ID: uint31 max, plus R-bit
+    };
+    DecodeLeadingStructure(kData);
+    if (!HasFailure()) {
+      EXPECT_EQ((1u << 24) - 1, structure_.payload_length);
+      EXPECT_EQ(static_cast<Http2FrameType>(255), structure_.type);
+      EXPECT_EQ(255, structure_.flags);
+      EXPECT_EQ(0x7FFFFFFFu, structure_.stream_id);
+    }
+  }
+}
+
+TEST_F(FrameHeaderDecoderTest, DecodesRandomized) {
+  TestDecodingRandomizedStructures(100);
+}
+
+//------------------------------------------------------------------------------
+
+class PriorityFieldsDecoderTest
+    : public StructureDecoderTest<Http2PriorityFields> {};
+
+TEST_F(PriorityFieldsDecoderTest, DecodesLiteral) {
+  {
+    const char kData[] = {
+        0x80u, 0x00, 0x00, 0x05,  // Exclusive (yes) and Dependency (5)
+        0xffu,                    // Weight: 256 (after adding 1)
+    };
+    DecodeLeadingStructure(kData);
+    if (!HasFailure()) {
+      EXPECT_EQ(5u, structure_.stream_dependency);
+      EXPECT_EQ(256u, structure_.weight);
+      EXPECT_EQ(true, structure_.is_exclusive);
+    }
+  }
+  {
+    const char kData[] = {
+        0x7f,  0xffu,
+        0xffu, 0xffu,  // Exclusive (no) and Dependency (0x7fffffff)
+        0x00,          // Weight: 1 (after adding 1)
+    };
+    DecodeLeadingStructure(kData);
+    if (!HasFailure()) {
+      EXPECT_EQ(StreamIdMask(), structure_.stream_dependency);
+      EXPECT_EQ(1u, structure_.weight);
+      EXPECT_FALSE(structure_.is_exclusive);
+    }
+  }
+}
+
+TEST_F(PriorityFieldsDecoderTest, DecodesRandomized) {
+  TestDecodingRandomizedStructures(100);
+}
+
+//------------------------------------------------------------------------------
+
+class RstStreamFieldsDecoderTest
+    : public StructureDecoderTest<Http2RstStreamFields> {};
+
+TEST_F(RstStreamFieldsDecoderTest, DecodesLiteral) {
+  {
+    const char kData[] = {
+        0x00, 0x00, 0x00, 0x01,  // Error: PROTOCOL_ERROR
+    };
+    DecodeLeadingStructure(kData);
+    if (!HasFailure()) {
+      EXPECT_TRUE(structure_.IsSupportedErrorCode());
+      EXPECT_EQ(Http2ErrorCode::PROTOCOL_ERROR, structure_.error_code);
+    }
+  }
+  {
+    const char kData[] = {
+        0xffu, 0xffu, 0xffu, 0xffu,  // Error: max uint32 (Unknown error code)
+    };
+    DecodeLeadingStructure(kData);
+    if (!HasFailure()) {
+      EXPECT_FALSE(structure_.IsSupportedErrorCode());
+      EXPECT_EQ(static_cast<Http2ErrorCode>(0xffffffff), structure_.error_code);
+    }
+  }
+}
+
+TEST_F(RstStreamFieldsDecoderTest, DecodesRandomized) {
+  TestDecodingRandomizedStructures(100);
+}
+
+//------------------------------------------------------------------------------
+
+class SettingFieldsDecoderTest
+    : public StructureDecoderTest<Http2SettingFields> {};
+
+TEST_F(SettingFieldsDecoderTest, DecodesLiteral) {
+  {
+    const char kData[] = {
+        0x00, 0x01,              // Setting: HEADER_TABLE_SIZE
+        0x00, 0x00, 0x40, 0x00,  // Value: 16K
+    };
+    DecodeLeadingStructure(kData);
+    if (!HasFailure()) {
+      EXPECT_TRUE(structure_.IsSupportedParameter());
+      EXPECT_EQ(Http2SettingsParameter::HEADER_TABLE_SIZE,
+                structure_.parameter);
+      EXPECT_EQ(1u << 14, structure_.value);
+    }
+  }
+  {
+    const char kData[] = {
+        0x00,  0x00,                 // Setting: Unknown (0)
+        0xffu, 0xffu, 0xffu, 0xffu,  // Value: max uint32
+    };
+    DecodeLeadingStructure(kData);
+    if (!HasFailure()) {
+      EXPECT_FALSE(structure_.IsSupportedParameter());
+      EXPECT_EQ(static_cast<Http2SettingsParameter>(0), structure_.parameter);
+    }
+  }
+}
+
+TEST_F(SettingFieldsDecoderTest, DecodesRandomized) {
+  TestDecodingRandomizedStructures(100);
+}
+
+//------------------------------------------------------------------------------
+
+class PushPromiseFieldsDecoderTest
+    : public StructureDecoderTest<Http2PushPromiseFields> {};
+
+TEST_F(PushPromiseFieldsDecoderTest, DecodesLiteral) {
+  {
+    const char kData[] = {
+        0x00, 0x01, 0x8au, 0x92u,  // Promised Stream ID: 101010
+    };
+    DecodeLeadingStructure(kData);
+    if (!HasFailure()) {
+      EXPECT_EQ(101010u, structure_.promised_stream_id);
+    }
+  }
+  {
+    // Promised stream id has R-bit (reserved for future use) set, which
+    // should be cleared by the decoder.
+    const char kData[] = {
+        0xffu, 0xffu, 0xffu, 0xffu,  // Promised Stream ID: max uint31 and R-bit
+    };
+    DecodeLeadingStructure(kData);
+    if (!HasFailure()) {
+      EXPECT_EQ(StreamIdMask(), structure_.promised_stream_id);
+    }
+  }
+}
+
+TEST_F(PushPromiseFieldsDecoderTest, DecodesRandomized) {
+  TestDecodingRandomizedStructures(100);
+}
+
+//------------------------------------------------------------------------------
+
+class PingFieldsDecoderTest : public StructureDecoderTest<Http2PingFields> {};
+
+TEST_F(PingFieldsDecoderTest, DecodesLiteral) {
+  {
+    // Each byte is different, so can detect if order changed.
+    const char kData[] = {
+        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    };
+    DecodeLeadingStructure(kData);
+    if (!HasFailure()) {
+      EXPECT_EQ(StringPiece(kData, 8), ToStringPiece(structure_.opaque_data));
+    }
+  }
+  {
+    // All zeros, detect problems handling NULs.
+    const char kData[] = {
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    };
+    DecodeLeadingStructure(kData);
+    if (!HasFailure()) {
+      EXPECT_EQ(StringPiece(kData, 8), ToStringPiece(structure_.opaque_data));
+    }
+  }
+  {
+    const char kData[] = {
+        0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu,
+    };
+    DecodeLeadingStructure(kData);
+    if (!HasFailure()) {
+      EXPECT_EQ(StringPiece(kData, 8), ToStringPiece(structure_.opaque_data));
+    }
+  }
+}
+
+TEST_F(PingFieldsDecoderTest, DecodesRandomized) {
+  TestDecodingRandomizedStructures(100);
+}
+
+//------------------------------------------------------------------------------
+
+class GoAwayFieldsDecoderTest : public StructureDecoderTest<Http2GoAwayFields> {
+};
+
+TEST_F(GoAwayFieldsDecoderTest, DecodesLiteral) {
+  {
+    const char kData[] = {
+        0x00, 0x00, 0x00, 0x00,  // Last Stream ID: 0
+        0x00, 0x00, 0x00, 0x00,  // Error: NO_ERROR (0)
+    };
+    DecodeLeadingStructure(kData);
+    if (!HasFailure()) {
+      EXPECT_EQ(0u, structure_.last_stream_id);
+      EXPECT_TRUE(structure_.IsSupportedErrorCode());
+      EXPECT_EQ(Http2ErrorCode::HTTP2_NO_ERROR, structure_.error_code);
+    }
+  }
+  {
+    const char kData[] = {
+        0x00, 0x00, 0x00, 0x01,  // Last Stream ID: 1
+        0x00, 0x00, 0x00, 0x0d,  // Error: HTTP_1_1_REQUIRED
+    };
+    DecodeLeadingStructure(kData);
+    if (!HasFailure()) {
+      EXPECT_EQ(1u, structure_.last_stream_id);
+      EXPECT_TRUE(structure_.IsSupportedErrorCode());
+      EXPECT_EQ(Http2ErrorCode::HTTP_1_1_REQUIRED, structure_.error_code);
+    }
+  }
+  {
+    const char kData[] = {
+        0xffu, 0xffu, 0xffu, 0xffu,  // Last Stream ID: max uint31 and R-bit
+        0xffu, 0xffu, 0xffu, 0xffu,  // Error: max uint32 (Unknown error code)
+    };
+    DecodeLeadingStructure(kData);
+    if (!HasFailure()) {
+      EXPECT_EQ(StreamIdMask(), structure_.last_stream_id);  // No high-bit.
+      EXPECT_FALSE(structure_.IsSupportedErrorCode());
+      EXPECT_EQ(static_cast<Http2ErrorCode>(0xffffffff), structure_.error_code);
+    }
+  }
+}
+
+TEST_F(GoAwayFieldsDecoderTest, DecodesRandomized) {
+  TestDecodingRandomizedStructures(100);
+}
+
+//------------------------------------------------------------------------------
+
+class WindowUpdateFieldsDecoderTest
+    : public StructureDecoderTest<Http2WindowUpdateFields> {};
+
+TEST_F(WindowUpdateFieldsDecoderTest, DecodesLiteral) {
+  {
+    const char kData[] = {
+        0x00, 0x01, 0x00, 0x00,  // Window Size Increment: 2 ^ 16
+    };
+    DecodeLeadingStructure(kData);
+    if (!HasFailure()) {
+      EXPECT_EQ(1u << 16, structure_.window_size_increment);
+    }
+  }
+  {
+    // Increment must be non-zero, but we need to be able to decode the invalid
+    // zero to detect it.
+    const char kData[] = {
+        0x00, 0x00, 0x00, 0x00,  // Window Size Increment: 0
+    };
+    DecodeLeadingStructure(kData);
+    if (!HasFailure()) {
+      EXPECT_EQ(0u, structure_.window_size_increment);
+    }
+  }
+  {
+    // Increment has R-bit (reserved for future use) set, which
+    // should be cleared by the decoder.
+    const char kData[] = {
+        0xffu, 0xffu, 0xffu,
+        0xffu,  // Window Size Increment: max uint31 and R-bit
+    };
+    DecodeLeadingStructure(kData);
+    if (!HasFailure()) {
+      EXPECT_EQ(StreamIdMask(), structure_.window_size_increment);
+    }
+  }
+}
+
+TEST_F(WindowUpdateFieldsDecoderTest, DecodesRandomized) {
+  TestDecodingRandomizedStructures(100);
+}
+
+//------------------------------------------------------------------------------
+
+class AltSvcFieldsDecoderTest : public StructureDecoderTest<Http2AltSvcFields> {
+};
+
+TEST_F(AltSvcFieldsDecoderTest, DecodesLiteral) {
+  {
+    const char kData[] = {
+        0x00, 0x00,  // Origin Length: 0
+    };
+    DecodeLeadingStructure(kData);
+    if (!HasFailure()) {
+      EXPECT_EQ(0, structure_.origin_length);
+    }
+  }
+  {
+    const char kData[] = {
+        0x00, 0x14,  // Origin Length: 20
+    };
+    DecodeLeadingStructure(kData);
+    if (!HasFailure()) {
+      EXPECT_EQ(20, structure_.origin_length);
+    }
+  }
+  {
+    const char kData[] = {
+        0xffu, 0xffu,  // Origin Length: uint16 max
+    };
+    DecodeLeadingStructure(kData);
+    if (!HasFailure()) {
+      EXPECT_EQ(65535, structure_.origin_length);
+    }
+  }
+}
+
+TEST_F(AltSvcFieldsDecoderTest, DecodesRandomized) {
+  TestDecodingRandomizedStructures(100);
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/decoder/decode_status.cc b/net/http2/decoder/decode_status.cc
new file mode 100644
index 0000000..99a1de0b5
--- /dev/null
+++ b/net/http2/decoder/decode_status.cc
@@ -0,0 +1,27 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/decode_status.h"
+
+#include "base/logging.h"
+
+namespace net {
+
+std::ostream& operator<<(std::ostream& out, DecodeStatus v) {
+  switch (v) {
+    case DecodeStatus::kDecodeDone:
+      return out << "DecodeDone";
+    case DecodeStatus::kDecodeInProgress:
+      return out << "DecodeInProgress";
+    case DecodeStatus::kDecodeError:
+      return out << "DecodeError";
+  }
+  // Since the value doesn't come over the wire, only a programming bug should
+  // result in reaching this point.
+  int unknown = static_cast<int>(v);
+  LOG(DFATAL) << "Unknown DecodeStatus " << unknown << std::hex << unknown;
+  return out << "UnknownDecodeStatus(" << unknown << ")";
+}
+
+}  // namespace net
diff --git a/net/http2/decoder/decode_status.h b/net/http2/decoder/decode_status.h
new file mode 100644
index 0000000..e5fbeeb
--- /dev/null
+++ b/net/http2/decoder/decode_status.h
@@ -0,0 +1,32 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_DECODE_STATUS_H_
+#define NET_HTTP2_DECODER_DECODE_STATUS_H_
+
+// Enum DecodeStatus is used to report the status of decoding of many
+// types of HTTP/2 and HPACK objects.
+
+#include <ostream>
+
+#include "net/base/net_export.h"
+
+namespace net {
+
+enum class DecodeStatus {
+  // Decoding is done.
+  kDecodeDone,
+
+  // Decoder needs more input to be able to make progress.
+  kDecodeInProgress,
+
+  // Decoding failed (e.g. HPACK variable length integer is too large, or
+  // an HTTP/2 frame has padding declared to be larger than the payload).
+  kDecodeError,
+};
+NET_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& out, DecodeStatus v);
+
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_DECODE_STATUS_H_
diff --git a/net/http2/decoder/frame_decoder_state.cc b/net/http2/decoder/frame_decoder_state.cc
new file mode 100644
index 0000000..e2b4406
--- /dev/null
+++ b/net/http2/decoder/frame_decoder_state.cc
@@ -0,0 +1,81 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/frame_decoder_state.h"
+
+namespace net {
+
+DecodeStatus FrameDecoderState::ReadPadLength(DecodeBuffer* db,
+                                              bool report_pad_length) {
+  DVLOG(2) << "ReadPadLength db->Remaining=" << db->Remaining()
+           << "; payload_length=" << frame_header().payload_length;
+  DCHECK(IsPaddable());
+  DCHECK(frame_header().IsPadded());
+
+  // Pad Length is always at the start of the frame, so remaining_payload_
+  // should equal payload_length at this point.
+  const uint32_t total_payload = frame_header().payload_length;
+  DCHECK_EQ(total_payload, remaining_payload_);
+  DCHECK_EQ(0u, remaining_padding_);
+
+  if (db->HasData()) {
+    const uint32_t pad_length = db->DecodeUInt8();
+    const uint32_t total_padding = pad_length + 1;
+    if (total_padding <= total_payload) {
+      remaining_padding_ = pad_length;
+      remaining_payload_ = total_payload - total_padding;
+      if (report_pad_length) {
+        listener()->OnPadLength(pad_length);
+      }
+      return DecodeStatus::kDecodeDone;
+    }
+    const uint32_t missing_length = total_padding - total_payload;
+    // To allow for the possibility of recovery, record the number of
+    // remaining bytes of the frame's payload (invalid though it is)
+    // in remaining_payload_.
+    remaining_payload_ = total_payload - 1;  // 1 for sizeof(Pad Length).
+    remaining_padding_ = 0;
+    listener()->OnPaddingTooLong(frame_header(), missing_length);
+    return DecodeStatus::kDecodeError;
+  }
+
+  if (total_payload == 0) {
+    remaining_payload_ = 0;
+    remaining_padding_ = 0;
+    listener()->OnPaddingTooLong(frame_header(), 1);
+    return DecodeStatus::kDecodeError;
+  }
+  // Need to wait for another buffer.
+  return DecodeStatus::kDecodeInProgress;
+}
+
+bool FrameDecoderState::SkipPadding(DecodeBuffer* db) {
+  DVLOG(2) << "SkipPadding remaining_padding_=" << remaining_padding_
+           << ", db->Remaining=" << db->Remaining()
+           << ", header: " << frame_header();
+  DCHECK_EQ(remaining_payload_, 0u);
+  DCHECK(IsPaddable()) << "header: " << frame_header();
+  DCHECK_GE(remaining_padding_, 0u);
+  DCHECK(remaining_padding_ == 0 || frame_header().IsPadded())
+      << "remaining_padding_=" << remaining_padding_
+      << ", header: " << frame_header();
+  const size_t avail = AvailablePadding(db);
+  if (avail > 0) {
+    listener()->OnPadding(db->cursor(), avail);
+    db->AdvanceCursor(avail);
+    remaining_padding_ -= avail;
+  }
+  return remaining_padding_ == 0;
+}
+
+DecodeStatus FrameDecoderState::ReportFrameSizeError() {
+  DVLOG(2) << "FrameDecoderState::ReportFrameSizeError: "
+           << " remaining_payload_=" << remaining_payload_
+           << "; remaining_padding_=" << remaining_padding_
+           << ", header: " << frame_header();
+  listener()->OnFrameSizeError(frame_header());
+  return DecodeStatus::kDecodeError;
+}
+
+}  // namespace net
diff --git a/net/http2/decoder/frame_decoder_state.h b/net/http2/decoder/frame_decoder_state.h
new file mode 100644
index 0000000..ddf64d1f
--- /dev/null
+++ b/net/http2/decoder/frame_decoder_state.h
@@ -0,0 +1,252 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_FRAME_DECODER_STATE_H_
+#define NET_HTTP2_DECODER_FRAME_DECODER_STATE_H_
+
+// FrameDecoderState provides state and behaviors in support of decoding
+// the common frame header and the payload of all frame types.
+// It is an input to all of the payload decoders.
+
+// TODO(jamessynge): Since FrameDecoderState has far more than state in it,
+// rename to FrameDecoderHelper, or similar.
+
+#include <stddef.h>
+
+#include "base/logging.h"
+#include "net/base/net_export.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/decoder/http2_structure_decoder.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+namespace test {
+class FrameDecoderStatePeer;
+}  // namespace test
+
+class NET_EXPORT_PRIVATE FrameDecoderState {
+ public:
+  FrameDecoderState() {}
+
+  // Sets the listener which the decoders should call as they decode HTTP/2
+  // frames. The listener can be changed at any time, which allows for replacing
+  // it with a no-op listener when an error is detected, either by the payload
+  // decoder (OnPaddingTooLong or OnFrameSizeError) or by the "real" listener.
+  // That in turn allows us to define Http2FrameDecoderListener such that all
+  // methods have return type void, with no direct way to indicate whether the
+  // decoder should stop, and to eliminate from the decoder all checks of the
+  // return value. Instead the listener/caller can simply replace the current
+  // listener with a no-op listener implementation.
+  // TODO(jamessynge): Make set_listener private as only Http2FrameDecoder
+  // and tests need to set it, so it doesn't need to be public.
+  void set_listener(Http2FrameDecoderListener* listener) {
+    listener_ = listener;
+  }
+  Http2FrameDecoderListener* listener() const { return listener_; }
+
+  // The most recently decoded frame header.
+  const Http2FrameHeader& frame_header() const { return frame_header_; }
+
+  // Decode a structure in the payload, adjusting remaining_payload_ to account
+  // for the consumed portion of the payload. Returns kDecodeDone when fully
+  // decoded, kDecodeError if it ran out of payload before decoding completed,
+  // and kDecodeInProgress if the decode buffer didn't have enough of the
+  // remaining payload.
+  template <class S>
+  DecodeStatus StartDecodingStructureInPayload(S* out, DecodeBuffer* db) {
+    DVLOG(2) << __func__ << "\n\tdb->Remaining=" << db->Remaining()
+             << "\n\tremaining_payload_=" << remaining_payload_
+             << "\n\tneed=" << S::EncodedSize();
+    DecodeStatus status =
+        structure_decoder_.Start(out, db, &remaining_payload_);
+    if (status != DecodeStatus::kDecodeError) {
+      return status;
+    }
+    DVLOG(2) << "StartDecodingStructureInPayload: detected frame size error";
+    return ReportFrameSizeError();
+  }
+
+  // Resume decoding of a structure that has been split across buffers,
+  // adjusting remaining_payload_ to account for the consumed portion of
+  // the payload. Returns values are as for StartDecodingStructureInPayload.
+  template <class S>
+  DecodeStatus ResumeDecodingStructureInPayload(S* out, DecodeBuffer* db) {
+    DVLOG(2) << __func__ << "\n\tdb->Remaining=" << db->Remaining()
+             << "\n\tremaining_payload_=" << remaining_payload_;
+    if (structure_decoder_.Resume(out, db, &remaining_payload_)) {
+      return DecodeStatus::kDecodeDone;
+    } else if (remaining_payload_ > 0) {
+      return DecodeStatus::kDecodeInProgress;
+    } else {
+      DVLOG(2) << "ResumeDecodingStructureInPayload: detected frame size error";
+      return ReportFrameSizeError();
+    }
+  }
+
+  // Initializes the two remaining* fields, which is needed if the frame's
+  // payload is split across buffers, or the decoder calls ReadPadLength or
+  // StartDecodingStructureInPayload, and of course the methods below which
+  // read those fields, as their names imply.
+  void InitializeRemainders() {
+    remaining_payload_ = frame_header().payload_length;
+    // Note that remaining_total_payload() relies on remaining_padding_ being
+    // zero for frames that have no padding.
+    remaining_padding_ = 0;
+  }
+
+  // Returns the number of bytes of the frame's payload that remain to be
+  // decoded, including any trailing padding. This method must only be called
+  // after the variables have been initialized, which in practice means once a
+  // payload decoder has called InitializeRemainders and/or ReadPadLength.
+  size_t remaining_total_payload() const {
+    DCHECK(IsPaddable() || remaining_padding_ == 0) << frame_header();
+    return remaining_payload_ + remaining_padding_;
+  }
+
+  // Returns the number of bytes of the frame's payload that remain to be
+  // decoded, excluding any trailing padding. This method must only be called
+  // after the variable has been initialized, which in practice means once a
+  // payload decoder has called InitializeRemainders; ReadPadLength will deduct
+  // the total number of padding bytes from remaining_payload_, including the
+  // size of the Pad Length field itself (1 byte).
+  size_t remaining_payload() const { return remaining_payload_; }
+
+  // Returns the number of bytes of the frame's payload that remain to be
+  // decoded, including any trailing padding. This method must only be called if
+  // the frame type allows padding, and after the variable has been initialized,
+  // which in practice means once a payload decoder has called
+  // InitializeRemainders and/or ReadPadLength.
+  size_t remaining_payload_and_padding() const {
+    DCHECK(IsPaddable()) << frame_header();
+    return remaining_payload_ + remaining_padding_;
+  }
+
+  // Returns the number of bytes of trailing padding after the payload that
+  // remain to be decoded. This method must only be called if the frame type
+  // allows padding, and after the variable has been initialized, which in
+  // practice means once a payload decoder has called InitializeRemainders,
+  // and isn't set to a non-zero value until ReadPadLength has been called.
+  uint32_t remaining_padding() const {
+    DCHECK(IsPaddable()) << frame_header();
+    return remaining_padding_;
+  }
+
+  // Returns the amount of trailing padding after the payload that remains to be
+  // decoded.
+  uint32_t remaining_padding_for_test() const { return remaining_padding_; }
+
+  // How many bytes of the remaining payload are in db?
+  size_t AvailablePayload(DecodeBuffer* db) const {
+    return db->MinLengthRemaining(remaining_payload_);
+  }
+
+  // How many bytes of the remaining payload and padding are in db?
+  // Call only for frames whose type is paddable.
+  size_t AvailablePayloadAndPadding(DecodeBuffer* db) const {
+    DCHECK(IsPaddable()) << frame_header();
+    return db->MinLengthRemaining(remaining_payload_ + remaining_padding_);
+  }
+
+  // How many bytes of the padding that have not yet been skipped are in db?
+  // Call only after remaining_padding_ has been set (for padded frames), or
+  // been cleared (for unpadded frames); and after all of the non-padding
+  // payload has been decoded.
+  size_t AvailablePadding(DecodeBuffer* db) const {
+    DCHECK(IsPaddable()) << frame_header();
+    DCHECK_EQ(remaining_payload_, 0u);
+    return db->MinLengthRemaining(remaining_padding_);
+  }
+
+  // Reduces remaining_payload_ by amount. To be called by a payload decoder
+  // after it has passed a variable length portion of the payload to the
+  // listener; remaining_payload_ will be automatically reduced when fixed
+  // size structures and padding, including the Pad Length field, are decoded.
+  void ConsumePayload(size_t amount) {
+    DCHECK_LE(amount, remaining_payload_);
+    remaining_payload_ -= amount;
+  }
+
+  // Reads the Pad Length field into remaining_padding_, and appropriately sets
+  // remaining_payload_. When present, the Pad Length field is always the first
+  // field in the payload, which this method relies on so that the caller need
+  // not set remaining_payload_ before calling this method.
+  // If report_pad_length is true, calls the listener's OnPadLength method when
+  // it decodes the Pad Length field.
+  // Returns kDecodeDone if the decode buffer was not empty (i.e. because the
+  // field is only a single byte long, it can always be decoded if the buffer is
+  // not empty).
+  // Returns kDecodeError if the buffer is empty because the frame has no
+  // payload (i.e. payload_length() == 0).
+  // Returns kDecodeInProgress if the buffer is empty but the frame has a
+  // payload.
+  DecodeStatus ReadPadLength(DecodeBuffer* db, bool report_pad_length);
+
+  // Skip the trailing padding bytes; only call once remaining_payload_==0.
+  // Returns true when the padding has been skipped.
+  // Does NOT check that the padding is all zeroes.
+  bool SkipPadding(DecodeBuffer* db);
+
+  // Calls the listener's OnFrameSizeError method and returns kDecodeError.
+  DecodeStatus ReportFrameSizeError();
+
+ private:
+  friend class Http2FrameDecoder;
+  friend class test::FrameDecoderStatePeer;
+
+  // Starts the decoding of a common frame header. Returns true if completed the
+  // decoding, false if the decode buffer didn't have enough data in it, in
+  // which case the decode buffer will have been drained and the caller should
+  // call ResumeDecodingFrameHeader when more data is available. This is called
+  // from Http2FrameDecoder, a friend class.
+  bool StartDecodingFrameHeader(DecodeBuffer* db) {
+    return structure_decoder_.Start(&frame_header_, db);
+  }
+
+  // Resumes decoding the common frame header after the preceding call to
+  // StartDecodingFrameHeader returned false, as did any subsequent calls to
+  // ResumeDecodingFrameHeader. This is called from Http2FrameDecoder,
+  // a friend class.
+  bool ResumeDecodingFrameHeader(DecodeBuffer* db) {
+    return structure_decoder_.Resume(&frame_header_, db);
+  }
+
+  // Clear any of the flags in the frame header that aren't set in valid_flags.
+  void RetainFlags(uint8_t valid_flags) {
+    frame_header_.RetainFlags(valid_flags);
+  }
+
+  // Clear all of the flags in the frame header; for use with frame types that
+  // don't define any flags, such as WINDOW_UPDATE.
+  void ClearFlags() { frame_header_.flags = Http2FrameFlag(); }
+
+  // Returns true if the type of frame being decoded can have padding.
+  bool IsPaddable() const {
+    return frame_header().type == Http2FrameType::DATA ||
+           frame_header().type == Http2FrameType::HEADERS ||
+           frame_header().type == Http2FrameType::PUSH_PROMISE;
+  }
+
+  Http2FrameDecoderListener* listener_ = nullptr;
+  Http2FrameHeader frame_header_;
+
+  // Number of bytes remaining to be decoded, if set; does not include the
+  // trailing padding once the length of padding has been determined.
+  // See ReadPadLength.
+  uint32_t remaining_payload_;
+
+  // The amount of trailing padding after the payload that remains to be
+  // decoded. See ReadPadLength.
+  uint32_t remaining_padding_;
+
+  // Generic decoder of structures, which takes care of buffering the needed
+  // bytes if the encoded structure is split across decode buffers.
+  Http2StructureDecoder structure_decoder_;
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_FRAME_DECODER_STATE_H_
diff --git a/net/http2/decoder/frame_decoder_state_test_util.cc b/net/http2/decoder/frame_decoder_state_test_util.cc
new file mode 100644
index 0000000..5988b8c
--- /dev/null
+++ b/net/http2/decoder/frame_decoder_state_test_util.cc
@@ -0,0 +1,34 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/frame_decoder_state_test_util.h"
+
+#include "base/logging.h"
+#include "net/http2/decoder/http2_structure_decoder_test_util.h"
+#include "net/http2/http2_structures.h"
+#include "net/http2/http2_structures_test_util.h"
+#include "net/http2/tools/http2_random.h"
+#include "net/http2/tools/random_decoder_test.h"
+
+namespace net {
+namespace test {
+
+// static
+void FrameDecoderStatePeer::Randomize(FrameDecoderState* p, RandomBase* rng) {
+  VLOG(1) << "FrameDecoderStatePeer::Randomize";
+  ::net::test::Randomize(&p->frame_header_, rng);
+  p->remaining_payload_ = rng->Rand32();
+  p->remaining_padding_ = rng->Rand32();
+  Http2StructureDecoderPeer::Randomize(&p->structure_decoder_, rng);
+}
+
+// static
+void FrameDecoderStatePeer::set_frame_header(const Http2FrameHeader& header,
+                                             FrameDecoderState* p) {
+  VLOG(1) << "FrameDecoderStatePeer::set_frame_header " << header;
+  p->frame_header_ = header;
+}
+
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/decoder/frame_decoder_state_test_util.h b/net/http2/decoder/frame_decoder_state_test_util.h
new file mode 100644
index 0000000..e32a401
--- /dev/null
+++ b/net/http2/decoder/frame_decoder_state_test_util.h
@@ -0,0 +1,36 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_FRAME_DECODER_STATE_TEST_UTIL_H_
+#define NET_HTTP2_DECODER_FRAME_DECODER_STATE_TEST_UTIL_H_
+
+#include "net/http2/decoder/frame_decoder_state.h"
+#include "net/http2/http2_structures.h"
+#include "net/http2/tools/random_decoder_test.h"
+
+namespace net {
+namespace test {
+
+class FrameDecoderStatePeer {
+ public:
+  // Randomizes (i.e. corrupts) the fields of the FrameDecoderState.
+  // PayloadDecoderBaseTest::StartDecoding calls this before passing the first
+  // decode buffer to the payload decoder, which increases the likelihood of
+  // detecting any use of prior states of the decoder on the decoding of
+  // future payloads.
+  static void Randomize(FrameDecoderState* p, RandomBase* rng);
+
+  // Inject a frame header into the FrameDecoderState.
+  // PayloadDecoderBaseTest::StartDecoding calls this just after calling
+  // Randomize (above), to simulate a full frame decoder having just finished
+  // decoding the common frame header and then calling the appropriate payload
+  // decoder based on the frame type in that frame header.
+  static void set_frame_header(const Http2FrameHeader& header,
+                               FrameDecoderState* p);
+};
+
+}  // namespace test
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_FRAME_DECODER_STATE_TEST_UTIL_H_
diff --git a/net/http2/decoder/frame_parts.cc b/net/http2/decoder/frame_parts.cc
new file mode 100644
index 0000000..33cd7bf
--- /dev/null
+++ b/net/http2/decoder/frame_parts.cc
@@ -0,0 +1,527 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/frame_parts.h"
+
+#include <type_traits>
+
+#include "base/logging.h"
+#include "net/base/escape.h"
+#include "net/http2/http2_structures_test_util.h"
+#include "net/http2/tools/failure.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::StringPiece;
+using std::string;
+using ::testing::AssertionFailure;
+using ::testing::AssertionResult;
+using ::testing::AssertionSuccess;
+using ::testing::ContainerEq;
+
+namespace net {
+namespace test {
+namespace {
+
+static_assert(std::is_base_of<Http2FrameDecoderListener, FrameParts>::value &&
+                  !std::is_abstract<FrameParts>::value,
+              "FrameParts needs to implement all of the methods of "
+              "Http2FrameDecoderListener");
+
+// Compare two optional variables of the same type.
+// TODO(jamessynge): Maybe create a ::testing::Matcher for this.
+template <class T>
+AssertionResult VerifyOptionalEq(const T& opt_a, const T& opt_b) {
+  if (opt_a) {
+    if (opt_b) {
+      VERIFY_EQ(opt_a.value(), opt_b.value());
+    } else {
+      return AssertionFailure() << "opt_b is not set; opt_a.value()="
+                                << opt_a.value();
+    }
+  } else if (opt_b) {
+    return AssertionFailure() << "opt_a is not set; opt_b.value()="
+                              << opt_b.value();
+  }
+  return AssertionSuccess();
+}
+
+}  // namespace
+
+FrameParts::FrameParts(const Http2FrameHeader& header) : frame_header(header) {
+  VLOG(1) << "FrameParts, header: " << frame_header;
+}
+
+FrameParts::FrameParts(const Http2FrameHeader& header, StringPiece payload)
+    : FrameParts(header) {
+  VLOG(1) << "FrameParts with payload.size() = " << payload.size();
+  payload.AppendToString(&this->payload);
+  opt_payload_length = payload.size();
+}
+FrameParts::FrameParts(const Http2FrameHeader& header,
+                       StringPiece payload,
+                       size_t total_pad_length)
+    : FrameParts(header, payload) {
+  VLOG(1) << "FrameParts with total_pad_length=" << total_pad_length;
+  SetTotalPadLength(total_pad_length);
+}
+
+FrameParts::FrameParts(const FrameParts& other) = default;
+
+FrameParts::~FrameParts() {}
+
+AssertionResult FrameParts::VerifyEquals(const FrameParts& that) const {
+#define COMMON_MESSAGE "\n  this: " << *this << "\n  that: " << that
+
+  VERIFY_EQ(frame_header, that.frame_header) << COMMON_MESSAGE;
+  VERIFY_EQ(payload, that.payload) << COMMON_MESSAGE;
+  VERIFY_EQ(padding, that.padding) << COMMON_MESSAGE;
+  VERIFY_EQ(altsvc_origin, that.altsvc_origin) << COMMON_MESSAGE;
+  VERIFY_EQ(altsvc_value, that.altsvc_value) << COMMON_MESSAGE;
+  VERIFY_EQ(settings, that.settings) << COMMON_MESSAGE;
+
+#define VERIFY_OPTIONAL_FIELD(field_name) \
+  VERIFY_SUCCESS(VerifyOptionalEq(field_name, that.field_name))
+
+  VERIFY_OPTIONAL_FIELD(opt_altsvc_origin_length) << COMMON_MESSAGE;
+  VERIFY_OPTIONAL_FIELD(opt_altsvc_value_length) << COMMON_MESSAGE;
+  VERIFY_OPTIONAL_FIELD(opt_goaway) << COMMON_MESSAGE;
+  VERIFY_OPTIONAL_FIELD(opt_missing_length) << COMMON_MESSAGE;
+  VERIFY_OPTIONAL_FIELD(opt_pad_length) << COMMON_MESSAGE;
+  VERIFY_OPTIONAL_FIELD(opt_ping) << COMMON_MESSAGE;
+  VERIFY_OPTIONAL_FIELD(opt_priority) << COMMON_MESSAGE;
+  VERIFY_OPTIONAL_FIELD(opt_push_promise) << COMMON_MESSAGE;
+  VERIFY_OPTIONAL_FIELD(opt_rst_stream_error_code) << COMMON_MESSAGE;
+  VERIFY_OPTIONAL_FIELD(opt_window_update_increment) << COMMON_MESSAGE;
+
+#undef VERIFY_OPTIONAL_FIELD
+
+  return AssertionSuccess();
+}
+
+void FrameParts::SetTotalPadLength(size_t total_pad_length) {
+  opt_pad_length.reset();
+  padding.clear();
+  if (total_pad_length > 0) {
+    ASSERT_LE(total_pad_length, 256u);
+    ASSERT_TRUE(frame_header.IsPadded());
+    opt_pad_length = total_pad_length - 1;
+    char zero = 0;
+    padding.append(opt_pad_length.value(), zero);
+  }
+
+  if (opt_pad_length) {
+    VLOG(1) << "SetTotalPadLength: pad_length=" << opt_pad_length.value();
+  } else {
+    VLOG(1) << "SetTotalPadLength: has no pad length";
+  }
+}
+
+void FrameParts::SetAltSvcExpected(StringPiece origin, StringPiece value) {
+  origin.AppendToString(&altsvc_origin);
+  value.AppendToString(&altsvc_value);
+  opt_altsvc_origin_length = origin.size();
+  opt_altsvc_value_length = value.size();
+}
+
+bool FrameParts::OnFrameHeader(const Http2FrameHeader& header) {
+  ADD_FAILURE() << "OnFrameHeader: " << *this;
+  return true;
+}
+
+void FrameParts::OnDataStart(const Http2FrameHeader& header) {
+  VLOG(1) << "OnDataStart: " << header;
+  ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::DATA)) << *this;
+  opt_payload_length = header.payload_length;
+}
+
+void FrameParts::OnDataPayload(const char* data, size_t len) {
+  VLOG(1) << "OnDataPayload: len=" << len << "; frame_header: " << frame_header;
+  ASSERT_TRUE(InFrameOfType(Http2FrameType::DATA)) << *this;
+  ASSERT_TRUE(
+      AppendString(StringPiece(data, len), &payload, &opt_payload_length));
+}
+
+void FrameParts::OnDataEnd() {
+  VLOG(1) << "OnDataEnd; frame_header: " << frame_header;
+  ASSERT_TRUE(EndFrameOfType(Http2FrameType::DATA)) << *this;
+}
+
+void FrameParts::OnHeadersStart(const Http2FrameHeader& header) {
+  VLOG(1) << "OnHeadersStart: " << header;
+  ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::HEADERS)) << *this;
+  opt_payload_length = header.payload_length;
+}
+
+void FrameParts::OnHeadersPriority(const Http2PriorityFields& priority) {
+  VLOG(1) << "OnHeadersPriority: priority: " << priority
+          << "; frame_header: " << frame_header;
+  ASSERT_TRUE(InFrameOfType(Http2FrameType::HEADERS)) << *this;
+  ASSERT_FALSE(opt_priority);
+  opt_priority = priority;
+  ASSERT_TRUE(opt_payload_length);
+  opt_payload_length =
+      opt_payload_length.value() - Http2PriorityFields::EncodedSize();
+}
+
+void FrameParts::OnHpackFragment(const char* data, size_t len) {
+  VLOG(1) << "OnHpackFragment: len=" << len
+          << "; frame_header: " << frame_header;
+  ASSERT_TRUE(got_start_callback);
+  ASSERT_FALSE(got_end_callback);
+  ASSERT_TRUE(FrameCanHaveHpackPayload(frame_header)) << *this;
+  ASSERT_TRUE(
+      AppendString(StringPiece(data, len), &payload, &opt_payload_length));
+}
+
+void FrameParts::OnHeadersEnd() {
+  VLOG(1) << "OnHeadersEnd; frame_header: " << frame_header;
+  ASSERT_TRUE(EndFrameOfType(Http2FrameType::HEADERS)) << *this;
+}
+
+void FrameParts::OnPriorityFrame(const Http2FrameHeader& header,
+                                 const Http2PriorityFields& priority) {
+  VLOG(1) << "OnPriorityFrame: " << header << "; priority: " << priority;
+  ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::PRIORITY)) << *this;
+  ASSERT_FALSE(opt_priority);
+  opt_priority = priority;
+  ASSERT_TRUE(EndFrameOfType(Http2FrameType::PRIORITY)) << *this;
+}
+
+void FrameParts::OnContinuationStart(const Http2FrameHeader& header) {
+  VLOG(1) << "OnContinuationStart: " << header;
+  ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::CONTINUATION)) << *this;
+  opt_payload_length = header.payload_length;
+}
+
+void FrameParts::OnContinuationEnd() {
+  VLOG(1) << "OnContinuationEnd; frame_header: " << frame_header;
+  ASSERT_TRUE(EndFrameOfType(Http2FrameType::CONTINUATION)) << *this;
+}
+
+void FrameParts::OnPadLength(size_t trailing_length) {
+  VLOG(1) << "OnPadLength: trailing_length=" << trailing_length;
+  ASSERT_TRUE(InPaddedFrame()) << *this;
+  ASSERT_FALSE(opt_pad_length);
+  ASSERT_TRUE(opt_payload_length);
+  size_t total_padding_length = trailing_length + 1;
+  ASSERT_GE(opt_payload_length.value(), static_cast<int>(total_padding_length));
+  opt_payload_length = opt_payload_length.value() - total_padding_length;
+  opt_pad_length = trailing_length;
+}
+
+void FrameParts::OnPadding(const char* pad, size_t skipped_length) {
+  VLOG(1) << "OnPadding: skipped_length=" << skipped_length;
+  ASSERT_TRUE(InPaddedFrame()) << *this;
+  ASSERT_TRUE(opt_pad_length);
+  ASSERT_TRUE(AppendString(StringPiece(pad, skipped_length), &padding,
+                           &opt_pad_length));
+}
+
+void FrameParts::OnRstStream(const Http2FrameHeader& header,
+                             Http2ErrorCode error_code) {
+  VLOG(1) << "OnRstStream: " << header << "; code=" << error_code;
+  ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::RST_STREAM)) << *this;
+  ASSERT_FALSE(opt_rst_stream_error_code);
+  opt_rst_stream_error_code = error_code;
+  ASSERT_TRUE(EndFrameOfType(Http2FrameType::RST_STREAM)) << *this;
+}
+
+void FrameParts::OnSettingsStart(const Http2FrameHeader& header) {
+  VLOG(1) << "OnSettingsStart: " << header;
+  ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::SETTINGS)) << *this;
+  ASSERT_EQ(0u, settings.size());
+  ASSERT_FALSE(header.IsAck()) << header;
+}
+
+void FrameParts::OnSetting(const Http2SettingFields& setting_fields) {
+  VLOG(1) << "OnSetting: " << setting_fields;
+  ASSERT_TRUE(InFrameOfType(Http2FrameType::SETTINGS)) << *this;
+  settings.push_back(setting_fields);
+}
+
+void FrameParts::OnSettingsEnd() {
+  VLOG(1) << "OnSettingsEnd; frame_header: " << frame_header;
+  ASSERT_TRUE(EndFrameOfType(Http2FrameType::SETTINGS)) << *this;
+}
+
+void FrameParts::OnSettingsAck(const Http2FrameHeader& header) {
+  VLOG(1) << "OnSettingsAck: " << header;
+  ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::SETTINGS)) << *this;
+  ASSERT_EQ(0u, settings.size());
+  ASSERT_TRUE(header.IsAck());
+  ASSERT_TRUE(EndFrameOfType(Http2FrameType::SETTINGS)) << *this;
+}
+
+void FrameParts::OnPushPromiseStart(const Http2FrameHeader& header,
+                                    const Http2PushPromiseFields& promise,
+                                    size_t total_padding_length) {
+  VLOG(1) << "OnPushPromiseStart header: " << header << "; promise: " << promise
+          << "; total_padding_length: " << total_padding_length;
+  ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::PUSH_PROMISE)) << *this;
+  ASSERT_GE(header.payload_length, Http2PushPromiseFields::EncodedSize());
+  opt_payload_length =
+      header.payload_length - Http2PushPromiseFields::EncodedSize();
+  ASSERT_FALSE(opt_push_promise);
+  opt_push_promise = promise;
+  if (total_padding_length > 0) {
+    ASSERT_GE(opt_payload_length.value(),
+              static_cast<int>(total_padding_length));
+    OnPadLength(total_padding_length - 1);
+  } else {
+    ASSERT_FALSE(header.IsPadded());
+  }
+}
+
+void FrameParts::OnPushPromiseEnd() {
+  VLOG(1) << "OnPushPromiseEnd; frame_header: " << frame_header;
+  ASSERT_TRUE(EndFrameOfType(Http2FrameType::PUSH_PROMISE)) << *this;
+}
+
+void FrameParts::OnPing(const Http2FrameHeader& header,
+                        const Http2PingFields& ping) {
+  VLOG(1) << "OnPing header: " << header << "   ping: " << ping;
+  ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::PING)) << *this;
+  ASSERT_FALSE(header.IsAck());
+  ASSERT_FALSE(opt_ping);
+  opt_ping = ping;
+  ASSERT_TRUE(EndFrameOfType(Http2FrameType::PING)) << *this;
+}
+
+void FrameParts::OnPingAck(const Http2FrameHeader& header,
+                           const Http2PingFields& ping) {
+  VLOG(1) << "OnPingAck header: " << header << "   ping: " << ping;
+  ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::PING)) << *this;
+  ASSERT_TRUE(header.IsAck());
+  ASSERT_FALSE(opt_ping);
+  opt_ping = ping;
+  ASSERT_TRUE(EndFrameOfType(Http2FrameType::PING)) << *this;
+}
+
+void FrameParts::OnGoAwayStart(const Http2FrameHeader& header,
+                               const Http2GoAwayFields& goaway) {
+  VLOG(1) << "OnGoAwayStart: " << goaway;
+  ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::GOAWAY)) << *this;
+  ASSERT_FALSE(opt_goaway);
+  opt_goaway = goaway;
+  opt_payload_length = header.payload_length - Http2GoAwayFields::EncodedSize();
+}
+
+void FrameParts::OnGoAwayOpaqueData(const char* data, size_t len) {
+  VLOG(1) << "OnGoAwayOpaqueData: len=" << len;
+  ASSERT_TRUE(InFrameOfType(Http2FrameType::GOAWAY)) << *this;
+  ASSERT_TRUE(
+      AppendString(StringPiece(data, len), &payload, &opt_payload_length));
+}
+
+void FrameParts::OnGoAwayEnd() {
+  VLOG(1) << "OnGoAwayEnd; frame_header: " << frame_header;
+  ASSERT_TRUE(EndFrameOfType(Http2FrameType::GOAWAY)) << *this;
+}
+
+void FrameParts::OnWindowUpdate(const Http2FrameHeader& header,
+                                uint32_t increment) {
+  VLOG(1) << "OnWindowUpdate header: " << header
+          << "     increment=" << increment;
+  ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::WINDOW_UPDATE)) << *this;
+  ASSERT_FALSE(opt_window_update_increment);
+  opt_window_update_increment = increment;
+  ASSERT_TRUE(EndFrameOfType(Http2FrameType::WINDOW_UPDATE)) << *this;
+}
+
+void FrameParts::OnAltSvcStart(const Http2FrameHeader& header,
+                               size_t origin_length,
+                               size_t value_length) {
+  VLOG(1) << "OnAltSvcStart: " << header
+          << "    origin_length: " << origin_length
+          << "    value_length: " << value_length;
+  ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::ALTSVC)) << *this;
+  ASSERT_FALSE(opt_altsvc_origin_length);
+  opt_altsvc_origin_length = origin_length;
+  ASSERT_FALSE(opt_altsvc_value_length);
+  opt_altsvc_value_length = value_length;
+}
+
+void FrameParts::OnAltSvcOriginData(const char* data, size_t len) {
+  VLOG(1) << "OnAltSvcOriginData: len=" << len;
+  ASSERT_TRUE(InFrameOfType(Http2FrameType::ALTSVC)) << *this;
+  ASSERT_TRUE(AppendString(StringPiece(data, len), &altsvc_origin,
+                           &opt_altsvc_origin_length));
+}
+
+void FrameParts::OnAltSvcValueData(const char* data, size_t len) {
+  VLOG(1) << "OnAltSvcValueData: len=" << len;
+  ASSERT_TRUE(InFrameOfType(Http2FrameType::ALTSVC)) << *this;
+  ASSERT_TRUE(AppendString(StringPiece(data, len), &altsvc_value,
+                           &opt_altsvc_value_length));
+}
+
+void FrameParts::OnAltSvcEnd() {
+  VLOG(1) << "OnAltSvcEnd; frame_header: " << frame_header;
+  ASSERT_TRUE(EndFrameOfType(Http2FrameType::ALTSVC)) << *this;
+}
+
+void FrameParts::OnUnknownStart(const Http2FrameHeader& header) {
+  VLOG(1) << "OnUnknownStart: " << header;
+  ASSERT_FALSE(IsSupportedHttp2FrameType(header.type)) << header;
+  ASSERT_FALSE(got_start_callback);
+  ASSERT_EQ(frame_header, header);
+  got_start_callback = true;
+  opt_payload_length = header.payload_length;
+}
+
+void FrameParts::OnUnknownPayload(const char* data, size_t len) {
+  VLOG(1) << "OnUnknownPayload: len=" << len;
+  ASSERT_FALSE(IsSupportedHttp2FrameType(frame_header.type)) << *this;
+  ASSERT_TRUE(got_start_callback);
+  ASSERT_FALSE(got_end_callback);
+  ASSERT_TRUE(
+      AppendString(StringPiece(data, len), &payload, &opt_payload_length));
+}
+
+void FrameParts::OnUnknownEnd() {
+  VLOG(1) << "OnUnknownEnd; frame_header: " << frame_header;
+  ASSERT_FALSE(IsSupportedHttp2FrameType(frame_header.type)) << *this;
+  ASSERT_TRUE(got_start_callback);
+  ASSERT_FALSE(got_end_callback);
+  got_end_callback = true;
+}
+
+void FrameParts::OnPaddingTooLong(const Http2FrameHeader& header,
+                                  size_t missing_length) {
+  VLOG(1) << "OnPaddingTooLong: " << header
+          << "; missing_length: " << missing_length;
+  ASSERT_EQ(frame_header, header);
+  ASSERT_FALSE(got_end_callback);
+  ASSERT_TRUE(FrameIsPadded(header));
+  ASSERT_FALSE(opt_pad_length);
+  ASSERT_FALSE(opt_missing_length);
+  opt_missing_length = missing_length;
+  got_start_callback = true;
+  got_end_callback = true;
+}
+
+void FrameParts::OnFrameSizeError(const Http2FrameHeader& header) {
+  VLOG(1) << "OnFrameSizeError: " << header;
+  ASSERT_EQ(frame_header, header);
+  ASSERT_FALSE(got_end_callback);
+  ASSERT_FALSE(has_frame_size_error);
+  has_frame_size_error = true;
+  got_end_callback = true;
+}
+
+void FrameParts::OutputTo(std::ostream& out) const {
+  out << "FrameParts{\n  frame_header: " << frame_header << "\n";
+  if (!payload.empty()) {
+    out << "  payload=\"" << EscapeQueryParamValue(payload, false) << "\"\n";
+  }
+  if (!padding.empty()) {
+    out << "  padding=\"" << EscapeQueryParamValue(padding, false) << "\"\n";
+  }
+  if (!altsvc_origin.empty()) {
+    out << "  altsvc_origin=\"" << EscapeQueryParamValue(altsvc_origin, false)
+        << "\"\n";
+  }
+  if (!altsvc_value.empty()) {
+    out << "  altsvc_value=\"" << EscapeQueryParamValue(altsvc_value, false)
+        << "\"\n";
+  }
+  if (opt_priority) {
+    out << "  priority=" << opt_priority.value() << "\n";
+  }
+  if (opt_rst_stream_error_code) {
+    out << "  rst_stream=" << opt_rst_stream_error_code.value() << "\n";
+  }
+  if (opt_push_promise) {
+    out << "  push_promise=" << opt_push_promise.value() << "\n";
+  }
+  if (opt_ping) {
+    out << "  ping=" << opt_ping.value() << "\n";
+  }
+  if (opt_goaway) {
+    out << "  goaway=" << opt_goaway.value() << "\n";
+  }
+  if (opt_window_update_increment) {
+    out << "  window_update=" << opt_window_update_increment.value() << "\n";
+  }
+  if (opt_payload_length) {
+    out << "  payload_length=" << opt_payload_length.value() << "\n";
+  }
+  if (opt_pad_length) {
+    out << "  pad_length=" << opt_pad_length.value() << "\n";
+  }
+  if (opt_missing_length) {
+    out << "  missing_length=" << opt_missing_length.value() << "\n";
+  }
+  if (opt_altsvc_origin_length) {
+    out << "  origin_length=" << opt_altsvc_origin_length.value() << "\n";
+  }
+  if (opt_altsvc_value_length) {
+    out << "  value_length=" << opt_altsvc_value_length.value() << "\n";
+  }
+  if (has_frame_size_error) {
+    out << "  has_frame_size_error\n";
+  }
+  if (got_start_callback) {
+    out << "  got_start_callback\n";
+  }
+  if (got_end_callback) {
+    out << "  got_end_callback\n";
+  }
+  for (size_t ndx = 0; ndx < settings.size(); ++ndx) {
+    out << "  setting[" << ndx << "]=" << settings[ndx];
+  }
+  out << "}";
+}
+
+AssertionResult FrameParts::StartFrameOfType(
+    const Http2FrameHeader& header,
+    Http2FrameType expected_frame_type) {
+  VERIFY_EQ(header.type, expected_frame_type);
+  VERIFY_FALSE(got_start_callback);
+  VERIFY_FALSE(got_end_callback);
+  VERIFY_EQ(frame_header, header);
+  got_start_callback = true;
+  return AssertionSuccess();
+}
+
+AssertionResult FrameParts::InFrameOfType(Http2FrameType expected_frame_type) {
+  VERIFY_TRUE(got_start_callback);
+  VERIFY_FALSE(got_end_callback);
+  VERIFY_EQ(frame_header.type, expected_frame_type);
+  return AssertionSuccess();
+}
+
+AssertionResult FrameParts::EndFrameOfType(Http2FrameType expected_frame_type) {
+  VERIFY_SUCCESS(InFrameOfType(expected_frame_type));
+  got_end_callback = true;
+  return AssertionSuccess();
+}
+
+AssertionResult FrameParts::InPaddedFrame() {
+  VERIFY_TRUE(got_start_callback);
+  VERIFY_FALSE(got_end_callback);
+  VERIFY_TRUE(FrameIsPadded(frame_header));
+  return AssertionSuccess();
+}
+
+AssertionResult FrameParts::AppendString(StringPiece source,
+                                         string* target,
+                                         base::Optional<int>* opt_length) {
+  source.AppendToString(target);
+  if (opt_length != nullptr) {
+    VERIFY_TRUE(*opt_length) << "Length is not set yet\n" << *this;
+    VERIFY_LE(target->size(), static_cast<size_t>(opt_length->value()))
+        << "String too large; source.size() = " << source.size() << "\n"
+        << *this;
+  }
+  return ::testing::AssertionSuccess();
+}
+
+std::ostream& operator<<(std::ostream& out, const FrameParts& v) {
+  v.OutputTo(out);
+  return out;
+}
+
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/decoder/frame_parts.h b/net/http2/decoder/frame_parts.h
new file mode 100644
index 0000000..c64dea69
--- /dev/null
+++ b/net/http2/decoder/frame_parts.h
@@ -0,0 +1,176 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_FRAME_PARTS_H_
+#define NET_HTTP2_DECODER_FRAME_PARTS_H_
+
+// FrameParts implements Http2FrameDecoderListener, recording the callbacks
+// during the decoding of a single frame. It is also used for comparing the
+// info that a test expects to be recorded during the decoding of a frame
+// with the actual recorded value (i.e. by providing a comparator).
+
+// TODO(jamessynge): Convert FrameParts to a class, hide the members, add
+// getters/setters.
+
+#include <stddef.h>
+
+#include <string>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/optional.h"
+#include "base/strings/string_piece.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+struct FrameParts;
+
+std::ostream& operator<<(std::ostream& out, const FrameParts& v);
+
+struct FrameParts : public Http2FrameDecoderListener {
+  // The first callback for every type of frame includes the frame header; this
+  // is the only constructor used during decoding of a frame.
+  explicit FrameParts(const Http2FrameHeader& header);
+
+  // For use in tests where the expected frame has a variable size payload.
+  FrameParts(const Http2FrameHeader& header, base::StringPiece payload);
+
+  // For use in tests where the expected frame has a variable size payload
+  // and may be padded.
+  FrameParts(const Http2FrameHeader& header,
+             base::StringPiece payload,
+             size_t total_pad_length);
+
+  FrameParts(const FrameParts& other);
+
+  ~FrameParts() override;
+
+  // Returns AssertionSuccess() if they're equal, else AssertionFailure()
+  // with info about the difference.
+  ::testing::AssertionResult VerifyEquals(const FrameParts& other) const;
+
+  // Format this FrameParts object.
+  void OutputTo(std::ostream& out) const;
+
+  // Set the total padding length (0 to 256).
+  void SetTotalPadLength(size_t total_pad_length);
+
+  // Set the origin and value expected in an ALTSVC frame.
+  void SetAltSvcExpected(base::StringPiece origin, base::StringPiece value);
+
+  // Http2FrameDecoderListener methods:
+  bool OnFrameHeader(const Http2FrameHeader& header) override;
+  void OnDataStart(const Http2FrameHeader& header) override;
+  void OnDataPayload(const char* data, size_t len) override;
+  void OnDataEnd() override;
+  void OnHeadersStart(const Http2FrameHeader& header) override;
+  void OnHeadersPriority(const Http2PriorityFields& priority) override;
+  void OnHpackFragment(const char* data, size_t len) override;
+  void OnHeadersEnd() override;
+  void OnPriorityFrame(const Http2FrameHeader& header,
+                       const Http2PriorityFields& priority) override;
+  void OnContinuationStart(const Http2FrameHeader& header) override;
+  void OnContinuationEnd() override;
+  void OnPadLength(size_t trailing_length) override;
+  void OnPadding(const char* pad, size_t skipped_length) override;
+  void OnRstStream(const Http2FrameHeader& header,
+                   Http2ErrorCode error_code) override;
+  void OnSettingsStart(const Http2FrameHeader& header) override;
+  void OnSetting(const Http2SettingFields& setting_fields) override;
+  void OnSettingsEnd() override;
+  void OnSettingsAck(const Http2FrameHeader& header) override;
+  void OnPushPromiseStart(const Http2FrameHeader& header,
+                          const Http2PushPromiseFields& promise,
+                          size_t total_padding_length) override;
+  void OnPushPromiseEnd() override;
+  void OnPing(const Http2FrameHeader& header,
+              const Http2PingFields& ping) override;
+  void OnPingAck(const Http2FrameHeader& header,
+                 const Http2PingFields& ping) override;
+  void OnGoAwayStart(const Http2FrameHeader& header,
+                     const Http2GoAwayFields& goaway) override;
+  void OnGoAwayOpaqueData(const char* data, size_t len) override;
+  void OnGoAwayEnd() override;
+  void OnWindowUpdate(const Http2FrameHeader& header,
+                      uint32_t increment) override;
+  void OnAltSvcStart(const Http2FrameHeader& header,
+                     size_t origin_length,
+                     size_t value_length) override;
+  void OnAltSvcOriginData(const char* data, size_t len) override;
+  void OnAltSvcValueData(const char* data, size_t len) override;
+  void OnAltSvcEnd() override;
+  void OnUnknownStart(const Http2FrameHeader& header) override;
+  void OnUnknownPayload(const char* data, size_t len) override;
+  void OnUnknownEnd() override;
+  void OnPaddingTooLong(const Http2FrameHeader& header,
+                        size_t missing_length) override;
+  void OnFrameSizeError(const Http2FrameHeader& header) override;
+
+  // The fields are public for access by tests.
+
+  const Http2FrameHeader frame_header;
+
+  std::string payload;
+  std::string padding;
+  std::string altsvc_origin;
+  std::string altsvc_value;
+
+  base::Optional<Http2PriorityFields> opt_priority;
+  base::Optional<Http2ErrorCode> opt_rst_stream_error_code;
+  base::Optional<Http2PushPromiseFields> opt_push_promise;
+  base::Optional<Http2PingFields> opt_ping;
+  base::Optional<Http2GoAwayFields> opt_goaway;
+
+  base::Optional<int> opt_pad_length;
+  base::Optional<int> opt_payload_length;
+  base::Optional<int> opt_missing_length;
+  base::Optional<int> opt_altsvc_origin_length;
+  base::Optional<int> opt_altsvc_value_length;
+
+  base::Optional<size_t> opt_window_update_increment;
+
+  bool has_frame_size_error = false;
+
+  std::vector<Http2SettingFields> settings;
+
+  // These booleans are not checked by CompareCollectedFrames.
+  bool got_start_callback = false;
+  bool got_end_callback = false;
+
+ private:
+  // ASSERT during an On* method that we're handling a frame of type
+  // expected_frame_type, and have not already received other On* methods
+  // (i.e. got_start_callback is false).
+  ::testing::AssertionResult StartFrameOfType(
+      const Http2FrameHeader& header,
+      Http2FrameType expected_frame_type);
+
+  // ASSERT that StartFrameOfType has already been called with
+  // expected_frame_type (i.e. got_start_callback has been called), and that
+  // EndFrameOfType has not yet been called (i.e. got_end_callback is false).
+  ::testing::AssertionResult InFrameOfType(Http2FrameType expected_frame_type);
+
+  // ASSERT that we're InFrameOfType, and then sets got_end_callback=true.
+  ::testing::AssertionResult EndFrameOfType(Http2FrameType expected_frame_type);
+
+  // ASSERT that we're in the middle of processing a frame that is padded.
+  ::testing::AssertionResult InPaddedFrame();
+
+  // Append source to target. If opt_length is not nullptr, then verifies that
+  // the optional has a value (i.e. that the necessary On*Start method has been
+  // called), and that target is not longer than opt_length->value().
+  ::testing::AssertionResult AppendString(base::StringPiece source,
+                                          std::string* target,
+                                          base::Optional<int>* opt_length);
+};
+
+}  // namespace test
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_FRAME_PARTS_H_
diff --git a/net/http2/decoder/frame_parts_collector.cc b/net/http2/decoder/frame_parts_collector.cc
new file mode 100644
index 0000000..05c54880
--- /dev/null
+++ b/net/http2/decoder/frame_parts_collector.cc
@@ -0,0 +1,112 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/frame_parts_collector.h"
+
+#include <utility>
+
+#include "base/logging.h"
+#include "net/http2/http2_structures_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+FramePartsCollector::FramePartsCollector() {}
+FramePartsCollector::~FramePartsCollector() {}
+
+void FramePartsCollector::Reset() {
+  current_frame_.reset();
+  collected_frames_.clear();
+  expected_header_set_ = false;
+}
+
+const FrameParts* FramePartsCollector::frame(size_t n) const {
+  if (n < size()) {
+    return collected_frames_.at(n).get();
+  }
+  CHECK(n == size());
+  return current_frame();
+}
+
+void FramePartsCollector::ExpectFrameHeader(const Http2FrameHeader& header) {
+  EXPECT_FALSE(IsInProgress());
+  EXPECT_FALSE(expected_header_set_) << "expected_header_: "
+                                     << expected_header_;
+  expected_header_ = header;
+  expected_header_set_ = true;
+  // OnFrameHeader is called before the flags are scrubbed, but the other
+  // methods are called after, so scrub the invalid flags from expected_header_.
+  ScrubFlagsOfHeader(&expected_header_);
+}
+
+void FramePartsCollector::TestExpectedHeader(const Http2FrameHeader& header) {
+  if (expected_header_set_) {
+    EXPECT_EQ(header, expected_header_);
+    expected_header_set_ = false;
+  }
+}
+
+Http2FrameDecoderListener* FramePartsCollector::StartFrame(
+    const Http2FrameHeader& header) {
+  TestExpectedHeader(header);
+  EXPECT_FALSE(IsInProgress());
+  if (current_frame_ == nullptr) {
+    current_frame_.reset(new FrameParts(header));
+  }
+  return current_frame();
+}
+
+Http2FrameDecoderListener* FramePartsCollector::StartAndEndFrame(
+    const Http2FrameHeader& header) {
+  TestExpectedHeader(header);
+  EXPECT_FALSE(IsInProgress());
+  if (current_frame_ == nullptr) {
+    current_frame_.reset(new FrameParts(header));
+  }
+  Http2FrameDecoderListener* result = current_frame();
+  collected_frames_.push_back(std::move(current_frame_));
+  return result;
+}
+
+Http2FrameDecoderListener* FramePartsCollector::CurrentFrame() {
+  EXPECT_TRUE(IsInProgress());
+  if (current_frame_ == nullptr) {
+    return &failing_listener_;
+  }
+  return current_frame();
+}
+
+Http2FrameDecoderListener* FramePartsCollector::EndFrame() {
+  EXPECT_TRUE(IsInProgress());
+  if (current_frame_ == nullptr) {
+    return &failing_listener_;
+  }
+  Http2FrameDecoderListener* result = current_frame();
+  collected_frames_.push_back(std::move(current_frame_));
+  return result;
+}
+
+Http2FrameDecoderListener* FramePartsCollector::FrameError(
+    const Http2FrameHeader& header) {
+  TestExpectedHeader(header);
+  if (current_frame_ == nullptr) {
+    // The decoder may detect an error before making any calls to the listener
+    // regarding the frame, in which case current_frame_==nullptr and we need
+    // to create a FrameParts instance.
+    current_frame_.reset(new FrameParts(header));
+  } else {
+    // Similarly, the decoder may have made calls to the listener regarding the
+    // frame before detecting the error; for example, the DATA payload decoder
+    // calls OnDataStart before it can detect padding errors, hence before it
+    // can call OnPaddingTooLong.
+    EXPECT_EQ(header, current_frame_->frame_header);
+  }
+  Http2FrameDecoderListener* result = current_frame();
+  collected_frames_.push_back(std::move(current_frame_));
+  return result;
+}
+
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/decoder/frame_parts_collector.h b/net/http2/decoder/frame_parts_collector.h
new file mode 100644
index 0000000..c411e48e
--- /dev/null
+++ b/net/http2/decoder/frame_parts_collector.h
@@ -0,0 +1,116 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_FRAME_PARTS_COLLECTOR_H_
+#define NET_HTTP2_DECODER_FRAME_PARTS_COLLECTOR_H_
+
+// FramePartsCollector is a base class for Http2FrameDecoderListener
+// implementations that create one FrameParts instance for each decoded frame.
+
+#include <stddef.h>
+
+#include <memory>
+#include <vector>
+
+#include "net/http2/decoder/frame_parts.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/decoder/http2_frame_decoder_listener_test_util.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+namespace test {
+
+class FramePartsCollector : public FailingHttp2FrameDecoderListener {
+ public:
+  FramePartsCollector();
+  ~FramePartsCollector() override;
+
+  // Toss out the collected data.
+  void Reset();
+
+  // Returns true if has started recording the info for a frame and has not yet
+  // finished doing so.
+  bool IsInProgress() const { return current_frame_ != nullptr; }
+
+  // Returns the FrameParts instance into which we're currently recording
+  // callback info if IsInProgress, else nullptr.
+  const FrameParts* current_frame() const { return current_frame_.get(); }
+
+  // Returns the completely collected FrameParts instances.
+  const std::vector<std::unique_ptr<FrameParts>>& collected_frames() const {
+    return collected_frames_;
+  }
+
+  // Returns the number of completely collected FrameParts instances.
+  size_t size() const { return collected_frames_.size(); }
+
+  // Returns the n'th frame, where 0 is the oldest of the collected frames,
+  // and n==size() is the frame currently being collected, if there is one.
+  // Returns nullptr if the requested index is not valid.
+  const FrameParts* frame(size_t n) const;
+
+ protected:
+  // In support of OnFrameHeader, set the header that we expect to be used in
+  // the next call.
+  // TODO(jamessynge): Remove ExpectFrameHeader et al. once done with supporting
+  // SpdyFramer's exact states.
+  void ExpectFrameHeader(const Http2FrameHeader& header);
+
+  // For use in implementing On*Start methods of Http2FrameDecoderListener,
+  // returns a FrameParts instance, which will be newly created if
+  // IsInProgress==false (which the caller should ensure), else will be the
+  // current_frame(); never returns nullptr.
+  // If called when IsInProgress==true, a test failure will be recorded.
+  Http2FrameDecoderListener* StartFrame(const Http2FrameHeader& header);
+
+  // For use in implementing On* callbacks, such as OnPingAck, that are the only
+  // call expected for the frame being decoded; not for On*Start methods.
+  // Returns a FrameParts instance, which will be newly created if
+  // IsInProgress==false (which the caller should ensure), else will be the
+  // current_frame(); never returns nullptr.
+  // If called when IsInProgress==true, a test failure will be recorded.
+  Http2FrameDecoderListener* StartAndEndFrame(const Http2FrameHeader& header);
+
+  // If IsInProgress==true, returns the FrameParts into which the current
+  // frame is being recorded; else records a test failure and returns
+  // failing_listener_, which will record a test failure when any of its
+  // On* methods is called.
+  Http2FrameDecoderListener* CurrentFrame();
+
+  // For use in implementing On*End methods, pushes the current frame onto
+  // the vector of completed frames, and returns a pointer to it for recording
+  // the info in the final call. If IsInProgress==false, records a test failure
+  // and returns failing_listener_, which will record a test failure when any
+  // of its On* methods is called.
+  Http2FrameDecoderListener* EndFrame();
+
+  // For use in implementing OnPaddingTooLong and OnFrameSizeError, is
+  // equivalent to EndFrame() if IsInProgress==true, else equivalent to
+  // StartAndEndFrame().
+  Http2FrameDecoderListener* FrameError(const Http2FrameHeader& header);
+
+ private:
+  // Returns the mutable FrameParts instance into which we're currently
+  // recording callback info if IsInProgress, else nullptr.
+  FrameParts* current_frame() { return current_frame_.get(); }
+
+  // If expected header is set, verify that it matches the header param.
+  // TODO(jamessynge): Remove TestExpectedHeader et al. once done
+  // with supporting SpdyFramer's exact states.
+  void TestExpectedHeader(const Http2FrameHeader& header);
+
+  std::unique_ptr<FrameParts> current_frame_;
+  std::vector<std::unique_ptr<FrameParts>> collected_frames_;
+  FailingHttp2FrameDecoderListener failing_listener_;
+
+  // TODO(jamessynge): Remove expected_header_ et al. once done with supporting
+  // SpdyFramer's exact states.
+  Http2FrameHeader expected_header_;
+  bool expected_header_set_ = false;
+};
+
+}  // namespace test
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_FRAME_PARTS_COLLECTOR_H_
diff --git a/net/http2/decoder/frame_parts_collector_listener.cc b/net/http2/decoder/frame_parts_collector_listener.cc
new file mode 100644
index 0000000..83a1ea34
--- /dev/null
+++ b/net/http2/decoder/frame_parts_collector_listener.cc
@@ -0,0 +1,230 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/frame_parts_collector_listener.h"
+
+#include "base/logging.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+bool FramePartsCollectorListener::OnFrameHeader(
+    const Http2FrameHeader& header) {
+  VLOG(1) << "OnFrameHeader: " << header;
+  ExpectFrameHeader(header);
+  return true;
+}
+
+void FramePartsCollectorListener::OnDataStart(const Http2FrameHeader& header) {
+  VLOG(1) << "OnDataStart: " << header;
+  StartFrame(header)->OnDataStart(header);
+}
+
+void FramePartsCollectorListener::OnDataPayload(const char* data, size_t len) {
+  VLOG(1) << "OnDataPayload: len=" << len;
+  CurrentFrame()->OnDataPayload(data, len);
+}
+
+void FramePartsCollectorListener::OnDataEnd() {
+  VLOG(1) << "OnDataEnd";
+  EndFrame()->OnDataEnd();
+}
+
+void FramePartsCollectorListener::OnHeadersStart(
+    const Http2FrameHeader& header) {
+  VLOG(1) << "OnHeadersStart: " << header;
+  StartFrame(header)->OnHeadersStart(header);
+}
+
+void FramePartsCollectorListener::OnHeadersPriority(
+    const Http2PriorityFields& priority) {
+  VLOG(1) << "OnHeadersPriority: " << priority;
+  CurrentFrame()->OnHeadersPriority(priority);
+}
+
+void FramePartsCollectorListener::OnHpackFragment(const char* data,
+                                                  size_t len) {
+  VLOG(1) << "OnHpackFragment: len=" << len;
+  CurrentFrame()->OnHpackFragment(data, len);
+}
+
+void FramePartsCollectorListener::OnHeadersEnd() {
+  VLOG(1) << "OnHeadersEnd";
+  EndFrame()->OnHeadersEnd();
+}
+
+void FramePartsCollectorListener::OnPriorityFrame(
+    const Http2FrameHeader& header,
+    const Http2PriorityFields& priority_fields) {
+  VLOG(1) << "OnPriority: " << header << "; " << priority_fields;
+  StartAndEndFrame(header)->OnPriorityFrame(header, priority_fields);
+}
+
+void FramePartsCollectorListener::OnContinuationStart(
+    const Http2FrameHeader& header) {
+  VLOG(1) << "OnContinuationStart: " << header;
+  StartFrame(header)->OnContinuationStart(header);
+}
+
+void FramePartsCollectorListener::OnContinuationEnd() {
+  VLOG(1) << "OnContinuationEnd";
+  EndFrame()->OnContinuationEnd();
+}
+
+void FramePartsCollectorListener::OnPadLength(size_t pad_length) {
+  VLOG(1) << "OnPadLength: " << pad_length;
+  CurrentFrame()->OnPadLength(pad_length);
+}
+
+void FramePartsCollectorListener::OnPadding(const char* padding,
+                                            size_t skipped_length) {
+  VLOG(1) << "OnPadding: " << skipped_length;
+  CurrentFrame()->OnPadding(padding, skipped_length);
+}
+
+void FramePartsCollectorListener::OnRstStream(const Http2FrameHeader& header,
+                                              Http2ErrorCode error_code) {
+  VLOG(1) << "OnRstStream: " << header << "; error_code=" << error_code;
+  StartAndEndFrame(header)->OnRstStream(header, error_code);
+}
+
+void FramePartsCollectorListener::OnSettingsStart(
+    const Http2FrameHeader& header) {
+  VLOG(1) << "OnSettingsStart: " << header;
+  EXPECT_EQ(Http2FrameType::SETTINGS, header.type) << header;
+  EXPECT_EQ(Http2FrameFlag(), header.flags) << header;
+  StartFrame(header)->OnSettingsStart(header);
+}
+
+void FramePartsCollectorListener::OnSetting(
+    const Http2SettingFields& setting_fields) {
+  VLOG(1) << "Http2SettingFields: setting_fields=" << setting_fields;
+  CurrentFrame()->OnSetting(setting_fields);
+}
+
+void FramePartsCollectorListener::OnSettingsEnd() {
+  VLOG(1) << "OnSettingsEnd";
+  EndFrame()->OnSettingsEnd();
+}
+
+void FramePartsCollectorListener::OnSettingsAck(
+    const Http2FrameHeader& header) {
+  VLOG(1) << "OnSettingsAck: " << header;
+  StartAndEndFrame(header)->OnSettingsAck(header);
+}
+
+void FramePartsCollectorListener::OnPushPromiseStart(
+    const Http2FrameHeader& header,
+    const Http2PushPromiseFields& promise,
+    size_t total_padding_length) {
+  VLOG(1) << "OnPushPromiseStart header: " << header << "  promise: " << promise
+          << "  total_padding_length: " << total_padding_length;
+  EXPECT_EQ(Http2FrameType::PUSH_PROMISE, header.type);
+  StartFrame(header)->OnPushPromiseStart(header, promise, total_padding_length);
+}
+
+void FramePartsCollectorListener::OnPushPromiseEnd() {
+  VLOG(1) << "OnPushPromiseEnd";
+  EndFrame()->OnPushPromiseEnd();
+}
+
+void FramePartsCollectorListener::OnPing(const Http2FrameHeader& header,
+                                         const Http2PingFields& ping) {
+  VLOG(1) << "OnPing: " << header << "; " << ping;
+  StartAndEndFrame(header)->OnPing(header, ping);
+}
+
+void FramePartsCollectorListener::OnPingAck(const Http2FrameHeader& header,
+                                            const Http2PingFields& ping) {
+  VLOG(1) << "OnPingAck: " << header << "; " << ping;
+  StartAndEndFrame(header)->OnPingAck(header, ping);
+}
+
+void FramePartsCollectorListener::OnGoAwayStart(
+    const Http2FrameHeader& header,
+    const Http2GoAwayFields& goaway) {
+  VLOG(1) << "OnGoAwayStart header: " << header << "; goaway: " << goaway;
+  StartFrame(header)->OnGoAwayStart(header, goaway);
+}
+
+void FramePartsCollectorListener::OnGoAwayOpaqueData(const char* data,
+                                                     size_t len) {
+  VLOG(1) << "OnGoAwayOpaqueData: len=" << len;
+  CurrentFrame()->OnGoAwayOpaqueData(data, len);
+}
+
+void FramePartsCollectorListener::OnGoAwayEnd() {
+  VLOG(1) << "OnGoAwayEnd";
+  EndFrame()->OnGoAwayEnd();
+}
+
+void FramePartsCollectorListener::OnWindowUpdate(
+    const Http2FrameHeader& header,
+    uint32_t window_size_increment) {
+  VLOG(1) << "OnWindowUpdate: " << header
+          << "; window_size_increment=" << window_size_increment;
+  EXPECT_EQ(Http2FrameType::WINDOW_UPDATE, header.type);
+  StartAndEndFrame(header)->OnWindowUpdate(header, window_size_increment);
+}
+
+void FramePartsCollectorListener::OnAltSvcStart(const Http2FrameHeader& header,
+                                                size_t origin_length,
+                                                size_t value_length) {
+  VLOG(1) << "OnAltSvcStart header: " << header
+          << "; origin_length=" << origin_length
+          << "; value_length=" << value_length;
+  StartFrame(header)->OnAltSvcStart(header, origin_length, value_length);
+}
+
+void FramePartsCollectorListener::OnAltSvcOriginData(const char* data,
+                                                     size_t len) {
+  VLOG(1) << "OnAltSvcOriginData: len=" << len;
+  CurrentFrame()->OnAltSvcOriginData(data, len);
+}
+
+void FramePartsCollectorListener::OnAltSvcValueData(const char* data,
+                                                    size_t len) {
+  VLOG(1) << "OnAltSvcValueData: len=" << len;
+  CurrentFrame()->OnAltSvcValueData(data, len);
+}
+
+void FramePartsCollectorListener::OnAltSvcEnd() {
+  VLOG(1) << "OnAltSvcEnd";
+  EndFrame()->OnAltSvcEnd();
+}
+
+void FramePartsCollectorListener::OnUnknownStart(
+    const Http2FrameHeader& header) {
+  VLOG(1) << "OnUnknownStart: " << header;
+  StartFrame(header)->OnUnknownStart(header);
+}
+
+void FramePartsCollectorListener::OnUnknownPayload(const char* data,
+                                                   size_t len) {
+  VLOG(1) << "OnUnknownPayload: len=" << len;
+  CurrentFrame()->OnUnknownPayload(data, len);
+}
+
+void FramePartsCollectorListener::OnUnknownEnd() {
+  VLOG(1) << "OnUnknownEnd";
+  EndFrame()->OnUnknownEnd();
+}
+
+void FramePartsCollectorListener::OnPaddingTooLong(
+    const Http2FrameHeader& header,
+    size_t missing_length) {
+  VLOG(1) << "OnPaddingTooLong: " << header
+          << "    missing_length: " << missing_length;
+  EndFrame()->OnPaddingTooLong(header, missing_length);
+}
+
+void FramePartsCollectorListener::OnFrameSizeError(
+    const Http2FrameHeader& header) {
+  VLOG(1) << "OnFrameSizeError: " << header;
+  FrameError(header)->OnFrameSizeError(header);
+}
+
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/decoder/frame_parts_collector_listener.h b/net/http2/decoder/frame_parts_collector_listener.h
new file mode 100644
index 0000000..89b7fed
--- /dev/null
+++ b/net/http2/decoder/frame_parts_collector_listener.h
@@ -0,0 +1,83 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_FRAME_PARTS_COLLECTOR_LISTENER_H_
+#define NET_HTTP2_DECODER_FRAME_PARTS_COLLECTOR_LISTENER_H_
+
+// FramePartsCollectorListener extends FramePartsCollector with an
+// implementation of every method of Http2FrameDecoderListener; it is
+// essentially the union of all the Listener classes in the tests of the
+// payload decoders (i.e. in ./payload_decoders/*_test.cc files), with the
+// addition of the OnFrameHeader method.
+// FramePartsCollectorListener supports tests of Http2FrameDecoder.
+
+#include <stddef.h>
+
+#include "net/http2/decoder/frame_parts_collector.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+namespace test {
+
+class FramePartsCollectorListener : public FramePartsCollector {
+ public:
+  FramePartsCollectorListener() {}
+  ~FramePartsCollectorListener() override {}
+
+  // TODO(jamessynge): Remove OnFrameHeader once done with supporting
+  // SpdyFramer's exact states.
+  bool OnFrameHeader(const Http2FrameHeader& header) override;
+  void OnDataStart(const Http2FrameHeader& header) override;
+  void OnDataPayload(const char* data, size_t len) override;
+  void OnDataEnd() override;
+  void OnHeadersStart(const Http2FrameHeader& header) override;
+  void OnHeadersPriority(const Http2PriorityFields& priority) override;
+  void OnHpackFragment(const char* data, size_t len) override;
+  void OnHeadersEnd() override;
+  void OnPriorityFrame(const Http2FrameHeader& header,
+                       const Http2PriorityFields& priority_fields) override;
+  void OnContinuationStart(const Http2FrameHeader& header) override;
+  void OnContinuationEnd() override;
+  void OnPadLength(size_t pad_length) override;
+  void OnPadding(const char* padding, size_t skipped_length) override;
+  void OnRstStream(const Http2FrameHeader& header,
+                   Http2ErrorCode error_code) override;
+  void OnSettingsStart(const Http2FrameHeader& header) override;
+  void OnSetting(const Http2SettingFields& setting_fields) override;
+  void OnSettingsEnd() override;
+  void OnSettingsAck(const Http2FrameHeader& header) override;
+  void OnPushPromiseStart(const Http2FrameHeader& header,
+                          const Http2PushPromiseFields& promise,
+                          size_t total_padding_length) override;
+  void OnPushPromiseEnd() override;
+  void OnPing(const Http2FrameHeader& header,
+              const Http2PingFields& ping) override;
+  void OnPingAck(const Http2FrameHeader& header,
+                 const Http2PingFields& ping) override;
+  void OnGoAwayStart(const Http2FrameHeader& header,
+                     const Http2GoAwayFields& goaway) override;
+  void OnGoAwayOpaqueData(const char* data, size_t len) override;
+  void OnGoAwayEnd() override;
+  void OnWindowUpdate(const Http2FrameHeader& header,
+                      uint32_t window_size_increment) override;
+  void OnAltSvcStart(const Http2FrameHeader& header,
+                     size_t origin_length,
+                     size_t value_length) override;
+  void OnAltSvcOriginData(const char* data, size_t len) override;
+  void OnAltSvcValueData(const char* data, size_t len) override;
+  void OnAltSvcEnd() override;
+  void OnUnknownStart(const Http2FrameHeader& header) override;
+  void OnUnknownPayload(const char* data, size_t len) override;
+  void OnUnknownEnd() override;
+  void OnPaddingTooLong(const Http2FrameHeader& header,
+                        size_t missing_length) override;
+  void OnFrameSizeError(const Http2FrameHeader& header) override;
+};
+
+}  // namespace test
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_FRAME_PARTS_COLLECTOR_LISTENER_H_
diff --git a/net/http2/decoder/http2_frame_decoder.cc b/net/http2/decoder/http2_frame_decoder.cc
new file mode 100644
index 0000000..fe24f91
--- /dev/null
+++ b/net/http2/decoder/http2_frame_decoder.cc
@@ -0,0 +1,426 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/http2_frame_decoder.h"
+
+#include "net/http2/http2_constants.h"
+
+namespace net {
+
+std::ostream& operator<<(std::ostream& out, Http2FrameDecoder::State v) {
+  switch (v) {
+    case Http2FrameDecoder::State::kStartDecodingHeader:
+      return out << "kStartDecodingHeader";
+    case Http2FrameDecoder::State::kResumeDecodingHeader:
+      return out << "kResumeDecodingHeader";
+    case Http2FrameDecoder::State::kResumeDecodingPayload:
+      return out << "kResumeDecodingPayload";
+    case Http2FrameDecoder::State::kDiscardPayload:
+      return out << "kDiscardPayload";
+  }
+  return out << static_cast<int>(v);
+}
+
+Http2FrameDecoder::Http2FrameDecoder(Http2FrameDecoderListener* listener)
+    : state_(State::kStartDecodingHeader),
+      maximum_payload_size_(Http2SettingsInfo::DefaultMaxFrameSize()) {
+  set_listener(listener);
+}
+
+void Http2FrameDecoder::set_listener(Http2FrameDecoderListener* listener) {
+  if (listener == nullptr) {
+    listener = &no_op_listener_;
+  }
+  frame_decoder_state_.set_listener(listener);
+}
+
+Http2FrameDecoderListener* Http2FrameDecoder::listener() const {
+  return frame_decoder_state_.listener();
+}
+
+DecodeStatus Http2FrameDecoder::DecodeFrame(DecodeBuffer* db) {
+  DVLOG(2) << "Http2FrameDecoder::DecodeFrame state=" << state_;
+  switch (state_) {
+    case State::kStartDecodingHeader:
+      if (frame_decoder_state_.StartDecodingFrameHeader(db)) {
+        return StartDecodingPayload(db);
+      }
+      state_ = State::kResumeDecodingHeader;
+      return DecodeStatus::kDecodeInProgress;
+
+    case State::kResumeDecodingHeader:
+      if (frame_decoder_state_.ResumeDecodingFrameHeader(db)) {
+        return StartDecodingPayload(db);
+      }
+      return DecodeStatus::kDecodeInProgress;
+
+    case State::kResumeDecodingPayload:
+      return ResumeDecodingPayload(db);
+
+    case State::kDiscardPayload:
+      return DiscardPayload(db);
+  }
+
+  NOTREACHED();
+  return DecodeStatus::kDecodeError;
+}
+
+size_t Http2FrameDecoder::remaining_payload() const {
+  return frame_decoder_state_.remaining_payload();
+}
+
+uint32_t Http2FrameDecoder::remaining_padding() const {
+  return frame_decoder_state_.remaining_padding();
+}
+
+DecodeStatus Http2FrameDecoder::StartDecodingPayload(DecodeBuffer* db) {
+  const Http2FrameHeader& header = frame_header();
+
+  // TODO(jamessynge): Remove OnFrameHeader once done with supporting
+  // SpdyFramer's exact states.
+  if (!listener()->OnFrameHeader(header)) {
+    DVLOG(2) << "OnFrameHeader rejected the frame, will discard; header: "
+             << header;
+    state_ = State::kDiscardPayload;
+    frame_decoder_state_.InitializeRemainders();
+    return DecodeStatus::kDecodeError;
+  }
+
+  if (header.payload_length > maximum_payload_size_) {
+    DVLOG(2) << "Payload length is greater than allowed: "
+             << header.payload_length << " > " << maximum_payload_size_
+             << "\n   header: " << header;
+    state_ = State::kDiscardPayload;
+    frame_decoder_state_.InitializeRemainders();
+    listener()->OnFrameSizeError(header);
+    return DecodeStatus::kDecodeError;
+  }
+
+  // The decode buffer can extend across many frames. Make sure that the
+  // buffer we pass to the start method that is specific to the frame type
+  // does not exend beyond this frame.
+  DecodeBufferSubset subset(db, header.payload_length);
+  DecodeStatus status;
+  switch (header.type) {
+    case Http2FrameType::DATA:
+      status = StartDecodingDataPayload(&subset);
+      break;
+
+    case Http2FrameType::HEADERS:
+      status = StartDecodingHeadersPayload(&subset);
+      break;
+
+    case Http2FrameType::PRIORITY:
+      status = StartDecodingPriorityPayload(&subset);
+      break;
+
+    case Http2FrameType::RST_STREAM:
+      status = StartDecodingRstStreamPayload(&subset);
+      break;
+
+    case Http2FrameType::SETTINGS:
+      status = StartDecodingSettingsPayload(&subset);
+      break;
+
+    case Http2FrameType::PUSH_PROMISE:
+      status = StartDecodingPushPromisePayload(&subset);
+      break;
+
+    case Http2FrameType::PING:
+      status = StartDecodingPingPayload(&subset);
+      break;
+
+    case Http2FrameType::GOAWAY:
+      status = StartDecodingGoAwayPayload(&subset);
+      break;
+
+    case Http2FrameType::WINDOW_UPDATE:
+      status = StartDecodingWindowUpdatePayload(&subset);
+      break;
+
+    case Http2FrameType::CONTINUATION:
+      status = StartDecodingContinuationPayload(&subset);
+      break;
+
+    case Http2FrameType::ALTSVC:
+      status = StartDecodingAltSvcPayload(&subset);
+      break;
+
+    default:
+      status = StartDecodingUnknownPayload(&subset);
+      break;
+  }
+
+  if (status == DecodeStatus::kDecodeDone) {
+    state_ = State::kStartDecodingHeader;
+    return status;
+  } else if (status == DecodeStatus::kDecodeInProgress) {
+    state_ = State::kResumeDecodingPayload;
+    return status;
+  } else {
+    state_ = State::kDiscardPayload;
+    return status;
+  }
+}
+
+DecodeStatus Http2FrameDecoder::ResumeDecodingPayload(DecodeBuffer* db) {
+  // The decode buffer can extend across many frames. Make sure that the
+  // buffer we pass to the start method that is specific to the frame type
+  // does not exend beyond this frame.
+  size_t remaining = frame_decoder_state_.remaining_total_payload();
+  DCHECK_LE(remaining, frame_header().payload_length);
+  DecodeBufferSubset subset(db, remaining);
+  DecodeStatus status;
+  switch (frame_header().type) {
+    case Http2FrameType::DATA:
+      status = ResumeDecodingDataPayload(&subset);
+      break;
+
+    case Http2FrameType::HEADERS:
+      status = ResumeDecodingHeadersPayload(&subset);
+      break;
+
+    case Http2FrameType::PRIORITY:
+      status = ResumeDecodingPriorityPayload(&subset);
+      break;
+
+    case Http2FrameType::RST_STREAM:
+      status = ResumeDecodingRstStreamPayload(&subset);
+      break;
+
+    case Http2FrameType::SETTINGS:
+      status = ResumeDecodingSettingsPayload(&subset);
+      break;
+
+    case Http2FrameType::PUSH_PROMISE:
+      status = ResumeDecodingPushPromisePayload(&subset);
+      break;
+
+    case Http2FrameType::PING:
+      status = ResumeDecodingPingPayload(&subset);
+      break;
+
+    case Http2FrameType::GOAWAY:
+      status = ResumeDecodingGoAwayPayload(&subset);
+      break;
+
+    case Http2FrameType::WINDOW_UPDATE:
+      status = ResumeDecodingWindowUpdatePayload(&subset);
+      break;
+
+    case Http2FrameType::CONTINUATION:
+      status = ResumeDecodingContinuationPayload(&subset);
+      break;
+
+    case Http2FrameType::ALTSVC:
+      status = ResumeDecodingAltSvcPayload(&subset);
+      break;
+
+    default:
+      status = ResumeDecodingUnknownPayload(&subset);
+      break;
+  }
+
+  if (status == DecodeStatus::kDecodeDone) {
+    state_ = State::kStartDecodingHeader;
+    return status;
+  } else if (status == DecodeStatus::kDecodeInProgress) {
+    return status;
+  } else {
+    state_ = State::kDiscardPayload;
+    return status;
+  }
+}
+
+// Clear any of the flags in the frame header that aren't set in valid_flags.
+void Http2FrameDecoder::RetainFlags(uint8_t valid_flags) {
+  frame_decoder_state_.RetainFlags(valid_flags);
+}
+
+// Clear all of the flags in the frame header; for use with frame types that
+// don't define any flags, such as WINDOW_UPDATE.
+void Http2FrameDecoder::ClearFlags() {
+  frame_decoder_state_.ClearFlags();
+}
+
+DecodeStatus Http2FrameDecoder::StartDecodingAltSvcPayload(DecodeBuffer* db) {
+  ClearFlags();
+  return altsvc_payload_decoder_.StartDecodingPayload(&frame_decoder_state_,
+                                                      db);
+}
+DecodeStatus Http2FrameDecoder::ResumeDecodingAltSvcPayload(DecodeBuffer* db) {
+  // The frame is not paddable.
+  DCHECK_EQ(frame_decoder_state_.remaining_total_payload(),
+            frame_decoder_state_.remaining_payload());
+  return altsvc_payload_decoder_.ResumeDecodingPayload(&frame_decoder_state_,
+                                                       db);
+}
+
+DecodeStatus Http2FrameDecoder::StartDecodingContinuationPayload(
+    DecodeBuffer* db) {
+  RetainFlags(Http2FrameFlag::FLAG_END_HEADERS);
+  return continuation_payload_decoder_.StartDecodingPayload(
+      &frame_decoder_state_, db);
+}
+DecodeStatus Http2FrameDecoder::ResumeDecodingContinuationPayload(
+    DecodeBuffer* db) {
+  // The frame is not paddable.
+  DCHECK_EQ(frame_decoder_state_.remaining_total_payload(),
+            frame_decoder_state_.remaining_payload());
+  return continuation_payload_decoder_.ResumeDecodingPayload(
+      &frame_decoder_state_, db);
+}
+
+DecodeStatus Http2FrameDecoder::StartDecodingDataPayload(DecodeBuffer* db) {
+  RetainFlags(Http2FrameFlag::FLAG_END_STREAM | Http2FrameFlag::FLAG_PADDED);
+  return data_payload_decoder_.StartDecodingPayload(&frame_decoder_state_, db);
+}
+DecodeStatus Http2FrameDecoder::ResumeDecodingDataPayload(DecodeBuffer* db) {
+  return data_payload_decoder_.ResumeDecodingPayload(&frame_decoder_state_, db);
+}
+
+DecodeStatus Http2FrameDecoder::StartDecodingGoAwayPayload(DecodeBuffer* db) {
+  ClearFlags();
+  return goaway_payload_decoder_.StartDecodingPayload(&frame_decoder_state_,
+                                                      db);
+}
+DecodeStatus Http2FrameDecoder::ResumeDecodingGoAwayPayload(DecodeBuffer* db) {
+  // The frame is not paddable.
+  DCHECK_EQ(frame_decoder_state_.remaining_total_payload(),
+            frame_decoder_state_.remaining_payload());
+  return goaway_payload_decoder_.ResumeDecodingPayload(&frame_decoder_state_,
+                                                       db);
+}
+
+DecodeStatus Http2FrameDecoder::StartDecodingHeadersPayload(DecodeBuffer* db) {
+  RetainFlags(Http2FrameFlag::FLAG_END_STREAM |
+              Http2FrameFlag::FLAG_END_HEADERS | Http2FrameFlag::FLAG_PADDED |
+              Http2FrameFlag::FLAG_PRIORITY);
+  return headers_payload_decoder_.StartDecodingPayload(&frame_decoder_state_,
+                                                       db);
+}
+DecodeStatus Http2FrameDecoder::ResumeDecodingHeadersPayload(DecodeBuffer* db) {
+  DCHECK_LE(frame_decoder_state_.remaining_payload_and_padding(),
+            frame_header().payload_length);
+  return headers_payload_decoder_.ResumeDecodingPayload(&frame_decoder_state_,
+                                                        db);
+}
+
+DecodeStatus Http2FrameDecoder::StartDecodingPingPayload(DecodeBuffer* db) {
+  RetainFlags(Http2FrameFlag::FLAG_ACK);
+  return ping_payload_decoder_.StartDecodingPayload(&frame_decoder_state_, db);
+}
+DecodeStatus Http2FrameDecoder::ResumeDecodingPingPayload(DecodeBuffer* db) {
+  // The frame is not paddable.
+  DCHECK_EQ(frame_decoder_state_.remaining_total_payload(),
+            frame_decoder_state_.remaining_payload());
+  return ping_payload_decoder_.ResumeDecodingPayload(&frame_decoder_state_, db);
+}
+
+DecodeStatus Http2FrameDecoder::StartDecodingPriorityPayload(DecodeBuffer* db) {
+  ClearFlags();
+  return priority_payload_decoder_.StartDecodingPayload(&frame_decoder_state_,
+                                                        db);
+}
+DecodeStatus Http2FrameDecoder::ResumeDecodingPriorityPayload(
+    DecodeBuffer* db) {
+  // The frame is not paddable.
+  DCHECK_EQ(frame_decoder_state_.remaining_total_payload(),
+            frame_decoder_state_.remaining_payload());
+  return priority_payload_decoder_.ResumeDecodingPayload(&frame_decoder_state_,
+                                                         db);
+}
+
+DecodeStatus Http2FrameDecoder::StartDecodingPushPromisePayload(
+    DecodeBuffer* db) {
+  RetainFlags(Http2FrameFlag::FLAG_END_HEADERS | Http2FrameFlag::FLAG_PADDED);
+  return push_promise_payload_decoder_.StartDecodingPayload(
+      &frame_decoder_state_, db);
+}
+DecodeStatus Http2FrameDecoder::ResumeDecodingPushPromisePayload(
+    DecodeBuffer* db) {
+  DCHECK_LE(frame_decoder_state_.remaining_payload_and_padding(),
+            frame_header().payload_length);
+  return push_promise_payload_decoder_.ResumeDecodingPayload(
+      &frame_decoder_state_, db);
+}
+
+DecodeStatus Http2FrameDecoder::StartDecodingRstStreamPayload(
+    DecodeBuffer* db) {
+  ClearFlags();
+  return rst_stream_payload_decoder_.StartDecodingPayload(&frame_decoder_state_,
+                                                          db);
+}
+DecodeStatus Http2FrameDecoder::ResumeDecodingRstStreamPayload(
+    DecodeBuffer* db) {
+  // The frame is not paddable.
+  DCHECK_EQ(frame_decoder_state_.remaining_total_payload(),
+            frame_decoder_state_.remaining_payload());
+  return rst_stream_payload_decoder_.ResumeDecodingPayload(
+      &frame_decoder_state_, db);
+}
+
+DecodeStatus Http2FrameDecoder::StartDecodingSettingsPayload(DecodeBuffer* db) {
+  RetainFlags(Http2FrameFlag::FLAG_ACK);
+  return settings_payload_decoder_.StartDecodingPayload(&frame_decoder_state_,
+                                                        db);
+}
+DecodeStatus Http2FrameDecoder::ResumeDecodingSettingsPayload(
+    DecodeBuffer* db) {
+  // The frame is not paddable.
+  DCHECK_EQ(frame_decoder_state_.remaining_total_payload(),
+            frame_decoder_state_.remaining_payload());
+  return settings_payload_decoder_.ResumeDecodingPayload(&frame_decoder_state_,
+                                                         db);
+}
+
+DecodeStatus Http2FrameDecoder::StartDecodingUnknownPayload(DecodeBuffer* db) {
+  // We don't known what type of frame this is, so we don't know which flags
+  // are valid, so we don't touch them.
+  return unknown_payload_decoder_.StartDecodingPayload(&frame_decoder_state_,
+                                                       db);
+}
+DecodeStatus Http2FrameDecoder::ResumeDecodingUnknownPayload(DecodeBuffer* db) {
+  // We don't known what type of frame this is, so we treat it as not paddable.
+  DCHECK_EQ(frame_decoder_state_.remaining_total_payload(),
+            frame_decoder_state_.remaining_payload());
+  return unknown_payload_decoder_.ResumeDecodingPayload(&frame_decoder_state_,
+                                                        db);
+}
+
+DecodeStatus Http2FrameDecoder::StartDecodingWindowUpdatePayload(
+    DecodeBuffer* db) {
+  ClearFlags();
+  return window_update_payload_decoder_.StartDecodingPayload(
+      &frame_decoder_state_, db);
+}
+DecodeStatus Http2FrameDecoder::ResumeDecodingWindowUpdatePayload(
+    DecodeBuffer* db) {
+  // The frame is not paddable.
+  DCHECK_EQ(frame_decoder_state_.remaining_total_payload(),
+            frame_decoder_state_.remaining_payload());
+  return window_update_payload_decoder_.ResumeDecodingPayload(
+      &frame_decoder_state_, db);
+}
+
+DecodeStatus Http2FrameDecoder::DiscardPayload(DecodeBuffer* db) {
+  DVLOG(2) << "remaining_payload=" << frame_decoder_state_.remaining_payload_
+           << "; remaining_padding=" << frame_decoder_state_.remaining_padding_;
+  frame_decoder_state_.remaining_payload_ +=
+      frame_decoder_state_.remaining_padding_;
+  frame_decoder_state_.remaining_padding_ = 0;
+  const size_t avail = frame_decoder_state_.AvailablePayload(db);
+  DVLOG(2) << "avail=" << avail;
+  if (avail > 0) {
+    frame_decoder_state_.ConsumePayload(avail);
+    db->AdvanceCursor(avail);
+  }
+  if (frame_decoder_state_.remaining_payload_ == 0) {
+    state_ = State::kStartDecodingHeader;
+    return DecodeStatus::kDecodeDone;
+  }
+  return DecodeStatus::kDecodeInProgress;
+}
+
+}  // namespace net
diff --git a/net/http2/decoder/http2_frame_decoder.h b/net/http2/decoder/http2_frame_decoder.h
new file mode 100644
index 0000000..f440993
--- /dev/null
+++ b/net/http2/decoder/http2_frame_decoder.h
@@ -0,0 +1,203 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_HTTP2_FRAME_DECODER_H_
+#define NET_HTTP2_DECODER_HTTP2_FRAME_DECODER_H_
+
+// Http2FrameDecoder decodes the available input until it reaches the end of
+// the input or it reaches the end of the first frame in the input.
+// Note that Http2FrameDecoder does only minimal validation; for example,
+// stream ids are not checked, nor is the sequence of frames such as
+// CONTINUATION frame placement.
+//
+// Http2FrameDecoder enters state kError once it has called the listener's
+// OnFrameSizeError or OnPaddingTooLong methods, and at this time has no
+// provision for leaving that state. While the HTTP/2 spec (RFC7540) allows
+// for some such errors to be considered as just stream errors in some cases,
+// this implementation treats them all as connection errors.
+
+#include <stddef.h>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "net/base/net_export.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/decoder/frame_decoder_state.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/decoder/payload_decoders/altsvc_payload_decoder.h"
+#include "net/http2/decoder/payload_decoders/continuation_payload_decoder.h"
+#include "net/http2/decoder/payload_decoders/data_payload_decoder.h"
+#include "net/http2/decoder/payload_decoders/goaway_payload_decoder.h"
+#include "net/http2/decoder/payload_decoders/headers_payload_decoder.h"
+#include "net/http2/decoder/payload_decoders/ping_payload_decoder.h"
+#include "net/http2/decoder/payload_decoders/priority_payload_decoder.h"
+#include "net/http2/decoder/payload_decoders/push_promise_payload_decoder.h"
+#include "net/http2/decoder/payload_decoders/rst_stream_payload_decoder.h"
+#include "net/http2/decoder/payload_decoders/settings_payload_decoder.h"
+#include "net/http2/decoder/payload_decoders/unknown_payload_decoder.h"
+#include "net/http2/decoder/payload_decoders/window_update_payload_decoder.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+namespace test {
+class Http2FrameDecoderPeer;
+}  // namespace test
+
+class NET_EXPORT_PRIVATE Http2FrameDecoder {
+ public:
+  explicit Http2FrameDecoder(Http2FrameDecoderListener* listener);
+  Http2FrameDecoder() : Http2FrameDecoder(nullptr) {}
+
+  // The decoder will call the listener's methods as it decodes a frame.
+  void set_listener(Http2FrameDecoderListener* listener);
+  Http2FrameDecoderListener* listener() const;
+
+  // The decoder will reject frame's whose payload
+  // length field exceeds the maximum payload size.
+  void set_maximum_payload_size(size_t v) { maximum_payload_size_ = v; }
+  size_t maximum_payload_size() const { return maximum_payload_size_; }
+
+  // Decodes the input up to the next frame boundary (i.e. at most one frame).
+  //
+  // Returns kDecodeDone if it decodes the final byte of a frame, OR if there
+  // is no input and it is awaiting the start of a new frame (e.g. if this
+  // is the first call to DecodeFrame, or if the previous call returned
+  // kDecodeDone).
+  //
+  // Returns kDecodeInProgress if it decodes all of the decode buffer, but has
+  // not reached the end of the frame.
+  //
+  // Returns kDecodeError if the frame's padding or length wasn't valid (i.e. if
+  // the decoder called either the listener's OnPaddingTooLong or
+  // OnFrameSizeError method).
+  DecodeStatus DecodeFrame(DecodeBuffer* db);
+
+  //////////////////////////////////////////////////////////////////////////////
+  // Methods that support Http2FrameDecoderAdapter.
+
+  // Is the remainder of the frame's payload being discarded?
+  bool IsDiscardingPayload() const { return state_ == State::kDiscardPayload; }
+
+  // Returns the number of bytes of the frame's payload that remain to be
+  // decoded, excluding any trailing padding. This method must only be called
+  // after the frame header has been decoded AND DecodeFrame has returned
+  // kDecodeInProgress.
+  size_t remaining_payload() const;
+
+  // Returns the number of bytes of trailing padding after the payload that
+  // remain to be decoded. This method must only be called if the frame type
+  // allows padding, and after the frame header has been decoded AND
+  // DecodeFrame has returned. Will return 0 if the Pad Length field has not
+  // yet been decoded.
+  uint32_t remaining_padding() const;
+
+ private:
+  enum class State {
+    // Ready to start decoding a new frame's header.
+    kStartDecodingHeader,
+    // Was in state kStartDecodingHeader, but unable to read the entire frame
+    // header, so needs more input to complete decoding the header.
+    kResumeDecodingHeader,
+
+    // Have decoded the frame header, and started decoding the available bytes
+    // of the frame's payload, but need more bytes to finish the job.
+    kResumeDecodingPayload,
+
+    // Decoding of the most recently started frame resulted in an error:
+    // OnPaddingTooLong or OnFrameSizeError was called to indicate that the
+    // decoder detected a problem, or OnFrameHeader returned false, indicating
+    // that the listener detected a problem. Regardless of which, the decoder
+    // will stay in state kDiscardPayload until it has been passed the rest
+    // of the bytes of the frame's payload that it hasn't yet seen, after
+    // which it will be ready to decode another frame.
+    kDiscardPayload,
+  };
+
+  friend class test::Http2FrameDecoderPeer;
+  friend std::ostream& operator<<(std::ostream& out, State v);
+
+  DecodeStatus StartDecodingPayload(DecodeBuffer* db);
+  DecodeStatus ResumeDecodingPayload(DecodeBuffer* db);
+  DecodeStatus DiscardPayload(DecodeBuffer* db);
+
+  const Http2FrameHeader& frame_header() const {
+    return frame_decoder_state_.frame_header();
+  }
+
+  // Clear any of the flags in the frame header that aren't set in valid_flags.
+  void RetainFlags(uint8_t valid_flags);
+
+  // Clear all of the flags in the frame header; for use with frame types that
+  // don't define any flags, such as WINDOW_UPDATE.
+  void ClearFlags();
+
+  // These methods call the StartDecodingPayload() method of the frame type's
+  // payload decoder, after first clearing invalid flags in the header. The
+  // caller must ensure that the decode buffer does not extend beyond the
+  // end of the payload (handled by Http2FrameDecoder::StartDecodingPayload).
+  DecodeStatus StartDecodingAltSvcPayload(DecodeBuffer* db);
+  DecodeStatus StartDecodingContinuationPayload(DecodeBuffer* db);
+  DecodeStatus StartDecodingDataPayload(DecodeBuffer* db);
+  DecodeStatus StartDecodingGoAwayPayload(DecodeBuffer* db);
+  DecodeStatus StartDecodingHeadersPayload(DecodeBuffer* db);
+  DecodeStatus StartDecodingPingPayload(DecodeBuffer* db);
+  DecodeStatus StartDecodingPriorityPayload(DecodeBuffer* db);
+  DecodeStatus StartDecodingPushPromisePayload(DecodeBuffer* db);
+  DecodeStatus StartDecodingRstStreamPayload(DecodeBuffer* db);
+  DecodeStatus StartDecodingSettingsPayload(DecodeBuffer* db);
+  DecodeStatus StartDecodingUnknownPayload(DecodeBuffer* db);
+  DecodeStatus StartDecodingWindowUpdatePayload(DecodeBuffer* db);
+
+  // These methods call the ResumeDecodingPayload() method of the frame type's
+  // payload decoder; they are called only if the preceding call to the
+  // corresponding Start method (above) returned kDecodeInProgress, as did any
+  // subsequent calls to the resume method.
+  // Unlike the Start methods, the decode buffer may extend beyond the
+  // end of the payload, so the method will create a DecodeBufferSubset
+  // before calling the ResumeDecodingPayload method of the frame type's
+  // payload decoder.
+  DecodeStatus ResumeDecodingAltSvcPayload(DecodeBuffer* db);
+  DecodeStatus ResumeDecodingContinuationPayload(DecodeBuffer* db);
+  DecodeStatus ResumeDecodingDataPayload(DecodeBuffer* db);
+  DecodeStatus ResumeDecodingGoAwayPayload(DecodeBuffer* db);
+  DecodeStatus ResumeDecodingHeadersPayload(DecodeBuffer* db);
+  DecodeStatus ResumeDecodingPingPayload(DecodeBuffer* db);
+  DecodeStatus ResumeDecodingPriorityPayload(DecodeBuffer* db);
+  DecodeStatus ResumeDecodingPushPromisePayload(DecodeBuffer* db);
+  DecodeStatus ResumeDecodingRstStreamPayload(DecodeBuffer* db);
+  DecodeStatus ResumeDecodingSettingsPayload(DecodeBuffer* db);
+  DecodeStatus ResumeDecodingUnknownPayload(DecodeBuffer* db);
+  DecodeStatus ResumeDecodingWindowUpdatePayload(DecodeBuffer* db);
+
+  FrameDecoderState frame_decoder_state_;
+
+  // We only need one payload decoder at a time, so they share the same storage.
+  union {
+    AltSvcPayloadDecoder altsvc_payload_decoder_;
+    ContinuationPayloadDecoder continuation_payload_decoder_;
+    DataPayloadDecoder data_payload_decoder_;
+    GoAwayPayloadDecoder goaway_payload_decoder_;
+    HeadersPayloadDecoder headers_payload_decoder_;
+    PingPayloadDecoder ping_payload_decoder_;
+    PriorityPayloadDecoder priority_payload_decoder_;
+    PushPromisePayloadDecoder push_promise_payload_decoder_;
+    RstStreamPayloadDecoder rst_stream_payload_decoder_;
+    SettingsPayloadDecoder settings_payload_decoder_;
+    UnknownPayloadDecoder unknown_payload_decoder_;
+    WindowUpdatePayloadDecoder window_update_payload_decoder_;
+  };
+
+  State state_;
+  size_t maximum_payload_size_;
+
+  // Listener used whenever caller passes nullptr to set_listener.
+  Http2FrameDecoderNoOpListener no_op_listener_;
+
+  DISALLOW_COPY_AND_ASSIGN(Http2FrameDecoder);
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_HTTP2_FRAME_DECODER_H_
diff --git a/net/http2/decoder/http2_frame_decoder_listener.cc b/net/http2/decoder/http2_frame_decoder_listener.cc
new file mode 100644
index 0000000..bf62ae4f
--- /dev/null
+++ b/net/http2/decoder/http2_frame_decoder_listener.cc
@@ -0,0 +1,14 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+
+namespace net {
+
+bool Http2FrameDecoderNoOpListener::OnFrameHeader(
+    const Http2FrameHeader& header) {
+  return true;
+}
+
+}  // namespace net
diff --git a/net/http2/decoder/http2_frame_decoder_listener.h b/net/http2/decoder/http2_frame_decoder_listener.h
new file mode 100644
index 0000000..3345ab6f
--- /dev/null
+++ b/net/http2/decoder/http2_frame_decoder_listener.h
@@ -0,0 +1,356 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_HTTP2_FRAME_DECODER_LISTENER_H_
+#define NET_HTTP2_DECODER_HTTP2_FRAME_DECODER_LISTENER_H_
+
+// Http2FrameDecoderListener is the interface which the HTTP/2 decoder uses
+// to report the decoded frames to a listener.
+//
+// The general design is to assume that the listener will copy the data it needs
+// (e.g. frame headers) and will keep track of the implicit state of the
+// decoding process (i.e. the decoder maintains just the information it needs in
+// order to perform the decoding). Therefore, the parameters are just those with
+// (potentially) new data, not previously provided info about the current frame.
+//
+// The calls are described as if they are made in quick succession, i.e. one
+// after another, but of course the decoder needs input to decode, and the
+// decoder will only call the listener once the necessary input has been
+// provided. For example: OnDataStart can only be called once the 9 bytes of
+// of an HTTP/2 common frame header have been received. The decoder will call
+// the listener methods as soon as possible to avoid almost all buffering.
+//
+// The listener interface is designed so that it is possible to exactly
+// reconstruct the serialized frames, with the exception of reserved bits,
+// including in the frame header's flags and stream_id fields, which will have
+// been cleared before the methods below are called.
+
+#include <stddef.h>
+
+#include <type_traits>
+
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+
+// TODO(jamessynge): Consider sorting the methods by frequency of call, if that
+// helps at all.
+class Http2FrameDecoderListener {
+ public:
+  Http2FrameDecoderListener() {}
+  virtual ~Http2FrameDecoderListener() {}
+
+  // Called once the common frame header has been decoded for any frame, and
+  // before any of the methods below, which will also be called. This method is
+  // included in this interface only for the purpose of supporting SpdyFramer
+  // semantics via an adapter. This is the only method that has a non-void
+  // return type, and this is just so that Http2FrameDecoderAdapter (called
+  // from SpdyFramer) can more readily pass existing tests that expect decoding
+  // to stop if the headers alone indicate an error. Return false to stop
+  // decoding just after decoding the header, else return true to continue
+  // decoding.
+  // TODO(jamessynge): Remove OnFrameHeader once done with supporting
+  // SpdyFramer's exact states.
+  virtual bool OnFrameHeader(const Http2FrameHeader& header) = 0;
+
+  //////////////////////////////////////////////////////////////////////////////
+
+  // Called once the common frame header has been decoded for a DATA frame,
+  // before examining the frame's payload, after which:
+  //   OnPadLength will be called if header.IsPadded() is true, i.e. if the
+  //     PADDED flag is set;
+  //   OnDataPayload will be called as the non-padding portion of the payload
+  //     is available until all of it has been provided;
+  //   OnPadding will be called if the frame is padded AND the Pad Length field
+  //     is greater than zero;
+  //   OnDataEnd will be called last. If the frame is unpadded and has no
+  //     payload, then this will be called immediately after OnDataStart.
+  virtual void OnDataStart(const Http2FrameHeader& header) = 0;
+
+  // Called when the next non-padding portion of a DATA frame's payload is
+  // received.
+  // |data| The start of |len| bytes of data.
+  // |len| The length of the data buffer. Maybe zero in some cases, which does
+  //       not mean anything special.
+  virtual void OnDataPayload(const char* data, size_t len) = 0;
+
+  // Called after an entire DATA frame has been received.
+  // If header.IsEndStream() == true, this is the last data for the stream.
+  virtual void OnDataEnd() = 0;
+
+  // Called once the common frame header has been decoded for a HEADERS frame,
+  // before examining the frame's payload, after which:
+  //   OnPadLength will be called if header.IsPadded() is true, i.e. if the
+  //     PADDED flag is set;
+  //   OnHeadersPriority will be called if header.HasPriority() is true, i.e. if
+  //     the frame has the PRIORITY flag;
+  //   OnHpackFragment as the remainder of the non-padding payload is available
+  //     until all if has been provided;
+  //   OnPadding will be called if the frame is padded AND the Pad Length field
+  //     is greater than zero;
+  //   OnHeadersEnd will be called last; If the frame is unpadded and has no
+  //     payload, then this will be called immediately after OnHeadersStart;
+  //     OnHeadersEnd indicates the end of the HPACK block only if the frame
+  //     header had the END_HEADERS flag set, else the END_HEADERS should be
+  //     looked for on a subsequent CONTINUATION frame.
+  virtual void OnHeadersStart(const Http2FrameHeader& header) = 0;
+
+  // Called when a HEADERS frame is received with the PRIORITY flag set and
+  // the priority fields have been decoded.
+  virtual void OnHeadersPriority(
+      const Http2PriorityFields& priority_fields) = 0;
+
+  // Called when a fragment (i.e. some or all of an HPACK Block) is received;
+  // this may be part of a HEADERS, PUSH_PROMISE or CONTINUATION frame.
+  // |data| The start of |len| bytes of data.
+  // |len| The length of the data buffer. Maybe zero in some cases, which does
+  //       not mean anything special, except that it simplified the decoder.
+  virtual void OnHpackFragment(const char* data, size_t len) = 0;
+
+  // Called after an entire HEADERS frame has been received. The frame is the
+  // end of the HEADERS if the END_HEADERS flag is set; else there should be
+  // CONTINUATION frames after this frame.
+  virtual void OnHeadersEnd() = 0;
+
+  // Called when an entire PRIORITY frame has been decoded.
+  virtual void OnPriorityFrame(const Http2FrameHeader& header,
+                               const Http2PriorityFields& priority_fields) = 0;
+
+  // Called once the common frame header has been decoded for a CONTINUATION
+  // frame, before examining the frame's payload, after which:
+  //   OnHpackFragment as the frame's payload is available until all of it
+  //     has been provided;
+  //   OnContinuationEnd will be called last; If the frame has no payload,
+  //     then this will be called immediately after OnContinuationStart;
+  //     the HPACK block is at an end if and only if the frame header passed
+  //     to OnContinuationStart had the END_HEADERS flag set.
+  virtual void OnContinuationStart(const Http2FrameHeader& header) = 0;
+
+  // Called after an entire CONTINUATION frame has been received. The frame is
+  // the end of the HEADERS if the END_HEADERS flag is set.
+  virtual void OnContinuationEnd() = 0;
+
+  // Called when Pad Length field has been read. Applies to DATA and HEADERS
+  // frames. For PUSH_PROMISE frames, the Pad Length + 1 is provided in the
+  // OnPushPromiseStart call as total_padding_length.
+  virtual void OnPadLength(size_t pad_length) = 0;
+
+  // Called when padding is skipped over.
+  virtual void OnPadding(const char* padding, size_t skipped_length) = 0;
+
+  // Called when an entire RST_STREAM frame has been decoded.
+  // This is the only callback for RST_STREAM frames.
+  virtual void OnRstStream(const Http2FrameHeader& header,
+                           Http2ErrorCode error_code) = 0;
+
+  // Called once the common frame header has been decoded for a SETTINGS frame
+  // without the ACK flag, before examining the frame's payload, after which:
+  //   OnSetting will be called in turn for each pair of settings parameter and
+  //     value found in the payload;
+  //   OnSettingsEnd will be called last; If the frame has no payload,
+  //     then this will be called immediately after OnSettingsStart.
+  // The frame header is passed so that the caller can check the stream_id,
+  // which should be zero, but that hasn't been checked by the decoder.
+  virtual void OnSettingsStart(const Http2FrameHeader& header) = 0;
+
+  // Called for each setting parameter and value within a SETTINGS frame.
+  virtual void OnSetting(const Http2SettingFields& setting_fields) = 0;
+
+  // Called after parsing the complete payload of SETTINGS frame (non-ACK).
+  virtual void OnSettingsEnd() = 0;
+
+  // Called when an entire SETTINGS frame, with the ACK flag, has been decoded.
+  virtual void OnSettingsAck(const Http2FrameHeader& header) = 0;
+
+  // Called just before starting to process the HPACK block of a PUSH_PROMISE
+  // frame. The Pad Length field has already been decoded at this point, so
+  // OnPadLength will not be called; note that total_padding_length is Pad
+  // Length + 1. After OnPushPromiseStart:
+  //   OnHpackFragment as the remainder of the non-padding payload is available
+  //     until all if has been provided;
+  //   OnPadding will be called if the frame is padded AND the Pad Length field
+  //     is greater than zero (i.e. total_padding_length > 1);
+  //   OnPushPromiseEnd will be called last; If the frame is unpadded and has no
+  //     payload, then this will be called immediately after OnPushPromiseStart.
+  virtual void OnPushPromiseStart(const Http2FrameHeader& header,
+                                  const Http2PushPromiseFields& promise,
+                                  size_t total_padding_length) = 0;
+
+  // Called after all of the HPACK block fragment and padding of a PUSH_PROMISE
+  // has been decoded and delivered to the listener. This call indicates the end
+  // of the HPACK block if and only if the frame header had the END_HEADERS flag
+  // set (i.e. header.IsEndHeaders() is true); otherwise the next block must be
+  // a CONTINUATION frame with the same stream id (not the same promised stream
+  // id).
+  virtual void OnPushPromiseEnd() = 0;
+
+  // Called when an entire PING frame, without the ACK flag, has been decoded.
+  virtual void OnPing(const Http2FrameHeader& header,
+                      const Http2PingFields& ping) = 0;
+
+  // Called when an entire PING frame, with the ACK flag, has been decoded.
+  virtual void OnPingAck(const Http2FrameHeader& header,
+                         const Http2PingFields& ping) = 0;
+
+  // Called after parsing a GOAWAY frame's header and fixed size fields, after
+  // which:
+  //   OnGoAwayOpaqueData will be called as opaque data of the payload becomes
+  //     available to the decoder, until all of it has been provided to the
+  //     listener;
+  //   OnGoAwayEnd will be called last, after all the opaque data has been
+  //     provided to the listener; if there is no opaque data, then OnGoAwayEnd
+  //     will be called immediately after OnGoAwayStart.
+  virtual void OnGoAwayStart(const Http2FrameHeader& header,
+                             const Http2GoAwayFields& goaway) = 0;
+
+  // Called when the next portion of a GOAWAY frame's payload is received.
+  // |data| The start of |len| bytes of opaque data.
+  // |len| The length of the opaque data buffer. Maybe zero in some cases,
+  //       which does not mean anything special.
+  virtual void OnGoAwayOpaqueData(const char* data, size_t len) = 0;
+
+  // Called after finishing decoding all of a GOAWAY frame.
+  virtual void OnGoAwayEnd() = 0;
+
+  // Called when an entire WINDOW_UPDATE frame has been decoded. The
+  // window_size_increment is required to be non-zero, but that has not been
+  // checked. If header.stream_id==0, the connection's flow control window is
+  // being increased, else the specified stream's flow control is being
+  // increased.
+  virtual void OnWindowUpdate(const Http2FrameHeader& header,
+                              uint32_t window_size_increment) = 0;
+
+  // Called when an ALTSVC frame header and origin length have been parsed.
+  // Either or both lengths may be zero. After OnAltSvcStart:
+  //   OnAltSvcOriginData will be called until all of the (optional) Origin
+  //     has been provided;
+  //   OnAltSvcValueData will be called until all of the Alt-Svc-Field-Value
+  //     has been provided;
+  //   OnAltSvcEnd will called last, after all of the origin and
+  //     Alt-Svc-Field-Value have been delivered to the listener.
+  virtual void OnAltSvcStart(const Http2FrameHeader& header,
+                             size_t origin_length,
+                             size_t value_length) = 0;
+
+  // Called when decoding the (optional) origin of an ALTSVC;
+  // the field is uninterpreted.
+  virtual void OnAltSvcOriginData(const char* data, size_t len) = 0;
+
+  // Called when decoding the Alt-Svc-Field-Value of an ALTSVC;
+  // the field is uninterpreted.
+  virtual void OnAltSvcValueData(const char* data, size_t len) = 0;
+
+  // Called after decoding all of a ALTSVC frame and providing to the listener
+  // via the above methods.
+  virtual void OnAltSvcEnd() = 0;
+
+  // Called when the common frame header has been decoded, but the frame type
+  // is unknown, after which:
+  //   OnUnknownPayload is called as the payload of the frame is provided to the
+  //     decoder, until all of the payload has been decoded;
+  //   OnUnknownEnd will called last, after the entire frame of the unknown type
+  //     has been decoded and provided to the listener.
+  virtual void OnUnknownStart(const Http2FrameHeader& header) = 0;
+
+  // Called when the payload of an unknown frame type is received.
+  // |data| A buffer containing the data received.
+  // |len| The length of the data buffer.
+  virtual void OnUnknownPayload(const char* data, size_t len) = 0;
+
+  // Called after decoding all of the payload of an unknown frame type.
+  virtual void OnUnknownEnd() = 0;
+
+  //////////////////////////////////////////////////////////////////////////////
+  // Below here are events indicating a problem has been detected during
+  // decoding (i.e. the received frames are malformed in some way).
+
+  // Padding field (uint8) has a value that is too large (i.e. the amount of
+  // padding is greater than the remainder of the payload that isn't required).
+  // From RFC Section 6.1, DATA:
+  //     If the length of the padding is the length of the frame payload or
+  //     greater, the recipient MUST treat this as a connection error
+  //     (Section 5.4.1) of type PROTOCOL_ERROR.
+  // The same is true for HEADERS and PUSH_PROMISE.
+  virtual void OnPaddingTooLong(const Http2FrameHeader& header,
+                                size_t missing_length) = 0;
+
+  // Frame size error. Depending upon the effected frame, this may or may not
+  // require terminating the connection, though that is probably the best thing
+  // to do.
+  // From RFC Section 4.2, Frame Size:
+  //     An endpoint MUST send an error code of FRAME_SIZE_ERROR if a frame
+  //     exceeds the size defined in SETTINGS_MAX_FRAME_SIZE, exceeds any limit
+  //     defined for the frame type, or is too small to contain mandatory frame
+  //     data. A frame size error in a frame that could alter the state of the
+  //     the entire connection MUST be treated as a connection error
+  //     (Section 5.4.1); this includes any frame carrying a header block
+  //     (Section 4.3) (that is, HEADERS, PUSH_PROMISE, and CONTINUATION),
+  //     SETTINGS, and any frame with a stream identifier of 0.
+  virtual void OnFrameSizeError(const Http2FrameHeader& header) = 0;
+};
+
+// Do nothing for each call. Useful for ignoring a frame that is invalid.
+class Http2FrameDecoderNoOpListener : public Http2FrameDecoderListener {
+ public:
+  Http2FrameDecoderNoOpListener() {}
+  ~Http2FrameDecoderNoOpListener() override {}
+
+  // TODO(jamessynge): Remove OnFrameHeader once done with supporting
+  // SpdyFramer's exact states.
+  bool OnFrameHeader(const Http2FrameHeader& header) override;
+
+  void OnDataStart(const Http2FrameHeader& header) override {}
+  void OnDataPayload(const char* data, size_t len) override {}
+  void OnDataEnd() override {}
+  void OnHeadersStart(const Http2FrameHeader& header) override {}
+  void OnHeadersPriority(const Http2PriorityFields& priority) override {}
+  void OnHpackFragment(const char* data, size_t len) override {}
+  void OnHeadersEnd() override {}
+  void OnPriorityFrame(const Http2FrameHeader& header,
+                       const Http2PriorityFields& priority) override {}
+  void OnContinuationStart(const Http2FrameHeader& header) override {}
+  void OnContinuationEnd() override {}
+  void OnPadLength(size_t trailing_length) override {}
+  void OnPadding(const char* padding, size_t skipped_length) override {}
+  void OnRstStream(const Http2FrameHeader& header,
+                   Http2ErrorCode error_code) override {}
+  void OnSettingsStart(const Http2FrameHeader& header) override {}
+  void OnSetting(const Http2SettingFields& setting_fields) override {}
+  void OnSettingsEnd() override {}
+  void OnSettingsAck(const Http2FrameHeader& header) override {}
+  void OnPushPromiseStart(const Http2FrameHeader& header,
+                          const Http2PushPromiseFields& promise,
+                          size_t total_padding_length) override {}
+  void OnPushPromiseEnd() override {}
+  void OnPing(const Http2FrameHeader& header,
+              const Http2PingFields& ping) override {}
+  void OnPingAck(const Http2FrameHeader& header,
+                 const Http2PingFields& ping) override {}
+  void OnGoAwayStart(const Http2FrameHeader& header,
+                     const Http2GoAwayFields& goaway) override {}
+  void OnGoAwayOpaqueData(const char* data, size_t len) override {}
+  void OnGoAwayEnd() override {}
+  void OnWindowUpdate(const Http2FrameHeader& header,
+                      uint32_t increment) override {}
+  void OnAltSvcStart(const Http2FrameHeader& header,
+                     size_t origin_length,
+                     size_t value_length) override {}
+  void OnAltSvcOriginData(const char* data, size_t len) override {}
+  void OnAltSvcValueData(const char* data, size_t len) override {}
+  void OnAltSvcEnd() override {}
+  void OnUnknownStart(const Http2FrameHeader& header) override {}
+  void OnUnknownPayload(const char* data, size_t len) override {}
+  void OnUnknownEnd() override {}
+  void OnPaddingTooLong(const Http2FrameHeader& header,
+                        size_t missing_length) override {}
+  void OnFrameSizeError(const Http2FrameHeader& header) override {}
+};
+
+static_assert(!std::is_abstract<Http2FrameDecoderNoOpListener>(),
+              "Http2FrameDecoderNoOpListener ought to be concrete.");
+
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_HTTP2_FRAME_DECODER_LISTENER_H_
diff --git a/net/http2/decoder/http2_frame_decoder_listener_test_util.cc b/net/http2/decoder/http2_frame_decoder_listener_test_util.cc
new file mode 100644
index 0000000..9159b6cd
--- /dev/null
+++ b/net/http2/decoder/http2_frame_decoder_listener_test_util.cc
@@ -0,0 +1,485 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/http2_frame_decoder_listener_test_util.h"
+
+#include "base/logging.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+FailingHttp2FrameDecoderListener::FailingHttp2FrameDecoderListener() {}
+FailingHttp2FrameDecoderListener::~FailingHttp2FrameDecoderListener() {}
+
+bool FailingHttp2FrameDecoderListener::OnFrameHeader(
+    const Http2FrameHeader& header) {
+  ADD_FAILURE() << "OnFrameHeader: " << header;
+  return false;
+}
+
+void FailingHttp2FrameDecoderListener::OnDataStart(
+    const Http2FrameHeader& header) {
+  FAIL() << "OnDataStart: " << header;
+}
+
+void FailingHttp2FrameDecoderListener::OnDataPayload(const char* data,
+                                                     size_t len) {
+  FAIL() << "OnDataPayload: len=" << len;
+}
+
+void FailingHttp2FrameDecoderListener::OnDataEnd() {
+  FAIL() << "OnDataEnd";
+}
+
+void FailingHttp2FrameDecoderListener::OnHeadersStart(
+    const Http2FrameHeader& header) {
+  FAIL() << "OnHeadersStart: " << header;
+}
+
+void FailingHttp2FrameDecoderListener::OnHeadersPriority(
+    const Http2PriorityFields& priority) {
+  FAIL() << "OnHeadersPriority: " << priority;
+}
+
+void FailingHttp2FrameDecoderListener::OnHpackFragment(const char* data,
+                                                       size_t len) {
+  FAIL() << "OnHpackFragment: len=" << len;
+}
+
+void FailingHttp2FrameDecoderListener::OnHeadersEnd() {
+  FAIL() << "OnHeadersEnd";
+}
+
+void FailingHttp2FrameDecoderListener::OnPriorityFrame(
+    const Http2FrameHeader& header,
+    const Http2PriorityFields& priority) {
+  FAIL() << "OnPriorityFrame: " << header << "; priority: " << priority;
+}
+
+void FailingHttp2FrameDecoderListener::OnContinuationStart(
+    const Http2FrameHeader& header) {
+  FAIL() << "OnContinuationStart: " << header;
+}
+
+void FailingHttp2FrameDecoderListener::OnContinuationEnd() {
+  FAIL() << "OnContinuationEnd";
+}
+
+void FailingHttp2FrameDecoderListener::OnPadLength(size_t trailing_length) {
+  FAIL() << "OnPadLength: trailing_length=" << trailing_length;
+}
+
+void FailingHttp2FrameDecoderListener::OnPadding(const char* padding,
+                                                 size_t skipped_length) {
+  FAIL() << "OnPadding: skipped_length=" << skipped_length;
+}
+
+void FailingHttp2FrameDecoderListener::OnRstStream(
+    const Http2FrameHeader& header,
+    Http2ErrorCode error_code) {
+  FAIL() << "OnRstStream: " << header << "; code=" << error_code;
+}
+
+void FailingHttp2FrameDecoderListener::OnSettingsStart(
+    const Http2FrameHeader& header) {
+  FAIL() << "OnSettingsStart: " << header;
+}
+
+void FailingHttp2FrameDecoderListener::OnSetting(
+    const Http2SettingFields& setting_fields) {
+  FAIL() << "OnSetting: " << setting_fields;
+}
+
+void FailingHttp2FrameDecoderListener::OnSettingsEnd() {
+  FAIL() << "OnSettingsEnd";
+}
+
+void FailingHttp2FrameDecoderListener::OnSettingsAck(
+    const Http2FrameHeader& header) {
+  FAIL() << "OnSettingsAck: " << header;
+}
+
+void FailingHttp2FrameDecoderListener::OnPushPromiseStart(
+    const Http2FrameHeader& header,
+    const Http2PushPromiseFields& promise,
+    size_t total_padding_length) {
+  FAIL() << "OnPushPromiseStart: " << header << "; promise: " << promise
+         << "; total_padding_length: " << total_padding_length;
+}
+
+void FailingHttp2FrameDecoderListener::OnPushPromiseEnd() {
+  FAIL() << "OnPushPromiseEnd";
+}
+
+void FailingHttp2FrameDecoderListener::OnPing(const Http2FrameHeader& header,
+                                              const Http2PingFields& ping) {
+  FAIL() << "OnPing: " << header << "; ping: " << ping;
+}
+
+void FailingHttp2FrameDecoderListener::OnPingAck(const Http2FrameHeader& header,
+                                                 const Http2PingFields& ping) {
+  FAIL() << "OnPingAck: " << header << "; ping: " << ping;
+}
+
+void FailingHttp2FrameDecoderListener::OnGoAwayStart(
+    const Http2FrameHeader& header,
+    const Http2GoAwayFields& goaway) {
+  FAIL() << "OnGoAwayStart: " << header << "; goaway: " << goaway;
+}
+
+void FailingHttp2FrameDecoderListener::OnGoAwayOpaqueData(const char* data,
+                                                          size_t len) {
+  FAIL() << "OnGoAwayOpaqueData: len=" << len;
+}
+
+void FailingHttp2FrameDecoderListener::OnGoAwayEnd() {
+  FAIL() << "OnGoAwayEnd";
+}
+
+void FailingHttp2FrameDecoderListener::OnWindowUpdate(
+    const Http2FrameHeader& header,
+    uint32_t increment) {
+  FAIL() << "OnWindowUpdate: " << header << "; increment=" << increment;
+}
+
+void FailingHttp2FrameDecoderListener::OnAltSvcStart(
+    const Http2FrameHeader& header,
+    size_t origin_length,
+    size_t value_length) {
+  FAIL() << "OnAltSvcStart: " << header << "; origin_length: " << origin_length
+         << "; value_length: " << value_length;
+}
+
+void FailingHttp2FrameDecoderListener::OnAltSvcOriginData(const char* data,
+                                                          size_t len) {
+  FAIL() << "OnAltSvcOriginData: len=" << len;
+}
+
+void FailingHttp2FrameDecoderListener::OnAltSvcValueData(const char* data,
+                                                         size_t len) {
+  FAIL() << "OnAltSvcValueData: len=" << len;
+}
+
+void FailingHttp2FrameDecoderListener::OnAltSvcEnd() {
+  FAIL() << "OnAltSvcEnd";
+}
+
+void FailingHttp2FrameDecoderListener::OnUnknownStart(
+    const Http2FrameHeader& header) {
+  FAIL() << "OnUnknownStart: " << header;
+}
+
+void FailingHttp2FrameDecoderListener::OnUnknownPayload(const char* data,
+                                                        size_t len) {
+  FAIL() << "OnUnknownPayload: len=" << len;
+}
+
+void FailingHttp2FrameDecoderListener::OnUnknownEnd() {
+  FAIL() << "OnUnknownEnd";
+}
+
+void FailingHttp2FrameDecoderListener::OnPaddingTooLong(
+    const Http2FrameHeader& header,
+    size_t missing_length) {
+  FAIL() << "OnPaddingTooLong: " << header
+         << "; missing_length: " << missing_length;
+}
+
+void FailingHttp2FrameDecoderListener::OnFrameSizeError(
+    const Http2FrameHeader& header) {
+  FAIL() << "OnFrameSizeError: " << header;
+}
+
+LoggingHttp2FrameDecoderListener::LoggingHttp2FrameDecoderListener()
+    : wrapped_(nullptr) {}
+LoggingHttp2FrameDecoderListener::LoggingHttp2FrameDecoderListener(
+    Http2FrameDecoderListener* wrapped)
+    : wrapped_(wrapped) {}
+LoggingHttp2FrameDecoderListener::~LoggingHttp2FrameDecoderListener() {}
+
+bool LoggingHttp2FrameDecoderListener::OnFrameHeader(
+    const Http2FrameHeader& header) {
+  VLOG(1) << "OnFrameHeader: " << header;
+  if (wrapped_ != nullptr) {
+    return wrapped_->OnFrameHeader(header);
+  }
+  return true;
+}
+
+void LoggingHttp2FrameDecoderListener::OnDataStart(
+    const Http2FrameHeader& header) {
+  VLOG(1) << "OnDataStart: " << header;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnDataStart(header);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnDataPayload(const char* data,
+                                                     size_t len) {
+  VLOG(1) << "OnDataPayload: len=" << len;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnDataPayload(data, len);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnDataEnd() {
+  VLOG(1) << "OnDataEnd";
+  if (wrapped_ != nullptr) {
+    wrapped_->OnDataEnd();
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnHeadersStart(
+    const Http2FrameHeader& header) {
+  VLOG(1) << "OnHeadersStart: " << header;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnHeadersStart(header);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnHeadersPriority(
+    const Http2PriorityFields& priority) {
+  VLOG(1) << "OnHeadersPriority: " << priority;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnHeadersPriority(priority);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnHpackFragment(const char* data,
+                                                       size_t len) {
+  VLOG(1) << "OnHpackFragment: len=" << len;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnHpackFragment(data, len);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnHeadersEnd() {
+  VLOG(1) << "OnHeadersEnd";
+  if (wrapped_ != nullptr) {
+    wrapped_->OnHeadersEnd();
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnPriorityFrame(
+    const Http2FrameHeader& header,
+    const Http2PriorityFields& priority) {
+  VLOG(1) << "OnPriorityFrame: " << header << "; priority: " << priority;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnPriorityFrame(header, priority);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnContinuationStart(
+    const Http2FrameHeader& header) {
+  VLOG(1) << "OnContinuationStart: " << header;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnContinuationStart(header);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnContinuationEnd() {
+  VLOG(1) << "OnContinuationEnd";
+  if (wrapped_ != nullptr) {
+    wrapped_->OnContinuationEnd();
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnPadLength(size_t trailing_length) {
+  VLOG(1) << "OnPadLength: trailing_length=" << trailing_length;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnPadLength(trailing_length);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnPadding(const char* padding,
+                                                 size_t skipped_length) {
+  VLOG(1) << "OnPadding: skipped_length=" << skipped_length;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnPadding(padding, skipped_length);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnRstStream(
+    const Http2FrameHeader& header,
+    Http2ErrorCode error_code) {
+  VLOG(1) << "OnRstStream: " << header << "; code=" << error_code;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnRstStream(header, error_code);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnSettingsStart(
+    const Http2FrameHeader& header) {
+  VLOG(1) << "OnSettingsStart: " << header;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnSettingsStart(header);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnSetting(
+    const Http2SettingFields& setting_fields) {
+  VLOG(1) << "OnSetting: " << setting_fields;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnSetting(setting_fields);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnSettingsEnd() {
+  VLOG(1) << "OnSettingsEnd";
+  if (wrapped_ != nullptr) {
+    wrapped_->OnSettingsEnd();
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnSettingsAck(
+    const Http2FrameHeader& header) {
+  VLOG(1) << "OnSettingsAck: " << header;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnSettingsAck(header);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnPushPromiseStart(
+    const Http2FrameHeader& header,
+    const Http2PushPromiseFields& promise,
+    size_t total_padding_length) {
+  VLOG(1) << "OnPushPromiseStart: " << header << "; promise: " << promise
+          << "; total_padding_length: " << total_padding_length;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnPushPromiseStart(header, promise, total_padding_length);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnPushPromiseEnd() {
+  VLOG(1) << "OnPushPromiseEnd";
+  if (wrapped_ != nullptr) {
+    wrapped_->OnPushPromiseEnd();
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnPing(const Http2FrameHeader& header,
+                                              const Http2PingFields& ping) {
+  VLOG(1) << "OnPing: " << header << "; ping: " << ping;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnPing(header, ping);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnPingAck(const Http2FrameHeader& header,
+                                                 const Http2PingFields& ping) {
+  VLOG(1) << "OnPingAck: " << header << "; ping: " << ping;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnPingAck(header, ping);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnGoAwayStart(
+    const Http2FrameHeader& header,
+    const Http2GoAwayFields& goaway) {
+  VLOG(1) << "OnGoAwayStart: " << header << "; goaway: " << goaway;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnGoAwayStart(header, goaway);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnGoAwayOpaqueData(const char* data,
+                                                          size_t len) {
+  VLOG(1) << "OnGoAwayOpaqueData: len=" << len;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnGoAwayOpaqueData(data, len);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnGoAwayEnd() {
+  VLOG(1) << "OnGoAwayEnd";
+  if (wrapped_ != nullptr) {
+    wrapped_->OnGoAwayEnd();
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnWindowUpdate(
+    const Http2FrameHeader& header,
+    uint32_t increment) {
+  VLOG(1) << "OnWindowUpdate: " << header << "; increment=" << increment;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnWindowUpdate(header, increment);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnAltSvcStart(
+    const Http2FrameHeader& header,
+    size_t origin_length,
+    size_t value_length) {
+  VLOG(1) << "OnAltSvcStart: " << header << "; origin_length: " << origin_length
+          << "; value_length: " << value_length;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnAltSvcStart(header, origin_length, value_length);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnAltSvcOriginData(const char* data,
+                                                          size_t len) {
+  VLOG(1) << "OnAltSvcOriginData: len=" << len;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnAltSvcOriginData(data, len);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnAltSvcValueData(const char* data,
+                                                         size_t len) {
+  VLOG(1) << "OnAltSvcValueData: len=" << len;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnAltSvcValueData(data, len);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnAltSvcEnd() {
+  VLOG(1) << "OnAltSvcEnd";
+  if (wrapped_ != nullptr) {
+    wrapped_->OnAltSvcEnd();
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnUnknownStart(
+    const Http2FrameHeader& header) {
+  VLOG(1) << "OnUnknownStart: " << header;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnUnknownStart(header);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnUnknownPayload(const char* data,
+                                                        size_t len) {
+  VLOG(1) << "OnUnknownPayload: len=" << len;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnUnknownPayload(data, len);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnUnknownEnd() {
+  VLOG(1) << "OnUnknownEnd";
+  if (wrapped_ != nullptr) {
+    wrapped_->OnUnknownEnd();
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnPaddingTooLong(
+    const Http2FrameHeader& header,
+    size_t missing_length) {
+  VLOG(1) << "OnPaddingTooLong: " << header
+          << "; missing_length: " << missing_length;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnPaddingTooLong(header, missing_length);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnFrameSizeError(
+    const Http2FrameHeader& header) {
+  VLOG(1) << "OnFrameSizeError: " << header;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnFrameSizeError(header);
+  }
+}
+
+}  // namespace net
diff --git a/net/http2/decoder/http2_frame_decoder_listener_test_util.h b/net/http2/decoder/http2_frame_decoder_listener_test_util.h
new file mode 100644
index 0000000..ead1b93a
--- /dev/null
+++ b/net/http2/decoder/http2_frame_decoder_listener_test_util.h
@@ -0,0 +1,141 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_HTTP2_FRAME_DECODER_LISTENER_TEST_UTIL_H_
+#define NET_HTTP2_DECODER_HTTP2_FRAME_DECODER_LISTENER_TEST_UTIL_H_
+
+#include <stddef.h>
+
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+
+// Fail if any of the methods are called. Allows a test to override only the
+// expected calls.
+class FailingHttp2FrameDecoderListener : public Http2FrameDecoderListener {
+ public:
+  FailingHttp2FrameDecoderListener();
+  ~FailingHttp2FrameDecoderListener() override;
+
+  // TODO(jamessynge): Remove OnFrameHeader once done with supporting
+  // SpdyFramer's exact states.
+  bool OnFrameHeader(const Http2FrameHeader& header) override;
+  void OnDataStart(const Http2FrameHeader& header) override;
+  void OnDataPayload(const char* data, size_t len) override;
+  void OnDataEnd() override;
+  void OnHeadersStart(const Http2FrameHeader& header) override;
+  void OnHeadersPriority(const Http2PriorityFields& priority) override;
+  void OnHpackFragment(const char* data, size_t len) override;
+  void OnHeadersEnd() override;
+  void OnPriorityFrame(const Http2FrameHeader& header,
+                       const Http2PriorityFields& priority) override;
+  void OnContinuationStart(const Http2FrameHeader& header) override;
+  void OnContinuationEnd() override;
+  void OnPadLength(size_t trailing_length) override;
+  void OnPadding(const char* padding, size_t skipped_length) override;
+  void OnRstStream(const Http2FrameHeader& header,
+                   Http2ErrorCode error_code) override;
+  void OnSettingsStart(const Http2FrameHeader& header) override;
+  void OnSetting(const Http2SettingFields& setting_fields) override;
+  void OnSettingsEnd() override;
+  void OnSettingsAck(const Http2FrameHeader& header) override;
+  void OnPushPromiseStart(const Http2FrameHeader& header,
+                          const Http2PushPromiseFields& promise,
+                          size_t total_padding_length) override;
+  void OnPushPromiseEnd() override;
+  void OnPing(const Http2FrameHeader& header,
+              const Http2PingFields& ping) override;
+  void OnPingAck(const Http2FrameHeader& header,
+                 const Http2PingFields& ping) override;
+  void OnGoAwayStart(const Http2FrameHeader& header,
+                     const Http2GoAwayFields& goaway) override;
+  void OnGoAwayOpaqueData(const char* data, size_t len) override;
+  void OnGoAwayEnd() override;
+  void OnWindowUpdate(const Http2FrameHeader& header,
+                      uint32_t increment) override;
+  void OnAltSvcStart(const Http2FrameHeader& header,
+                     size_t origin_length,
+                     size_t value_length) override;
+  void OnAltSvcOriginData(const char* data, size_t len) override;
+  void OnAltSvcValueData(const char* data, size_t len) override;
+  void OnAltSvcEnd() override;
+  void OnUnknownStart(const Http2FrameHeader& header) override;
+  void OnUnknownPayload(const char* data, size_t len) override;
+  void OnUnknownEnd() override;
+  void OnPaddingTooLong(const Http2FrameHeader& header,
+                        size_t missing_length) override;
+  void OnFrameSizeError(const Http2FrameHeader& header) override;
+
+ private:
+  void EnsureNotAbstract() { FailingHttp2FrameDecoderListener instance; }
+};
+
+// VLOG's all the calls it receives, and forwards those calls to an optional
+// listener.
+class LoggingHttp2FrameDecoderListener : public Http2FrameDecoderListener {
+ public:
+  LoggingHttp2FrameDecoderListener();
+  explicit LoggingHttp2FrameDecoderListener(Http2FrameDecoderListener* wrapped);
+  ~LoggingHttp2FrameDecoderListener() override;
+
+  // TODO(jamessynge): Remove OnFrameHeader once done with supporting
+  // SpdyFramer's exact states.
+  bool OnFrameHeader(const Http2FrameHeader& header) override;
+  void OnDataStart(const Http2FrameHeader& header) override;
+  void OnDataPayload(const char* data, size_t len) override;
+  void OnDataEnd() override;
+  void OnHeadersStart(const Http2FrameHeader& header) override;
+  void OnHeadersPriority(const Http2PriorityFields& priority) override;
+  void OnHpackFragment(const char* data, size_t len) override;
+  void OnHeadersEnd() override;
+  void OnPriorityFrame(const Http2FrameHeader& header,
+                       const Http2PriorityFields& priority) override;
+  void OnContinuationStart(const Http2FrameHeader& header) override;
+  void OnContinuationEnd() override;
+  void OnPadLength(size_t trailing_length) override;
+  void OnPadding(const char* padding, size_t skipped_length) override;
+  void OnRstStream(const Http2FrameHeader& header,
+                   Http2ErrorCode error_code) override;
+  void OnSettingsStart(const Http2FrameHeader& header) override;
+  void OnSetting(const Http2SettingFields& setting_fields) override;
+  void OnSettingsEnd() override;
+  void OnSettingsAck(const Http2FrameHeader& header) override;
+  void OnPushPromiseStart(const Http2FrameHeader& header,
+                          const Http2PushPromiseFields& promise,
+                          size_t total_padding_length) override;
+  void OnPushPromiseEnd() override;
+  void OnPing(const Http2FrameHeader& header,
+              const Http2PingFields& ping) override;
+  void OnPingAck(const Http2FrameHeader& header,
+                 const Http2PingFields& ping) override;
+  void OnGoAwayStart(const Http2FrameHeader& header,
+                     const Http2GoAwayFields& goaway) override;
+  void OnGoAwayOpaqueData(const char* data, size_t len) override;
+  void OnGoAwayEnd() override;
+  void OnWindowUpdate(const Http2FrameHeader& header,
+                      uint32_t increment) override;
+  void OnAltSvcStart(const Http2FrameHeader& header,
+                     size_t origin_length,
+                     size_t value_length) override;
+  void OnAltSvcOriginData(const char* data, size_t len) override;
+  void OnAltSvcValueData(const char* data, size_t len) override;
+  void OnAltSvcEnd() override;
+  void OnUnknownStart(const Http2FrameHeader& header) override;
+  void OnUnknownPayload(const char* data, size_t len) override;
+  void OnUnknownEnd() override;
+  void OnPaddingTooLong(const Http2FrameHeader& header,
+                        size_t missing_length) override;
+  void OnFrameSizeError(const Http2FrameHeader& header) override;
+
+ private:
+  void EnsureNotAbstract() { LoggingHttp2FrameDecoderListener instance; }
+
+  Http2FrameDecoderListener* wrapped_;
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_HTTP2_FRAME_DECODER_LISTENER_TEST_UTIL_H_
diff --git a/net/http2/decoder/http2_frame_decoder_test.cc b/net/http2/decoder/http2_frame_decoder_test.cc
new file mode 100644
index 0000000..c2ec0a0d
--- /dev/null
+++ b/net/http2/decoder/http2_frame_decoder_test.cc
@@ -0,0 +1,946 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/http2_frame_decoder.h"
+
+// Tests of Http2FrameDecoder.
+
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/logging.h"
+#include "net/http2/decoder/frame_parts.h"
+#include "net/http2/decoder/frame_parts_collector_listener.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/tools/failure.h"
+#include "net/http2/tools/http2_random.h"
+#include "net/http2/tools/random_decoder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::StringPiece;
+using std::string;
+using ::testing::AssertionResult;
+using ::testing::AssertionSuccess;
+
+namespace net {
+namespace test {
+class Http2FrameDecoderPeer {
+ public:
+  static size_t remaining_total_payload(Http2FrameDecoder* decoder) {
+    return decoder->frame_decoder_state_.remaining_total_payload();
+  }
+};
+
+namespace {
+
+class Http2FrameDecoderTest : public RandomDecoderTest {
+ public:
+  AssertionResult ValidatorForDecodePayloadExpectingError(
+      const FrameParts& expected,
+      const DecodeBuffer& input,
+      DecodeStatus status) {
+    VERIFY_EQ(status, DecodeStatus::kDecodeError);
+    VERIFY_AND_RETURN_SUCCESS(VerifyCollected(expected));
+  }
+
+  AssertionResult ValidatorForBeyondMaximum(const FrameParts& expected,
+                                            const DecodeBuffer& input,
+                                            DecodeStatus status) {
+    VERIFY_EQ(status, DecodeStatus::kDecodeError);
+    // The decoder detects this error after decoding the header, and without
+    // trying to decode the payload.
+    VERIFY_EQ(input.Offset(), Http2FrameHeader::EncodedSize());
+    VERIFY_AND_RETURN_SUCCESS(VerifyCollected(expected));
+  }
+
+ protected:
+  void SetUp() override {
+    // On any one run of this suite, we'll always choose the same value for
+    // use_default_reconstruct_ because the random seed is the same for each
+    // test case, but across runs the random seed changes.
+    use_default_reconstruct_ = Random().OneIn(2);
+  }
+
+  DecodeStatus StartDecoding(DecodeBuffer* db) override {
+    DVLOG(2) << "StartDecoding, db->Remaining=" << db->Remaining();
+    collector_.Reset();
+    PrepareDecoder();
+
+    DecodeStatus status = decoder_.DecodeFrame(db);
+    if (status != DecodeStatus::kDecodeInProgress) {
+      // Keep track of this so that a concrete test can verify that both fast
+      // and slow decoding paths have been tested.
+      ++fast_decode_count_;
+      if (status == DecodeStatus::kDecodeError) {
+        ConfirmDiscardsRemainingPayload();
+      }
+    }
+    return status;
+  }
+
+  DecodeStatus ResumeDecoding(DecodeBuffer* db) override {
+    DVLOG(2) << "ResumeDecoding, db->Remaining=" << db->Remaining();
+    DecodeStatus status = decoder_.DecodeFrame(db);
+    if (status != DecodeStatus::kDecodeInProgress) {
+      // Keep track of this so that a concrete test can verify that both fast
+      // and slow decoding paths have been tested.
+      ++slow_decode_count_;
+      if (status == DecodeStatus::kDecodeError) {
+        ConfirmDiscardsRemainingPayload();
+      }
+    }
+    return status;
+  }
+
+  // When an error is returned, the decoder is in state kDiscardPayload, and
+  // stays there until the remaining bytes of the frame's payload have been
+  // skipped over. There are no callbacks for this situation.
+  void ConfirmDiscardsRemainingPayload() {
+    ASSERT_TRUE(decoder_.IsDiscardingPayload());
+    size_t remaining =
+        Http2FrameDecoderPeer::remaining_total_payload(&decoder_);
+    // The decoder will discard the remaining bytes, but not go beyond that,
+    // which these conditions verify.
+    size_t extra = 10;
+    string junk(remaining + extra, '0');
+    DecodeBuffer tmp(junk);
+    EXPECT_EQ(DecodeStatus::kDecodeDone, decoder_.DecodeFrame(&tmp));
+    EXPECT_EQ(remaining, tmp.Offset());
+    EXPECT_EQ(extra, tmp.Remaining());
+    EXPECT_FALSE(decoder_.IsDiscardingPayload());
+  }
+
+  void PrepareDecoder() {
+    // Save and restore the maximum_payload_size when reconstructing
+    // the decoder.
+    size_t maximum_payload_size = decoder_.maximum_payload_size();
+
+    // Alternate which constructor is used.
+    if (use_default_reconstruct_) {
+      decoder_.~Http2FrameDecoder();
+      new (&decoder_) Http2FrameDecoder;
+      decoder_.set_listener(&collector_);
+    } else {
+      decoder_.~Http2FrameDecoder();
+      new (&decoder_) Http2FrameDecoder(&collector_);
+    }
+    decoder_.set_maximum_payload_size(maximum_payload_size);
+
+    use_default_reconstruct_ = !use_default_reconstruct_;
+  }
+
+  void ResetDecodeSpeedCounters() {
+    fast_decode_count_ = 0;
+    slow_decode_count_ = 0;
+  }
+
+  AssertionResult VerifyCollected(const FrameParts& expected) {
+    VERIFY_FALSE(collector_.IsInProgress());
+    VERIFY_EQ(1u, collector_.size());
+    VERIFY_AND_RETURN_SUCCESS(expected.VerifyEquals(*collector_.frame(0)));
+  }
+
+  AssertionResult DecodePayloadAndValidateSeveralWays(StringPiece payload,
+                                                      Validator validator) {
+    DecodeBuffer db(payload);
+    bool start_decoding_requires_non_empty = false;
+    return DecodeAndValidateSeveralWays(&db, start_decoding_requires_non_empty,
+                                        validator);
+  }
+
+  AssertionResult ValidatorForDecodePayloadAndValidateSeveralWays(
+      const FrameParts& expected,
+      const DecodeBuffer& input,
+      DecodeStatus status) {
+    VERIFY_EQ(status, DecodeStatus::kDecodeDone);
+    VERIFY_AND_RETURN_SUCCESS(VerifyCollected(expected));
+  }
+
+  // Decode one frame's payload and confirm that the listener recorded the
+  // expected FrameParts instance, and only one FrameParts instance. The
+  // payload will be decoded several times with different partitionings
+  // of the payload, and after each the validator will be called.
+  AssertionResult DecodePayloadAndValidateSeveralWays(
+      StringPiece payload,
+      const FrameParts& expected) {
+    Validator validator = base::Bind(
+        &Http2FrameDecoderTest::ValidatorForDecodePayloadAndValidateSeveralWays,
+        base::Unretained(this), base::ConstRef(expected));
+    ResetDecodeSpeedCounters();
+    VERIFY_SUCCESS(DecodePayloadAndValidateSeveralWays(
+        payload, ValidateDoneAndEmpty(validator)));
+    VERIFY_GT(fast_decode_count_, 0u);
+    VERIFY_GT(slow_decode_count_, 0u);
+
+    // Repeat with more input; it should stop without reading that input.
+    string next_frame = Random().RandString(10);
+    string input;
+    payload.AppendToString(&input);
+    input += next_frame;
+
+    ResetDecodeSpeedCounters();
+    VERIFY_SUCCESS(DecodePayloadAndValidateSeveralWays(
+        payload, ValidateDoneAndOffset(payload.size(), validator)));
+    VERIFY_GT(fast_decode_count_, 0u);
+    VERIFY_GT(slow_decode_count_, 0u);
+
+    return AssertionSuccess();
+  }
+
+  template <size_t N>
+  AssertionResult DecodePayloadAndValidateSeveralWays(
+      const char (&buf)[N],
+      const FrameParts& expected) {
+    return DecodePayloadAndValidateSeveralWays(StringPiece(buf, N), expected);
+  }
+
+  template <size_t N>
+  AssertionResult DecodePayloadAndValidateSeveralWays(
+      const char (&buf)[N],
+      const Http2FrameHeader& header) {
+    return DecodePayloadAndValidateSeveralWays(StringPiece(buf, N),
+                                               FrameParts(header));
+  }
+
+  template <size_t N>
+  AssertionResult DecodePayloadExpectingError(const char (&buf)[N],
+                                              const FrameParts& expected) {
+    ResetDecodeSpeedCounters();
+    EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(
+        ToStringPiece(buf),
+        base::Bind(
+            &Http2FrameDecoderTest::ValidatorForDecodePayloadExpectingError,
+            base::Unretained(this), expected)));
+    EXPECT_GT(fast_decode_count_, 0u);
+    EXPECT_GT(slow_decode_count_, 0u);
+    return AssertionSuccess();
+  }
+
+  template <size_t N>
+  AssertionResult DecodePayloadExpectingFrameSizeError(const char (&buf)[N],
+                                                       FrameParts expected) {
+    expected.has_frame_size_error = true;
+    VERIFY_AND_RETURN_SUCCESS(DecodePayloadExpectingError(buf, expected));
+  }
+
+  template <size_t N>
+  AssertionResult DecodePayloadExpectingFrameSizeError(
+      const char (&buf)[N],
+      const Http2FrameHeader& header) {
+    return DecodePayloadExpectingFrameSizeError(buf, FrameParts(header));
+  }
+
+  // Count of payloads that are fully decoded by StartDecodingPayload or for
+  // which an error was detected by StartDecodingPayload.
+  size_t fast_decode_count_ = 0;
+
+  // Count of payloads that required calling ResumeDecodingPayload in order to
+  // decode completely, or for which an error was detected by
+  // ResumeDecodingPayload.
+  size_t slow_decode_count_ = 0;
+
+  FramePartsCollectorListener collector_;
+  Http2FrameDecoder decoder_;
+  bool use_default_reconstruct_;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// Tests that pass the minimum allowed size for the frame type, which is often
+// empty. The tests are in order by frame type value (i.e. 0 for DATA frames).
+
+TEST_F(Http2FrameDecoderTest, DataEmpty) {
+  const char kFrameData[] = {
+      0x00, 0x00, 0x00,        // Payload length: 0
+      0x00,                    // DATA
+      0x00,                    // Flags: none
+      0x00, 0x00, 0x00, 0x00,  // Stream ID: 0 (invalid but unchecked here)
+  };
+  Http2FrameHeader header(0, Http2FrameType::DATA, 0, 0);
+  FrameParts expected(header, "");
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, HeadersEmpty) {
+  const char kFrameData[] = {
+      0x00, 0x00, 0x00,        // Payload length: 0
+      0x01,                    // HEADERS
+      0x00,                    // Flags: none
+      0x00, 0x00, 0x00, 0x01,  // Stream ID: 0  (REQUIRES ID)
+  };
+  Http2FrameHeader header(0, Http2FrameType::HEADERS, 0, 1);
+  FrameParts expected(header, "");
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, Priority) {
+  const char kFrameData[] = {
+      0x00,  0x00, 0x05,        // Length: 5
+      0x02,                     //   Type: PRIORITY
+      0x00,                     //  Flags: none
+      0x00,  0x00, 0x00, 0x02,  // Stream: 2
+      0x80u, 0x00, 0x00, 0x01,  // Parent: 1 (Exclusive)
+      0x10,                     // Weight: 17
+  };
+  Http2FrameHeader header(5, Http2FrameType::PRIORITY, 0, 2);
+  FrameParts expected(header);
+  expected.opt_priority = Http2PriorityFields(1, 17, true);
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, RstStream) {
+  const char kFrameData[] = {
+      0x00, 0x00, 0x04,        // Length: 4
+      0x03,                    //   Type: RST_STREAM
+      0x00,                    //  Flags: none
+      0x00, 0x00, 0x00, 0x01,  // Stream: 1
+      0x00, 0x00, 0x00, 0x01,  //  Error: PROTOCOL_ERROR
+  };
+  Http2FrameHeader header(4, Http2FrameType::RST_STREAM, 0, 1);
+  FrameParts expected(header);
+  expected.opt_rst_stream_error_code = Http2ErrorCode::PROTOCOL_ERROR;
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, SettingsEmpty) {
+  const char kFrameData[] = {
+      0x00, 0x00, 0x00,        // Length: 0
+      0x04,                    //   Type: SETTINGS
+      0x00,                    //  Flags: none
+      0x00, 0x00, 0x00, 0x01,  // Stream: 1 (invalid but unchecked here)
+  };
+  Http2FrameHeader header(0, Http2FrameType::SETTINGS, 0, 1);
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, header));
+}
+
+TEST_F(Http2FrameDecoderTest, SettingsAck) {
+  const char kFrameData[] = {
+      0x00, 0x00, 0x00,        //   Length: 6
+      0x04,                    //     Type: SETTINGS
+      0x01,                    //    Flags: ACK
+      0x00, 0x00, 0x00, 0x00,  //   Stream: 0
+  };
+  Http2FrameHeader header(0, Http2FrameType::SETTINGS, Http2FrameFlag::FLAG_ACK,
+                          0);
+  FrameParts expected(header);
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, PushPromiseMinimal) {
+  const char kFrameData[] = {
+      0x00, 0x00, 0x04,        // Payload length: 4
+      0x05,                    // PUSH_PROMISE
+      0x04,                    // Flags: END_HEADERS
+      0x00, 0x00, 0x00, 0x02,  //   Stream: 2 (invalid but unchecked here)
+      0x00, 0x00, 0x00, 0x01,  // Promised: 1 (invalid but unchecked here)
+  };
+  Http2FrameHeader header(4, Http2FrameType::PUSH_PROMISE,
+                          Http2FrameFlag::FLAG_END_HEADERS, 2);
+  FrameParts expected(header, "");
+  expected.opt_push_promise = Http2PushPromiseFields{1};
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, Ping) {
+  const char kFrameData[] = {
+      0x00,  0x00, 0x08,        //   Length: 8
+      0x06,                     //     Type: PING
+      0xfeu,                    //    Flags: no valid flags
+      0x00,  0x00, 0x00, 0x00,  //   Stream: 0
+      's',   'o',  'm',  'e',   // "some"
+      'd',   'a',  't',  'a',   // "data"
+  };
+  Http2FrameHeader header(8, Http2FrameType::PING, 0, 0);
+  FrameParts expected(header);
+  expected.opt_ping = Http2PingFields{{'s', 'o', 'm', 'e', 'd', 'a', 't', 'a'}};
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, PingAck) {
+  const char kFrameData[] = {
+      0x00,  0x00, 0x08,        //   Length: 8
+      0x06,                     //     Type: PING
+      0xffu,                    //    Flags: ACK (plus all invalid flags)
+      0x00,  0x00, 0x00, 0x00,  //   Stream: 0
+      's',   'o',  'm',  'e',   // "some"
+      'd',   'a',  't',  'a',   // "data"
+  };
+  Http2FrameHeader header(8, Http2FrameType::PING, Http2FrameFlag::FLAG_ACK, 0);
+  FrameParts expected(header);
+  expected.opt_ping = Http2PingFields{{'s', 'o', 'm', 'e', 'd', 'a', 't', 'a'}};
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, GoAwayMinimal) {
+  const char kFrameData[] = {
+      0x00,  0x00, 0x08,         // Length: 8 (no opaque data)
+      0x07,                      //   Type: GOAWAY
+      0xffu,                     //  Flags: 0xff (no valid flags)
+      0x00,  0x00, 0x00, 0x01,   // Stream: 1 (invalid but unchecked here)
+      0x80u, 0x00, 0x00, 0xffu,  //   Last: 255 (plus R bit)
+      0x00,  0x00, 0x00, 0x09,   //  Error: COMPRESSION_ERROR
+  };
+  Http2FrameHeader header(8, Http2FrameType::GOAWAY, 0, 1);
+  FrameParts expected(header);
+  expected.opt_goaway =
+      Http2GoAwayFields(255, Http2ErrorCode::COMPRESSION_ERROR);
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, WindowUpdate) {
+  const char kFrameData[] = {
+      0x00,  0x00, 0x04,        // Length: 4
+      0x08,                     //   Type: WINDOW_UPDATE
+      0x0f,                     //  Flags: 0xff (no valid flags)
+      0x00,  0x00, 0x00, 0x01,  // Stream: 1
+      0x80u, 0x00, 0x04, 0x00,  //   Incr: 1024 (plus R bit)
+  };
+  Http2FrameHeader header(4, Http2FrameType::WINDOW_UPDATE, 0, 1);
+  FrameParts expected(header);
+  expected.opt_window_update_increment = 1024;
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, ContinuationEmpty) {
+  const char kFrameData[] = {
+      0x00, 0x00, 0x00,        // Payload length: 0
+      0x09,                    // CONTINUATION
+      0x00,                    // Flags: none
+      0x00, 0x00, 0x00, 0x00,  // Stream ID: 0 (invalid but unchecked here)
+  };
+  Http2FrameHeader header(0, Http2FrameType::CONTINUATION, 0, 0);
+  FrameParts expected(header);
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, AltSvcMinimal) {
+  const char kFrameData[] = {
+      0x00,  0x00, 0x02,        // Payload length: 2
+      0x0a,                     // ALTSVC
+      0xffu,                    // Flags: none (plus 0xff)
+      0x00,  0x00, 0x00, 0x00,  // Stream ID: 0 (invalid but unchecked here)
+      0x00,  0x00,              // Origin Length: 0
+  };
+  Http2FrameHeader header(2, Http2FrameType::ALTSVC, 0, 0);
+  FrameParts expected(header);
+  expected.opt_altsvc_origin_length = 0;
+  expected.opt_altsvc_value_length = 0;
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, UnknownEmpty) {
+  const char kFrameData[] = {
+      0x00,  0x00, 0x00,        // Payload length: 0
+      0x20,                     // 32 (unknown)
+      0xffu,                    // Flags: all
+      0x00,  0x00, 0x00, 0x00,  // Stream ID: 0
+  };
+  Http2FrameHeader header(0, static_cast<Http2FrameType>(32), 0xff, 0);
+  FrameParts expected(header);
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Tests of longer payloads, for those frame types that allow longer payloads.
+
+TEST_F(Http2FrameDecoderTest, DataPayload) {
+  const char kFrameData[] = {
+      0x00,  0x00, 0x03,        // Payload length: 7
+      0x00,                     // DATA
+      0x80u,                    // Flags: 0x80
+      0x00,  0x00, 0x02, 0x02,  // Stream ID: 514
+      'a',   'b',  'c',         // Data
+  };
+  Http2FrameHeader header(3, Http2FrameType::DATA, 0, 514);
+  FrameParts expected(header, "abc");
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, HeadersPayload) {
+  const char kFrameData[] = {
+      0x00, 0x00, 0x03,        // Payload length: 3
+      0x01,                    // HEADERS
+      0x05,                    // Flags: END_STREAM | END_HEADERS
+      0x00, 0x00, 0x00, 0x02,  // Stream ID: 0  (REQUIRES ID)
+      'a',  'b',  'c',         // HPACK fragment (doesn't have to be valid)
+  };
+  Http2FrameHeader header(
+      3, Http2FrameType::HEADERS,
+      Http2FrameFlag::FLAG_END_STREAM | Http2FrameFlag::FLAG_END_HEADERS, 2);
+  FrameParts expected(header, "abc");
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, HeadersPriority) {
+  const char kFrameData[] = {
+      0x00,  0x00, 0x05,        // Payload length: 5
+      0x01,                     // HEADERS
+      0x20,                     // Flags: PRIORITY
+      0x00,  0x00, 0x00, 0x02,  // Stream ID: 0  (REQUIRES ID)
+      0x00,  0x00, 0x00, 0x01,  // Parent: 1 (Not Exclusive)
+      0xffu,                    // Weight: 256
+  };
+  Http2FrameHeader header(5, Http2FrameType::HEADERS,
+                          Http2FrameFlag::FLAG_PRIORITY, 2);
+  FrameParts expected(header);
+  expected.opt_priority = Http2PriorityFields(1, 256, false);
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, Settings) {
+  const char kFrameData[] = {
+      0x00, 0x00, 0x0c,        // Length: 12
+      0x04,                    //   Type: SETTINGS
+      0x00,                    //  Flags: none
+      0x00, 0x00, 0x00, 0x00,  // Stream: 0
+      0x00, 0x04,              //  Param: INITIAL_WINDOW_SIZE
+      0x0a, 0x0b, 0x0c, 0x0d,  //  Value: 168496141
+      0x00, 0x02,              //  Param: ENABLE_PUSH
+      0x00, 0x00, 0x00, 0x03,  //  Value: 3 (invalid but unchecked here)
+  };
+  Http2FrameHeader header(12, Http2FrameType::SETTINGS, 0, 0);
+  FrameParts expected(header);
+  expected.settings.push_back(Http2SettingFields(
+      Http2SettingsParameter::INITIAL_WINDOW_SIZE, 168496141));
+  expected.settings.push_back(
+      Http2SettingFields(Http2SettingsParameter::ENABLE_PUSH, 3));
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, PushPromisePayload) {
+  const char kFrameData[] = {
+      0x00, 0x00, 7,            // Payload length: 7
+      0x05,                     // PUSH_PROMISE
+      0x04,                     // Flags: END_HEADERS
+      0x00, 0x00, 0x00, 0xffu,  // Stream ID: 255
+      0x00, 0x00, 0x01, 0x00,   // Promised: 256
+      'a',  'b',  'c',          // HPACK fragment (doesn't have to be valid)
+  };
+  Http2FrameHeader header(7, Http2FrameType::PUSH_PROMISE,
+                          Http2FrameFlag::FLAG_END_HEADERS, 255);
+  FrameParts expected(header, "abc");
+  expected.opt_push_promise = Http2PushPromiseFields{256};
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, GoAwayOpaqueData) {
+  const char kFrameData[] = {
+      0x00,  0x00, 0x0e,        // Length: 14
+      0x07,                     //   Type: GOAWAY
+      0xffu,                    //  Flags: 0xff (no valid flags)
+      0x80u, 0x00, 0x00, 0x00,  // Stream: 0 (plus R bit)
+      0x00,  0x00, 0x01, 0x00,  //   Last: 256
+      0x00,  0x00, 0x00, 0x03,  //  Error: FLOW_CONTROL_ERROR
+      'o',   'p',  'a',  'q',  'u', 'e',
+  };
+  Http2FrameHeader header(14, Http2FrameType::GOAWAY, 0, 0);
+  FrameParts expected(header, "opaque");
+  expected.opt_goaway =
+      Http2GoAwayFields(256, Http2ErrorCode::FLOW_CONTROL_ERROR);
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, ContinuationPayload) {
+  const char kFrameData[] = {
+      0x00,  0x00, 0x03,        // Payload length: 3
+      0x09,                     // CONTINUATION
+      0xffu,                    // Flags: END_HEADERS | 0xfb
+      0x00,  0x00, 0x00, 0x02,  // Stream ID: 2
+      'a',   'b',  'c',         // Data
+  };
+  Http2FrameHeader header(3, Http2FrameType::CONTINUATION,
+                          Http2FrameFlag::FLAG_END_HEADERS, 2);
+  FrameParts expected(header, "abc");
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, AltSvcPayload) {
+  const char kFrameData[] = {
+      0x00, 0x00, 0x08,        // Payload length: 3
+      0x0a,                    // ALTSVC
+      0x00,                    // Flags: none
+      0x00, 0x00, 0x00, 0x02,  // Stream ID: 2
+      0x00, 0x03,              // Origin Length: 0
+      'a',  'b',  'c',         // Origin
+      'd',  'e',  'f',         // Value
+  };
+  Http2FrameHeader header(8, Http2FrameType::ALTSVC, 0, 2);
+  FrameParts expected(header);
+  expected.SetAltSvcExpected("abc", "def");
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, UnknownPayload) {
+  const char kFrameData[] = {
+      0x00, 0x00, 0x03,        // Payload length: 3
+      0x30,                    // 48 (unknown)
+      0x00,                    // Flags: none
+      0x00, 0x00, 0x00, 0x02,  // Stream ID: 2
+      'a',  'b',  'c',         // Payload
+  };
+  Http2FrameHeader header(3, static_cast<Http2FrameType>(48), 0, 2);
+  FrameParts expected(header, "abc");
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Tests of padded payloads, for those frame types that allow padding.
+
+TEST_F(Http2FrameDecoderTest, DataPayloadAndPadding) {
+  const char kFrameData[] = {
+      0x00, 0x00, 0x07,        // Payload length: 7
+      0x00,                    // DATA
+      0x09,                    // Flags: END_STREAM | PADDED
+      0x00, 0x00, 0x00, 0x02,  // Stream ID: 0  (REQUIRES ID)
+      0x03,                    // Pad Len
+      'a',  'b',  'c',         // Data
+      0x00, 0x00, 0x00,        // Padding
+  };
+  Http2FrameHeader header(
+      7, Http2FrameType::DATA,
+      Http2FrameFlag::FLAG_END_STREAM | Http2FrameFlag::FLAG_PADDED, 2);
+  size_t total_pad_length = 4;  // Including the Pad Length field.
+  FrameParts expected(header, "abc", total_pad_length);
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, HeadersPayloadAndPadding) {
+  const char kFrameData[] = {
+      0x00, 0x00, 0x07,        // Payload length: 7
+      0x01,                    // HEADERS
+      0x08,                    // Flags: PADDED
+      0x00, 0x00, 0x00, 0x02,  // Stream ID: 0  (REQUIRES ID)
+      0x03,                    // Pad Len
+      'a',  'b',  'c',         // HPACK fragment (doesn't have to be valid)
+      0x00, 0x00, 0x00,        // Padding
+  };
+  Http2FrameHeader header(7, Http2FrameType::HEADERS,
+                          Http2FrameFlag::FLAG_PADDED, 2);
+  size_t total_pad_length = 4;  // Including the Pad Length field.
+  FrameParts expected(header, "abc", total_pad_length);
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, HeadersPayloadPriorityAndPadding) {
+  const char kFrameData[] = {
+      0x00,  0x00, 0x0c,        // Payload length: 12
+      0x01,                     // HEADERS
+      0xffu,                    // Flags: all, including undefined
+      0x00,  0x00, 0x00, 0x02,  // Stream ID: 0  (REQUIRES ID)
+      0x03,                     // Pad Len
+      0x80u, 0x00, 0x00, 0x01,  // Parent: 1 (Exclusive)
+      0x10,                     // Weight: 17
+      'a',   'b',  'c',         // HPACK fragment (doesn't have to be valid)
+      0x00,  0x00, 0x00,        // Padding
+  };
+  Http2FrameHeader header(
+      12, Http2FrameType::HEADERS,
+      Http2FrameFlag::FLAG_END_STREAM | Http2FrameFlag::FLAG_END_HEADERS |
+          Http2FrameFlag::FLAG_PADDED | Http2FrameFlag::FLAG_PRIORITY,
+      2);
+  size_t total_pad_length = 4;  // Including the Pad Length field.
+  FrameParts expected(header, "abc", total_pad_length);
+  expected.opt_priority = Http2PriorityFields(1, 17, true);
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, PushPromisePayloadAndPadding) {
+  const char kFrameData[] = {
+      0x00,  0x00, 11,          // Payload length: 11
+      0x05,                     // PUSH_PROMISE
+      0xffu,                    // Flags: END_HEADERS | PADDED | 0xf3
+      0x00,  0x00, 0x00, 0x01,  // Stream ID: 1
+      0x03,                     // Pad Len
+      0x00,  0x00, 0x00, 0x02,  // Promised: 2
+      'a',   'b',  'c',         // HPACK fragment (doesn't have to be valid)
+      0x00,  0x00, 0x00,        // Padding
+  };
+  Http2FrameHeader header(
+      11, Http2FrameType::PUSH_PROMISE,
+      Http2FrameFlag::FLAG_END_HEADERS | Http2FrameFlag::FLAG_PADDED, 1);
+  size_t total_pad_length = 4;  // Including the Pad Length field.
+  FrameParts expected(header, "abc", total_pad_length);
+  expected.opt_push_promise = Http2PushPromiseFields{2};
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Payload too short errors.
+
+TEST_F(Http2FrameDecoderTest, DataMissingPadLengthField) {
+  const char kFrameData[] = {
+      0x00, 0x00, 0x00,        // Payload length: 0
+      0x00,                    // DATA
+      0x08,                    // Flags: PADDED
+      0x00, 0x00, 0x00, 0x01,  // Stream ID: 1
+  };
+  Http2FrameHeader header(0, Http2FrameType::DATA, Http2FrameFlag::FLAG_PADDED,
+                          1);
+  FrameParts expected(header);
+  expected.opt_missing_length = 1;
+  EXPECT_TRUE(DecodePayloadExpectingError(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, HeaderPaddingTooLong) {
+  const char kFrameData[] = {
+      0x00,  0x00, 0x02,        // Payload length: 0
+      0x01,                     // HEADERS
+      0x08,                     // Flags: PADDED
+      0x00,  0x01, 0x00, 0x00,  // Stream ID: 65536
+      0xffu,                    // Pad Len: 255
+      0x00,                     // Only one byte of padding
+  };
+  Http2FrameHeader header(2, Http2FrameType::HEADERS,
+                          Http2FrameFlag::FLAG_PADDED, 65536);
+  FrameParts expected(header);
+  expected.opt_missing_length = 254;
+  EXPECT_TRUE(DecodePayloadExpectingError(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, HeaderMissingPriority) {
+  const char kFrameData[] = {
+      0x00, 0x00, 0x04,        // Payload length: 0
+      0x01,                    // HEADERS
+      0x20,                    // Flags: PRIORITY
+      0x00, 0x01, 0x00, 0x00,  // Stream ID: 65536
+      0x00, 0x00, 0x00, 0x00,  // Priority (truncated)
+  };
+  Http2FrameHeader header(4, Http2FrameType::HEADERS,
+                          Http2FrameFlag::FLAG_PRIORITY, 65536);
+  EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
+}
+
+TEST_F(Http2FrameDecoderTest, PriorityTooShort) {
+  const char kFrameData[] = {
+      0x00,  0x00, 0x04,        // Length: 5
+      0x02,                     //   Type: PRIORITY
+      0x00,                     //  Flags: none
+      0x00,  0x00, 0x00, 0x02,  // Stream: 2
+      0x80u, 0x00, 0x00, 0x01,  // Parent: 1 (Exclusive)
+  };
+  Http2FrameHeader header(4, Http2FrameType::PRIORITY, 0, 2);
+  EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
+}
+
+TEST_F(Http2FrameDecoderTest, RstStreamTooShort) {
+  const char kFrameData[] = {
+      0x00, 0x00, 0x03,        // Length: 4
+      0x03,                    //   Type: RST_STREAM
+      0x00,                    //  Flags: none
+      0x00, 0x00, 0x00, 0x01,  // Stream: 1
+      0x00, 0x00, 0x00,        //  Truncated
+  };
+  Http2FrameHeader header(3, Http2FrameType::RST_STREAM, 0, 1);
+  EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
+}
+
+// SETTINGS frames must a multiple of 6 bytes long, so an 9 byte payload is
+// invalid.
+TEST_F(Http2FrameDecoderTest, SettingsWrongSize) {
+  const char kFrameData[] = {
+      0x00, 0x00, 0x09,        // Length: 2
+      0x04,                    //   Type: SETTINGS
+      0x00,                    //  Flags: none
+      0x00, 0x00, 0x00, 0x00,  // Stream: 0
+      0x00, 0x02,              //  Param: ENABLE_PUSH
+      0x00, 0x00, 0x00, 0x03,  //  Value: 1
+      0x00, 0x04,              //  Param: INITIAL_WINDOW_SIZE
+      0x00,                    //  Value: Truncated
+  };
+  Http2FrameHeader header(9, Http2FrameType::SETTINGS, 0, 0);
+  FrameParts expected(header);
+  expected.settings.push_back(
+      Http2SettingFields(Http2SettingsParameter::ENABLE_PUSH, 3));
+  EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, PushPromiseTooShort) {
+  const char kFrameData[] = {
+      0x00, 0x00, 3,           // Payload length: 3
+      0x05,                    // PUSH_PROMISE
+      0x00,                    // Flags: none
+      0x00, 0x00, 0x00, 0x01,  // Stream ID: 1
+      0x00, 0x00, 0x00,        // Truncated promise id
+  };
+  Http2FrameHeader header(3, Http2FrameType::PUSH_PROMISE, 0, 1);
+  EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
+}
+
+TEST_F(Http2FrameDecoderTest, PushPromisePaddedTruncatedPromise) {
+  const char kFrameData[] = {
+      0x00, 0x00, 4,           // Payload length: 4
+      0x05,                    // PUSH_PROMISE
+      0x08,                    // Flags: PADDED
+      0x00, 0x00, 0x00, 0x01,  // Stream ID: 1
+      0x00,                    // Pad Len
+      0x00, 0x00, 0x00,        // Truncated promise id
+  };
+  Http2FrameHeader header(4, Http2FrameType::PUSH_PROMISE,
+                          Http2FrameFlag::FLAG_PADDED, 1);
+  EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
+}
+
+TEST_F(Http2FrameDecoderTest, PingTooShort) {
+  const char kFrameData[] = {
+      0x00,  0x00, 0x07,        //   Length: 8
+      0x06,                     //     Type: PING
+      0xfeu,                    //    Flags: no valid flags
+      0x00,  0x00, 0x00, 0x00,  //   Stream: 0
+      's',   'o',  'm',  'e',   // "some"
+      'd',   'a',  't',         // Too little
+  };
+  Http2FrameHeader header(7, Http2FrameType::PING, 0, 0);
+  EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
+}
+
+TEST_F(Http2FrameDecoderTest, GoAwayTooShort) {
+  const char kFrameData[] = {
+      0x00,  0x00, 0x00,        // Length: 0
+      0x07,                     //   Type: GOAWAY
+      0xffu,                    //  Flags: 0xff (no valid flags)
+      0x00,  0x00, 0x00, 0x00,  // Stream: 0
+  };
+  Http2FrameHeader header(0, Http2FrameType::GOAWAY, 0, 0);
+  EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
+}
+
+TEST_F(Http2FrameDecoderTest, WindowUpdateTooShort) {
+  const char kFrameData[] = {
+      0x00,  0x00, 0x03,        // Length: 3
+      0x08,                     //   Type: WINDOW_UPDATE
+      0x0f,                     //  Flags: 0xff (no valid flags)
+      0x00,  0x00, 0x00, 0x01,  // Stream: 1
+      0x80u, 0x00, 0x04,        // Truncated
+  };
+  Http2FrameHeader header(3, Http2FrameType::WINDOW_UPDATE, 0, 1);
+  EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
+}
+
+TEST_F(Http2FrameDecoderTest, AltSvcTruncatedOriginLength) {
+  const char kFrameData[] = {
+      0x00, 0x00, 0x01,        // Payload length: 3
+      0x0a,                    // ALTSVC
+      0x00,                    // Flags: none
+      0x00, 0x00, 0x00, 0x02,  // Stream ID: 2
+      0x00,                    // Origin Length: truncated
+  };
+  Http2FrameHeader header(1, Http2FrameType::ALTSVC, 0, 2);
+  EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
+}
+
+TEST_F(Http2FrameDecoderTest, AltSvcTruncatedOrigin) {
+  const char kFrameData[] = {
+      0x00, 0x00, 0x05,        // Payload length: 3
+      0x0a,                    // ALTSVC
+      0x00,                    // Flags: none
+      0x00, 0x00, 0x00, 0x02,  // Stream ID: 2
+      0x00, 0x04,              // Origin Length: 4 (too long)
+      'a',  'b',  'c',         // Origin
+  };
+  Http2FrameHeader header(5, Http2FrameType::ALTSVC, 0, 2);
+  EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Payload too long errors.
+
+// The decoder calls the listener's OnFrameSizeError method if the frame's
+// payload is longer than the currently configured maximum payload size.
+TEST_F(Http2FrameDecoderTest, BeyondMaximum) {
+  decoder_.set_maximum_payload_size(2);
+  const char kFrameData[] = {
+      0x00, 0x00, 0x07,        // Payload length: 7
+      0x00,                    // DATA
+      0x09,                    // Flags: END_STREAM | PADDED
+      0x00, 0x00, 0x00, 0x02,  // Stream ID: 0  (REQUIRES ID)
+      0x03,                    // Pad Len
+      'a',  'b',  'c',         // Data
+      0x00, 0x00, 0x00,        // Padding
+  };
+  Http2FrameHeader header(
+      7, Http2FrameType::DATA,
+      Http2FrameFlag::FLAG_END_STREAM | Http2FrameFlag::FLAG_PADDED, 2);
+  FrameParts expected(header);
+  expected.has_frame_size_error = true;
+  ResetDecodeSpeedCounters();
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(
+      ToStringPiece(kFrameData),
+      base::Bind(&Http2FrameDecoderTest::ValidatorForBeyondMaximum,
+                 base::Unretained(this), expected)));
+  EXPECT_GT(fast_decode_count_, 0u);
+  EXPECT_GT(slow_decode_count_, 0u);
+}
+
+TEST_F(Http2FrameDecoderTest, PriorityTooLong) {
+  const char kFrameData[] = {
+      0x00,  0x00, 0x06,        // Length: 5
+      0x02,                     //   Type: PRIORITY
+      0x00,                     //  Flags: none
+      0x00,  0x00, 0x00, 0x02,  // Stream: 2
+      0x80u, 0x00, 0x00, 0x01,  // Parent: 1 (Exclusive)
+      0x10,                     // Weight: 17
+      0x00,                     // Too much
+  };
+  Http2FrameHeader header(6, Http2FrameType::PRIORITY, 0, 2);
+  EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
+}
+
+TEST_F(Http2FrameDecoderTest, RstStreamTooLong) {
+  const char kFrameData[] = {
+      0x00, 0x00, 0x05,        // Length: 4
+      0x03,                    //   Type: RST_STREAM
+      0x00,                    //  Flags: none
+      0x00, 0x00, 0x00, 0x01,  // Stream: 1
+      0x00, 0x00, 0x00, 0x01,  //  Error: PROTOCOL_ERROR
+      0x00,                    // Too much
+  };
+  Http2FrameHeader header(5, Http2FrameType::RST_STREAM, 0, 1);
+  EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
+}
+
+TEST_F(Http2FrameDecoderTest, SettingsAckTooLong) {
+  const char kFrameData[] = {
+      0x00, 0x00, 0x06,        //   Length: 6
+      0x04,                    //     Type: SETTINGS
+      0x01,                    //    Flags: ACK
+      0x00, 0x00, 0x00, 0x00,  //   Stream: 0
+      0x00, 0x00,              //   Extra
+      0x00, 0x00, 0x00, 0x00,  //   Extra
+  };
+  Http2FrameHeader header(6, Http2FrameType::SETTINGS, Http2FrameFlag::FLAG_ACK,
+                          0);
+  EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
+}
+
+TEST_F(Http2FrameDecoderTest, PingAckTooLong) {
+  const char kFrameData[] = {
+      0x00,  0x00, 0x09,        //   Length: 8
+      0x06,                     //     Type: PING
+      0xffu,                    //    Flags: ACK | 0xfe
+      0x00,  0x00, 0x00, 0x00,  //   Stream: 0
+      's',   'o',  'm',  'e',   // "some"
+      'd',   'a',  't',  'a',   // "data"
+      0x00,                     // Too much
+  };
+  Http2FrameHeader header(9, Http2FrameType::PING, Http2FrameFlag::FLAG_ACK, 0);
+  EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
+}
+
+TEST_F(Http2FrameDecoderTest, WindowUpdateTooLong) {
+  const char kFrameData[] = {
+      0x00,  0x00, 0x05,        // Length: 5
+      0x08,                     //   Type: WINDOW_UPDATE
+      0x0f,                     //  Flags: 0xff (no valid flags)
+      0x00,  0x00, 0x00, 0x01,  // Stream: 1
+      0x80u, 0x00, 0x04, 0x00,  //   Incr: 1024 (plus R bit)
+      0x00,                     // Too much
+  };
+  Http2FrameHeader header(5, Http2FrameType::WINDOW_UPDATE, 0, 1);
+  EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/decoder/http2_structure_decoder.cc b/net/http2/decoder/http2_structure_decoder.cc
new file mode 100644
index 0000000..a7b1dca4
--- /dev/null
+++ b/net/http2/decoder/http2_structure_decoder.cc
@@ -0,0 +1,90 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/http2_structure_decoder.h"
+
+#include <algorithm>
+
+#include "net/http2/tools/http2_bug_tracker.h"
+
+namespace net {
+
+// Below we have some defensive coding: if we somehow run off the end, don't
+// overwrite lots of memory. Note that most of this decoder is not defensive
+// against bugs in the decoder, only against malicious encoders, but since
+// we're copying memory into a buffer here, let's make sure we don't allow a
+// small mistake to grow larger. The decoder will get stuck if we hit the
+// HTTP2_BUG conditions, but shouldn't corrupt memory.
+
+uint32_t Http2StructureDecoder::IncompleteStart(DecodeBuffer* db,
+                                                uint32_t target_size) {
+  if (target_size > sizeof buffer_) {
+    HTTP2_BUG << "target_size too large for buffer: " << target_size;
+    return 0;
+  }
+  const uint32_t num_to_copy = db->MinLengthRemaining(target_size);
+  memcpy(buffer_, db->cursor(), num_to_copy);
+  offset_ = num_to_copy;
+  db->AdvanceCursor(num_to_copy);
+  return num_to_copy;
+}
+
+DecodeStatus Http2StructureDecoder::IncompleteStart(DecodeBuffer* db,
+                                                    uint32_t* remaining_payload,
+                                                    uint32_t target_size) {
+  DVLOG(1) << "IncompleteStart@" << this
+           << ": *remaining_payload=" << *remaining_payload
+           << "; target_size=" << target_size
+           << "; db->Remaining=" << db->Remaining();
+  *remaining_payload -=
+      IncompleteStart(db, std::min(target_size, *remaining_payload));
+  if (*remaining_payload > 0 && db->Empty()) {
+    return DecodeStatus::kDecodeInProgress;
+  }
+  DVLOG(1) << "IncompleteStart: kDecodeError";
+  return DecodeStatus::kDecodeError;
+}
+
+bool Http2StructureDecoder::ResumeFillingBuffer(DecodeBuffer* db,
+                                                uint32_t target_size) {
+  DVLOG(2) << "ResumeFillingBuffer@" << this << ": target_size=" << target_size
+           << "; offset_=" << offset_ << "; db->Remaining=" << db->Remaining();
+  if (target_size < offset_) {
+    HTTP2_BUG << "Already filled buffer_! target_size=" << target_size
+              << "    offset_=" << offset_;
+    return false;
+  }
+  const uint32_t needed = target_size - offset_;
+  const uint32_t num_to_copy = db->MinLengthRemaining(needed);
+  DVLOG(2) << "ResumeFillingBuffer num_to_copy=" << num_to_copy;
+  memcpy(&buffer_[offset_], db->cursor(), num_to_copy);
+  db->AdvanceCursor(num_to_copy);
+  offset_ += num_to_copy;
+  return needed == num_to_copy;
+}
+
+bool Http2StructureDecoder::ResumeFillingBuffer(DecodeBuffer* db,
+                                                uint32_t* remaining_payload,
+                                                uint32_t target_size) {
+  DVLOG(2) << "ResumeFillingBuffer@" << this << ": target_size=" << target_size
+           << "; offset_=" << offset_
+           << "; *remaining_payload=" << *remaining_payload
+           << "; db->Remaining=" << db->Remaining();
+  if (target_size < offset_) {
+    HTTP2_BUG << "Already filled buffer_! target_size=" << target_size
+              << "    offset_=" << offset_;
+    return false;
+  }
+  const uint32_t needed = target_size - offset_;
+  const uint32_t num_to_copy =
+      db->MinLengthRemaining(std::min(needed, *remaining_payload));
+  DVLOG(2) << "ResumeFillingBuffer num_to_copy=" << num_to_copy;
+  memcpy(&buffer_[offset_], db->cursor(), num_to_copy);
+  db->AdvanceCursor(num_to_copy);
+  offset_ += num_to_copy;
+  *remaining_payload -= num_to_copy;
+  return needed == num_to_copy;
+}
+
+}  // namespace net
diff --git a/net/http2/decoder/http2_structure_decoder.h b/net/http2/decoder/http2_structure_decoder.h
new file mode 100644
index 0000000..8665e96
--- /dev/null
+++ b/net/http2/decoder/http2_structure_decoder.h
@@ -0,0 +1,129 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_HTTP2_STRUCTURE_DECODER_H_
+#define NET_HTTP2_DECODER_HTTP2_STRUCTURE_DECODER_H_
+
+// Http2StructureDecoder is a class for decoding the fixed size structures in
+// the HTTP/2 spec, defined in net/http2/http2_structures.h. This class
+// is in aid of deciding whether to keep the SlowDecode methods which I
+// (jamessynge) now think may not be worth their complexity. In particular,
+// if most transport buffers are large, so it is rare that a structure is
+// split across buffer boundaries, than the cost of buffering upon
+// those rare occurrences is small, which then simplifies the callers.
+
+#include "base/logging.h"
+#include "net/base/net_export.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_http2_structures.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+namespace test {
+class Http2StructureDecoderPeer;
+}  // namespace test
+
+class NET_EXPORT_PRIVATE Http2StructureDecoder {
+ public:
+  // The caller needs to keep track of whether to call Start or Resume.
+  //
+  // Start has an optimization for the case where the DecodeBuffer holds the
+  // entire encoded structure; in that case it decodes into *out and returns
+  // true, and does NOT touch the data members of the Http2StructureDecoder
+  // instance because the caller won't be calling Resume later.
+  //
+  // However, if the DecodeBuffer is too small to hold the entire encoded
+  // structure, Start copies the available bytes into the Http2StructureDecoder
+  // instance, and returns false to indicate that it has not been able to
+  // complete the decoding.
+  //
+  template <class S>
+  bool Start(S* out, DecodeBuffer* db) {
+    static_assert(S::EncodedSize() <= sizeof buffer_, "buffer_ is too small");
+    DVLOG(2) << __func__ << "@" << this << ": db->Remaining=" << db->Remaining()
+             << "; EncodedSize=" << S::EncodedSize();
+    if (db->Remaining() >= S::EncodedSize()) {
+      DoDecode(out, db);
+      return true;
+    }
+    IncompleteStart(db, S::EncodedSize());
+    return false;
+  }
+
+  template <class S>
+  bool Resume(S* out, DecodeBuffer* db) {
+    DVLOG(2) << __func__ << "@" << this << ": offset_=" << offset_
+             << "; db->Remaining=" << db->Remaining();
+    if (ResumeFillingBuffer(db, S::EncodedSize())) {
+      // We have the whole thing now.
+      DVLOG(2) << __func__ << "@" << this << "    offset_=" << offset_
+               << "    Ready to decode from buffer_.";
+      DecodeBuffer buffer_db(buffer_, S::EncodedSize());
+      DoDecode(out, &buffer_db);
+      return true;
+    }
+    DCHECK_LT(offset_, S::EncodedSize());
+    return false;
+  }
+
+  // A second pair of Start and Resume, where the caller has a variable,
+  // |remaining_payload| that is both tested for sufficiency and updated
+  // during decoding. Note that the decode buffer may extend beyond the
+  // remaining payload because the buffer may include padding.
+  template <class S>
+  DecodeStatus Start(S* out, DecodeBuffer* db, uint32_t* remaining_payload) {
+    static_assert(S::EncodedSize() <= sizeof buffer_, "buffer_ is too small");
+    DVLOG(2) << __func__ << "@" << this
+             << ": *remaining_payload=" << *remaining_payload
+             << "; db->Remaining=" << db->Remaining()
+             << "; EncodedSize=" << S::EncodedSize();
+    if (db->MinLengthRemaining(*remaining_payload) >= S::EncodedSize()) {
+      DoDecode(out, db);
+      *remaining_payload -= S::EncodedSize();
+      return DecodeStatus::kDecodeDone;
+    }
+    return IncompleteStart(db, remaining_payload, S::EncodedSize());
+  }
+
+  template <class S>
+  bool Resume(S* out, DecodeBuffer* db, uint32_t* remaining_payload) {
+    DVLOG(3) << __func__ << "@" << this << ": offset_=" << offset_
+             << "; *remaining_payload=" << *remaining_payload
+             << "; db->Remaining=" << db->Remaining()
+             << "; EncodedSize=" << S::EncodedSize();
+    if (ResumeFillingBuffer(db, remaining_payload, S::EncodedSize())) {
+      // We have the whole thing now.
+      DVLOG(2) << __func__ << "@" << this << ": offset_=" << offset_
+               << "; Ready to decode from buffer_.";
+      DecodeBuffer buffer_db(buffer_, S::EncodedSize());
+      DoDecode(out, &buffer_db);
+      return true;
+    }
+    DCHECK_LT(offset_, S::EncodedSize());
+    return false;
+  }
+
+  uint32_t offset() const { return offset_; }
+
+ private:
+  friend class test::Http2StructureDecoderPeer;
+
+  uint32_t IncompleteStart(DecodeBuffer* db, uint32_t target_size);
+  DecodeStatus IncompleteStart(DecodeBuffer* db,
+                               uint32_t* remaining_payload,
+                               uint32_t target_size);
+
+  bool ResumeFillingBuffer(DecodeBuffer* db, uint32_t target_size);
+  bool ResumeFillingBuffer(DecodeBuffer* db,
+                           uint32_t* remaining_payload,
+                           uint32_t target_size);
+
+  uint32_t offset_;
+  char buffer_[Http2FrameHeader::EncodedSize()];
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_HTTP2_STRUCTURE_DECODER_H_
diff --git a/net/http2/decoder/http2_structure_decoder_test.cc b/net/http2/decoder/http2_structure_decoder_test.cc
new file mode 100644
index 0000000..68479a1
--- /dev/null
+++ b/net/http2/decoder/http2_structure_decoder_test.cc
@@ -0,0 +1,512 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/http2_structure_decoder.h"
+
+// Tests decoding all of the fixed size HTTP/2 structures (i.e. those defined in
+// net/http2/http2_structures.h) using Http2StructureDecoder, which handles
+// buffering of structures split across input buffer boundaries, and in turn
+// uses DoDecode when it has all of a structure in a contiguous buffer.
+
+// NOTE: This tests the first pair of Start and Resume, which don't take
+// a remaining_payload parameter. The other pair are well tested via the
+// payload decoder tests, though...
+// TODO(jamessynge): Create type parameterized tests for Http2StructureDecoder
+// where the type is the type of structure, and with testing of both pairs of
+// Start and Resume methods; note that it appears that the first pair will be
+// used only for Http2FrameHeader, and the other pair only for structures in the
+// frame payload.
+
+#include <stddef.h>
+#include <string>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/logging.h"
+#include "base/strings/string_piece.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures_test_util.h"
+#include "net/http2/tools/failure.h"
+#include "net/http2/tools/http2_frame_builder.h"
+#include "net/http2/tools/random_decoder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::AssertionFailure;
+using ::testing::AssertionResult;
+using ::testing::AssertionSuccess;
+using base::StringPiece;
+using std::string;
+
+namespace net {
+namespace test {
+namespace {
+const bool kMayReturnZeroOnFirst = false;
+
+template <class S>
+string SerializeStructure(const S& s) {
+  Http2FrameBuilder fb;
+  fb.Append(s);
+  EXPECT_EQ(S::EncodedSize(), fb.size());
+  return fb.buffer();
+}
+
+template <class S>
+class Http2StructureDecoderTest : public RandomDecoderTest {
+ protected:
+  typedef S Structure;
+
+  Http2StructureDecoderTest() {
+    // IF the test adds more data after the encoded structure, stop as
+    // soon as the structure is decoded.
+    stop_decode_on_done_ = true;
+  }
+
+  DecodeStatus StartDecoding(DecodeBuffer* b) override {
+    // Overwrite the current contents of |structure_|, in to which we'll
+    // decode the buffer, so that we can be confident that we really decoded
+    // the structure every time.
+    structure_.~S();
+    new (&structure_) S;
+    uint32_t old_remaining = b->Remaining();
+    if (structure_decoder_.Start(&structure_, b)) {
+      EXPECT_EQ(old_remaining - S::EncodedSize(), b->Remaining());
+      ++fast_decode_count_;
+      return DecodeStatus::kDecodeDone;
+    } else {
+      EXPECT_LT(structure_decoder_.offset(), S::EncodedSize());
+      EXPECT_EQ(0u, b->Remaining());
+      EXPECT_EQ(old_remaining - structure_decoder_.offset(), b->Remaining());
+      ++incomplete_start_count_;
+      return DecodeStatus::kDecodeInProgress;
+    }
+  }
+
+  DecodeStatus ResumeDecoding(DecodeBuffer* b) override {
+    uint32_t old_offset = structure_decoder_.offset();
+    EXPECT_LT(old_offset, S::EncodedSize());
+    uint32_t avail = b->Remaining();
+    if (structure_decoder_.Resume(&structure_, b)) {
+      EXPECT_LE(S::EncodedSize(), old_offset + avail);
+      EXPECT_EQ(b->Remaining(), avail - (S::EncodedSize() - old_offset));
+      ++slow_decode_count_;
+      return DecodeStatus::kDecodeDone;
+    } else {
+      EXPECT_LT(structure_decoder_.offset(), S::EncodedSize());
+      EXPECT_EQ(0u, b->Remaining());
+      EXPECT_GT(S::EncodedSize(), old_offset + avail);
+      ++incomplete_resume_count_;
+      return DecodeStatus::kDecodeInProgress;
+    }
+  }
+
+  AssertionResult ValidatorForDecodeLeadingStructure(const S* expected,
+                                                     const DecodeBuffer& db,
+                                                     DecodeStatus status) {
+    VERIFY_EQ(*expected, structure_);
+    return AssertionSuccess();
+  }
+
+  // Fully decodes the Structure at the start of data, and confirms it matches
+  // *expected (if provided).
+  AssertionResult DecodeLeadingStructure(const S* expected, StringPiece data) {
+    VERIFY_LE(S::EncodedSize(), data.size());
+    DecodeBuffer original(data);
+
+    // The validator is called after each of the several times that the input
+    // DecodeBuffer is decoded, each with a different segmentation of the input.
+    // Validate that structure_ matches the expected value, if provided.
+    Validator validator =
+        (expected == nullptr)
+            ? base::Bind(&SucceedingValidator)
+            : base::Bind(&Http2StructureDecoderTest::
+                             ValidatorForDecodeLeadingStructure,
+                         base::Unretained(this), expected);
+
+    // Before that, validate that decoding is done and that we've advanced
+    // the cursor the expected amount.
+    Validator wrapped_validator =
+        ValidateDoneAndOffset(S::EncodedSize(), validator);
+
+    // Decode several times, with several segmentations of the input buffer.
+    fast_decode_count_ = 0;
+    slow_decode_count_ = 0;
+    incomplete_start_count_ = 0;
+    incomplete_resume_count_ = 0;
+    VERIFY_SUCCESS(DecodeAndValidateSeveralWays(
+        &original, kMayReturnZeroOnFirst, wrapped_validator));
+    VERIFY_FALSE(HasFailure());
+    VERIFY_EQ(S::EncodedSize(), structure_decoder_.offset());
+    VERIFY_EQ(S::EncodedSize(), original.Offset());
+    VERIFY_LT(0u, fast_decode_count_);
+    VERIFY_LT(0u, slow_decode_count_);
+    VERIFY_LT(0u, incomplete_start_count_);
+
+    // If the structure is large enough so that SelectZeroOrOne will have
+    // caused Resume to return false, check that occurred.
+    if (S::EncodedSize() >= 2) {
+      VERIFY_LE(0u, incomplete_resume_count_);
+    } else {
+      VERIFY_EQ(0u, incomplete_resume_count_);
+    }
+    if (expected != nullptr) {
+      DVLOG(1) << "DecodeLeadingStructure expected: " << *expected;
+      DVLOG(1) << "DecodeLeadingStructure   actual: " << structure_;
+      VERIFY_EQ(*expected, structure_);
+    }
+    return AssertionSuccess();
+  }
+
+  template <size_t N>
+  AssertionResult DecodeLeadingStructure(const char (&data)[N]) {
+    VERIFY_AND_RETURN_SUCCESS(
+        DecodeLeadingStructure(nullptr, StringPiece(data, N)));
+  }
+
+  // Encode the structure |in_s| into bytes, then decode the bytes
+  // and validate that the decoder produced the same field values.
+  AssertionResult EncodeThenDecode(const S& in_s) {
+    string bytes = SerializeStructure(in_s);
+    VERIFY_EQ(S::EncodedSize(), bytes.size());
+    VERIFY_AND_RETURN_SUCCESS(DecodeLeadingStructure(&in_s, bytes));
+  }
+
+  // Repeatedly fill a structure with random but valid contents, encode it, then
+  // decode it, and finally validate that the decoded structure matches the
+  // random input. Lather-rinse-and-repeat.
+  AssertionResult TestDecodingRandomizedStructures(size_t count) {
+    for (size_t i = 0; i < count; ++i) {
+      Structure input;
+      Randomize(&input, RandomPtr());
+      VERIFY_SUCCESS(EncodeThenDecode(input));
+    }
+    return AssertionSuccess();
+  }
+
+  AssertionResult TestDecodingRandomizedStructures() {
+    VERIFY_SUCCESS(TestDecodingRandomizedStructures(100));
+    return AssertionSuccess();
+  }
+
+  uint32_t decode_offset_ = 0;
+  S structure_;
+  Http2StructureDecoder structure_decoder_;
+  size_t fast_decode_count_ = 0;
+  size_t slow_decode_count_ = 0;
+  size_t incomplete_start_count_ = 0;
+  size_t incomplete_resume_count_ = 0;
+};
+
+class Http2FrameHeaderDecoderTest
+    : public Http2StructureDecoderTest<Http2FrameHeader> {};
+
+TEST_F(Http2FrameHeaderDecoderTest, DecodesLiteral) {
+  {
+    // Realistic input.
+    const char kData[] = {
+        0x00, 0x00, 0x05,        // Payload length: 5
+        0x01,                    // Frame type: HEADERS
+        0x08,                    // Flags: PADDED
+        0x00, 0x00, 0x00, 0x01,  // Stream ID: 1
+        0x04,                    // Padding length: 4
+        0x00, 0x00, 0x00, 0x00,  // Padding bytes
+    };
+    ASSERT_TRUE(DecodeLeadingStructure(kData));
+    EXPECT_EQ(5u, structure_.payload_length);
+    EXPECT_EQ(Http2FrameType::HEADERS, structure_.type);
+    EXPECT_EQ(Http2FrameFlag::FLAG_PADDED, structure_.flags);
+    EXPECT_EQ(1u, structure_.stream_id);
+  }
+  {
+    // Unlikely input.
+    const char kData[] = {
+        0xffu, 0xffu, 0xffu,         // Payload length: uint24 max
+        0xffu,                       // Frame type: Unknown
+        0xffu,                       // Flags: Unknown/All
+        0xffu, 0xffu, 0xffu, 0xffu,  // Stream ID: uint31 max, plus R-bit
+    };
+    ASSERT_TRUE(DecodeLeadingStructure(kData));
+    EXPECT_EQ((1u << 24) - 1u, structure_.payload_length);
+    EXPECT_EQ(static_cast<Http2FrameType>(255), structure_.type);
+    EXPECT_EQ(255, structure_.flags);
+    EXPECT_EQ(0x7FFFFFFFu, structure_.stream_id);
+  }
+}
+
+TEST_F(Http2FrameHeaderDecoderTest, DecodesRandomized) {
+  TestDecodingRandomizedStructures();
+}
+
+//------------------------------------------------------------------------------
+
+class Http2PriorityFieldsDecoderTest
+    : public Http2StructureDecoderTest<Http2PriorityFields> {};
+
+TEST_F(Http2PriorityFieldsDecoderTest, DecodesLiteral) {
+  {
+    const char kData[] = {
+        0x80u, 0x00, 0x00, 0x05,  // Exclusive (yes) and Dependency (5)
+        0xffu,                    // Weight: 256 (after adding 1)
+    };
+    ASSERT_TRUE(DecodeLeadingStructure(kData));
+    EXPECT_EQ(5u, structure_.stream_dependency);
+    EXPECT_EQ(256u, structure_.weight);
+    EXPECT_EQ(true, structure_.is_exclusive);
+  }
+  {
+    const char kData[] = {
+        0x7fu, 0xffu,
+        0xffu, 0xffu,  // Exclusive (no) and Dependency (0x7fffffff)
+        0x00u,         // Weight: 1 (after adding 1)
+    };
+    ASSERT_TRUE(DecodeLeadingStructure(kData));
+    EXPECT_EQ(StreamIdMask(), structure_.stream_dependency);
+    EXPECT_EQ(1u, structure_.weight);
+    EXPECT_FALSE(structure_.is_exclusive);
+  }
+}
+
+TEST_F(Http2PriorityFieldsDecoderTest, DecodesRandomized) {
+  TestDecodingRandomizedStructures();
+}
+
+//------------------------------------------------------------------------------
+
+class Http2RstStreamFieldsDecoderTest
+    : public Http2StructureDecoderTest<Http2RstStreamFields> {};
+
+TEST_F(Http2RstStreamFieldsDecoderTest, DecodesLiteral) {
+  {
+    const char kData[] = {
+        0x00, 0x00, 0x00, 0x01,  // Error: PROTOCOL_ERROR
+    };
+    ASSERT_TRUE(DecodeLeadingStructure(kData));
+    EXPECT_TRUE(structure_.IsSupportedErrorCode());
+    EXPECT_EQ(Http2ErrorCode::PROTOCOL_ERROR, structure_.error_code);
+  }
+  {
+    const char kData[] = {
+        0xffu, 0xffu, 0xffu, 0xffu,  // Error: max uint32 (Unknown error code)
+    };
+    ASSERT_TRUE(DecodeLeadingStructure(kData));
+    EXPECT_FALSE(structure_.IsSupportedErrorCode());
+    EXPECT_EQ(static_cast<Http2ErrorCode>(0xffffffff), structure_.error_code);
+  }
+}
+
+TEST_F(Http2RstStreamFieldsDecoderTest, DecodesRandomized) {
+  TestDecodingRandomizedStructures();
+}
+
+//------------------------------------------------------------------------------
+
+class Http2SettingFieldsDecoderTest
+    : public Http2StructureDecoderTest<Http2SettingFields> {};
+
+TEST_F(Http2SettingFieldsDecoderTest, DecodesLiteral) {
+  {
+    const char kData[] = {
+        0x00, 0x01,              // Setting: HEADER_TABLE_SIZE
+        0x00, 0x00, 0x40, 0x00,  // Value: 16K
+    };
+    ASSERT_TRUE(DecodeLeadingStructure(kData));
+    EXPECT_TRUE(structure_.IsSupportedParameter());
+    EXPECT_EQ(Http2SettingsParameter::HEADER_TABLE_SIZE, structure_.parameter);
+    EXPECT_EQ(1u << 14, structure_.value);
+  }
+  {
+    const char kData[] = {
+        0x00,  0x00,                 // Setting: Unknown (0)
+        0xffu, 0xffu, 0xffu, 0xffu,  // Value: max uint32
+    };
+    ASSERT_TRUE(DecodeLeadingStructure(kData));
+    EXPECT_FALSE(structure_.IsSupportedParameter());
+    EXPECT_EQ(static_cast<Http2SettingsParameter>(0), structure_.parameter);
+  }
+}
+
+TEST_F(Http2SettingFieldsDecoderTest, DecodesRandomized) {
+  TestDecodingRandomizedStructures();
+}
+
+//------------------------------------------------------------------------------
+
+class Http2PushPromiseFieldsDecoderTest
+    : public Http2StructureDecoderTest<Http2PushPromiseFields> {};
+
+TEST_F(Http2PushPromiseFieldsDecoderTest, DecodesLiteral) {
+  {
+    const char kData[] = {
+        0x00, 0x01, 0x8au, 0x92u,  // Promised Stream ID: 101010
+    };
+    ASSERT_TRUE(DecodeLeadingStructure(kData));
+    EXPECT_EQ(101010u, structure_.promised_stream_id);
+  }
+  {
+    // Promised stream id has R-bit (reserved for future use) set, which
+    // should be cleared by the decoder.
+    const char kData[] = {
+        0xffu, 0xffu, 0xffu, 0xffu,  // Promised Stream ID: max uint31 and R-bit
+    };
+    ASSERT_TRUE(DecodeLeadingStructure(kData));
+    EXPECT_EQ(StreamIdMask(), structure_.promised_stream_id);
+  }
+}
+
+TEST_F(Http2PushPromiseFieldsDecoderTest, DecodesRandomized) {
+  TestDecodingRandomizedStructures();
+}
+
+//------------------------------------------------------------------------------
+
+class Http2PingFieldsDecoderTest
+    : public Http2StructureDecoderTest<Http2PingFields> {};
+
+TEST_F(Http2PingFieldsDecoderTest, DecodesLiteral) {
+  {
+    // Each byte is different, so can detect if order changed.
+    const char kData[] = {
+        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    };
+    ASSERT_TRUE(DecodeLeadingStructure(kData));
+    EXPECT_EQ(StringPiece(kData, 8), ToStringPiece(structure_.opaque_data));
+  }
+  {
+    // All zeros, detect problems handling NULs.
+    const char kData[] = {
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    };
+    ASSERT_TRUE(DecodeLeadingStructure(kData));
+    EXPECT_EQ(StringPiece(kData, 8), ToStringPiece(structure_.opaque_data));
+  }
+  {
+    const char kData[] = {
+        0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu,
+    };
+    ASSERT_TRUE(DecodeLeadingStructure(kData));
+    EXPECT_EQ(StringPiece(kData, 8), ToStringPiece(structure_.opaque_data));
+  }
+}
+
+TEST_F(Http2PingFieldsDecoderTest, DecodesRandomized) {
+  TestDecodingRandomizedStructures();
+}
+
+//------------------------------------------------------------------------------
+
+class Http2GoAwayFieldsDecoderTest
+    : public Http2StructureDecoderTest<Http2GoAwayFields> {};
+
+TEST_F(Http2GoAwayFieldsDecoderTest, DecodesLiteral) {
+  {
+    const char kData[] = {
+        0x00, 0x00, 0x00, 0x00,  // Last Stream ID: 0
+        0x00, 0x00, 0x00, 0x00,  // Error: NO_ERROR (0)
+    };
+    ASSERT_TRUE(DecodeLeadingStructure(kData));
+    EXPECT_EQ(0u, structure_.last_stream_id);
+    EXPECT_TRUE(structure_.IsSupportedErrorCode());
+    EXPECT_EQ(Http2ErrorCode::HTTP2_NO_ERROR, structure_.error_code);
+  }
+  {
+    const char kData[] = {
+        0x00, 0x00, 0x00, 0x01,  // Last Stream ID: 1
+        0x00, 0x00, 0x00, 0x0d,  // Error: HTTP_1_1_REQUIRED
+    };
+    ASSERT_TRUE(DecodeLeadingStructure(kData));
+    EXPECT_EQ(1u, structure_.last_stream_id);
+    EXPECT_TRUE(structure_.IsSupportedErrorCode());
+    EXPECT_EQ(Http2ErrorCode::HTTP_1_1_REQUIRED, structure_.error_code);
+  }
+  {
+    const char kData[] = {
+        0xffu, 0xffu, 0xffu, 0xffu,  // Last Stream ID: max uint31 and R-bit
+        0xffu, 0xffu, 0xffu, 0xffu,  // Error: max uint32 (Unknown error code)
+    };
+    ASSERT_TRUE(DecodeLeadingStructure(kData));
+    EXPECT_EQ(StreamIdMask(), structure_.last_stream_id);  // No high-bit.
+    EXPECT_FALSE(structure_.IsSupportedErrorCode());
+    EXPECT_EQ(static_cast<Http2ErrorCode>(0xffffffff), structure_.error_code);
+  }
+}
+
+TEST_F(Http2GoAwayFieldsDecoderTest, DecodesRandomized) {
+  TestDecodingRandomizedStructures();
+}
+
+//------------------------------------------------------------------------------
+
+class Http2WindowUpdateFieldsDecoderTest
+    : public Http2StructureDecoderTest<Http2WindowUpdateFields> {};
+
+TEST_F(Http2WindowUpdateFieldsDecoderTest, DecodesLiteral) {
+  {
+    const char kData[] = {
+        0x00, 0x01, 0x00, 0x00,  // Window Size Increment: 2 ^ 16
+    };
+    ASSERT_TRUE(DecodeLeadingStructure(kData));
+    EXPECT_EQ(1u << 16, structure_.window_size_increment);
+  }
+  {
+    // Increment must be non-zero, but we need to be able to decode the invalid
+    // zero to detect it.
+    const char kData[] = {
+        0x00, 0x00, 0x00, 0x00,  // Window Size Increment: 0
+    };
+    ASSERT_TRUE(DecodeLeadingStructure(kData));
+    EXPECT_EQ(0u, structure_.window_size_increment);
+  }
+  {
+    // Increment has R-bit (reserved for future use) set, which
+    // should be cleared by the decoder.
+    const char kData[] = {
+        0xffu, 0xffu, 0xffu,
+        0xffu,  // Window Size Increment: max uint31 and R-bit
+    };
+    ASSERT_TRUE(DecodeLeadingStructure(kData));
+    EXPECT_EQ(StreamIdMask(), structure_.window_size_increment);
+  }
+}
+
+TEST_F(Http2WindowUpdateFieldsDecoderTest, DecodesRandomized) {
+  TestDecodingRandomizedStructures();
+}
+
+//------------------------------------------------------------------------------
+
+class Http2AltSvcFieldsDecoderTest
+    : public Http2StructureDecoderTest<Http2AltSvcFields> {};
+
+TEST_F(Http2AltSvcFieldsDecoderTest, DecodesLiteral) {
+  {
+    const char kData[] = {
+        0x00, 0x00,  // Origin Length: 0
+    };
+    ASSERT_TRUE(DecodeLeadingStructure(kData));
+    EXPECT_EQ(0, structure_.origin_length);
+  }
+  {
+    const char kData[] = {
+        0x00, 0x14,  // Origin Length: 20
+    };
+    ASSERT_TRUE(DecodeLeadingStructure(kData));
+    EXPECT_EQ(20, structure_.origin_length);
+  }
+  {
+    const char kData[] = {
+        0xffu, 0xffu,  // Origin Length: uint16 max
+    };
+    ASSERT_TRUE(DecodeLeadingStructure(kData));
+    EXPECT_EQ(65535, structure_.origin_length);
+  }
+}
+
+TEST_F(Http2AltSvcFieldsDecoderTest, DecodesRandomized) {
+  TestDecodingRandomizedStructures();
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/decoder/http2_structure_decoder_test_util.h b/net/http2/decoder/http2_structure_decoder_test_util.h
new file mode 100644
index 0000000..b45211f7
--- /dev/null
+++ b/net/http2/decoder/http2_structure_decoder_test_util.h
@@ -0,0 +1,30 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_HTTP2_STRUCTURE_DECODER_TEST_UTIL_H_
+#define NET_HTTP2_DECODER_HTTP2_STRUCTURE_DECODER_TEST_UTIL_H_
+
+#include "net/http2/decoder/http2_structure_decoder.h"
+
+#include <cstddef>
+
+#include "net/http2/tools/http2_random.h"
+
+namespace net {
+namespace test {
+
+class Http2StructureDecoderPeer {
+ public:
+  static void Randomize(Http2StructureDecoder* p, RandomBase* rng) {
+    p->offset_ = rng->Rand32();
+    for (size_t i = 0; i < sizeof p->buffer_; ++i) {
+      p->buffer_[i] = rng->Rand8();
+    }
+  }
+};
+
+}  // namespace test
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_HTTP2_STRUCTURE_DECODER_TEST_UTIL_H_
diff --git a/net/http2/decoder/payload_decoders/altsvc_payload_decoder.cc b/net/http2/decoder/payload_decoders/altsvc_payload_decoder.cc
new file mode 100644
index 0000000..fe0e8c2
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/altsvc_payload_decoder.cc
@@ -0,0 +1,143 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/altsvc_payload_decoder.h"
+
+#include <stddef.h>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures.h"
+#include "net/http2/tools/http2_bug_tracker.h"
+
+namespace net {
+
+std::ostream& operator<<(std::ostream& out,
+                         AltSvcPayloadDecoder::PayloadState v) {
+  switch (v) {
+    case AltSvcPayloadDecoder::PayloadState::kStartDecodingStruct:
+      return out << "kStartDecodingStruct";
+    case AltSvcPayloadDecoder::PayloadState::kMaybeDecodedStruct:
+      return out << "kMaybeDecodedStruct";
+    case AltSvcPayloadDecoder::PayloadState::kDecodingStrings:
+      return out << "kDecodingStrings";
+    case AltSvcPayloadDecoder::PayloadState::kResumeDecodingStruct:
+      return out << "kResumeDecodingStruct";
+  }
+  return out << static_cast<int>(v);
+}
+
+DecodeStatus AltSvcPayloadDecoder::StartDecodingPayload(
+    FrameDecoderState* state,
+    DecodeBuffer* db) {
+  DVLOG(2) << "AltSvcPayloadDecoder::StartDecodingPayload: "
+           << state->frame_header();
+  DCHECK_EQ(Http2FrameType::ALTSVC, state->frame_header().type);
+  DCHECK_LE(db->Remaining(), state->frame_header().payload_length);
+  DCHECK_EQ(0, state->frame_header().flags);
+
+  state->InitializeRemainders();
+  payload_state_ = PayloadState::kStartDecodingStruct;
+
+  return ResumeDecodingPayload(state, db);
+}
+
+DecodeStatus AltSvcPayloadDecoder::ResumeDecodingPayload(
+    FrameDecoderState* state,
+    DecodeBuffer* db) {
+  const Http2FrameHeader& frame_header = state->frame_header();
+  DVLOG(2) << "AltSvcPayloadDecoder::ResumeDecodingPayload: " << frame_header;
+  DCHECK_EQ(Http2FrameType::ALTSVC, frame_header.type);
+  DCHECK_LE(state->remaining_payload(), frame_header.payload_length);
+  DCHECK_LE(db->Remaining(), state->remaining_payload());
+  DCHECK_NE(PayloadState::kMaybeDecodedStruct, payload_state_);
+  // |status| has to be initialized to some value to avoid compiler error in
+  // case PayloadState::kMaybeDecodedStruct below, but value does not matter,
+  // see DCHECK_NE above.
+  DecodeStatus status = DecodeStatus::kDecodeError;
+  while (true) {
+    DVLOG(2) << "AltSvcPayloadDecoder::ResumeDecodingPayload payload_state_="
+             << payload_state_;
+    switch (payload_state_) {
+      case PayloadState::kStartDecodingStruct:
+        status = state->StartDecodingStructureInPayload(&altsvc_fields_, db);
+      // FALLTHROUGH_INTENDED
+
+      case PayloadState::kMaybeDecodedStruct:
+        if (status == DecodeStatus::kDecodeDone &&
+            altsvc_fields_.origin_length <= state->remaining_payload()) {
+          size_t origin_length = altsvc_fields_.origin_length;
+          size_t value_length = state->remaining_payload() - origin_length;
+          state->listener()->OnAltSvcStart(frame_header, origin_length,
+                                           value_length);
+        } else if (status != DecodeStatus::kDecodeDone) {
+          DCHECK(state->remaining_payload() > 0 ||
+                 status == DecodeStatus::kDecodeError)
+              << "\nremaining_payload: " << state->remaining_payload()
+              << "\nstatus: " << status << "\nheader: " << frame_header;
+          // Assume in progress.
+          payload_state_ = PayloadState::kResumeDecodingStruct;
+          return status;
+        } else {
+          // The origin's length is longer than the remaining payload.
+          DCHECK_GT(altsvc_fields_.origin_length, state->remaining_payload());
+          return state->ReportFrameSizeError();
+        }
+      // FALLTHROUGH_INTENDED
+
+      case PayloadState::kDecodingStrings:
+        return DecodeStrings(state, db);
+
+      case PayloadState::kResumeDecodingStruct:
+        status = state->ResumeDecodingStructureInPayload(&altsvc_fields_, db);
+        payload_state_ = PayloadState::kMaybeDecodedStruct;
+        continue;
+    }
+    HTTP2_BUG << "PayloadState: " << payload_state_;
+  }
+}
+
+DecodeStatus AltSvcPayloadDecoder::DecodeStrings(FrameDecoderState* state,
+                                                 DecodeBuffer* db) {
+  DVLOG(2) << "AltSvcPayloadDecoder::DecodeStrings remaining_payload="
+           << state->remaining_payload()
+           << ", db->Remaining=" << db->Remaining();
+  // Note that we don't explicitly keep track of exactly how far through the
+  // origin; instead we compute it from how much is left of the original
+  // payload length and the decoded total length of the origin.
+  size_t origin_length = altsvc_fields_.origin_length;
+  size_t value_length = state->frame_header().payload_length - origin_length -
+                        Http2AltSvcFields::EncodedSize();
+  if (state->remaining_payload() > value_length) {
+    size_t remaining_origin_length = state->remaining_payload() - value_length;
+    size_t avail = db->MinLengthRemaining(remaining_origin_length);
+    state->listener()->OnAltSvcOriginData(db->cursor(), avail);
+    db->AdvanceCursor(avail);
+    state->ConsumePayload(avail);
+    if (remaining_origin_length > avail) {
+      payload_state_ = PayloadState::kDecodingStrings;
+      return DecodeStatus::kDecodeInProgress;
+    }
+  }
+  // All that is left is the value string.
+  DCHECK_LE(state->remaining_payload(), value_length);
+  DCHECK_LE(db->Remaining(), state->remaining_payload());
+  if (db->HasData()) {
+    size_t avail = db->Remaining();
+    state->listener()->OnAltSvcValueData(db->cursor(), avail);
+    db->AdvanceCursor(avail);
+    state->ConsumePayload(avail);
+  }
+  if (state->remaining_payload() == 0) {
+    state->listener()->OnAltSvcEnd();
+    return DecodeStatus::kDecodeDone;
+  }
+  payload_state_ = PayloadState::kDecodingStrings;
+  return DecodeStatus::kDecodeInProgress;
+}
+
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/altsvc_payload_decoder.h b/net/http2/decoder/payload_decoders/altsvc_payload_decoder.h
new file mode 100644
index 0000000..f92fa7f9
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/altsvc_payload_decoder.h
@@ -0,0 +1,64 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_PAYLOAD_DECODERS_ALTSVC_PAYLOAD_DECODER_H_
+#define NET_HTTP2_DECODER_PAYLOAD_DECODERS_ALTSVC_PAYLOAD_DECODER_H_
+
+// Decodes the payload of a ALTSVC frame.
+
+#include "net/base/net_export.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/decoder/frame_decoder_state.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+namespace test {
+class AltSvcPayloadDecoderPeer;
+}  // namespace test
+
+class NET_EXPORT_PRIVATE AltSvcPayloadDecoder {
+ public:
+  // States during decoding of a ALTSVC frame.
+  enum class PayloadState {
+    // Start decoding the fixed size structure at the start of an ALTSVC
+    // frame (Http2AltSvcFields).
+    kStartDecodingStruct,
+
+    // Handle the DecodeStatus returned from starting or resuming the
+    // decoding of Http2AltSvcFields. If complete, calls OnAltSvcStart.
+    kMaybeDecodedStruct,
+
+    // Reports the value of the strings (origin and value) of an ALTSVC frame
+    // to the listener.
+    kDecodingStrings,
+
+    // The initial decode buffer wasn't large enough for the Http2AltSvcFields,
+    // so this state resumes the decoding when ResumeDecodingPayload is called
+    // later with a new DecodeBuffer.
+    kResumeDecodingStruct,
+  };
+
+  // Starts the decoding of a ALTSVC frame's payload, and completes it if the
+  // entire payload is in the provided decode buffer.
+  DecodeStatus StartDecodingPayload(FrameDecoderState* state, DecodeBuffer* db);
+
+  // Resumes decoding a ALTSVC frame's payload that has been split across
+  // decode buffers.
+  DecodeStatus ResumeDecodingPayload(FrameDecoderState* state,
+                                     DecodeBuffer* db);
+
+ private:
+  friend class test::AltSvcPayloadDecoderPeer;
+
+  // Implements state kDecodingStrings.
+  DecodeStatus DecodeStrings(FrameDecoderState* state, DecodeBuffer* db);
+
+  Http2AltSvcFields altsvc_fields_;
+  PayloadState payload_state_;
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_PAYLOAD_DECODERS_ALTSVC_PAYLOAD_DECODER_H_
diff --git a/net/http2/decoder/payload_decoders/altsvc_payload_decoder_test.cc b/net/http2/decoder/payload_decoders/altsvc_payload_decoder_test.cc
new file mode 100644
index 0000000..e10fc50
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/altsvc_payload_decoder_test.cc
@@ -0,0 +1,133 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/altsvc_payload_decoder.h"
+
+#include <stddef.h>
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "net/http2/decoder/frame_parts.h"
+#include "net/http2/decoder/frame_parts_collector.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/decoder/payload_decoders/payload_decoder_base_test_util.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures_test_util.h"
+#include "net/http2/tools/http2_frame_builder.h"
+#include "net/http2/tools/http2_random.h"
+#include "net/http2/tools/random_decoder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::string;
+
+namespace net {
+namespace test {
+
+// Provides friend access to an instance of the payload decoder, and also
+// provides info to aid in testing.
+class AltSvcPayloadDecoderPeer {
+ public:
+  static constexpr Http2FrameType FrameType() { return Http2FrameType::ALTSVC; }
+
+  // Returns the mask of flags that affect the decoding of the payload (i.e.
+  // flags that that indicate the presence of certain fields or padding).
+  static constexpr uint8_t FlagsAffectingPayloadDecoding() { return 0; }
+
+  static void Randomize(AltSvcPayloadDecoder* p, RandomBase* rng) {
+    CorruptEnum(&p->payload_state_, rng);
+    test::Randomize(&p->altsvc_fields_, rng);
+    VLOG(1) << "AltSvcPayloadDecoderPeer::Randomize altsvc_fields_="
+            << p->altsvc_fields_;
+  }
+};
+
+namespace {
+
+struct Listener : public FramePartsCollector {
+  void OnAltSvcStart(const Http2FrameHeader& header,
+                     size_t origin_length,
+                     size_t value_length) override {
+    VLOG(1) << "OnAltSvcStart header: " << header
+            << "; origin_length=" << origin_length
+            << "; value_length=" << value_length;
+    StartFrame(header)->OnAltSvcStart(header, origin_length, value_length);
+  }
+
+  void OnAltSvcOriginData(const char* data, size_t len) override {
+    VLOG(1) << "OnAltSvcOriginData: len=" << len;
+    CurrentFrame()->OnAltSvcOriginData(data, len);
+  }
+
+  void OnAltSvcValueData(const char* data, size_t len) override {
+    VLOG(1) << "OnAltSvcValueData: len=" << len;
+    CurrentFrame()->OnAltSvcValueData(data, len);
+  }
+
+  void OnAltSvcEnd() override {
+    VLOG(1) << "OnAltSvcEnd";
+    EndFrame()->OnAltSvcEnd();
+  }
+
+  void OnFrameSizeError(const Http2FrameHeader& header) override {
+    VLOG(1) << "OnFrameSizeError: " << header;
+    FrameError(header)->OnFrameSizeError(header);
+  }
+};
+
+class AltSvcPayloadDecoderTest
+    : public AbstractPayloadDecoderTest<AltSvcPayloadDecoder,
+                                        AltSvcPayloadDecoderPeer,
+                                        Listener> {};
+
+// Confirm we get an error if the payload is not long enough to hold
+// Http2AltSvcFields and the indicated length of origin.
+TEST_F(AltSvcPayloadDecoderTest, Truncated) {
+  Http2FrameBuilder fb;
+  fb.Append(Http2AltSvcFields{0xffff});  // The longest possible origin length.
+  fb.Append("Too little origin!");
+  EXPECT_TRUE(VerifyDetectsFrameSizeError(
+      0, fb.buffer(),
+      base::Bind(&AbstractPayloadDecoderTest::SucceedingApproveSize)));
+}
+
+class AltSvcPayloadLengthTests : public AltSvcPayloadDecoderTest,
+                                 public ::testing::WithParamInterface<
+                                     ::testing::tuple<uint16_t, uint32_t>> {
+ protected:
+  AltSvcPayloadLengthTests()
+      : origin_length_(::testing::get<0>(GetParam())),
+        value_length_(::testing::get<1>(GetParam())) {
+    VLOG(1) << "################  origin_length_=" << origin_length_
+            << "   value_length_=" << value_length_ << "  ################";
+  }
+
+  const uint16_t origin_length_;
+  const uint32_t value_length_;
+};
+
+INSTANTIATE_TEST_CASE_P(VariousOriginAndValueLengths,
+                        AltSvcPayloadLengthTests,
+                        ::testing::Combine(::testing::Values(0, 1, 3, 65535),
+                                           ::testing::Values(0, 1, 3, 65537)));
+
+TEST_P(AltSvcPayloadLengthTests, ValidOriginAndValueLength) {
+  string origin = Random().RandString(origin_length_);
+  string value = Random().RandString(value_length_);
+  Http2FrameBuilder fb;
+  fb.Append(Http2AltSvcFields{origin_length_});
+  fb.Append(origin);
+  fb.Append(value);
+  Http2FrameHeader header(fb.size(), Http2FrameType::ALTSVC, RandFlags(),
+                          RandStreamId());
+  set_frame_header(header);
+  FrameParts expected(header);
+  expected.SetAltSvcExpected(origin, value);
+  ASSERT_TRUE(DecodePayloadAndValidateSeveralWays(fb.buffer(), expected));
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/continuation_payload_decoder.cc b/net/http2/decoder/payload_decoders/continuation_payload_decoder.cc
new file mode 100644
index 0000000..ac5371c0
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/continuation_payload_decoder.cc
@@ -0,0 +1,58 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/continuation_payload_decoder.h"
+
+#include <stddef.h>
+
+#include "base/logging.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+
+DecodeStatus ContinuationPayloadDecoder::StartDecodingPayload(
+    FrameDecoderState* state,
+    DecodeBuffer* db) {
+  const Http2FrameHeader& frame_header = state->frame_header();
+  const uint32_t total_length = frame_header.payload_length;
+
+  DVLOG(2) << "ContinuationPayloadDecoder::StartDecodingPayload: "
+           << frame_header;
+  DCHECK_EQ(Http2FrameType::CONTINUATION, frame_header.type);
+  DCHECK_LE(db->Remaining(), total_length);
+  DCHECK_EQ(0, frame_header.flags & ~(Http2FrameFlag::FLAG_END_HEADERS));
+
+  state->InitializeRemainders();
+  state->listener()->OnContinuationStart(frame_header);
+  return ResumeDecodingPayload(state, db);
+}
+
+DecodeStatus ContinuationPayloadDecoder::ResumeDecodingPayload(
+    FrameDecoderState* state,
+    DecodeBuffer* db) {
+  DVLOG(2) << "ContinuationPayloadDecoder::ResumeDecodingPayload"
+           << "  remaining_payload=" << state->remaining_payload()
+           << "  db->Remaining=" << db->Remaining();
+  DCHECK_EQ(Http2FrameType::CONTINUATION, state->frame_header().type);
+  DCHECK_LE(state->remaining_payload(), state->frame_header().payload_length);
+  DCHECK_LE(db->Remaining(), state->remaining_payload());
+
+  size_t avail = db->Remaining();
+  DCHECK_LE(avail, state->remaining_payload());
+  if (avail > 0) {
+    state->listener()->OnHpackFragment(db->cursor(), avail);
+    db->AdvanceCursor(avail);
+    state->ConsumePayload(avail);
+  }
+  if (state->remaining_payload() == 0) {
+    state->listener()->OnContinuationEnd();
+    return DecodeStatus::kDecodeDone;
+  }
+  return DecodeStatus::kDecodeInProgress;
+}
+
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/continuation_payload_decoder.h b/net/http2/decoder/payload_decoders/continuation_payload_decoder.h
new file mode 100644
index 0000000..4ab58aa
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/continuation_payload_decoder.h
@@ -0,0 +1,31 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_PAYLOAD_DECODERS_CONTINUATION_PAYLOAD_DECODER_H_
+#define NET_HTTP2_DECODER_PAYLOAD_DECODERS_CONTINUATION_PAYLOAD_DECODER_H_
+
+// Decodes the payload of a CONTINUATION frame.
+
+#include "net/base/net_export.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/decoder/frame_decoder_state.h"
+
+namespace net {
+
+class NET_EXPORT_PRIVATE ContinuationPayloadDecoder {
+ public:
+  // Starts the decoding of a CONTINUATION frame's payload, and completes
+  // it if the entire payload is in the provided decode buffer.
+  DecodeStatus StartDecodingPayload(FrameDecoderState* state, DecodeBuffer* db);
+
+  // Resumes decoding a CONTINUATION frame's payload that has been split across
+  // decode buffers.
+  DecodeStatus ResumeDecodingPayload(FrameDecoderState* state,
+                                     DecodeBuffer* db);
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_PAYLOAD_DECODERS_CONTINUATION_PAYLOAD_DECODER_H_
diff --git a/net/http2/decoder/payload_decoders/continuation_payload_decoder_test.cc b/net/http2/decoder/payload_decoders/continuation_payload_decoder_test.cc
new file mode 100644
index 0000000..3244039
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/continuation_payload_decoder_test.cc
@@ -0,0 +1,94 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/continuation_payload_decoder.h"
+
+#include <stddef.h>
+
+#include <string>
+#include <type_traits>
+
+#include "base/logging.h"
+#include "net/http2/decoder/frame_parts.h"
+#include "net/http2/decoder/frame_parts_collector.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/decoder/payload_decoders/payload_decoder_base_test_util.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures.h"
+#include "net/http2/tools/random_decoder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::string;
+
+namespace net {
+namespace test {
+
+// Provides friend access to an instance of the payload decoder, and also
+// provides info to aid in testing.
+class ContinuationPayloadDecoderPeer {
+ public:
+  static constexpr Http2FrameType FrameType() {
+    return Http2FrameType::CONTINUATION;
+  }
+
+  // Returns the mask of flags that affect the decoding of the payload (i.e.
+  // flags that that indicate the presence of certain fields or padding).
+  static constexpr uint8_t FlagsAffectingPayloadDecoding() { return 0; }
+
+  static void Randomize(ContinuationPayloadDecoder* p, RandomBase* rng) {
+    // ContinuationPayloadDecoder has no fields,
+    // so there is nothing to randomize.
+    static_assert(std::is_empty<ContinuationPayloadDecoder>::value,
+                  "Need to randomize fields of ContinuationPayloadDecoder");
+  }
+};
+
+namespace {
+
+struct Listener : public FramePartsCollector {
+  void OnContinuationStart(const Http2FrameHeader& header) override {
+    VLOG(1) << "OnContinuationStart: " << header;
+    StartFrame(header)->OnContinuationStart(header);
+  }
+
+  void OnHpackFragment(const char* data, size_t len) override {
+    VLOG(1) << "OnHpackFragment: len=" << len;
+    CurrentFrame()->OnHpackFragment(data, len);
+  }
+
+  void OnContinuationEnd() override {
+    VLOG(1) << "OnContinuationEnd";
+    EndFrame()->OnContinuationEnd();
+  }
+};
+
+class ContinuationPayloadDecoderTest
+    : public AbstractPayloadDecoderTest<ContinuationPayloadDecoder,
+                                        ContinuationPayloadDecoderPeer,
+                                        Listener>,
+      public ::testing::WithParamInterface<uint32_t> {
+ protected:
+  ContinuationPayloadDecoderTest() : length_(GetParam()) {
+    VLOG(1) << "################  length_=" << length_ << "  ################";
+  }
+
+  const uint32_t length_;
+};
+
+INSTANTIATE_TEST_CASE_P(VariousLengths,
+                        ContinuationPayloadDecoderTest,
+                        ::testing::Values(0, 1, 2, 3, 4, 5, 6));
+
+TEST_P(ContinuationPayloadDecoderTest, ValidLength) {
+  string hpack_payload = Random().RandString(length_);
+  Http2FrameHeader frame_header(length_, Http2FrameType::CONTINUATION,
+                                RandFlags(), RandStreamId());
+  set_frame_header(frame_header);
+  FrameParts expected(frame_header, hpack_payload);
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(hpack_payload, expected));
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/data_payload_decoder.cc b/net/http2/decoder/payload_decoders/data_payload_decoder.cc
new file mode 100644
index 0000000..60b8a714
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/data_payload_decoder.cc
@@ -0,0 +1,123 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/data_payload_decoder.h"
+
+#include <stddef.h>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures.h"
+#include "net/http2/tools/http2_bug_tracker.h"
+
+namespace net {
+
+std::ostream& operator<<(std::ostream& out,
+                         DataPayloadDecoder::PayloadState v) {
+  switch (v) {
+    case DataPayloadDecoder::PayloadState::kReadPadLength:
+      return out << "kReadPadLength";
+    case DataPayloadDecoder::PayloadState::kReadPayload:
+      return out << "kReadPayload";
+    case DataPayloadDecoder::PayloadState::kSkipPadding:
+      return out << "kSkipPadding";
+  }
+  return out << static_cast<int>(v);
+}
+
+DecodeStatus DataPayloadDecoder::StartDecodingPayload(FrameDecoderState* state,
+                                                      DecodeBuffer* db) {
+  const Http2FrameHeader& frame_header = state->frame_header();
+  const uint32_t total_length = frame_header.payload_length;
+
+  DVLOG(2) << "DataPayloadDecoder::StartDecodingPayload: " << frame_header;
+  DCHECK_EQ(Http2FrameType::DATA, frame_header.type);
+  DCHECK_LE(db->Remaining(), total_length);
+  DCHECK_EQ(
+      0, frame_header.flags &
+             ~(Http2FrameFlag::FLAG_END_STREAM | Http2FrameFlag::FLAG_PADDED));
+
+  // Special case for the hoped for common case: unpadded and fits fully into
+  // the decode buffer. TO BE SEEN if that is true. It certainly requires that
+  // the transport buffers be large (e.g. >> 16KB typically).
+  // TODO(jamessynge) Add counters.
+  DVLOG(2) << "StartDecodingPayload total_length=" << total_length;
+  if (!frame_header.IsPadded()) {
+    DVLOG(2) << "StartDecodingPayload !IsPadded";
+    if (db->Remaining() == total_length) {
+      DVLOG(2) << "StartDecodingPayload all present";
+      // Note that we don't cache the listener field so that the callee can
+      // replace it if the frame is bad.
+      // If this case is common enough, consider combining the 3 callbacks
+      // into one.
+      state->listener()->OnDataStart(frame_header);
+      if (total_length > 0) {
+        state->listener()->OnDataPayload(db->cursor(), total_length);
+        db->AdvanceCursor(total_length);
+      }
+      state->listener()->OnDataEnd();
+      return DecodeStatus::kDecodeDone;
+    }
+    payload_state_ = PayloadState::kReadPayload;
+  } else {
+    payload_state_ = PayloadState::kReadPadLength;
+  }
+  state->InitializeRemainders();
+  state->listener()->OnDataStart(frame_header);
+  return ResumeDecodingPayload(state, db);
+}
+
+DecodeStatus DataPayloadDecoder::ResumeDecodingPayload(FrameDecoderState* state,
+                                                       DecodeBuffer* db) {
+  DVLOG(2) << "DataPayloadDecoder::ResumeDecodingPayload payload_state_="
+           << payload_state_;
+  const Http2FrameHeader& frame_header = state->frame_header();
+  DCHECK_EQ(Http2FrameType::DATA, frame_header.type);
+  DCHECK_LE(state->remaining_payload_and_padding(),
+            frame_header.payload_length);
+  DCHECK_LE(db->Remaining(), state->remaining_payload_and_padding());
+  DecodeStatus status;
+  size_t avail;
+  switch (payload_state_) {
+    case PayloadState::kReadPadLength:
+      // ReadPadLength handles the OnPadLength callback, and updating the
+      // remaining_payload and remaining_padding fields. If the amount of
+      // padding is too large to fit in the frame's payload, ReadPadLength
+      // instead calls OnPaddingTooLong and returns kDecodeError.
+      status = state->ReadPadLength(db, /*report_pad_length*/ true);
+      if (status != DecodeStatus::kDecodeDone) {
+        return status;
+      }
+    // FALLTHROUGH_INTENDED
+
+    case PayloadState::kReadPayload:
+      avail = state->AvailablePayload(db);
+      if (avail > 0) {
+        state->listener()->OnDataPayload(db->cursor(), avail);
+        db->AdvanceCursor(avail);
+        state->ConsumePayload(avail);
+      }
+      if (state->remaining_payload() > 0) {
+        payload_state_ = PayloadState::kReadPayload;
+        return DecodeStatus::kDecodeInProgress;
+      }
+    // FALLTHROUGH_INTENDED
+
+    case PayloadState::kSkipPadding:
+      // SkipPadding handles the OnPadding callback.
+      if (state->SkipPadding(db)) {
+        state->listener()->OnDataEnd();
+        return DecodeStatus::kDecodeDone;
+      }
+      payload_state_ = PayloadState::kSkipPadding;
+      return DecodeStatus::kDecodeInProgress;
+  }
+  HTTP2_BUG << "PayloadState: " << payload_state_;
+  return DecodeStatus::kDecodeError;
+}
+
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/data_payload_decoder.h b/net/http2/decoder/payload_decoders/data_payload_decoder.h
new file mode 100644
index 0000000..2fe09596
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/data_payload_decoder.h
@@ -0,0 +1,54 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_PAYLOAD_DECODERS_DATA_PAYLOAD_DECODER_H_
+#define NET_HTTP2_DECODER_PAYLOAD_DECODERS_DATA_PAYLOAD_DECODER_H_
+
+// Decodes the payload of a DATA frame.
+
+#include "net/base/net_export.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/decoder/frame_decoder_state.h"
+
+namespace net {
+namespace test {
+class DataPayloadDecoderPeer;
+}  // namespace test
+
+class NET_EXPORT_PRIVATE DataPayloadDecoder {
+ public:
+  // States during decoding of a DATA frame.
+  enum class PayloadState {
+    // The frame is padded and we need to read the PAD_LENGTH field (1 byte),
+    // and then call OnPadLength
+    kReadPadLength,
+
+    // Report the non-padding portion of the payload to the listener's
+    // OnDataPayload method.
+    kReadPayload,
+
+    // The decoder has finished with the non-padding portion of the payload,
+    // and is now ready to skip the trailing padding, if the frame has any.
+    kSkipPadding,
+  };
+
+  // Starts decoding a DATA frame's payload, and completes it if
+  // the entire payload is in the provided decode buffer.
+  DecodeStatus StartDecodingPayload(FrameDecoderState* state, DecodeBuffer* db);
+
+  // Resumes decoding a DATA frame's payload that has been split across
+  // decode buffers.
+  DecodeStatus ResumeDecodingPayload(FrameDecoderState* state,
+                                     DecodeBuffer* db);
+
+ private:
+  friend class test::DataPayloadDecoderPeer;
+
+  PayloadState payload_state_;
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_PAYLOAD_DECODERS_DATA_PAYLOAD_DECODER_H_
diff --git a/net/http2/decoder/payload_decoders/data_payload_decoder_test.cc b/net/http2/decoder/payload_decoders/data_payload_decoder_test.cc
new file mode 100644
index 0000000..bedb32f
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/data_payload_decoder_test.cc
@@ -0,0 +1,120 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/data_payload_decoder.h"
+
+#include <stddef.h>
+
+#include <string>
+
+#include "base/logging.h"
+#include "net/http2/decoder/frame_parts.h"
+#include "net/http2/decoder/frame_parts_collector.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/decoder/payload_decoders/payload_decoder_base_test_util.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures.h"
+#include "net/http2/http2_structures_test_util.h"
+#include "net/http2/tools/failure.h"
+#include "net/http2/tools/http2_frame_builder.h"
+#include "net/http2/tools/http2_random.h"
+#include "net/http2/tools/random_decoder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::AssertionResult;
+using std::string;
+
+namespace net {
+namespace test {
+
+// Provides friend access to an instance of the payload decoder, and also
+// provides info to aid in testing.
+class DataPayloadDecoderPeer {
+ public:
+  static constexpr Http2FrameType FrameType() { return Http2FrameType::DATA; }
+
+  // Returns the mask of flags that affect the decoding of the payload (i.e.
+  // flags that that indicate the presence of certain fields or padding).
+  static constexpr uint8_t FlagsAffectingPayloadDecoding() {
+    return Http2FrameFlag::FLAG_PADDED;
+  }
+
+  static void Randomize(DataPayloadDecoder* p, RandomBase* rng) {
+    VLOG(1) << "DataPayloadDecoderPeer::Randomize";
+    CorruptEnum(&p->payload_state_, rng);
+  }
+};
+
+namespace {
+
+struct Listener : public FramePartsCollector {
+  void OnDataStart(const Http2FrameHeader& header) override {
+    VLOG(1) << "OnDataStart: " << header;
+    StartFrame(header)->OnDataStart(header);
+  }
+
+  void OnDataPayload(const char* data, size_t len) override {
+    VLOG(1) << "OnDataPayload: len=" << len;
+    CurrentFrame()->OnDataPayload(data, len);
+  }
+
+  void OnDataEnd() override {
+    VLOG(1) << "OnDataEnd";
+    EndFrame()->OnDataEnd();
+  }
+
+  void OnPadLength(size_t pad_length) override {
+    VLOG(1) << "OnPadLength: " << pad_length;
+    CurrentFrame()->OnPadLength(pad_length);
+  }
+
+  void OnPadding(const char* padding, size_t skipped_length) override {
+    VLOG(1) << "OnPadding: " << skipped_length;
+    CurrentFrame()->OnPadding(padding, skipped_length);
+  }
+
+  void OnPaddingTooLong(const Http2FrameHeader& header,
+                        size_t missing_length) override {
+    VLOG(1) << "OnPaddingTooLong: " << header
+            << "    missing_length: " << missing_length;
+    EndFrame()->OnPaddingTooLong(header, missing_length);
+  }
+};
+
+class DataPayloadDecoderTest
+    : public AbstractPaddablePayloadDecoderTest<DataPayloadDecoder,
+                                                DataPayloadDecoderPeer,
+                                                Listener> {
+ protected:
+  AssertionResult CreateAndDecodeDataOfSize(size_t data_size) {
+    Reset();
+    uint8_t flags = RandFlags();
+
+    string data_payload = Random().RandString(data_size);
+    frame_builder_.Append(data_payload);
+    MaybeAppendTrailingPadding();
+
+    Http2FrameHeader frame_header(frame_builder_.size(), Http2FrameType::DATA,
+                                  flags, RandStreamId());
+    set_frame_header(frame_header);
+    ScrubFlagsOfHeader(&frame_header);
+    FrameParts expected(frame_header, data_payload, total_pad_length_);
+    VERIFY_AND_RETURN_SUCCESS(
+        DecodePayloadAndValidateSeveralWays(frame_builder_.buffer(), expected));
+  }
+};
+
+INSTANTIATE_TEST_CASE_P(VariousPadLengths,
+                        DataPayloadDecoderTest,
+                        ::testing::Values(0, 1, 2, 3, 4, 254, 255, 256));
+
+TEST_P(DataPayloadDecoderTest, VariousDataPayloadSizes) {
+  for (size_t data_size : {0, 1, 2, 3, 255, 256, 1024}) {
+    EXPECT_TRUE(CreateAndDecodeDataOfSize(data_size));
+  }
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/goaway_payload_decoder.cc b/net/http2/decoder/payload_decoders/goaway_payload_decoder.cc
new file mode 100644
index 0000000..f5b597f
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/goaway_payload_decoder.cc
@@ -0,0 +1,118 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/goaway_payload_decoder.h"
+
+#include <stddef.h>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures.h"
+#include "net/http2/tools/http2_bug_tracker.h"
+
+namespace net {
+
+std::ostream& operator<<(std::ostream& out,
+                         GoAwayPayloadDecoder::PayloadState v) {
+  switch (v) {
+    case GoAwayPayloadDecoder::PayloadState::kStartDecodingFixedFields:
+      return out << "kStartDecodingFixedFields";
+    case GoAwayPayloadDecoder::PayloadState::kHandleFixedFieldsStatus:
+      return out << "kHandleFixedFieldsStatus";
+    case GoAwayPayloadDecoder::PayloadState::kReadOpaqueData:
+      return out << "kReadOpaqueData";
+    case GoAwayPayloadDecoder::PayloadState::kResumeDecodingFixedFields:
+      return out << "kResumeDecodingFixedFields";
+  }
+
+  NOTREACHED();
+  return out;
+}
+
+DecodeStatus GoAwayPayloadDecoder::StartDecodingPayload(
+    FrameDecoderState* state,
+    DecodeBuffer* db) {
+  DVLOG(2) << "GoAwayPayloadDecoder::StartDecodingPayload: "
+           << state->frame_header();
+  DCHECK_EQ(Http2FrameType::GOAWAY, state->frame_header().type);
+  DCHECK_LE(db->Remaining(), state->frame_header().payload_length);
+  DCHECK_EQ(0, state->frame_header().flags);
+
+  state->InitializeRemainders();
+  payload_state_ = PayloadState::kStartDecodingFixedFields;
+  return ResumeDecodingPayload(state, db);
+}
+
+DecodeStatus GoAwayPayloadDecoder::ResumeDecodingPayload(
+    FrameDecoderState* state,
+    DecodeBuffer* db) {
+  DVLOG(2) << "GoAwayPayloadDecoder::ResumeDecodingPayload: remaining_payload="
+           << state->remaining_payload()
+           << ", db->Remaining=" << db->Remaining();
+
+  const Http2FrameHeader& frame_header = state->frame_header();
+  DCHECK_EQ(Http2FrameType::GOAWAY, frame_header.type);
+  DCHECK_LE(db->Remaining(), frame_header.payload_length);
+  DCHECK_NE(PayloadState::kHandleFixedFieldsStatus, payload_state_);
+
+  // |status| has to be initialized to some value to avoid compiler error in
+  // case PayloadState::kHandleFixedFieldsStatus below, but value does not
+  // matter, see DCHECK_NE above.
+  DecodeStatus status = DecodeStatus::kDecodeError;
+  size_t avail;
+  while (true) {
+    DVLOG(2) << "GoAwayPayloadDecoder::ResumeDecodingPayload payload_state_="
+             << payload_state_;
+    switch (payload_state_) {
+      case PayloadState::kStartDecodingFixedFields:
+        status = state->StartDecodingStructureInPayload(&goaway_fields_, db);
+      // FALLTHROUGH_INTENDED
+
+      case PayloadState::kHandleFixedFieldsStatus:
+        if (status == DecodeStatus::kDecodeDone) {
+          state->listener()->OnGoAwayStart(frame_header, goaway_fields_);
+        } else {
+          // Not done decoding the structure. Either we've got more payload
+          // to decode, or we've run out because the payload is too short,
+          // in which case OnFrameSizeError will have already been called.
+          DCHECK((status == DecodeStatus::kDecodeInProgress &&
+                  state->remaining_payload() > 0) ||
+                 (status == DecodeStatus::kDecodeError &&
+                  state->remaining_payload() == 0))
+              << "\n status=" << status
+              << "; remaining_payload=" << state->remaining_payload();
+          payload_state_ = PayloadState::kResumeDecodingFixedFields;
+          return status;
+        }
+      // FALLTHROUGH_INTENDED
+
+      case PayloadState::kReadOpaqueData:
+        // The opaque data is all the remains to be decoded, so anything left
+        // in the decode buffer is opaque data.
+        avail = db->Remaining();
+        if (avail > 0) {
+          state->listener()->OnGoAwayOpaqueData(db->cursor(), avail);
+          db->AdvanceCursor(avail);
+          state->ConsumePayload(avail);
+        }
+        if (state->remaining_payload() > 0) {
+          payload_state_ = PayloadState::kReadOpaqueData;
+          return DecodeStatus::kDecodeInProgress;
+        }
+        state->listener()->OnGoAwayEnd();
+        return DecodeStatus::kDecodeDone;
+
+      case PayloadState::kResumeDecodingFixedFields:
+        status = state->ResumeDecodingStructureInPayload(&goaway_fields_, db);
+        payload_state_ = PayloadState::kHandleFixedFieldsStatus;
+        continue;
+    }
+    HTTP2_BUG << "PayloadState: " << payload_state_;
+  }
+}
+
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/goaway_payload_decoder.h b/net/http2/decoder/payload_decoders/goaway_payload_decoder.h
new file mode 100644
index 0000000..5aefc6c
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/goaway_payload_decoder.h
@@ -0,0 +1,66 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_PAYLOAD_DECODERS_GOAWAY_PAYLOAD_DECODER_H_
+#define NET_HTTP2_DECODER_PAYLOAD_DECODERS_GOAWAY_PAYLOAD_DECODER_H_
+
+// Decodes the payload of a GOAWAY frame.
+
+// TODO(jamessynge): Sweep through all payload decoders, changing the names of
+// the PayloadState enums so that they are really states, and not actions.
+
+#include "net/base/net_export.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/decoder/frame_decoder_state.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+namespace test {
+class GoAwayPayloadDecoderPeer;
+}  // namespace test
+
+class NET_EXPORT_PRIVATE GoAwayPayloadDecoder {
+ public:
+  // States during decoding of a GOAWAY frame.
+  enum class PayloadState {
+    // At the start of the GOAWAY frame payload, ready to start decoding the
+    // fixed size fields into goaway_fields_.
+    kStartDecodingFixedFields,
+
+    // Handle the DecodeStatus returned from starting or resuming the
+    // decoding of Http2GoAwayFields into goaway_fields_. If complete,
+    // calls OnGoAwayStart.
+    kHandleFixedFieldsStatus,
+
+    // Report the Opaque Data portion of the payload to the listener's
+    // OnGoAwayOpaqueData method, and call OnGoAwayEnd when the end of the
+    // payload is reached.
+    kReadOpaqueData,
+
+    // The fixed size fields weren't all available when the decoder first
+    // tried to decode them (state kStartDecodingFixedFields); this state
+    // resumes the decoding when ResumeDecodingPayload is called later.
+    kResumeDecodingFixedFields,
+  };
+
+  // Starts the decoding of a GOAWAY frame's payload, and completes it if
+  // the entire payload is in the provided decode buffer.
+  DecodeStatus StartDecodingPayload(FrameDecoderState* state, DecodeBuffer* db);
+
+  // Resumes decoding a GOAWAY frame's payload that has been split across
+  // decode buffers.
+  DecodeStatus ResumeDecodingPayload(FrameDecoderState* state,
+                                     DecodeBuffer* db);
+
+ private:
+  friend class test::GoAwayPayloadDecoderPeer;
+
+  Http2GoAwayFields goaway_fields_;
+  PayloadState payload_state_;
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_PAYLOAD_DECODERS_GOAWAY_PAYLOAD_DECODER_H_
diff --git a/net/http2/decoder/payload_decoders/goaway_payload_decoder_test.cc b/net/http2/decoder/payload_decoders/goaway_payload_decoder_test.cc
new file mode 100644
index 0000000..248ac0d
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/goaway_payload_decoder_test.cc
@@ -0,0 +1,122 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/goaway_payload_decoder.h"
+
+#include <stddef.h>
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "net/http2/decoder/frame_parts.h"
+#include "net/http2/decoder/frame_parts_collector.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/decoder/payload_decoders/payload_decoder_base_test_util.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures_test_util.h"
+#include "net/http2/tools/http2_frame_builder.h"
+#include "net/http2/tools/http2_random.h"
+#include "net/http2/tools/random_decoder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::string;
+
+namespace net {
+namespace test {
+
+class GoAwayPayloadDecoderPeer {
+ public:
+  static constexpr Http2FrameType FrameType() { return Http2FrameType::GOAWAY; }
+
+  // Returns the mask of flags that affect the decoding of the payload (i.e.
+  // flags that that indicate the presence of certain fields or padding).
+  static constexpr uint8_t FlagsAffectingPayloadDecoding() { return 0; }
+
+  static void Randomize(GoAwayPayloadDecoder* p, RandomBase* rng) {
+    CorruptEnum(&p->payload_state_, rng);
+    test::Randomize(&p->goaway_fields_, rng);
+    VLOG(1) << "GoAwayPayloadDecoderPeer::Randomize goaway_fields: "
+            << p->goaway_fields_;
+  }
+};
+
+namespace {
+
+struct Listener : public FramePartsCollector {
+  void OnGoAwayStart(const Http2FrameHeader& header,
+                     const Http2GoAwayFields& goaway) override {
+    VLOG(1) << "OnGoAwayStart header: " << header << "; goaway: " << goaway;
+    StartFrame(header)->OnGoAwayStart(header, goaway);
+  }
+
+  void OnGoAwayOpaqueData(const char* data, size_t len) override {
+    VLOG(1) << "OnGoAwayOpaqueData: len=" << len;
+    CurrentFrame()->OnGoAwayOpaqueData(data, len);
+  }
+
+  void OnGoAwayEnd() override {
+    VLOG(1) << "OnGoAwayEnd";
+    EndFrame()->OnGoAwayEnd();
+  }
+
+  void OnFrameSizeError(const Http2FrameHeader& header) override {
+    VLOG(1) << "OnFrameSizeError: " << header;
+    FrameError(header)->OnFrameSizeError(header);
+  }
+};
+
+class GoAwayPayloadDecoderTest
+    : public AbstractPayloadDecoderTest<GoAwayPayloadDecoder,
+                                        GoAwayPayloadDecoderPeer,
+                                        Listener> {
+ public:
+  static bool ApproveSizeForTruncated(size_t size) {
+    return size != Http2GoAwayFields::EncodedSize();
+  }
+};
+
+// Confirm we get an error if the payload is not long enough to hold
+// Http2GoAwayFields.
+TEST_F(GoAwayPayloadDecoderTest, Truncated) {
+  Http2FrameBuilder fb;
+  fb.Append(Http2GoAwayFields(123, Http2ErrorCode::ENHANCE_YOUR_CALM));
+  EXPECT_TRUE(VerifyDetectsFrameSizeError(
+      0, fb.buffer(),
+      base::Bind(&GoAwayPayloadDecoderTest::ApproveSizeForTruncated)));
+}
+
+class GoAwayOpaqueDataLengthTests
+    : public GoAwayPayloadDecoderTest,
+      public ::testing::WithParamInterface<uint32_t> {
+ protected:
+  GoAwayOpaqueDataLengthTests() : length_(GetParam()) {
+    VLOG(1) << "################  length_=" << length_ << "  ################";
+  }
+
+  const uint32_t length_;
+};
+
+INSTANTIATE_TEST_CASE_P(VariousLengths,
+                        GoAwayOpaqueDataLengthTests,
+                        ::testing::Values(0, 1, 2, 3, 4, 5, 6));
+
+TEST_P(GoAwayOpaqueDataLengthTests, ValidLength) {
+  Http2GoAwayFields goaway;
+  Randomize(&goaway, RandomPtr());
+  string opaque_data = Random().RandString(length_);
+  Http2FrameBuilder fb;
+  fb.Append(goaway);
+  fb.Append(opaque_data);
+  Http2FrameHeader header(fb.size(), Http2FrameType::GOAWAY, RandFlags(),
+                          RandStreamId());
+  set_frame_header(header);
+  FrameParts expected(header, opaque_data);
+  expected.opt_goaway = goaway;
+  ASSERT_TRUE(DecodePayloadAndValidateSeveralWays(fb.buffer(), expected));
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/headers_payload_decoder.cc b/net/http2/decoder/payload_decoders/headers_payload_decoder.cc
new file mode 100644
index 0000000..eff1d3b
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/headers_payload_decoder.cc
@@ -0,0 +1,173 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/headers_payload_decoder.h"
+
+#include <stddef.h>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures.h"
+#include "net/http2/tools/http2_bug_tracker.h"
+
+namespace net {
+
+std::ostream& operator<<(std::ostream& out,
+                         HeadersPayloadDecoder::PayloadState v) {
+  switch (v) {
+    case HeadersPayloadDecoder::PayloadState::kReadPadLength:
+      return out << "kReadPadLength";
+    case HeadersPayloadDecoder::PayloadState::kStartDecodingPriorityFields:
+      return out << "kStartDecodingPriorityFields";
+    case HeadersPayloadDecoder::PayloadState::kResumeDecodingPriorityFields:
+      return out << "kResumeDecodingPriorityFields";
+    case HeadersPayloadDecoder::PayloadState::kReadPayload:
+      return out << "kReadPayload";
+    case HeadersPayloadDecoder::PayloadState::kSkipPadding:
+      return out << "kSkipPadding";
+  }
+  return out << static_cast<int>(v);
+}
+
+DecodeStatus HeadersPayloadDecoder::StartDecodingPayload(
+    FrameDecoderState* state,
+    DecodeBuffer* db) {
+  const Http2FrameHeader& frame_header = state->frame_header();
+  const uint32_t total_length = frame_header.payload_length;
+
+  DVLOG(2) << "HeadersPayloadDecoder::StartDecodingPayload: " << frame_header;
+
+  DCHECK_EQ(Http2FrameType::HEADERS, frame_header.type);
+  DCHECK_LE(db->Remaining(), total_length);
+  DCHECK_EQ(
+      0,
+      frame_header.flags &
+          ~(Http2FrameFlag::FLAG_END_STREAM | Http2FrameFlag::FLAG_END_HEADERS |
+            Http2FrameFlag::FLAG_PADDED | Http2FrameFlag::FLAG_PRIORITY));
+
+  // Special case for HEADERS frames that contain only the HPACK block
+  // (fragment or whole) and that fit fully into the decode buffer.
+  // Why? Unencoded browser GET requests are typically under 1K and HPACK
+  // commonly shrinks request headers by 80%, so we can expect this to
+  // be common.
+  // TODO(jamessynge) Add counters here and to Spdy for determining how
+  // common this situation is. A possible approach is to create a
+  // Http2FrameDecoderListener that counts the callbacks and then forwards
+  // them on to another listener, which makes it easy to add and remove
+  // counting on a connection or even frame basis.
+
+  // PADDED and PRIORITY both extra steps to decode, but if neither flag is
+  // set then we can decode faster.
+  const auto payload_flags =
+      Http2FrameFlag::FLAG_PADDED | Http2FrameFlag::FLAG_PRIORITY;
+  if (!frame_header.HasAnyFlags(payload_flags)) {
+    DVLOG(2) << "StartDecodingPayload !IsPadded && !HasPriority";
+    if (db->Remaining() == total_length) {
+      DVLOG(2) << "StartDecodingPayload all present";
+      // Note that we don't cache the listener field so that the callee can
+      // replace it if the frame is bad.
+      // If this case is common enough, consider combining the 3 callbacks
+      // into one, especially if END_HEADERS is also set.
+      state->listener()->OnHeadersStart(frame_header);
+      if (total_length > 0) {
+        state->listener()->OnHpackFragment(db->cursor(), total_length);
+        db->AdvanceCursor(total_length);
+      }
+      state->listener()->OnHeadersEnd();
+      return DecodeStatus::kDecodeDone;
+    }
+    payload_state_ = PayloadState::kReadPayload;
+  } else if (frame_header.IsPadded()) {
+    payload_state_ = PayloadState::kReadPadLength;
+  } else {
+    DCHECK(frame_header.HasPriority()) << frame_header;
+    payload_state_ = PayloadState::kStartDecodingPriorityFields;
+  }
+  state->InitializeRemainders();
+  state->listener()->OnHeadersStart(frame_header);
+  return ResumeDecodingPayload(state, db);
+}
+
+DecodeStatus HeadersPayloadDecoder::ResumeDecodingPayload(
+    FrameDecoderState* state,
+    DecodeBuffer* db) {
+  DVLOG(2) << "HeadersPayloadDecoder::ResumeDecodingPayload "
+           << "remaining_payload=" << state->remaining_payload()
+           << "; db->Remaining=" << db->Remaining();
+
+  const Http2FrameHeader& frame_header = state->frame_header();
+
+  DCHECK_EQ(Http2FrameType::HEADERS, frame_header.type);
+  DCHECK_LE(state->remaining_payload_and_padding(),
+            frame_header.payload_length);
+  DCHECK_LE(db->Remaining(), state->remaining_payload_and_padding());
+  DecodeStatus status;
+  size_t avail;
+  while (true) {
+    DVLOG(2) << "HeadersPayloadDecoder::ResumeDecodingPayload payload_state_="
+             << payload_state_;
+    switch (payload_state_) {
+      case PayloadState::kReadPadLength:
+        // ReadPadLength handles the OnPadLength callback, and updating the
+        // remaining_payload and remaining_padding fields. If the amount of
+        // padding is too large to fit in the frame's payload, ReadPadLength
+        // instead calls OnPaddingTooLong and returns kDecodeError.
+        status = state->ReadPadLength(db, /*report_pad_length*/ true);
+        if (status != DecodeStatus::kDecodeDone) {
+          return status;
+        }
+        if (!frame_header.HasPriority()) {
+          payload_state_ = PayloadState::kReadPayload;
+          continue;
+        }
+      // FALLTHROUGH_INTENDED
+
+      case PayloadState::kStartDecodingPriorityFields:
+        status = state->StartDecodingStructureInPayload(&priority_fields_, db);
+        if (status != DecodeStatus::kDecodeDone) {
+          payload_state_ = PayloadState::kResumeDecodingPriorityFields;
+          return status;
+        }
+        state->listener()->OnHeadersPriority(priority_fields_);
+      // FALLTHROUGH_INTENDED
+
+      case PayloadState::kReadPayload:
+        avail = state->AvailablePayload(db);
+        if (avail > 0) {
+          state->listener()->OnHpackFragment(db->cursor(), avail);
+          db->AdvanceCursor(avail);
+          state->ConsumePayload(avail);
+        }
+        if (state->remaining_payload() > 0) {
+          payload_state_ = PayloadState::kReadPayload;
+          return DecodeStatus::kDecodeInProgress;
+        }
+      // FALLTHROUGH_INTENDED
+
+      case PayloadState::kSkipPadding:
+        // SkipPadding handles the OnPadding callback.
+        if (state->SkipPadding(db)) {
+          state->listener()->OnHeadersEnd();
+          return DecodeStatus::kDecodeDone;
+        }
+        payload_state_ = PayloadState::kSkipPadding;
+        return DecodeStatus::kDecodeInProgress;
+
+      case PayloadState::kResumeDecodingPriorityFields:
+        status = state->ResumeDecodingStructureInPayload(&priority_fields_, db);
+        if (status != DecodeStatus::kDecodeDone) {
+          return status;
+        }
+        state->listener()->OnHeadersPriority(priority_fields_);
+        payload_state_ = PayloadState::kReadPayload;
+        continue;
+    }
+    HTTP2_BUG << "PayloadState: " << payload_state_;
+  }
+}
+
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/headers_payload_decoder.h b/net/http2/decoder/payload_decoders/headers_payload_decoder.h
new file mode 100644
index 0000000..76e6a082
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/headers_payload_decoder.h
@@ -0,0 +1,67 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_PAYLOAD_DECODERS_HEADERS_PAYLOAD_DECODER_H_
+#define NET_HTTP2_DECODER_PAYLOAD_DECODERS_HEADERS_PAYLOAD_DECODER_H_
+
+// Decodes the payload of a HEADERS frame.
+
+#include "net/base/net_export.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/decoder/frame_decoder_state.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+namespace test {
+class HeadersPayloadDecoderPeer;
+}  // namespace test
+
+class NET_EXPORT_PRIVATE HeadersPayloadDecoder {
+ public:
+  // States during decoding of a HEADERS frame, unless the fast path kicks
+  // in, in which case the state machine will be bypassed.
+  enum class PayloadState {
+    // The PADDED flag is set, and we now need to read the Pad Length field
+    // (the first byte of the payload, after the common frame header).
+    kReadPadLength,
+
+    // The PRIORITY flag is set, and we now need to read the fixed size priority
+    // fields (E, Stream Dependency, Weight) into priority_fields_.  Calls on
+    // OnHeadersPriority if completely decodes those fields.
+    kStartDecodingPriorityFields,
+
+    // The decoder passes the non-padding portion of the remaining payload
+    // (i.e. the HPACK block fragment) to the listener's OnHpackFragment method.
+    kReadPayload,
+
+    // The decoder has finished with the HPACK block fragment, and is now
+    // ready to skip the trailing padding, if the frame has any.
+    kSkipPadding,
+
+    // The fixed size fields weren't all available when the decoder first tried
+    // to decode them (state kStartDecodingPriorityFields); this state resumes
+    // the decoding when ResumeDecodingPayload is called later.
+    kResumeDecodingPriorityFields,
+  };
+
+  // Starts the decoding of a HEADERS frame's payload, and completes it if
+  // the entire payload is in the provided decode buffer.
+  DecodeStatus StartDecodingPayload(FrameDecoderState* state, DecodeBuffer* db);
+
+  // Resumes decoding a HEADERS frame's payload that has been split across
+  // decode buffers.
+  DecodeStatus ResumeDecodingPayload(FrameDecoderState* state,
+                                     DecodeBuffer* db);
+
+ private:
+  friend class test::HeadersPayloadDecoderPeer;
+
+  PayloadState payload_state_;
+  Http2PriorityFields priority_fields_;
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_PAYLOAD_DECODERS_HEADERS_PAYLOAD_DECODER_H_
diff --git a/net/http2/decoder/payload_decoders/headers_payload_decoder_test.cc b/net/http2/decoder/payload_decoders/headers_payload_decoder_test.cc
new file mode 100644
index 0000000..89bd29f
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/headers_payload_decoder_test.cc
@@ -0,0 +1,173 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/headers_payload_decoder.h"
+
+#include <stddef.h>
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "net/http2/decoder/frame_parts.h"
+#include "net/http2/decoder/frame_parts_collector.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/decoder/payload_decoders/payload_decoder_base_test_util.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures_test_util.h"
+#include "net/http2/tools/http2_frame_builder.h"
+#include "net/http2/tools/http2_random.h"
+#include "net/http2/tools/random_decoder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::string;
+
+namespace net {
+namespace test {
+
+class HeadersPayloadDecoderPeer {
+ public:
+  static constexpr Http2FrameType FrameType() {
+    return Http2FrameType::HEADERS;
+  }
+
+  // Returns the mask of flags that affect the decoding of the payload (i.e.
+  // flags that that indicate the presence of certain fields or padding).
+  static constexpr uint8_t FlagsAffectingPayloadDecoding() {
+    return Http2FrameFlag::FLAG_PADDED | Http2FrameFlag::FLAG_PRIORITY;
+  }
+
+  static void Randomize(HeadersPayloadDecoder* p, RandomBase* rng) {
+    CorruptEnum(&p->payload_state_, rng);
+    test::Randomize(&p->priority_fields_, rng);
+    VLOG(1) << "HeadersPayloadDecoderPeer::Randomize priority_fields_: "
+            << p->priority_fields_;
+  }
+};
+
+namespace {
+
+// Listener handles all On* methods that are expected to be called. If any other
+// On* methods of Http2FrameDecoderListener is called then the test fails; this
+// is achieved by way of FailingHttp2FrameDecoderListener, the base class of
+// FramePartsCollector.
+// These On* methods make use of StartFrame, EndFrame, etc. of the base class
+// to create and access to FrameParts instance(s) that will record the details.
+// After decoding, the test validation code can access the FramePart instance(s)
+// via the public methods of FramePartsCollector.
+struct Listener : public FramePartsCollector {
+  void OnHeadersStart(const Http2FrameHeader& header) override {
+    VLOG(1) << "OnHeadersStart: " << header;
+    StartFrame(header)->OnHeadersStart(header);
+  }
+
+  void OnHeadersPriority(const Http2PriorityFields& priority) override {
+    VLOG(1) << "OnHeadersPriority: " << priority;
+    CurrentFrame()->OnHeadersPriority(priority);
+  }
+
+  void OnHpackFragment(const char* data, size_t len) override {
+    VLOG(1) << "OnHpackFragment: len=" << len;
+    CurrentFrame()->OnHpackFragment(data, len);
+  }
+
+  void OnHeadersEnd() override {
+    VLOG(1) << "OnHeadersEnd";
+    EndFrame()->OnHeadersEnd();
+  }
+
+  void OnPadLength(size_t pad_length) override {
+    VLOG(1) << "OnPadLength: " << pad_length;
+    CurrentFrame()->OnPadLength(pad_length);
+  }
+
+  void OnPadding(const char* padding, size_t skipped_length) override {
+    VLOG(1) << "OnPadding: " << skipped_length;
+    CurrentFrame()->OnPadding(padding, skipped_length);
+  }
+
+  void OnPaddingTooLong(const Http2FrameHeader& header,
+                        size_t missing_length) override {
+    VLOG(1) << "OnPaddingTooLong: " << header
+            << "; missing_length: " << missing_length;
+    FrameError(header)->OnPaddingTooLong(header, missing_length);
+  }
+
+  void OnFrameSizeError(const Http2FrameHeader& header) override {
+    VLOG(1) << "OnFrameSizeError: " << header;
+    FrameError(header)->OnFrameSizeError(header);
+  }
+};
+
+class HeadersPayloadDecoderTest
+    : public AbstractPaddablePayloadDecoderTest<HeadersPayloadDecoder,
+                                                HeadersPayloadDecoderPeer,
+                                                Listener> {
+ public:
+  static bool ApproveSizeForTruncated(size_t size) {
+    return size != Http2PriorityFields::EncodedSize();
+  }
+};
+
+INSTANTIATE_TEST_CASE_P(VariousPadLengths,
+                        HeadersPayloadDecoderTest,
+                        ::testing::Values(0, 1, 2, 3, 4, 254, 255, 256));
+
+// Decode various sizes of (fake) HPACK payload, both with and without the
+// PRIORITY flag set.
+TEST_P(HeadersPayloadDecoderTest, VariousHpackPayloadSizes) {
+  for (size_t hpack_size : {0, 1, 2, 3, 255, 256, 1024}) {
+    LOG(INFO) << "###########   hpack_size = " << hpack_size << "  ###########";
+    Http2PriorityFields priority(RandStreamId(), 1 + Random().Rand8(),
+                                 Random().OneIn(2));
+
+    for (bool has_priority : {false, true}) {
+      Reset();
+      ASSERT_EQ(IsPadded() ? 1u : 0u, frame_builder_.size());
+      uint8_t flags = RandFlags();
+      if (has_priority) {
+        flags |= Http2FrameFlag::FLAG_PRIORITY;
+        frame_builder_.Append(priority);
+      }
+
+      string hpack_payload = Random().RandString(hpack_size);
+      frame_builder_.Append(hpack_payload);
+
+      MaybeAppendTrailingPadding();
+      Http2FrameHeader frame_header(frame_builder_.size(),
+                                    Http2FrameType::HEADERS, flags,
+                                    RandStreamId());
+      set_frame_header(frame_header);
+      ScrubFlagsOfHeader(&frame_header);
+      FrameParts expected(frame_header, hpack_payload, total_pad_length_);
+      if (has_priority) {
+        expected.opt_priority = priority;
+      }
+      EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(frame_builder_.buffer(),
+                                                      expected));
+    }
+  }
+}
+
+// Confirm we get an error if the PRIORITY flag is set but the payload is
+// not long enough, regardless of the amount of (valid) padding.
+TEST_P(HeadersPayloadDecoderTest, Truncated) {
+  Http2FrameBuilder fb;
+  fb.Append(Http2PriorityFields(RandStreamId(), 1 + Random().Rand8(),
+                                Random().OneIn(2)));
+  EXPECT_TRUE(VerifyDetectsMultipleFrameSizeErrors(
+      Http2FrameFlag::FLAG_PRIORITY, fb.buffer(),
+      base::Bind(&HeadersPayloadDecoderTest::ApproveSizeForTruncated),
+      total_pad_length_));
+}
+
+// Confirm we get an error if the PADDED flag is set but the payload is not
+// long enough to hold even the Pad Length amount of padding.
+TEST_P(HeadersPayloadDecoderTest, PaddingTooLong) {
+  EXPECT_TRUE(VerifyDetectsPaddingTooLong());
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/payload_decoder_base_test_util.cc b/net/http2/decoder/payload_decoders/payload_decoder_base_test_util.cc
new file mode 100644
index 0000000..d96d481b
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/payload_decoder_base_test_util.cc
@@ -0,0 +1,98 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/payload_decoder_base_test_util.h"
+
+#include "net/http2/decoder/frame_decoder_state_test_util.h"
+#include "net/http2/http2_structures_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+PayloadDecoderBaseTest::PayloadDecoderBaseTest() {
+  // If the test adds more data after the frame payload,
+  // stop as soon as the payload is decoded.
+  stop_decode_on_done_ = true;
+  frame_header_is_set_ = false;
+  Randomize(&frame_header_, RandomPtr());
+}
+
+DecodeStatus PayloadDecoderBaseTest::StartDecoding(DecodeBuffer* db) {
+  DVLOG(2) << "StartDecoding, db->Remaining=" << db->Remaining();
+  // Make sure sub-class has set frame_header_ so that we can inject it
+  // into the payload decoder below.
+  if (!frame_header_is_set_) {
+    ADD_FAILURE() << "frame_header_ is not set";
+    return DecodeStatus::kDecodeError;
+  }
+  // The contract with the payload decoders is that they won't receive a
+  // decode buffer that extends beyond the end of the frame.
+  if (db->Remaining() > frame_header_.payload_length) {
+    ADD_FAILURE() << "DecodeBuffer has too much data: " << db->Remaining()
+                  << " > " << frame_header_.payload_length;
+    return DecodeStatus::kDecodeError;
+  }
+
+  // Prepare the payload decoder.
+  PreparePayloadDecoder();
+
+  // Reconstruct the FrameDecoderState, prepare the listener, and add it to
+  // the FrameDecoderState.
+  frame_decoder_state_.~FrameDecoderState();
+  new (&frame_decoder_state_) FrameDecoderState;
+  frame_decoder_state_.set_listener(PrepareListener());
+
+  // Make sure that a listener was provided.
+  if (frame_decoder_state_.listener() == nullptr) {
+    ADD_FAILURE() << "PrepareListener must return a listener.";
+    return DecodeStatus::kDecodeError;
+  }
+
+  // Now that nothing in the payload decoder should be valid, inject the
+  // Http2FrameHeader whose payload we're about to decode. That header is the
+  // only state that a payload decoder should expect is valid when its Start
+  // method is called.
+  FrameDecoderStatePeer::set_frame_header(frame_header_, &frame_decoder_state_);
+  DecodeStatus status = StartDecodingPayload(db);
+  if (status != DecodeStatus::kDecodeInProgress) {
+    // Keep track of this so that a concrete test can verify that both fast
+    // and slow decoding paths have been tested.
+    ++fast_decode_count_;
+  }
+  return status;
+}
+
+DecodeStatus PayloadDecoderBaseTest::ResumeDecoding(DecodeBuffer* db) {
+  DVLOG(2) << "ResumeDecoding, db->Remaining=" << db->Remaining();
+  DecodeStatus status = ResumeDecodingPayload(db);
+  if (status != DecodeStatus::kDecodeInProgress) {
+    // Keep track of this so that a concrete test can verify that both fast
+    // and slow decoding paths have been tested.
+    ++slow_decode_count_;
+  }
+  return status;
+}
+
+::testing::AssertionResult
+PayloadDecoderBaseTest::DecodePayloadAndValidateSeveralWays(
+    base::StringPiece payload,
+    Validator validator) {
+  VERIFY_TRUE(frame_header_is_set_);
+  // Cap the payload to be decoded at the declared payload length. This is
+  // required by the decoders' preconditions; they are designed on the
+  // assumption that they're never passed more than they're permitted to
+  // consume.
+  // Note that it is OK if the payload is too short; the validator may be
+  // designed to check for that.
+  if (payload.size() > frame_header_.payload_length) {
+    payload = base::StringPiece(payload.data(), frame_header_.payload_length);
+  }
+  DecodeBuffer db(payload);
+  ResetDecodeSpeedCounters();
+  const bool kMayReturnZeroOnFirst = false;
+  return DecodeAndValidateSeveralWays(&db, kMayReturnZeroOnFirst, validator);
+}
+
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/payload_decoder_base_test_util.h b/net/http2/decoder/payload_decoders/payload_decoder_base_test_util.h
new file mode 100644
index 0000000..b49b15ef
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/payload_decoder_base_test_util.h
@@ -0,0 +1,486 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_PAYLOAD_DECODERS_PAYLOAD_DECODER_BASE_TEST_UTIL_H_
+#define NET_HTTP2_DECODER_PAYLOAD_DECODERS_PAYLOAD_DECODER_BASE_TEST_UTIL_H_
+
+// Base class for testing concrete payload decoder classes.
+
+#include <stddef.h>
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/strings/string_piece.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/decoder/frame_decoder_state.h"
+#include "net/http2/decoder/frame_parts.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_constants_test_util.h"
+#include "net/http2/http2_structures.h"
+#include "net/http2/tools/http2_frame_builder.h"
+#include "net/http2/tools/random_decoder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+// Base class for tests of payload decoders. Below this there is a templated
+// sub-class that adds a bunch of type specific features.
+class PayloadDecoderBaseTest : public RandomDecoderTest {
+ protected:
+  PayloadDecoderBaseTest();
+
+  // Virtual functions to be implemented by the test classes for the individual
+  // payload decoders...
+
+  // Start decoding the payload.
+  virtual DecodeStatus StartDecodingPayload(DecodeBuffer* db) = 0;
+
+  // Resume decoding the payload.
+  virtual DecodeStatus ResumeDecodingPayload(DecodeBuffer* db) = 0;
+
+  // In support of ensuring that we're really accessing and updating the
+  // decoder, prepare the decoder by, for example, overwriting the decoder.
+  virtual void PreparePayloadDecoder() = 0;
+
+  // Get the listener to be inserted into the FrameDecoderState, ready for
+  // listening (e.g. reset if it is a FramePartsCollector).
+  virtual Http2FrameDecoderListener* PrepareListener() = 0;
+
+  // Record a frame header for use on each call to StartDecoding.
+  void set_frame_header(const Http2FrameHeader& header) {
+    EXPECT_EQ(0, InvalidFlagMaskForFrameType(header.type) & header.flags);
+    if (!frame_header_is_set_ || frame_header_ != header) {
+      VLOG(2) << "set_frame_header: " << frame_header_;
+    }
+    frame_header_ = header;
+    frame_header_is_set_ = true;
+  }
+
+  const Http2FrameHeader& frame_header() const {
+    CHECK(frame_header_is_set_);
+    return frame_header_;
+  }
+
+  FrameDecoderState* mutable_state() { return &frame_decoder_state_; }
+
+  // Randomize the payload decoder, sets the payload decoder's frame_header_,
+  // then start decoding the payload. Called by RandomDecoderTest. This method
+  // is final so that we can always perform certain actions when
+  // RandomDecoderTest starts the decoding of a payload, such as randomizing the
+  // the payload decoder, injecting the frame header and counting fast decoding
+  // cases. Sub-classes must implement StartDecodingPayload to perform their
+  // initial decoding of a frame's payload.
+  DecodeStatus StartDecoding(DecodeBuffer* db) final;
+
+  // Called by RandomDecoderTest. This method is final so that we can always
+  // perform certain actions when RandomDecoderTest calls it, such as counting
+  // slow decode cases. Sub-classes must implement ResumeDecodingPayload to
+  // continue decoding the frame's payload, which must not all be in one buffer.
+  DecodeStatus ResumeDecoding(DecodeBuffer* db) final;
+
+  // Given the specified payload (without the common frame header), decode
+  // it with several partitionings of the payload.
+  ::testing::AssertionResult DecodePayloadAndValidateSeveralWays(
+      base::StringPiece payload,
+      Validator validator);
+
+  // TODO(jamessynge): Add helper method for verifying these are both non-zero,
+  // and call the new method from tests that expect successful decoding.
+  void ResetDecodeSpeedCounters() {
+    fast_decode_count_ = 0;
+    slow_decode_count_ = 0;
+  }
+
+  // Count of payloads that are full decoded by StartDecodingPayload, or that
+  // an error was detected by StartDecodingPayload.
+  size_t fast_decode_count_ = 0;
+
+  // Count of payloads that require calling ResumeDecodingPayload in order to
+  // decode them completely (or to detect an error during decoding).
+  size_t slow_decode_count_ = 0;
+
+ private:
+  bool frame_header_is_set_ = false;
+  Http2FrameHeader frame_header_;
+  FrameDecoderState frame_decoder_state_;
+};
+
+// Base class for payload decoders of type Decoder, with corresponding test
+// peer of type DecoderPeer, and using class Listener as the implementation
+// of Http2FrameDecoderListenerInterface to be used during decoding.
+// Typically Listener is a sub-class of FramePartsCollector.
+// SupportedFrameType is set to false only for UnknownPayloadDecoder.
+template <class Decoder,
+          class DecoderPeer,
+          class Listener,
+          bool SupportedFrameType = true>
+class AbstractPayloadDecoderTest : public PayloadDecoderBaseTest {
+ public:
+  static bool SucceedingApproveSize(size_t size) { return true; }
+
+ protected:
+  // An ApproveSize function returns true to approve decoding the specified
+  // size of payload, else false to skip that size. Typically used for negative
+  // tests; for example, decoding a SETTINGS frame at all sizes except for
+  // multiples of 6.
+  typedef base::Callback<bool(size_t size)> ApproveSize;
+
+  AbstractPayloadDecoderTest() {}
+
+  // These tests are in setup rather than the constructor for two reasons:
+  // 1) Constructors are not allowed to fail, so gUnit documents that EXPECT_*
+  //    and ASSERT_* are not allowed in constructors, and should instead be in
+  //    SetUp if they are needed before the body of the test is executed.
+  // 2) To allow the sub-class constructor to make any desired modifications to
+  //    the DecoderPeer before these tests are executed; in particular,
+  //    UnknownPayloadDecoderPeer has not got a fixed frame type, but it is
+  //    instead set during the test's constructor.
+  void SetUp() override {
+    PayloadDecoderBaseTest::SetUp();
+
+    // Confirm that DecoderPeer et al returns sensible values. Using auto as the
+    // variable type so that no (narrowing) conversions take place that hide
+    // problems; i.e. if someone changes KnownFlagsMaskForFrameType so that it
+    // doesn't return a uint8, and has bits above the low-order 8 bits set, this
+    // bit of paranoia should detect the problem before we get too far.
+    auto frame_type = DecoderPeer::FrameType();
+    if (SupportedFrameType) {
+      EXPECT_TRUE(IsSupportedHttp2FrameType(frame_type)) << frame_type;
+    } else {
+      EXPECT_FALSE(IsSupportedHttp2FrameType(frame_type)) << frame_type;
+    }
+
+    auto known_flags = KnownFlagsMaskForFrameType(frame_type);
+    EXPECT_EQ(known_flags, known_flags & 0xff);
+
+    auto flags_to_avoid = DecoderPeer::FlagsAffectingPayloadDecoding();
+    EXPECT_EQ(flags_to_avoid, flags_to_avoid & known_flags);
+  }
+
+  void PreparePayloadDecoder() override {
+    payload_decoder_.~Decoder();
+    new (&payload_decoder_) Decoder;
+  }
+
+  Http2FrameDecoderListener* PrepareListener() override {
+    listener_.Reset();
+    return &listener_;
+  }
+
+  // Returns random flags, but only those valid for the frame type, yet not
+  // those that the DecoderPeer says will affect the decoding of the payload
+  // (e.g. the PRIORTY flag on a HEADERS frame or PADDED on DATA frames).
+  uint8_t RandFlags() {
+    return Random().Rand8() &
+           KnownFlagsMaskForFrameType(DecoderPeer::FrameType()) &
+           ~DecoderPeer::FlagsAffectingPayloadDecoding();
+  }
+
+  // Start decoding the payload.
+  DecodeStatus StartDecodingPayload(DecodeBuffer* db) override {
+    DVLOG(2) << "StartDecodingPayload, db->Remaining=" << db->Remaining();
+    return payload_decoder_.StartDecodingPayload(mutable_state(), db);
+  }
+
+  // Resume decoding the payload.
+  DecodeStatus ResumeDecodingPayload(DecodeBuffer* db) override {
+    DVLOG(2) << "ResumeDecodingPayload, db->Remaining=" << db->Remaining();
+    return payload_decoder_.ResumeDecodingPayload(mutable_state(), db);
+  }
+
+  // Wrap |validator| in another one which will check that we've reached the
+  // expected state of kDecodeError with OnFrameSizeError having been called by
+  AssertionResult ValidatorForDecodePayloadAndValidateSeveralWays(
+      const FrameParts& expected) {
+    VERIFY_FALSE(listener_.IsInProgress());
+    VERIFY_EQ(1u, listener_.size());
+    VERIFY_AND_RETURN_SUCCESS(expected.VerifyEquals(*listener_.frame(0)));
+  }
+
+  // Decode one frame's payload and confirm that the listener recorded the
+  // expected FrameParts instance, and only FrameParts instance. The payload
+  // will be decoded several times with different partitionings of the payload,
+  // and after each the validator will be called.
+  AssertionResult DecodePayloadAndValidateSeveralWays(
+      base::StringPiece payload,
+      const FrameParts& expected) {
+    return PayloadDecoderBaseTest::DecodePayloadAndValidateSeveralWays(
+        payload, this->ValidateDoneAndEmpty(base::Bind(
+                     &AbstractPayloadDecoderTest::
+                         ValidatorForDecodePayloadAndValidateSeveralWays,
+                     base::Unretained(this), base::ConstRef(expected))));
+  }
+
+  // Wrap |validator| in another one which will check that we've reached the
+  // expected state of kDecodeError with OnFrameSizeError having been called by
+  // the payload decoder.
+  AssertionResult ValidatorForVerifyDetectsFrameSizeError(
+      const Http2FrameHeader& header,
+      const Validator& validator,
+      const DecodeBuffer& input,
+      DecodeStatus status) {
+    DVLOG(2) << "VerifyDetectsFrameSizeError validator; status=" << status
+             << "; input.Remaining=" << input.Remaining();
+    VERIFY_EQ(DecodeStatus::kDecodeError, status);
+    VERIFY_FALSE(listener_.IsInProgress());
+    VERIFY_EQ(1u, listener_.size());
+    const FrameParts* frame = listener_.frame(0);
+    VERIFY_EQ(header, frame->frame_header);
+    VERIFY_TRUE(frame->has_frame_size_error);
+    // Verify did not get OnPaddingTooLong, as we should only ever produce
+    // one of these two errors for a single frame.
+    VERIFY_FALSE(frame->opt_missing_length);
+    return validator.Run(input, status);
+  }
+
+  // Decode one frame's payload, expecting that the final status will be
+  // kDecodeError, and that OnFrameSizeError will have been called on the
+  // listener. The payload will be decoded several times with different
+  // partitionings of the payload. The type WrappedValidator is either
+  // RandomDecoderTest::Validator, RandomDecoderTest::NoArgValidator or
+  // std::nullptr_t (not extra validation).
+  template <typename WrappedValidator>
+  ::testing::AssertionResult VerifyDetectsFrameSizeError(
+      base::StringPiece payload,
+      const Http2FrameHeader& header,
+      WrappedValidator wrapped_validator) {
+    set_frame_header(header);
+    // If wrapped_validator is not a RandomDecoderTest::Validator, make it so.
+    Validator validator = this->ToValidator(wrapped_validator);
+    VERIFY_AND_RETURN_SUCCESS(
+        PayloadDecoderBaseTest::DecodePayloadAndValidateSeveralWays(
+            payload, base::Bind(&AbstractPayloadDecoderTest::
+                                    ValidatorForVerifyDetectsFrameSizeError,
+                                base::Unretained(this), base::ConstRef(header),
+                                base::ConstRef(validator))));
+  }
+
+  // Confirm that we get OnFrameSizeError when trying to decode unpadded_payload
+  // at all sizes from zero to unpadded_payload.size(), except those sizes not
+  // approved by approve_size.
+  // If total_pad_length is greater than zero, then that amount of padding
+  // is added to the payload (including the Pad Length field).
+  // The flags will be required_flags, PADDED if total_pad_length > 0, and some
+  // randomly selected flag bits not excluded by FlagsAffectingPayloadDecoding.
+  ::testing::AssertionResult VerifyDetectsMultipleFrameSizeErrors(
+      uint8_t required_flags,
+      base::StringPiece unpadded_payload,
+      ApproveSize approve_size,
+      int total_pad_length) {
+    // required_flags should come from those that are defined for the frame
+    // type AND are those that affect the decoding of the payload (otherwise,
+    // the flag shouldn't be required).
+    Http2FrameType frame_type = DecoderPeer::FrameType();
+    VERIFY_EQ(required_flags,
+              required_flags & KnownFlagsMaskForFrameType(frame_type));
+    VERIFY_EQ(required_flags,
+              required_flags & DecoderPeer::FlagsAffectingPayloadDecoding());
+
+    if (0 != (Http2FrameFlag::FLAG_PADDED &
+              KnownFlagsMaskForFrameType(frame_type))) {
+      // Frame type supports padding.
+      if (total_pad_length == 0) {
+        required_flags &= ~Http2FrameFlag::FLAG_PADDED;
+      } else {
+        required_flags |= Http2FrameFlag::FLAG_PADDED;
+      }
+    } else {
+      VERIFY_EQ(0, total_pad_length);
+    }
+
+    bool validated = false;
+    for (size_t real_payload_size = 0;
+         real_payload_size <= unpadded_payload.size(); ++real_payload_size) {
+      if (!approve_size.Run(real_payload_size)) {
+        continue;
+      }
+      VLOG(1) << "real_payload_size=" << real_payload_size;
+      uint8_t flags = required_flags | RandFlags();
+      Http2FrameBuilder fb;
+      if (total_pad_length > 0) {
+        // total_pad_length_ includes the size of the Pad Length field, and thus
+        // ranges from 0 (no PADDED flag) to 256 (Pad Length == 255).
+        fb.AppendUInt8(total_pad_length - 1);
+      }
+      // Append a subset of the unpadded_payload, which the decoder should
+      // determine is not a valid amount.
+      fb.Append(unpadded_payload.substr(0, real_payload_size));
+      if (total_pad_length > 0) {
+        fb.AppendZeroes(total_pad_length - 1);
+      }
+      // We choose a random stream id because the payload decoders aren't
+      // checking stream ids.
+      uint32_t stream_id = RandStreamId();
+      Http2FrameHeader header(fb.size(), frame_type, flags, stream_id);
+      VERIFY_SUCCESS(VerifyDetectsFrameSizeError(
+          fb.buffer(), header, base::Bind(&SucceedingValidator)));
+      validated = true;
+    }
+    VERIFY_TRUE(validated);
+    return ::testing::AssertionSuccess();
+  }
+
+  // As above, but for frames without padding.
+  ::testing::AssertionResult VerifyDetectsFrameSizeError(
+      uint8_t required_flags,
+      base::StringPiece unpadded_payload,
+      ApproveSize approve_size) {
+    Http2FrameType frame_type = DecoderPeer::FrameType();
+    uint8_t known_flags = KnownFlagsMaskForFrameType(frame_type);
+    VERIFY_EQ(0, known_flags & Http2FrameFlag::FLAG_PADDED);
+    VERIFY_EQ(0, required_flags & Http2FrameFlag::FLAG_PADDED);
+    VERIFY_AND_RETURN_SUCCESS(VerifyDetectsMultipleFrameSizeErrors(
+        required_flags, unpadded_payload, approve_size, 0));
+  }
+
+  Listener listener_;
+  union {
+    // Confirm at compile time that Decoder can be in an anonymous union,
+    // i.e. complain loudly if Decoder has members that prevent this, as it
+    // becomes annoying and possibly difficult to deal with non-anonymous
+    // unions and such union members.
+    Decoder payload_decoder_;
+  };
+};
+
+// A base class for tests parameterized by the total number of bytes of
+// padding, including the Pad Length field (i.e. a total_pad_length of 0
+// means unpadded as there is then no room for the Pad Length field).
+// The frame type must support padding.
+template <class Decoder, class DecoderPeer, class Listener>
+class AbstractPaddablePayloadDecoderTest
+    : public AbstractPayloadDecoderTest<Decoder, DecoderPeer, Listener>,
+      public ::testing::WithParamInterface<int> {
+  typedef AbstractPayloadDecoderTest<Decoder, DecoderPeer, Listener> Base;
+
+ protected:
+  using Base::Random;
+  using Base::RandStreamId;
+  using Base::set_frame_header;
+  using Base::listener_;
+  typedef typename Base::Validator Validator;
+
+  AbstractPaddablePayloadDecoderTest() : total_pad_length_(GetParam()) {
+    LOG(INFO) << "total_pad_length_ = " << total_pad_length_;
+  }
+
+  // Note that total_pad_length_ includes the size of the Pad Length field,
+  // and thus ranges from 0 (no PADDED flag) to 256 (Pad Length == 255).
+  bool IsPadded() const { return total_pad_length_ > 0; }
+
+  // Value of the Pad Length field. Only call if IsPadded.
+  size_t pad_length() const {
+    EXPECT_TRUE(IsPadded());
+    return total_pad_length_ - 1;
+  }
+
+  // Clear the frame builder and add the Pad Length field if appropriate.
+  void Reset() {
+    frame_builder_ = Http2FrameBuilder();
+    if (IsPadded()) {
+      frame_builder_.AppendUInt8(pad_length());
+    }
+  }
+
+  void MaybeAppendTrailingPadding() {
+    if (IsPadded()) {
+      frame_builder_.AppendZeroes(pad_length());
+    }
+  }
+
+  uint8_t RandFlags() {
+    uint8_t flags = Base::RandFlags();
+    if (IsPadded()) {
+      flags |= Http2FrameFlag::FLAG_PADDED;
+    } else {
+      flags &= ~Http2FrameFlag::FLAG_PADDED;
+    }
+    return flags;
+  }
+
+  static ::testing::AssertionResult ValidatorForVerifyDetectsPaddingTooLong(
+      const Http2FrameHeader& header,
+      int expected_missing_length,
+      const Listener& listener,
+      const DecodeBuffer& input,
+      DecodeStatus status) {
+    VERIFY_EQ(DecodeStatus::kDecodeError, status);
+    VERIFY_FALSE(listener.IsInProgress());
+    VERIFY_EQ(1u, listener.size());
+    const FrameParts* frame = listener.frame(0);
+    VERIFY_EQ(header, frame->frame_header);
+    VERIFY_TRUE(frame->opt_missing_length);
+    VERIFY_EQ(expected_missing_length, frame->opt_missing_length.value());
+    // Verify did not get OnFrameSizeError.
+    VERIFY_FALSE(frame->has_frame_size_error);
+    return ::testing::AssertionSuccess();
+  }
+
+  // Verify that we get OnPaddingTooLong when decoding payload, and that the
+  // amount of missing padding is as specified. header.IsPadded must be true,
+  // and the payload must be empty or the PadLength field must be too large.
+  ::testing::AssertionResult VerifyDetectsPaddingTooLong(
+      base::StringPiece payload,
+      const Http2FrameHeader& header,
+      int expected_missing_length) {
+    set_frame_header(header);
+    auto& listener = listener_;
+    VERIFY_AND_RETURN_SUCCESS(
+        PayloadDecoderBaseTest::DecodePayloadAndValidateSeveralWays(
+            payload, base::Bind(&AbstractPaddablePayloadDecoderTest::
+                                    ValidatorForVerifyDetectsPaddingTooLong,
+                                header, expected_missing_length,
+                                base::ConstRef(listener))));
+  }
+
+  // Verifies that we get OnPaddingTooLong for a padded frame payload whose
+  // (randomly selected) payload length is less than total_pad_length_.
+  // Flags will be selected at random, except PADDED will be set and
+  // flags_to_avoid will not be set. The stream id is selected at random.
+  ::testing::AssertionResult VerifyDetectsPaddingTooLong() {
+    uint8_t flags = RandFlags() | Http2FrameFlag::FLAG_PADDED;
+
+    // Create an all padding payload for total_pad_length_.
+    int payload_length = 0;
+    Http2FrameBuilder fb;
+    if (IsPadded()) {
+      fb.AppendUInt8(pad_length());
+      fb.AppendZeroes(pad_length());
+      VLOG(1) << "fb.size=" << fb.size();
+      // Pick a random length for the payload that is shorter than neccesary.
+      payload_length = Random().Rand32() % fb.size();
+    }
+
+    VLOG(1) << "payload_length=" << payload_length;
+    std::string payload = fb.buffer().substr(0, payload_length);
+
+    // The missing length is the amount we cut off the end, unless
+    // payload_length is zero, in which case the decoder knows only that 1
+    // byte, the Pad Length field, is missing.
+    int missing_length = payload_length == 0 ? 1 : fb.size() - payload_length;
+    VLOG(1) << "missing_length=" << missing_length;
+
+    const Http2FrameHeader header(payload_length, DecoderPeer::FrameType(),
+                                  flags, RandStreamId());
+    VERIFY_AND_RETURN_SUCCESS(
+        VerifyDetectsPaddingTooLong(payload, header, missing_length));
+  }
+
+  // total_pad_length_ includes the size of the Pad Length field, and thus
+  // ranges from 0 (no PADDED flag) to 256 (Pad Length == 255).
+  const size_t total_pad_length_;
+  Http2FrameBuilder frame_builder_;
+};
+
+}  // namespace test
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_PAYLOAD_DECODERS_PAYLOAD_DECODER_BASE_TEST_UTIL_H_
diff --git a/net/http2/decoder/payload_decoders/ping_payload_decoder.cc b/net/http2/decoder/payload_decoders/ping_payload_decoder.cc
new file mode 100644
index 0000000..abe43d0a
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/ping_payload_decoder.cc
@@ -0,0 +1,89 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/ping_payload_decoder.h"
+
+#include "base/logging.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/http2_constants.h"
+
+namespace net {
+namespace {
+constexpr auto kOpaqueSize = Http2PingFields::EncodedSize();
+}
+
+DecodeStatus PingPayloadDecoder::StartDecodingPayload(FrameDecoderState* state,
+                                                      DecodeBuffer* db) {
+  const Http2FrameHeader& frame_header = state->frame_header();
+  const uint32_t total_length = frame_header.payload_length;
+
+  DVLOG(2) << "PingPayloadDecoder::StartDecodingPayload: " << frame_header;
+  DCHECK_EQ(Http2FrameType::PING, frame_header.type);
+  DCHECK_LE(db->Remaining(), total_length);
+  DCHECK_EQ(0, frame_header.flags & ~(Http2FrameFlag::FLAG_ACK));
+
+  // Is the payload entirely in the decode buffer and is it the correct size?
+  // Given the size of the header and payload (17 bytes total), this is most
+  // likely the case the vast majority of the time.
+  if (db->Remaining() == kOpaqueSize && total_length == kOpaqueSize) {
+    // Special case this situation as it allows us to avoid any copying;
+    // the other path makes two copies, first into the buffer in
+    // Http2StructureDecoder as it accumulates the 8 bytes of opaque data,
+    // and a second copy into the Http2PingFields member of in this class.
+    // This supports the claim that this decoder is (mostly) non-buffering.
+    static_assert(sizeof(Http2PingFields) == kOpaqueSize,
+                  "If not, then can't enter this block!");
+    auto ping = reinterpret_cast<const Http2PingFields*>(db->cursor());
+    if (frame_header.IsAck()) {
+      state->listener()->OnPingAck(frame_header, *ping);
+    } else {
+      state->listener()->OnPing(frame_header, *ping);
+    }
+    db->AdvanceCursor(kOpaqueSize);
+    return DecodeStatus::kDecodeDone;
+  }
+  state->InitializeRemainders();
+  return HandleStatus(
+      state, state->StartDecodingStructureInPayload(&ping_fields_, db));
+}
+
+DecodeStatus PingPayloadDecoder::ResumeDecodingPayload(FrameDecoderState* state,
+                                                       DecodeBuffer* db) {
+  DVLOG(2) << "ResumeDecodingPayload: remaining_payload="
+           << state->remaining_payload();
+  DCHECK_EQ(Http2FrameType::PING, state->frame_header().type);
+  DCHECK_LE(db->Remaining(), state->frame_header().payload_length);
+  return HandleStatus(
+      state, state->ResumeDecodingStructureInPayload(&ping_fields_, db));
+}
+
+DecodeStatus PingPayloadDecoder::HandleStatus(FrameDecoderState* state,
+                                              DecodeStatus status) {
+  DVLOG(2) << "HandleStatus: status=" << status
+           << "; remaining_payload=" << state->remaining_payload();
+  if (status == DecodeStatus::kDecodeDone) {
+    if (state->remaining_payload() == 0) {
+      const Http2FrameHeader& frame_header = state->frame_header();
+      if (frame_header.IsAck()) {
+        state->listener()->OnPingAck(frame_header, ping_fields_);
+      } else {
+        state->listener()->OnPing(frame_header, ping_fields_);
+      }
+      return DecodeStatus::kDecodeDone;
+    }
+    // Payload is too long.
+    return state->ReportFrameSizeError();
+  }
+  // Not done decoding the structure. Either we've got more payload to decode,
+  // or we've run out because the payload is too short.
+  DCHECK(
+      (status == DecodeStatus::kDecodeInProgress &&
+       state->remaining_payload() > 0) ||
+      (status == DecodeStatus::kDecodeError && state->remaining_payload() == 0))
+      << "\n status=" << status
+      << "; remaining_payload=" << state->remaining_payload();
+  return status;
+}
+
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/ping_payload_decoder.h b/net/http2/decoder/payload_decoders/ping_payload_decoder.h
new file mode 100644
index 0000000..0eb10a2e
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/ping_payload_decoder.h
@@ -0,0 +1,43 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_PAYLOAD_DECODERS_PING_PAYLOAD_DECODER_H_
+#define NET_HTTP2_DECODER_PAYLOAD_DECODERS_PING_PAYLOAD_DECODER_H_
+
+// Decodes the payload of a PING frame; for the RFC, see:
+//     http://httpwg.org/specs/rfc7540.html#PING
+
+#include "net/base/net_export.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/decoder/frame_decoder_state.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+namespace test {
+class PingPayloadDecoderPeer;
+}  // namespace test
+
+class NET_EXPORT_PRIVATE PingPayloadDecoder {
+ public:
+  // Starts the decoding of a PING frame's payload, and completes it if the
+  // entire payload is in the provided decode buffer.
+  DecodeStatus StartDecodingPayload(FrameDecoderState* state, DecodeBuffer* db);
+
+  // Resumes decoding a PING frame's payload that has been split across
+  // decode buffers.
+  DecodeStatus ResumeDecodingPayload(FrameDecoderState* state,
+                                     DecodeBuffer* db);
+
+ private:
+  friend class test::PingPayloadDecoderPeer;
+
+  DecodeStatus HandleStatus(FrameDecoderState* state, DecodeStatus status);
+
+  Http2PingFields ping_fields_;
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_PAYLOAD_DECODERS_PING_PAYLOAD_DECODER_H_
diff --git a/net/http2/decoder/payload_decoders/ping_payload_decoder_test.cc b/net/http2/decoder/payload_decoders/ping_payload_decoder_test.cc
new file mode 100644
index 0000000..c3696f7c
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/ping_payload_decoder_test.cc
@@ -0,0 +1,122 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/ping_payload_decoder.h"
+
+#include <stddef.h>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "net/http2/decoder/frame_parts.h"
+#include "net/http2/decoder/frame_parts_collector.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/decoder/payload_decoders/payload_decoder_base_test_util.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures_test_util.h"
+#include "net/http2/tools/http2_frame_builder.h"
+#include "net/http2/tools/http2_random.h"
+#include "net/http2/tools/random_decoder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+class PingPayloadDecoderPeer {
+ public:
+  static constexpr Http2FrameType FrameType() { return Http2FrameType::PING; }
+
+  // Returns the mask of flags that affect the decoding of the payload (i.e.
+  // flags that that indicate the presence of certain fields or padding).
+  static constexpr uint8_t FlagsAffectingPayloadDecoding() { return 0; }
+
+  static void Randomize(PingPayloadDecoder* p, RandomBase* rng) {
+    VLOG(1) << "PingPayloadDecoderPeer::Randomize";
+    test::Randomize(&p->ping_fields_, rng);
+  }
+};
+
+namespace {
+
+struct Listener : public FramePartsCollector {
+  void OnPing(const Http2FrameHeader& header,
+              const Http2PingFields& ping) override {
+    VLOG(1) << "OnPing: " << header << "; " << ping;
+    StartAndEndFrame(header)->OnPing(header, ping);
+  }
+
+  void OnPingAck(const Http2FrameHeader& header,
+                 const Http2PingFields& ping) override {
+    VLOG(1) << "OnPingAck: " << header << "; " << ping;
+    StartAndEndFrame(header)->OnPingAck(header, ping);
+  }
+
+  void OnFrameSizeError(const Http2FrameHeader& header) override {
+    VLOG(1) << "OnFrameSizeError: " << header;
+    FrameError(header)->OnFrameSizeError(header);
+  }
+};
+
+class PingPayloadDecoderTest
+    : public AbstractPayloadDecoderTest<PingPayloadDecoder,
+                                        PingPayloadDecoderPeer,
+                                        Listener> {
+ public:
+  static bool ApproveSizeForWrongSize(size_t size) {
+    return size != Http2PingFields::EncodedSize();
+  }
+
+ protected:
+  Http2PingFields RandPingFields() {
+    Http2PingFields fields;
+    test::Randomize(&fields, RandomPtr());
+    return fields;
+  }
+};
+
+// Confirm we get an error if the payload is not the correct size to hold
+// exactly one Http2PingFields.
+TEST_F(PingPayloadDecoderTest, WrongSize) {
+  Http2FrameBuilder fb;
+  fb.Append(RandPingFields());
+  fb.Append(RandPingFields());
+  fb.Append(RandPingFields());
+  EXPECT_TRUE(VerifyDetectsFrameSizeError(
+      0, fb.buffer(),
+      base::Bind(&PingPayloadDecoderTest::ApproveSizeForWrongSize)));
+}
+
+TEST_F(PingPayloadDecoderTest, Ping) {
+  for (int n = 0; n < 100; ++n) {
+    Http2PingFields fields = RandPingFields();
+    Http2FrameBuilder fb;
+    fb.Append(fields);
+    Http2FrameHeader header(fb.size(), Http2FrameType::PING,
+                            RandFlags() & ~Http2FrameFlag::FLAG_ACK,
+                            RandStreamId());
+    set_frame_header(header);
+    FrameParts expected(header);
+    expected.opt_ping = fields;
+    EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(fb.buffer(), expected));
+  }
+}
+
+TEST_F(PingPayloadDecoderTest, PingAck) {
+  for (int n = 0; n < 100; ++n) {
+    Http2PingFields fields;
+    Randomize(&fields, RandomPtr());
+    Http2FrameBuilder fb;
+    fb.Append(fields);
+    Http2FrameHeader header(fb.size(), Http2FrameType::PING,
+                            RandFlags() | Http2FrameFlag::FLAG_ACK,
+                            RandStreamId());
+    set_frame_header(header);
+    FrameParts expected(header);
+    expected.opt_ping = fields;
+    EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(fb.buffer(), expected));
+  }
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/priority_payload_decoder.cc b/net/http2/decoder/payload_decoders/priority_payload_decoder.cc
new file mode 100644
index 0000000..51ae80b
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/priority_payload_decoder.cc
@@ -0,0 +1,64 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/priority_payload_decoder.h"
+
+#include "base/logging.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+
+DecodeStatus PriorityPayloadDecoder::StartDecodingPayload(
+    FrameDecoderState* state,
+    DecodeBuffer* db) {
+  DVLOG(2) << "PriorityPayloadDecoder::StartDecodingPayload: "
+           << state->frame_header();
+  DCHECK_EQ(Http2FrameType::PRIORITY, state->frame_header().type);
+  DCHECK_LE(db->Remaining(), state->frame_header().payload_length);
+  // PRIORITY frames have no flags.
+  DCHECK_EQ(0, state->frame_header().flags);
+  state->InitializeRemainders();
+  return HandleStatus(
+      state, state->StartDecodingStructureInPayload(&priority_fields_, db));
+}
+
+DecodeStatus PriorityPayloadDecoder::ResumeDecodingPayload(
+    FrameDecoderState* state,
+    DecodeBuffer* db) {
+  DVLOG(2) << "PriorityPayloadDecoder::ResumeDecodingPayload"
+           << "  remaining_payload=" << state->remaining_payload()
+           << "  db->Remaining=" << db->Remaining();
+  DCHECK_EQ(Http2FrameType::PRIORITY, state->frame_header().type);
+  DCHECK_LE(db->Remaining(), state->frame_header().payload_length);
+  return HandleStatus(
+      state, state->ResumeDecodingStructureInPayload(&priority_fields_, db));
+}
+
+DecodeStatus PriorityPayloadDecoder::HandleStatus(FrameDecoderState* state,
+                                                  DecodeStatus status) {
+  if (status == DecodeStatus::kDecodeDone) {
+    if (state->remaining_payload() == 0) {
+      state->listener()->OnPriorityFrame(state->frame_header(),
+                                         priority_fields_);
+      return DecodeStatus::kDecodeDone;
+    }
+    // Payload is too long.
+    return state->ReportFrameSizeError();
+  }
+  // Not done decoding the structure. Either we've got more payload to decode,
+  // or we've run out because the payload is too short, in which case
+  // OnFrameSizeError will have already been called.
+  DCHECK(
+      (status == DecodeStatus::kDecodeInProgress &&
+       state->remaining_payload() > 0) ||
+      (status == DecodeStatus::kDecodeError && state->remaining_payload() == 0))
+      << "\n status=" << status
+      << "; remaining_payload=" << state->remaining_payload();
+  return status;
+}
+
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/priority_payload_decoder.h b/net/http2/decoder/payload_decoders/priority_payload_decoder.h
new file mode 100644
index 0000000..d41a404
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/priority_payload_decoder.h
@@ -0,0 +1,44 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_PAYLOAD_DECODERS_PRIORITY_PAYLOAD_DECODER_H_
+#define NET_HTTP2_DECODER_PAYLOAD_DECODERS_PRIORITY_PAYLOAD_DECODER_H_
+
+// Decodes the payload of a PRIORITY frame.
+
+#include "net/base/net_export.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/decoder/frame_decoder_state.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+namespace test {
+class PriorityPayloadDecoderPeer;
+}  // namespace test
+
+class NET_EXPORT_PRIVATE PriorityPayloadDecoder {
+ public:
+  // Starts the decoding of a PRIORITY frame's payload, and completes it if
+  // the entire payload is in the provided decode buffer.
+  DecodeStatus StartDecodingPayload(FrameDecoderState* state, DecodeBuffer* db);
+
+  // Resumes decoding a PRIORITY frame that has been split across decode
+  // buffers.
+  DecodeStatus ResumeDecodingPayload(FrameDecoderState* state,
+                                     DecodeBuffer* db);
+
+ private:
+  friend class test::PriorityPayloadDecoderPeer;
+
+  // Determines whether to report the PRIORITY to the listener, wait for more
+  // input, or to report a Frame Size Error.
+  DecodeStatus HandleStatus(FrameDecoderState* state, DecodeStatus status);
+
+  Http2PriorityFields priority_fields_;
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_PAYLOAD_DECODERS_PRIORITY_PAYLOAD_DECODER_H_
diff --git a/net/http2/decoder/payload_decoders/priority_payload_decoder_test.cc b/net/http2/decoder/payload_decoders/priority_payload_decoder_test.cc
new file mode 100644
index 0000000..dabcba5
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/priority_payload_decoder_test.cc
@@ -0,0 +1,100 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/priority_payload_decoder.h"
+
+#include <stddef.h>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "net/http2/decoder/frame_parts.h"
+#include "net/http2/decoder/frame_parts_collector.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/decoder/payload_decoders/payload_decoder_base_test_util.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures_test_util.h"
+#include "net/http2/tools/http2_frame_builder.h"
+#include "net/http2/tools/http2_random.h"
+#include "net/http2/tools/random_decoder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+class PriorityPayloadDecoderPeer {
+ public:
+  static constexpr Http2FrameType FrameType() {
+    return Http2FrameType::PRIORITY;
+  }
+
+  // Returns the mask of flags that affect the decoding of the payload (i.e.
+  // flags that that indicate the presence of certain fields or padding).
+  static constexpr uint8_t FlagsAffectingPayloadDecoding() { return 0; }
+
+  static void Randomize(PriorityPayloadDecoder* p, RandomBase* rng) {
+    VLOG(1) << "PriorityPayloadDecoderPeer::Randomize";
+    test::Randomize(&p->priority_fields_, rng);
+  }
+};
+
+namespace {
+
+struct Listener : public FramePartsCollector {
+  void OnPriorityFrame(const Http2FrameHeader& header,
+                       const Http2PriorityFields& priority_fields) override {
+    VLOG(1) << "OnPriority: " << header << "; " << priority_fields;
+    StartAndEndFrame(header)->OnPriorityFrame(header, priority_fields);
+  }
+
+  void OnFrameSizeError(const Http2FrameHeader& header) override {
+    VLOG(1) << "OnFrameSizeError: " << header;
+    FrameError(header)->OnFrameSizeError(header);
+  }
+};
+
+class PriorityPayloadDecoderTest
+    : public AbstractPayloadDecoderTest<PriorityPayloadDecoder,
+                                        PriorityPayloadDecoderPeer,
+                                        Listener> {
+ public:
+  static bool ApproveSizeForWrongSize(size_t size) {
+    return size != Http2PriorityFields::EncodedSize();
+  }
+
+ protected:
+  Http2PriorityFields RandPriorityFields() {
+    Http2PriorityFields fields;
+    test::Randomize(&fields, RandomPtr());
+    return fields;
+  }
+};
+
+// Confirm we get an error if the payload is not the correct size to hold
+// exactly one Http2PriorityFields.
+TEST_F(PriorityPayloadDecoderTest, WrongSize) {
+  Http2FrameBuilder fb;
+  fb.Append(RandPriorityFields());
+  fb.Append(RandPriorityFields());
+  EXPECT_TRUE(VerifyDetectsFrameSizeError(
+      0, fb.buffer(),
+      base::Bind(&PriorityPayloadDecoderTest::ApproveSizeForWrongSize)));
+}
+
+TEST_F(PriorityPayloadDecoderTest, VariousPayloads) {
+  for (int n = 0; n < 100; ++n) {
+    Http2PriorityFields fields = RandPriorityFields();
+    Http2FrameBuilder fb;
+    fb.Append(fields);
+    Http2FrameHeader header(fb.size(), Http2FrameType::PRIORITY, RandFlags(),
+                            RandStreamId());
+    set_frame_header(header);
+    FrameParts expected(header);
+    expected.opt_priority = fields;
+    EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(fb.buffer(), expected));
+  }
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/push_promise_payload_decoder.cc b/net/http2/decoder/payload_decoders/push_promise_payload_decoder.cc
new file mode 100644
index 0000000..f160d1e
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/push_promise_payload_decoder.cc
@@ -0,0 +1,172 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/push_promise_payload_decoder.h"
+
+#include <stddef.h>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures.h"
+#include "net/http2/tools/http2_bug_tracker.h"
+
+namespace net {
+
+std::ostream& operator<<(std::ostream& out,
+                         PushPromisePayloadDecoder::PayloadState v) {
+  switch (v) {
+    case PushPromisePayloadDecoder::PayloadState::kReadPadLength:
+      return out << "kReadPadLength";
+    case PushPromisePayloadDecoder::PayloadState::
+        kStartDecodingPushPromiseFields:
+      return out << "kStartDecodingPushPromiseFields";
+    case PushPromisePayloadDecoder::PayloadState::kReadPayload:
+      return out << "kReadPayload";
+    case PushPromisePayloadDecoder::PayloadState::kSkipPadding:
+      return out << "kSkipPadding";
+    case PushPromisePayloadDecoder::PayloadState::
+        kResumeDecodingPushPromiseFields:
+      return out << "kResumeDecodingPushPromiseFields";
+  }
+  return out << static_cast<int>(v);
+}
+
+DecodeStatus PushPromisePayloadDecoder::StartDecodingPayload(
+    FrameDecoderState* state,
+    DecodeBuffer* db) {
+  const Http2FrameHeader& frame_header = state->frame_header();
+  const uint32_t total_length = frame_header.payload_length;
+
+  DVLOG(2) << "PushPromisePayloadDecoder::StartDecodingPayload: "
+           << frame_header;
+
+  DCHECK_EQ(Http2FrameType::PUSH_PROMISE, frame_header.type);
+  DCHECK_LE(db->Remaining(), total_length);
+  DCHECK_EQ(
+      0, frame_header.flags &
+             ~(Http2FrameFlag::FLAG_END_HEADERS | Http2FrameFlag::FLAG_PADDED));
+
+  if (!frame_header.IsPadded()) {
+    // If it turns out that PUSH_PROMISE frames without padding are sufficiently
+    // common, and that they are usually short enough that they fit entirely
+    // into one DecodeBuffer, we can detect that here and implement a special
+    // case, avoiding the state machine in ResumeDecodingPayload.
+    payload_state_ = PayloadState::kStartDecodingPushPromiseFields;
+  } else {
+    payload_state_ = PayloadState::kReadPadLength;
+  }
+  state->InitializeRemainders();
+  return ResumeDecodingPayload(state, db);
+}
+
+DecodeStatus PushPromisePayloadDecoder::ResumeDecodingPayload(
+    FrameDecoderState* state,
+    DecodeBuffer* db) {
+  DVLOG(2) << "UnknownPayloadDecoder::ResumeDecodingPayload"
+           << "  remaining_payload=" << state->remaining_payload()
+           << "  db->Remaining=" << db->Remaining();
+
+  const Http2FrameHeader& frame_header = state->frame_header();
+  DCHECK_EQ(Http2FrameType::PUSH_PROMISE, frame_header.type);
+  DCHECK_LE(state->remaining_payload(), frame_header.payload_length);
+  DCHECK_LE(db->Remaining(), frame_header.payload_length);
+
+  DecodeStatus status;
+  while (true) {
+    DVLOG(2)
+        << "PushPromisePayloadDecoder::ResumeDecodingPayload payload_state_="
+        << payload_state_;
+    switch (payload_state_) {
+      case PayloadState::kReadPadLength:
+        DCHECK_EQ(state->remaining_payload(), frame_header.payload_length);
+        // ReadPadLength handles the OnPadLength callback, and updating the
+        // remaining_payload and remaining_padding fields. If the amount of
+        // padding is too large to fit in the frame's payload, ReadPadLength
+        // instead calls OnPaddingTooLong and returns kDecodeError.
+        // Suppress the call to OnPadLength because we haven't yet called
+        // OnPushPromiseStart, which needs to wait until we've decoded the
+        // Promised Stream ID.
+        status = state->ReadPadLength(db, /*report_pad_length*/ false);
+        if (status != DecodeStatus::kDecodeDone) {
+          payload_state_ = PayloadState::kReadPadLength;
+          return status;
+        }
+      // FALLTHROUGH_INTENDED
+
+      case PayloadState::kStartDecodingPushPromiseFields:
+        status =
+            state->StartDecodingStructureInPayload(&push_promise_fields_, db);
+        if (status != DecodeStatus::kDecodeDone) {
+          payload_state_ = PayloadState::kResumeDecodingPushPromiseFields;
+          return status;
+        }
+        // Finished decoding the Promised Stream ID. Can now tell the listener
+        // that we're starting to decode a PUSH_PROMISE frame.
+        ReportPushPromise(state);
+      // FALLTHROUGH_INTENDED
+
+      case PayloadState::kReadPayload:
+        DCHECK_LT(state->remaining_payload(), frame_header.payload_length);
+        DCHECK_LE(state->remaining_payload(),
+                  frame_header.payload_length -
+                      Http2PushPromiseFields::EncodedSize());
+        DCHECK_LE(
+            state->remaining_payload(),
+            frame_header.payload_length -
+                Http2PushPromiseFields::EncodedSize() -
+                (frame_header.IsPadded() ? (1 + state->remaining_padding())
+                                         : 0));
+        {
+          size_t avail = state->AvailablePayload(db);
+          state->listener()->OnHpackFragment(db->cursor(), avail);
+          db->AdvanceCursor(avail);
+          state->ConsumePayload(avail);
+        }
+        if (state->remaining_payload() > 0) {
+          payload_state_ = PayloadState::kReadPayload;
+          return DecodeStatus::kDecodeInProgress;
+        }
+      // FALLTHROUGH_INTENDED
+
+      case PayloadState::kSkipPadding:
+        // SkipPadding handles the OnPadding callback.
+        if (state->SkipPadding(db)) {
+          state->listener()->OnPushPromiseEnd();
+          return DecodeStatus::kDecodeDone;
+        }
+        payload_state_ = PayloadState::kSkipPadding;
+        return DecodeStatus::kDecodeInProgress;
+
+      case PayloadState::kResumeDecodingPushPromiseFields:
+        status =
+            state->ResumeDecodingStructureInPayload(&push_promise_fields_, db);
+        if (status == DecodeStatus::kDecodeDone) {
+          // Finished decoding the Promised Stream ID. Can now tell the listener
+          // that we're starting to decode a PUSH_PROMISE frame.
+          ReportPushPromise(state);
+          payload_state_ = PayloadState::kReadPayload;
+          continue;
+        }
+        payload_state_ = PayloadState::kResumeDecodingPushPromiseFields;
+        return status;
+    }
+    HTTP2_BUG << "PayloadState: " << payload_state_;
+  }
+}
+
+void PushPromisePayloadDecoder::ReportPushPromise(FrameDecoderState* state) {
+  const Http2FrameHeader& frame_header = state->frame_header();
+  if (frame_header.IsPadded()) {
+    state->listener()->OnPushPromiseStart(frame_header, push_promise_fields_,
+                                          1 + state->remaining_padding());
+  } else {
+    state->listener()->OnPushPromiseStart(frame_header, push_promise_fields_,
+                                          0);
+  }
+}
+
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/push_promise_payload_decoder.h b/net/http2/decoder/payload_decoders/push_promise_payload_decoder.h
new file mode 100644
index 0000000..fe6c523
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/push_promise_payload_decoder.h
@@ -0,0 +1,66 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_PAYLOAD_DECODERS_PUSH_PROMISE_PAYLOAD_DECODER_H_
+#define NET_HTTP2_DECODER_PAYLOAD_DECODERS_PUSH_PROMISE_PAYLOAD_DECODER_H_
+
+// Decodes the payload of a PUSH_PROMISE frame.
+
+#include "net/base/net_export.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/decoder/frame_decoder_state.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+namespace test {
+class PushPromisePayloadDecoderPeer;
+}  // namespace test
+
+class NET_EXPORT_PRIVATE PushPromisePayloadDecoder {
+ public:
+  // States during decoding of a PUSH_PROMISE frame.
+  enum class PayloadState {
+    // The frame is padded and we need to read the PAD_LENGTH field (1 byte).
+    kReadPadLength,
+
+    // Ready to start decoding the fixed size fields of the PUSH_PROMISE
+    // frame into push_promise_fields_.
+    kStartDecodingPushPromiseFields,
+
+    // The decoder has already called OnPushPromiseStart, and is now reporting
+    // the HPACK block fragment to the listener's OnHpackFragment method.
+    kReadPayload,
+
+    // The decoder has finished with the HPACK block fragment, and is now
+    // ready to skip the trailing padding, if the frame has any.
+    kSkipPadding,
+
+    // The fixed size fields weren't all available when the decoder first tried
+    // to decode them (state kStartDecodingPushPromiseFields); this state
+    // resumes the decoding when ResumeDecodingPayload is called later.
+    kResumeDecodingPushPromiseFields,
+  };
+
+  // Starts the decoding of a PUSH_PROMISE frame's payload, and completes it if
+  // the entire payload is in the provided decode buffer.
+  DecodeStatus StartDecodingPayload(FrameDecoderState* state, DecodeBuffer* db);
+
+  // Resumes decoding a PUSH_PROMISE frame's payload that has been split across
+  // decode buffers.
+  DecodeStatus ResumeDecodingPayload(FrameDecoderState* state,
+                                     DecodeBuffer* db);
+
+ private:
+  friend class test::PushPromisePayloadDecoderPeer;
+
+  void ReportPushPromise(FrameDecoderState* state);
+
+  PayloadState payload_state_;
+  Http2PushPromiseFields push_promise_fields_;
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_PAYLOAD_DECODERS_PUSH_PROMISE_PAYLOAD_DECODER_H_
diff --git a/net/http2/decoder/payload_decoders/push_promise_payload_decoder_test.cc b/net/http2/decoder/payload_decoders/push_promise_payload_decoder_test.cc
new file mode 100644
index 0000000..ba1a815
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/push_promise_payload_decoder_test.cc
@@ -0,0 +1,151 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/push_promise_payload_decoder.h"
+
+#include <stddef.h>
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "net/http2/decoder/frame_parts.h"
+#include "net/http2/decoder/frame_parts_collector.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/decoder/payload_decoders/payload_decoder_base_test_util.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures_test_util.h"
+#include "net/http2/tools/http2_frame_builder.h"
+#include "net/http2/tools/http2_random.h"
+#include "net/http2/tools/random_decoder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::string;
+
+namespace net {
+namespace test {
+
+// Provides friend access to an instance of the payload decoder, and also
+// provides info to aid in testing.
+class PushPromisePayloadDecoderPeer {
+ public:
+  static constexpr Http2FrameType FrameType() {
+    return Http2FrameType::PUSH_PROMISE;
+  }
+
+  // Returns the mask of flags that affect the decoding of the payload (i.e.
+  // flags that that indicate the presence of certain fields or padding).
+  static constexpr uint8_t FlagsAffectingPayloadDecoding() {
+    return Http2FrameFlag::FLAG_PADDED;
+  }
+
+  static void Randomize(PushPromisePayloadDecoder* p, RandomBase* rng) {
+    VLOG(1) << "PushPromisePayloadDecoderPeer::Randomize";
+    CorruptEnum(&p->payload_state_, rng);
+    test::Randomize(&p->push_promise_fields_, rng);
+  }
+};
+
+namespace {
+
+// Listener listens for only those methods expected by the payload decoder
+// under test, and forwards them onto the FrameParts instance for the current
+// frame.
+struct Listener : public FramePartsCollector {
+  void OnPushPromiseStart(const Http2FrameHeader& header,
+                          const Http2PushPromiseFields& promise,
+                          size_t total_padding_length) override {
+    VLOG(1) << "OnPushPromiseStart header: " << header
+            << "  promise: " << promise
+            << "  total_padding_length: " << total_padding_length;
+    EXPECT_EQ(Http2FrameType::PUSH_PROMISE, header.type);
+    StartFrame(header)->OnPushPromiseStart(header, promise,
+                                           total_padding_length);
+  }
+
+  void OnHpackFragment(const char* data, size_t len) override {
+    VLOG(1) << "OnHpackFragment: len=" << len;
+    CurrentFrame()->OnHpackFragment(data, len);
+  }
+
+  void OnPushPromiseEnd() override {
+    VLOG(1) << "OnPushPromiseEnd";
+    EndFrame()->OnPushPromiseEnd();
+  }
+
+  void OnPadding(const char* padding, size_t skipped_length) override {
+    VLOG(1) << "OnPadding: " << skipped_length;
+    CurrentFrame()->OnPadding(padding, skipped_length);
+  }
+
+  void OnPaddingTooLong(const Http2FrameHeader& header,
+                        size_t missing_length) override {
+    VLOG(1) << "OnPaddingTooLong: " << header
+            << "; missing_length: " << missing_length;
+    FrameError(header)->OnPaddingTooLong(header, missing_length);
+  }
+
+  void OnFrameSizeError(const Http2FrameHeader& header) override {
+    VLOG(1) << "OnFrameSizeError: " << header;
+    FrameError(header)->OnFrameSizeError(header);
+  }
+};
+
+class PushPromisePayloadDecoderTest
+    : public AbstractPaddablePayloadDecoderTest<PushPromisePayloadDecoder,
+                                                PushPromisePayloadDecoderPeer,
+                                                Listener> {
+ public:
+  static bool ApproveSizeForTruncated(size_t size) {
+    return size != Http2PushPromiseFields::EncodedSize();
+  }
+};
+
+INSTANTIATE_TEST_CASE_P(VariousPadLengths,
+                        PushPromisePayloadDecoderTest,
+                        ::testing::Values(0, 1, 2, 3, 4, 254, 255, 256));
+
+// Payload contains the required Http2PushPromiseFields, followed by some
+// (fake) HPACK payload.
+TEST_P(PushPromisePayloadDecoderTest, VariousHpackPayloadSizes) {
+  for (size_t hpack_size : {0, 1, 2, 3, 255, 256, 1024}) {
+    LOG(INFO) << "###########   hpack_size = " << hpack_size << "  ###########";
+    Reset();
+    string hpack_payload = Random().RandString(hpack_size);
+    Http2PushPromiseFields push_promise{RandStreamId()};
+    frame_builder_.Append(push_promise);
+    frame_builder_.Append(hpack_payload);
+    MaybeAppendTrailingPadding();
+    Http2FrameHeader frame_header(frame_builder_.size(),
+                                  Http2FrameType::PUSH_PROMISE, RandFlags(),
+                                  RandStreamId());
+    set_frame_header(frame_header);
+    FrameParts expected(frame_header, hpack_payload, total_pad_length_);
+    expected.opt_push_promise = push_promise;
+    EXPECT_TRUE(
+        DecodePayloadAndValidateSeveralWays(frame_builder_.buffer(), expected));
+  }
+}
+
+// Confirm we get an error if the payload is not long enough for the required
+// portion of the payload, regardless of the amount of (valid) padding.
+TEST_P(PushPromisePayloadDecoderTest, Truncated) {
+  Http2PushPromiseFields push_promise{RandStreamId()};
+  Http2FrameBuilder fb;
+  fb.Append(push_promise);
+  EXPECT_TRUE(VerifyDetectsMultipleFrameSizeErrors(
+      0, fb.buffer(),
+      base::Bind(&PushPromisePayloadDecoderTest::ApproveSizeForTruncated),
+      total_pad_length_));
+}
+
+// Confirm we get an error if the PADDED flag is set but the payload is not
+// long enough to hold even the Pad Length amount of padding.
+TEST_P(PushPromisePayloadDecoderTest, PaddingTooLong) {
+  EXPECT_TRUE(VerifyDetectsPaddingTooLong());
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/rst_stream_payload_decoder.cc b/net/http2/decoder/payload_decoders/rst_stream_payload_decoder.cc
new file mode 100644
index 0000000..e9d86fb
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/rst_stream_payload_decoder.cc
@@ -0,0 +1,66 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/rst_stream_payload_decoder.h"
+
+#include "base/logging.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+
+DecodeStatus RstStreamPayloadDecoder::StartDecodingPayload(
+    FrameDecoderState* state,
+    DecodeBuffer* db) {
+  DVLOG(2) << "RstStreamPayloadDecoder::StartDecodingPayload: "
+           << state->frame_header();
+  DCHECK_EQ(Http2FrameType::RST_STREAM, state->frame_header().type);
+  DCHECK_LE(db->Remaining(), state->frame_header().payload_length);
+  // RST_STREAM has no flags.
+  DCHECK_EQ(0, state->frame_header().flags);
+  state->InitializeRemainders();
+  return HandleStatus(
+      state, state->StartDecodingStructureInPayload(&rst_stream_fields_, db));
+}
+
+DecodeStatus RstStreamPayloadDecoder::ResumeDecodingPayload(
+    FrameDecoderState* state,
+    DecodeBuffer* db) {
+  DVLOG(2) << "RstStreamPayloadDecoder::ResumeDecodingPayload"
+           << "  remaining_payload=" << state->remaining_payload()
+           << "  db->Remaining=" << db->Remaining();
+  DCHECK_EQ(Http2FrameType::RST_STREAM, state->frame_header().type);
+  DCHECK_LE(db->Remaining(), state->frame_header().payload_length);
+  return HandleStatus(
+      state, state->ResumeDecodingStructureInPayload(&rst_stream_fields_, db));
+}
+
+DecodeStatus RstStreamPayloadDecoder::HandleStatus(FrameDecoderState* state,
+                                                   DecodeStatus status) {
+  DVLOG(2) << "HandleStatus: status=" << status
+           << "; remaining_payload=" << state->remaining_payload();
+  if (status == DecodeStatus::kDecodeDone) {
+    if (state->remaining_payload() == 0) {
+      state->listener()->OnRstStream(state->frame_header(),
+                                     rst_stream_fields_.error_code);
+      return DecodeStatus::kDecodeDone;
+    }
+    // Payload is too long.
+    return state->ReportFrameSizeError();
+  }
+  // Not done decoding the structure. Either we've got more payload to decode,
+  // or we've run out because the payload is too short, in which case
+  // OnFrameSizeError will have already been called by the FrameDecoderState.
+  DCHECK(
+      (status == DecodeStatus::kDecodeInProgress &&
+       state->remaining_payload() > 0) ||
+      (status == DecodeStatus::kDecodeError && state->remaining_payload() == 0))
+      << "\n status=" << status
+      << "; remaining_payload=" << state->remaining_payload();
+  return status;
+}
+
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/rst_stream_payload_decoder.h b/net/http2/decoder/payload_decoders/rst_stream_payload_decoder.h
new file mode 100644
index 0000000..cdfd6587
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/rst_stream_payload_decoder.h
@@ -0,0 +1,42 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_PAYLOAD_DECODERS_RST_STREAM_PAYLOAD_DECODER_H_
+#define NET_HTTP2_DECODER_PAYLOAD_DECODERS_RST_STREAM_PAYLOAD_DECODER_H_
+
+// Decodes the payload of a RST_STREAM frame.
+
+#include "net/base/net_export.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/decoder/frame_decoder_state.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+namespace test {
+class RstStreamPayloadDecoderPeer;
+}  // namespace test
+
+class NET_EXPORT_PRIVATE RstStreamPayloadDecoder {
+ public:
+  // Starts the decoding of a RST_STREAM frame's payload, and completes it if
+  // the entire payload is in the provided decode buffer.
+  DecodeStatus StartDecodingPayload(FrameDecoderState* state, DecodeBuffer* db);
+
+  // Resumes decoding a RST_STREAM frame's payload that has been split across
+  // decode buffers.
+  DecodeStatus ResumeDecodingPayload(FrameDecoderState* state,
+                                     DecodeBuffer* db);
+
+ private:
+  friend class test::RstStreamPayloadDecoderPeer;
+
+  DecodeStatus HandleStatus(FrameDecoderState* state, DecodeStatus status);
+
+  Http2RstStreamFields rst_stream_fields_;
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_PAYLOAD_DECODERS_RST_STREAM_PAYLOAD_DECODER_H_
diff --git a/net/http2/decoder/payload_decoders/rst_stream_payload_decoder_test.cc b/net/http2/decoder/payload_decoders/rst_stream_payload_decoder_test.cc
new file mode 100644
index 0000000..703d98ff
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/rst_stream_payload_decoder_test.cc
@@ -0,0 +1,102 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/rst_stream_payload_decoder.h"
+
+#include <stddef.h>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "net/http2/decoder/frame_parts.h"
+#include "net/http2/decoder/frame_parts_collector.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/decoder/payload_decoders/payload_decoder_base_test_util.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_constants_test_util.h"
+#include "net/http2/http2_structures_test_util.h"
+#include "net/http2/tools/http2_frame_builder.h"
+#include "net/http2/tools/http2_random.h"
+#include "net/http2/tools/random_decoder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+class RstStreamPayloadDecoderPeer {
+ public:
+  static constexpr Http2FrameType FrameType() {
+    return Http2FrameType::RST_STREAM;
+  }
+
+  // Returns the mask of flags that affect the decoding of the payload (i.e.
+  // flags that that indicate the presence of certain fields or padding).
+  static constexpr uint8_t FlagsAffectingPayloadDecoding() { return 0; }
+
+  static void Randomize(RstStreamPayloadDecoder* p, RandomBase* rng) {
+    VLOG(1) << "RstStreamPayloadDecoderPeer::Randomize";
+    test::Randomize(&p->rst_stream_fields_, rng);
+  }
+};
+
+namespace {
+
+struct Listener : public FramePartsCollector {
+  void OnRstStream(const Http2FrameHeader& header,
+                   Http2ErrorCode error_code) override {
+    VLOG(1) << "OnRstStream: " << header << "; error_code=" << error_code;
+    StartAndEndFrame(header)->OnRstStream(header, error_code);
+  }
+
+  void OnFrameSizeError(const Http2FrameHeader& header) override {
+    VLOG(1) << "OnFrameSizeError: " << header;
+    FrameError(header)->OnFrameSizeError(header);
+  }
+};
+
+class RstStreamPayloadDecoderTest
+    : public AbstractPayloadDecoderTest<RstStreamPayloadDecoder,
+                                        RstStreamPayloadDecoderPeer,
+                                        Listener> {
+ public:
+  static bool ApproveSizeForWrongSize(size_t size) {
+    return size != Http2RstStreamFields::EncodedSize();
+  }
+
+ protected:
+  Http2RstStreamFields RandRstStreamFields() {
+    Http2RstStreamFields fields;
+    test::Randomize(&fields, RandomPtr());
+    return fields;
+  }
+};
+
+// Confirm we get an error if the payload is not the correct size to hold
+// exactly one Http2RstStreamFields.
+TEST_F(RstStreamPayloadDecoderTest, WrongSize) {
+  Http2FrameBuilder fb;
+  fb.Append(RandRstStreamFields());
+  fb.Append(RandRstStreamFields());
+  fb.Append(RandRstStreamFields());
+  EXPECT_TRUE(VerifyDetectsFrameSizeError(
+      0, fb.buffer(),
+      base::Bind(&RstStreamPayloadDecoderTest::ApproveSizeForWrongSize)));
+}
+
+TEST_F(RstStreamPayloadDecoderTest, AllErrors) {
+  for (auto error_code : AllHttp2ErrorCodes()) {
+    Http2RstStreamFields fields{error_code};
+    Http2FrameBuilder fb;
+    fb.Append(fields);
+    Http2FrameHeader header(fb.size(), Http2FrameType::RST_STREAM, RandFlags(),
+                            RandStreamId());
+    set_frame_header(header);
+    FrameParts expected(header);
+    expected.opt_rst_stream_error_code = error_code;
+    EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(fb.buffer(), expected));
+  }
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/settings_payload_decoder.cc b/net/http2/decoder/payload_decoders/settings_payload_decoder.cc
new file mode 100644
index 0000000..f8cc52cb
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/settings_payload_decoder.cc
@@ -0,0 +1,97 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/settings_payload_decoder.h"
+
+#include "base/logging.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+
+DecodeStatus SettingsPayloadDecoder::StartDecodingPayload(
+    FrameDecoderState* state,
+    DecodeBuffer* db) {
+  const Http2FrameHeader& frame_header = state->frame_header();
+  const uint32_t total_length = frame_header.payload_length;
+
+  DVLOG(2) << "SettingsPayloadDecoder::StartDecodingPayload: " << frame_header;
+  DCHECK_EQ(Http2FrameType::SETTINGS, frame_header.type);
+  DCHECK_LE(db->Remaining(), total_length);
+  DCHECK_EQ(0, frame_header.flags & ~(Http2FrameFlag::FLAG_ACK));
+
+  if (frame_header.IsAck()) {
+    if (total_length == 0) {
+      state->listener()->OnSettingsAck(frame_header);
+      return DecodeStatus::kDecodeDone;
+    } else {
+      state->InitializeRemainders();
+      return state->ReportFrameSizeError();
+    }
+  } else {
+    state->InitializeRemainders();
+    state->listener()->OnSettingsStart(frame_header);
+    return StartDecodingSettings(state, db);
+  }
+}
+
+DecodeStatus SettingsPayloadDecoder::ResumeDecodingPayload(
+    FrameDecoderState* state,
+    DecodeBuffer* db) {
+  DVLOG(2) << "SettingsPayloadDecoder::ResumeDecodingPayload"
+           << "  remaining_payload=" << state->remaining_payload()
+           << "  db->Remaining=" << db->Remaining();
+  DCHECK_EQ(Http2FrameType::SETTINGS, state->frame_header().type);
+  DCHECK_LE(db->Remaining(), state->frame_header().payload_length);
+
+  DecodeStatus status =
+      state->ResumeDecodingStructureInPayload(&setting_fields_, db);
+  if (status == DecodeStatus::kDecodeDone) {
+    state->listener()->OnSetting(setting_fields_);
+    return StartDecodingSettings(state, db);
+  }
+  return HandleNotDone(state, db, status);
+}
+
+DecodeStatus SettingsPayloadDecoder::StartDecodingSettings(
+    FrameDecoderState* state,
+    DecodeBuffer* db) {
+  DVLOG(2) << "SettingsPayloadDecoder::StartDecodingSettings"
+           << "  remaining_payload=" << state->remaining_payload()
+           << "  db->Remaining=" << db->Remaining();
+  while (state->remaining_payload() > 0) {
+    DecodeStatus status =
+        state->StartDecodingStructureInPayload(&setting_fields_, db);
+    if (status == DecodeStatus::kDecodeDone) {
+      state->listener()->OnSetting(setting_fields_);
+      continue;
+    }
+    return HandleNotDone(state, db, status);
+  }
+  DVLOG(2) << "LEAVING SettingsPayloadDecoder::StartDecodingSettings"
+           << "\n\tdb->Remaining=" << db->Remaining()
+           << "\n\t remaining_payload=" << state->remaining_payload();
+  state->listener()->OnSettingsEnd();
+  return DecodeStatus::kDecodeDone;
+}
+
+DecodeStatus SettingsPayloadDecoder::HandleNotDone(FrameDecoderState* state,
+                                                   DecodeBuffer* db,
+                                                   DecodeStatus status) {
+  // Not done decoding the structure. Either we've got more payload to decode,
+  // or we've run out because the payload is too short, in which case
+  // OnFrameSizeError will have already been called.
+  DCHECK(
+      (status == DecodeStatus::kDecodeInProgress &&
+       state->remaining_payload() > 0) ||
+      (status == DecodeStatus::kDecodeError && state->remaining_payload() == 0))
+      << "\n status=" << status
+      << "; remaining_payload=" << state->remaining_payload()
+      << "; db->Remaining=" << db->Remaining();
+  return status;
+}
+
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/settings_payload_decoder.h b/net/http2/decoder/payload_decoders/settings_payload_decoder.h
new file mode 100644
index 0000000..40d3c36
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/settings_payload_decoder.h
@@ -0,0 +1,54 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_PAYLOAD_DECODERS_SETTINGS_PAYLOAD_DECODER_H_
+#define NET_HTTP2_DECODER_PAYLOAD_DECODERS_SETTINGS_PAYLOAD_DECODER_H_
+
+// Decodes the payload of a SETTINGS frame; for the RFC, see:
+//     http://httpwg.org/specs/rfc7540.html#SETTINGS
+
+#include "net/base/net_export.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/decoder/frame_decoder_state.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+namespace test {
+class SettingsPayloadDecoderPeer;
+}  // namespace test
+
+class NET_EXPORT_PRIVATE SettingsPayloadDecoder {
+ public:
+  // Starts the decoding of a SETTINGS frame's payload, and completes it if
+  // the entire payload is in the provided decode buffer.
+  DecodeStatus StartDecodingPayload(FrameDecoderState* state, DecodeBuffer* db);
+
+  // Resumes decoding a SETTINGS frame that has been split across decode
+  // buffers.
+  DecodeStatus ResumeDecodingPayload(FrameDecoderState* state,
+                                     DecodeBuffer* db);
+
+ private:
+  friend class test::SettingsPayloadDecoderPeer;
+
+  // Decodes as many settings as are available in the decode buffer, starting at
+  // the first byte of one setting; if a single setting is split across buffers,
+  // ResumeDecodingPayload will handle starting from where the previous call
+  // left off, and then will call StartDecodingSettings.
+  DecodeStatus StartDecodingSettings(FrameDecoderState* state,
+                                     DecodeBuffer* db);
+
+  // Decoding a single SETTING returned a status other than kDecodeDone; this
+  // method just brings together the DCHECKs to reduce duplication.
+  DecodeStatus HandleNotDone(FrameDecoderState* state,
+                             DecodeBuffer* db,
+                             DecodeStatus status);
+
+  Http2SettingFields setting_fields_;
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_PAYLOAD_DECODERS_SETTINGS_PAYLOAD_DECODER_H_
diff --git a/net/http2/decoder/payload_decoders/settings_payload_decoder_test.cc b/net/http2/decoder/payload_decoders/settings_payload_decoder_test.cc
new file mode 100644
index 0000000..7a53797
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/settings_payload_decoder_test.cc
@@ -0,0 +1,176 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/settings_payload_decoder.h"
+
+#include <stddef.h>
+
+#include <vector>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "net/http2/decoder/frame_parts.h"
+#include "net/http2/decoder/frame_parts_collector.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/decoder/payload_decoders/payload_decoder_base_test_util.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_constants_test_util.h"
+#include "net/http2/http2_structures_test_util.h"
+#include "net/http2/tools/http2_frame_builder.h"
+#include "net/http2/tools/http2_random.h"
+#include "net/http2/tools/random_decoder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+class SettingsPayloadDecoderPeer {
+ public:
+  static constexpr Http2FrameType FrameType() {
+    return Http2FrameType::SETTINGS;
+  }
+
+  // Returns the mask of flags that affect the decoding of the payload (i.e.
+  // flags that that indicate the presence of certain fields or padding).
+  static constexpr uint8_t FlagsAffectingPayloadDecoding() {
+    return Http2FrameFlag::FLAG_ACK;
+  }
+
+  static void Randomize(SettingsPayloadDecoder* p, RandomBase* rng) {
+    VLOG(1) << "SettingsPayloadDecoderPeer::Randomize";
+    test::Randomize(&p->setting_fields_, rng);
+  }
+};
+
+namespace {
+
+struct Listener : public FramePartsCollector {
+  void OnSettingsStart(const Http2FrameHeader& header) override {
+    VLOG(1) << "OnSettingsStart: " << header;
+    EXPECT_EQ(Http2FrameType::SETTINGS, header.type) << header;
+    EXPECT_EQ(Http2FrameFlag(), header.flags) << header;
+    StartFrame(header)->OnSettingsStart(header);
+  }
+
+  void OnSetting(const Http2SettingFields& setting_fields) override {
+    VLOG(1) << "Http2SettingFields: setting_fields=" << setting_fields;
+    CurrentFrame()->OnSetting(setting_fields);
+  }
+
+  void OnSettingsEnd() override {
+    VLOG(1) << "OnSettingsEnd";
+    EndFrame()->OnSettingsEnd();
+  }
+
+  void OnSettingsAck(const Http2FrameHeader& header) override {
+    VLOG(1) << "OnSettingsAck: " << header;
+    StartAndEndFrame(header)->OnSettingsAck(header);
+  }
+
+  void OnFrameSizeError(const Http2FrameHeader& header) override {
+    VLOG(1) << "OnFrameSizeError: " << header;
+    FrameError(header)->OnFrameSizeError(header);
+  }
+};
+
+class SettingsPayloadDecoderTest
+    : public AbstractPayloadDecoderTest<SettingsPayloadDecoder,
+                                        SettingsPayloadDecoderPeer,
+                                        Listener> {
+ public:
+  static bool ApproveSizeForSettingsWrongSize(size_t size) {
+    // Should get an error if size is not an integral multiple of the size
+    // of one setting.
+    return 0 != (size % Http2SettingFields::EncodedSize());
+  }
+
+  static bool ApproveSizeForSettingsAkcWrongSize(size_t size) {
+    return size != 0;
+  }
+
+ protected:
+  Http2SettingFields RandSettingsFields() {
+    Http2SettingFields fields;
+    test::Randomize(&fields, RandomPtr());
+    return fields;
+  }
+};
+
+// Confirm we get an error if the SETTINGS payload is not the correct size
+// to hold exactly zero or more whole Http2SettingFields.
+TEST_F(SettingsPayloadDecoderTest, SettingsWrongSize) {
+  Http2FrameBuilder fb;
+  fb.Append(RandSettingsFields());
+  fb.Append(RandSettingsFields());
+  fb.Append(RandSettingsFields());
+  EXPECT_TRUE(VerifyDetectsFrameSizeError(
+      0, fb.buffer(),
+      base::Bind(
+          &SettingsPayloadDecoderTest::ApproveSizeForSettingsWrongSize)));
+}
+
+// Confirm we get an error if the SETTINGS ACK payload is not empty.
+TEST_F(SettingsPayloadDecoderTest, SettingsAkcWrongSize) {
+  Http2FrameBuilder fb;
+  fb.Append(RandSettingsFields());
+  fb.Append(RandSettingsFields());
+  fb.Append(RandSettingsFields());
+  EXPECT_TRUE(VerifyDetectsFrameSizeError(
+      Http2FrameFlag::FLAG_ACK, fb.buffer(),
+      base::Bind(
+          &SettingsPayloadDecoderTest::ApproveSizeForSettingsAkcWrongSize)));
+}
+
+// SETTINGS must have stream_id==0, but the payload decoder doesn't check that.
+TEST_F(SettingsPayloadDecoderTest, SettingsAck) {
+  for (int stream_id = 0; stream_id < 3; ++stream_id) {
+    Http2FrameHeader header(0, Http2FrameType::SETTINGS,
+                            RandFlags() | Http2FrameFlag::FLAG_ACK, stream_id);
+    set_frame_header(header);
+    FrameParts expected(header);
+    EXPECT_TRUE(DecodePayloadAndValidateSeveralWays("", expected));
+  }
+}
+
+// Try several values of each known SETTINGS parameter.
+TEST_F(SettingsPayloadDecoderTest, OneRealSetting) {
+  std::vector<uint32_t> values = {0, 1, 0xffffffff, Random().Rand32()};
+  for (auto param : AllHttp2SettingsParameters()) {
+    for (uint32_t value : values) {
+      Http2SettingFields fields(param, value);
+      Http2FrameBuilder fb;
+      fb.Append(fields);
+      Http2FrameHeader header(fb.size(), Http2FrameType::SETTINGS, RandFlags(),
+                              RandStreamId());
+      set_frame_header(header);
+      FrameParts expected(header);
+      expected.settings.push_back(fields);
+      EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(fb.buffer(), expected));
+    }
+  }
+}
+
+// Decode a SETTINGS frame with lots of fields.
+TEST_F(SettingsPayloadDecoderTest, ManySettings) {
+  const uint32_t num_settings = 100;
+  const uint32_t size = Http2SettingFields::EncodedSize() * num_settings;
+  Http2FrameHeader header(size, Http2FrameType::SETTINGS,
+                          RandFlags(),  // & ~Http2FrameFlag::FLAG_ACK,
+                          RandStreamId());
+  set_frame_header(header);
+  FrameParts expected(header);
+  Http2FrameBuilder fb;
+  for (size_t n = 0; n < num_settings; ++n) {
+    Http2SettingFields fields(static_cast<Http2SettingsParameter>(n),
+                              Random().Rand32());
+    fb.Append(fields);
+    expected.settings.push_back(fields);
+  }
+  ASSERT_EQ(size, fb.size());
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(fb.buffer(), expected));
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/unknown_payload_decoder.cc b/net/http2/decoder/payload_decoders/unknown_payload_decoder.cc
new file mode 100644
index 0000000..ec020c1
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/unknown_payload_decoder.cc
@@ -0,0 +1,55 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/unknown_payload_decoder.h"
+
+#include <stddef.h>
+
+#include "base/logging.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+
+DecodeStatus UnknownPayloadDecoder::StartDecodingPayload(
+    FrameDecoderState* state,
+    DecodeBuffer* db) {
+  const Http2FrameHeader& frame_header = state->frame_header();
+
+  DVLOG(2) << "UnknownPayloadDecoder::StartDecodingPayload: " << frame_header;
+  DCHECK(!IsSupportedHttp2FrameType(frame_header.type)) << frame_header;
+  DCHECK_LE(db->Remaining(), frame_header.payload_length);
+
+  state->InitializeRemainders();
+  state->listener()->OnUnknownStart(frame_header);
+  return ResumeDecodingPayload(state, db);
+}
+
+DecodeStatus UnknownPayloadDecoder::ResumeDecodingPayload(
+    FrameDecoderState* state,
+    DecodeBuffer* db) {
+  DVLOG(2) << "UnknownPayloadDecoder::ResumeDecodingPayload "
+           << "remaining_payload=" << state->remaining_payload()
+           << "; db->Remaining=" << db->Remaining();
+  DCHECK(!IsSupportedHttp2FrameType(state->frame_header().type))
+      << state->frame_header();
+  DCHECK_LE(state->remaining_payload(), state->frame_header().payload_length);
+  DCHECK_LE(db->Remaining(), state->remaining_payload());
+
+  size_t avail = db->Remaining();
+  if (avail > 0) {
+    state->listener()->OnUnknownPayload(db->cursor(), avail);
+    db->AdvanceCursor(avail);
+    state->ConsumePayload(avail);
+  }
+  if (state->remaining_payload() == 0) {
+    state->listener()->OnUnknownEnd();
+    return DecodeStatus::kDecodeDone;
+  }
+  return DecodeStatus::kDecodeInProgress;
+}
+
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/unknown_payload_decoder.h b/net/http2/decoder/payload_decoders/unknown_payload_decoder.h
new file mode 100644
index 0000000..26a1ee2
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/unknown_payload_decoder.h
@@ -0,0 +1,33 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_PAYLOAD_DECODERS_UNKNOWN_PAYLOAD_DECODER_H_
+#define NET_HTTP2_DECODER_PAYLOAD_DECODERS_UNKNOWN_PAYLOAD_DECODER_H_
+
+// Decodes the payload of a frame whose type unknown.  According to the HTTP/2
+// specification (http://httpwg.org/specs/rfc7540.html#FrameHeader):
+//     Implementations MUST ignore and discard any frame that has
+//     a type that is unknown.
+
+#include "net/base/net_export.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/decoder/frame_decoder_state.h"
+
+namespace net {
+
+class NET_EXPORT_PRIVATE UnknownPayloadDecoder {
+ public:
+  // Starts decoding a payload of unknown type; just passes it to the listener.
+  DecodeStatus StartDecodingPayload(FrameDecoderState* state, DecodeBuffer* db);
+
+  // Resumes decoding a payload of unknown type that has been split across
+  // decode buffers.
+  DecodeStatus ResumeDecodingPayload(FrameDecoderState* state,
+                                     DecodeBuffer* db);
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_PAYLOAD_DECODERS_UNKNOWN_PAYLOAD_DECODER_H_
diff --git a/net/http2/decoder/payload_decoders/unknown_payload_decoder_test.cc b/net/http2/decoder/payload_decoders/unknown_payload_decoder_test.cc
new file mode 100644
index 0000000..302cec47
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/unknown_payload_decoder_test.cc
@@ -0,0 +1,108 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/unknown_payload_decoder.h"
+
+#include <stddef.h>
+
+#include <string>
+#include <type_traits>
+
+#include "base/logging.h"
+#include "net/http2/decoder/frame_parts.h"
+#include "net/http2/decoder/frame_parts_collector.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/decoder/payload_decoders/payload_decoder_base_test_util.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures.h"
+#include "net/http2/tools/http2_random.h"
+#include "net/http2/tools/random_decoder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::string;
+
+namespace net {
+namespace test {
+namespace {
+Http2FrameType g_unknown_frame_type;
+}  // namespace
+
+// Provides friend access to an instance of the payload decoder, and also
+// provides info to aid in testing.
+class UnknownPayloadDecoderPeer {
+ public:
+  static Http2FrameType FrameType() { return g_unknown_frame_type; }
+
+  // Returns the mask of flags that affect the decoding of the payload (i.e.
+  // flags that that indicate the presence of certain fields or padding).
+  static constexpr uint8_t FlagsAffectingPayloadDecoding() { return 0; }
+
+  static void Randomize(UnknownPayloadDecoder* p, RandomBase* rng) {
+    // UnknownPayloadDecoder has no fields, so there is nothing to randomize.
+    static_assert(std::is_empty<UnknownPayloadDecoder>::value,
+                  "Need to randomize fields of UnknownPayloadDecoder");
+  }
+};
+
+namespace {
+
+struct Listener : public FramePartsCollector {
+  void OnUnknownStart(const Http2FrameHeader& header) override {
+    VLOG(1) << "OnUnknownStart: " << header;
+    StartFrame(header)->OnUnknownStart(header);
+  }
+
+  void OnUnknownPayload(const char* data, size_t len) override {
+    VLOG(1) << "OnUnknownPayload: len=" << len;
+    CurrentFrame()->OnUnknownPayload(data, len);
+  }
+
+  void OnUnknownEnd() override {
+    VLOG(1) << "OnUnknownEnd";
+    EndFrame()->OnUnknownEnd();
+  }
+};
+
+constexpr bool SupportedFrameType = false;
+
+class UnknownPayloadDecoderTest
+    : public AbstractPayloadDecoderTest<UnknownPayloadDecoder,
+                                        UnknownPayloadDecoderPeer,
+                                        Listener,
+                                        SupportedFrameType>,
+      public ::testing::WithParamInterface<uint32_t> {
+ protected:
+  UnknownPayloadDecoderTest() : length_(GetParam()) {
+    VLOG(1) << "################  length_=" << length_ << "  ################";
+
+    // Each test case will choose a random frame type that isn't supported.
+    do {
+      g_unknown_frame_type = static_cast<Http2FrameType>(Random().Rand8());
+    } while (IsSupportedHttp2FrameType(g_unknown_frame_type));
+  }
+
+  const uint32_t length_;
+};
+
+INSTANTIATE_TEST_CASE_P(VariousLengths,
+                        UnknownPayloadDecoderTest,
+                        ::testing::Values(0, 1, 2, 3, 255, 256));
+
+TEST_P(UnknownPayloadDecoderTest, ValidLength) {
+  string unknown_payload = Random().RandString(length_);
+  Http2FrameHeader frame_header(length_, g_unknown_frame_type, Random().Rand8(),
+                                RandStreamId());
+  set_frame_header(frame_header);
+  FrameParts expected(frame_header, unknown_payload);
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(unknown_payload, expected));
+  // TODO(jamessynge): Check here (and in other such tests) that the fast
+  // and slow decode counts are both non-zero. Perhaps also add some kind of
+  // test for the listener having been called. That could simply be a test
+  // that there is a single collected FrameParts instance, and that it matches
+  // expected.
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/window_update_payload_decoder.cc b/net/http2/decoder/payload_decoders/window_update_payload_decoder.cc
new file mode 100644
index 0000000..bc1e54ce1
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/window_update_payload_decoder.cc
@@ -0,0 +1,82 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/window_update_payload_decoder.h"
+
+#include "base/logging.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_http2_structures.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+
+DecodeStatus WindowUpdatePayloadDecoder::StartDecodingPayload(
+    FrameDecoderState* state,
+    DecodeBuffer* db) {
+  const Http2FrameHeader& frame_header = state->frame_header();
+  const uint32_t total_length = frame_header.payload_length;
+
+  DVLOG(2) << "WindowUpdatePayloadDecoder::StartDecodingPayload: "
+           << frame_header;
+
+  DCHECK_EQ(Http2FrameType::WINDOW_UPDATE, frame_header.type);
+  DCHECK_LE(db->Remaining(), total_length);
+
+  // WINDOW_UPDATE frames have no flags.
+  DCHECK_EQ(0, frame_header.flags);
+
+  // Special case for when the payload is the correct size and entirely in
+  // the buffer.
+  if (db->Remaining() == Http2WindowUpdateFields::EncodedSize() &&
+      total_length == Http2WindowUpdateFields::EncodedSize()) {
+    DoDecode(&window_update_fields_, db);
+    state->listener()->OnWindowUpdate(
+        frame_header, window_update_fields_.window_size_increment);
+    return DecodeStatus::kDecodeDone;
+  }
+  state->InitializeRemainders();
+  return HandleStatus(state, state->StartDecodingStructureInPayload(
+                                 &window_update_fields_, db));
+}
+
+DecodeStatus WindowUpdatePayloadDecoder::ResumeDecodingPayload(
+    FrameDecoderState* state,
+    DecodeBuffer* db) {
+  DVLOG(2) << "ResumeDecodingPayload: remaining_payload="
+           << state->remaining_payload()
+           << "; db->Remaining=" << db->Remaining();
+  DCHECK_EQ(Http2FrameType::WINDOW_UPDATE, state->frame_header().type);
+  DCHECK_LE(db->Remaining(), state->frame_header().payload_length);
+  return HandleStatus(state, state->ResumeDecodingStructureInPayload(
+                                 &window_update_fields_, db));
+}
+
+DecodeStatus WindowUpdatePayloadDecoder::HandleStatus(FrameDecoderState* state,
+                                                      DecodeStatus status) {
+  DVLOG(2) << "HandleStatus: status=" << status
+           << "; remaining_payload=" << state->remaining_payload();
+  if (status == DecodeStatus::kDecodeDone) {
+    if (state->remaining_payload() == 0) {
+      state->listener()->OnWindowUpdate(
+          state->frame_header(), window_update_fields_.window_size_increment);
+      return DecodeStatus::kDecodeDone;
+    }
+    // Payload is too long.
+    return state->ReportFrameSizeError();
+  }
+  // Not done decoding the structure. Either we've got more payload to decode,
+  // or we've run out because the payload is too short, in which case
+  // OnFrameSizeError will have already been called.
+  DCHECK(
+      (status == DecodeStatus::kDecodeInProgress &&
+       state->remaining_payload() > 0) ||
+      (status == DecodeStatus::kDecodeError && state->remaining_payload() == 0))
+      << "\n status=" << status
+      << "; remaining_payload=" << state->remaining_payload();
+  return status;
+}
+
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/window_update_payload_decoder.h b/net/http2/decoder/payload_decoders/window_update_payload_decoder.h
new file mode 100644
index 0000000..5764361
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/window_update_payload_decoder.h
@@ -0,0 +1,42 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_PAYLOAD_DECODERS_WINDOW_UPDATE_PAYLOAD_DECODER_H_
+#define NET_HTTP2_DECODER_PAYLOAD_DECODERS_WINDOW_UPDATE_PAYLOAD_DECODER_H_
+
+// Decodes the payload of a WINDOW_UPDATE frame.
+
+#include "net/base/net_export.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/decoder/frame_decoder_state.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+namespace test {
+class WindowUpdatePayloadDecoderPeer;
+}  // namespace test
+
+class NET_EXPORT_PRIVATE WindowUpdatePayloadDecoder {
+ public:
+  // Starts decoding a WINDOW_UPDATE frame's payload, and completes it if
+  // the entire payload is in the provided decode buffer.
+  DecodeStatus StartDecodingPayload(FrameDecoderState* state, DecodeBuffer* db);
+
+  // Resumes decoding a WINDOW_UPDATE frame's payload that has been split across
+  // decode buffers.
+  DecodeStatus ResumeDecodingPayload(FrameDecoderState* state,
+                                     DecodeBuffer* db);
+
+ private:
+  friend class test::WindowUpdatePayloadDecoderPeer;
+
+  DecodeStatus HandleStatus(FrameDecoderState* state, DecodeStatus status);
+
+  Http2WindowUpdateFields window_update_fields_;
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_PAYLOAD_DECODERS_WINDOW_UPDATE_PAYLOAD_DECODER_H_
diff --git a/net/http2/decoder/payload_decoders/window_update_payload_decoder_test.cc b/net/http2/decoder/payload_decoders/window_update_payload_decoder_test.cc
new file mode 100644
index 0000000..5517dab
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/window_update_payload_decoder_test.cc
@@ -0,0 +1,106 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/window_update_payload_decoder.h"
+
+#include <stddef.h>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "net/http2/decoder/frame_parts.h"
+#include "net/http2/decoder/frame_parts_collector.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/decoder/payload_decoders/payload_decoder_base_test_util.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures_test_util.h"
+#include "net/http2/tools/http2_frame_builder.h"
+#include "net/http2/tools/http2_random.h"
+#include "net/http2/tools/random_decoder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+class WindowUpdatePayloadDecoderPeer {
+ public:
+  static constexpr Http2FrameType FrameType() {
+    return Http2FrameType::WINDOW_UPDATE;
+  }
+
+  // Returns the mask of flags that affect the decoding of the payload (i.e.
+  // flags that that indicate the presence of certain fields or padding).
+  static constexpr uint8_t FlagsAffectingPayloadDecoding() { return 0; }
+
+  static void Randomize(WindowUpdatePayloadDecoder* p, RandomBase* rng) {
+    test::Randomize(&p->window_update_fields_, rng);
+    VLOG(1) << "WindowUpdatePayloadDecoderPeer::Randomize "
+            << "window_update_fields_: " << p->window_update_fields_;
+  }
+};
+
+namespace {
+
+struct Listener : public FramePartsCollector {
+  void OnWindowUpdate(const Http2FrameHeader& header,
+                      uint32_t window_size_increment) override {
+    VLOG(1) << "OnWindowUpdate: " << header
+            << "; window_size_increment=" << window_size_increment;
+    EXPECT_EQ(Http2FrameType::WINDOW_UPDATE, header.type);
+    StartAndEndFrame(header)->OnWindowUpdate(header, window_size_increment);
+  }
+
+  void OnFrameSizeError(const Http2FrameHeader& header) override {
+    VLOG(1) << "OnFrameSizeError: " << header;
+    FrameError(header)->OnFrameSizeError(header);
+  }
+};
+
+class WindowUpdatePayloadDecoderTest
+    : public AbstractPayloadDecoderTest<WindowUpdatePayloadDecoder,
+                                        WindowUpdatePayloadDecoderPeer,
+                                        Listener> {
+ public:
+  static bool ApproveSizeForWrongSize(size_t size) {
+    return size != Http2WindowUpdateFields::EncodedSize();
+  }
+
+ protected:
+  Http2WindowUpdateFields RandWindowUpdateFields() {
+    Http2WindowUpdateFields fields;
+    test::Randomize(&fields, RandomPtr());
+    VLOG(3) << "RandWindowUpdateFields: " << fields;
+    return fields;
+  }
+};
+
+// Confirm we get an error if the payload is not the correct size to hold
+// exactly one Http2WindowUpdateFields.
+TEST_F(WindowUpdatePayloadDecoderTest, WrongSize) {
+  Http2FrameBuilder fb;
+  fb.Append(RandWindowUpdateFields());
+  fb.Append(RandWindowUpdateFields());
+  fb.Append(RandWindowUpdateFields());
+  EXPECT_TRUE(VerifyDetectsFrameSizeError(
+      0, fb.buffer(),
+      base::Bind(&WindowUpdatePayloadDecoderTest::ApproveSizeForWrongSize)));
+}
+
+TEST_F(WindowUpdatePayloadDecoderTest, VariousPayloads) {
+  for (int n = 0; n < 100; ++n) {
+    uint32_t stream_id = n == 0 ? 0 : RandStreamId();
+    Http2WindowUpdateFields fields = RandWindowUpdateFields();
+    Http2FrameBuilder fb;
+    fb.Append(fields);
+    Http2FrameHeader header(fb.size(), Http2FrameType::WINDOW_UPDATE,
+                            RandFlags(), stream_id);
+    set_frame_header(header);
+    FrameParts expected(header);
+    expected.opt_window_update_increment = fields.window_size_increment;
+    EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(fb.buffer(), expected));
+  }
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/hpack/decoder/hpack_block_collector.cc b/net/http2/hpack/decoder/hpack_block_collector.cc
new file mode 100644
index 0000000..94f58ee4
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_block_collector.cc
@@ -0,0 +1,154 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/decoder/hpack_block_collector.h"
+
+#include <algorithm>
+#include <memory>
+
+#include "base/logging.h"
+#include "net/http2/tools/failure.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::AssertionResult;
+using ::testing::AssertionSuccess;
+using std::string;
+
+using base::StringPiece;
+
+namespace net {
+namespace test {
+
+HpackBlockCollector::HpackBlockCollector() {}
+HpackBlockCollector::HpackBlockCollector(const HpackBlockCollector& other)
+    : pending_entry_(other.pending_entry_), entries_(other.entries_) {}
+HpackBlockCollector::~HpackBlockCollector() {}
+
+void HpackBlockCollector::OnIndexedHeader(size_t index) {
+  pending_entry_.OnIndexedHeader(index);
+  PushPendingEntry();
+}
+void HpackBlockCollector::OnDynamicTableSizeUpdate(size_t size) {
+  pending_entry_.OnDynamicTableSizeUpdate(size);
+  PushPendingEntry();
+}
+void HpackBlockCollector::OnStartLiteralHeader(HpackEntryType header_type,
+                                               size_t maybe_name_index) {
+  pending_entry_.OnStartLiteralHeader(header_type, maybe_name_index);
+}
+void HpackBlockCollector::OnNameStart(bool huffman_encoded, size_t len) {
+  pending_entry_.OnNameStart(huffman_encoded, len);
+}
+void HpackBlockCollector::OnNameData(const char* data, size_t len) {
+  pending_entry_.OnNameData(data, len);
+}
+void HpackBlockCollector::OnNameEnd() {
+  pending_entry_.OnNameEnd();
+}
+void HpackBlockCollector::OnValueStart(bool huffman_encoded, size_t len) {
+  pending_entry_.OnValueStart(huffman_encoded, len);
+}
+void HpackBlockCollector::OnValueData(const char* data, size_t len) {
+  pending_entry_.OnValueData(data, len);
+}
+void HpackBlockCollector::OnValueEnd() {
+  pending_entry_.OnValueEnd();
+  PushPendingEntry();
+}
+
+void HpackBlockCollector::PushPendingEntry() {
+  EXPECT_TRUE(pending_entry_.IsComplete());
+  DVLOG(2) << "PushPendingEntry: " << pending_entry_;
+  entries_.push_back(pending_entry_);
+  EXPECT_TRUE(entries_.back().IsComplete());
+  pending_entry_.Clear();
+}
+void HpackBlockCollector::Clear() {
+  pending_entry_.Clear();
+  entries_.clear();
+}
+
+void HpackBlockCollector::ExpectIndexedHeader(size_t index) {
+  entries_.push_back(
+      HpackEntryCollector(HpackEntryType::kIndexedHeader, index));
+}
+void HpackBlockCollector::ExpectDynamicTableSizeUpdate(size_t size) {
+  entries_.push_back(
+      HpackEntryCollector(HpackEntryType::kDynamicTableSizeUpdate, size));
+}
+void HpackBlockCollector::ExpectNameIndexAndLiteralValue(HpackEntryType type,
+                                                         size_t index,
+                                                         bool value_huffman,
+                                                         const string& value) {
+  entries_.push_back(HpackEntryCollector(type, index, value_huffman, value));
+}
+void HpackBlockCollector::ExpectLiteralNameAndValue(HpackEntryType type,
+                                                    bool name_huffman,
+                                                    const string& name,
+                                                    bool value_huffman,
+                                                    const string& value) {
+  entries_.push_back(
+      HpackEntryCollector(type, name_huffman, name, value_huffman, value));
+}
+
+void HpackBlockCollector::ShuffleEntries(RandomBase* rng) {
+  std::random_shuffle(entries_.begin(), entries_.end());
+}
+
+void HpackBlockCollector::AppendToHpackBlockBuilder(
+    HpackBlockBuilder* hbb) const {
+  CHECK(IsNotPending());
+  for (const auto& entry : entries_) {
+    entry.AppendToHpackBlockBuilder(hbb);
+  }
+}
+
+AssertionResult HpackBlockCollector::ValidateSoleIndexedHeader(
+    size_t ndx) const {
+  VERIFY_TRUE(pending_entry_.IsClear());
+  VERIFY_EQ(1u, entries_.size());
+  VERIFY_TRUE(entries_.front().ValidateIndexedHeader(ndx));
+  return AssertionSuccess();
+}
+AssertionResult HpackBlockCollector::ValidateSoleLiteralValueHeader(
+    HpackEntryType expected_type,
+    size_t expected_index,
+    bool expected_value_huffman,
+    StringPiece expected_value) const {
+  VERIFY_TRUE(pending_entry_.IsClear());
+  VERIFY_EQ(1u, entries_.size());
+  VERIFY_TRUE(entries_.front().ValidateLiteralValueHeader(
+      expected_type, expected_index, expected_value_huffman, expected_value));
+  return AssertionSuccess();
+}
+AssertionResult HpackBlockCollector::ValidateSoleLiteralNameValueHeader(
+    HpackEntryType expected_type,
+    bool expected_name_huffman,
+    StringPiece expected_name,
+    bool expected_value_huffman,
+    StringPiece expected_value) const {
+  VERIFY_TRUE(pending_entry_.IsClear());
+  VERIFY_EQ(1u, entries_.size());
+  VERIFY_TRUE(entries_.front().ValidateLiteralNameValueHeader(
+      expected_type, expected_name_huffman, expected_name,
+      expected_value_huffman, expected_value));
+  return AssertionSuccess();
+}
+AssertionResult HpackBlockCollector::ValidateSoleDynamicTableSizeUpdate(
+    size_t size) const {
+  VERIFY_TRUE(pending_entry_.IsClear());
+  VERIFY_EQ(1u, entries_.size());
+  VERIFY_TRUE(entries_.front().ValidateDynamicTableSizeUpdate(size));
+  return AssertionSuccess();
+}
+
+AssertionResult HpackBlockCollector::VerifyEq(
+    const HpackBlockCollector& that) const {
+  VERIFY_EQ(pending_entry_, that.pending_entry_);
+  VERIFY_EQ(entries_, that.entries_);
+  return AssertionSuccess();
+}
+
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/hpack/decoder/hpack_block_collector.h b/net/http2/hpack/decoder/hpack_block_collector.h
new file mode 100644
index 0000000..2b283b9
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_block_collector.h
@@ -0,0 +1,128 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_HPACK_DECODER_HPACK_BLOCK_COLLECTOR_H_
+#define NET_HTTP2_HPACK_DECODER_HPACK_BLOCK_COLLECTOR_H_
+
+// HpackBlockCollector implements HpackEntryDecoderListener in order to record
+// the calls using HpackEntryCollector instances (one per HPACK entry). This
+// supports testing of HpackBlockDecoder, which decodes entire HPACK blocks.
+//
+// In addition to implementing the callback methods, HpackBlockCollector also
+// supports comparing two HpackBlockCollector instances (i.e. an expected and
+// an actual), or a sole HPACK entry against an expected value.
+
+#include <stddef.h>
+
+#include <string>
+#include <vector>
+
+#include "base/strings/string_piece.h"
+#include "net/http2/hpack/decoder/hpack_entry_collector.h"
+#include "net/http2/hpack/decoder/hpack_entry_decoder_listener.h"
+#include "net/http2/hpack/http2_hpack_constants.h"
+#include "net/http2/hpack/tools/hpack_block_builder.h"
+
+namespace net {
+namespace test {
+
+class RandomBase;
+
+class HpackBlockCollector : public HpackEntryDecoderListener {
+ public:
+  // Implementations of HpackEntryDecoderListener, forwarding to pending_entry_,
+  // an HpackEntryCollector for the "in-progress" HPACK entry. OnIndexedHeader
+  // and OnDynamicTableSizeUpdate are pending only for that one call, while
+  // OnStartLiteralHeader is followed by many calls, ending with OnValueEnd.
+  // Once all the calls for one HPACK entry have been received, PushPendingEntry
+  // is used to append the pending_entry_ entry to the collected entries_.
+  HpackBlockCollector();
+  HpackBlockCollector(const HpackBlockCollector& other);
+  ~HpackBlockCollector() override;
+  void OnIndexedHeader(size_t index) override;
+  void OnDynamicTableSizeUpdate(size_t size) override;
+  void OnStartLiteralHeader(HpackEntryType header_type,
+                            size_t maybe_name_index) override;
+  void OnNameStart(bool huffman_encoded, size_t len) override;
+  void OnNameData(const char* data, size_t len) override;
+  void OnNameEnd() override;
+  void OnValueStart(bool huffman_encoded, size_t len) override;
+  void OnValueData(const char* data, size_t len) override;
+  void OnValueEnd() override;
+
+  // Methods for creating a set of expectations (i.e. HPACK entries to compare
+  // against those collected by another instance of HpackBlockCollector).
+
+  // Add an HPACK entry for an indexed header.
+  void ExpectIndexedHeader(size_t index);
+
+  // Add an HPACK entry for a dynamic table size update.
+  void ExpectDynamicTableSizeUpdate(size_t size);
+
+  // Add an HPACK entry for a header entry with an index for the name, and a
+  // literal value.
+  void ExpectNameIndexAndLiteralValue(HpackEntryType type,
+                                      size_t index,
+                                      bool value_huffman,
+                                      const std::string& value);
+
+  // Add an HPACK entry for a header entry with a literal name and value.
+  void ExpectLiteralNameAndValue(HpackEntryType type,
+                                 bool name_huffman,
+                                 const std::string& name,
+                                 bool value_huffman,
+                                 const std::string& value);
+
+  // Shuffle the entries, in support of generating an HPACK block of entries
+  // in some random order.
+  void ShuffleEntries(RandomBase* rng);
+
+  // Serialize entries_ to the HpackBlockBuilder.
+  void AppendToHpackBlockBuilder(HpackBlockBuilder* hbb) const;
+
+  // Return AssertionSuccess if there is just one entry, and it is an
+  // Indexed Header with the specified index.
+  ::testing::AssertionResult ValidateSoleIndexedHeader(size_t ndx) const;
+
+  // Return AssertionSuccess if there is just one entry, and it is a
+  // Dynamic Table Size Update with the specified size.
+  ::testing::AssertionResult ValidateSoleDynamicTableSizeUpdate(
+      size_t size) const;
+
+  // Return AssertionSuccess if there is just one entry, and it is a Header
+  // entry with an index for the name and a literal value.
+  ::testing::AssertionResult ValidateSoleLiteralValueHeader(
+      HpackEntryType expected_type,
+      size_t expected_index,
+      bool expected_value_huffman,
+      base::StringPiece expected_value) const;
+
+  // Return AssertionSuccess if there is just one entry, and it is a Header
+  // with a literal name and literal value.
+  ::testing::AssertionResult ValidateSoleLiteralNameValueHeader(
+      HpackEntryType expected_type,
+      bool expected_name_huffman,
+      base::StringPiece expected_name,
+      bool expected_value_huffman,
+      base::StringPiece expected_value) const;
+
+  bool IsNotPending() const { return pending_entry_.IsClear(); }
+  bool IsClear() const { return IsNotPending() && entries_.empty(); }
+  void Clear();
+
+  ::testing::AssertionResult VerifyEq(const HpackBlockCollector& that) const;
+
+ private:
+  // Push the value of pending_entry_ onto entries_, and clear pending_entry_.
+  // The pending_entry_ must be complete.
+  void PushPendingEntry();
+
+  HpackEntryCollector pending_entry_;
+  std::vector<HpackEntryCollector> entries_;
+};
+
+}  // namespace test
+}  // namespace net
+
+#endif  // NET_HTTP2_HPACK_DECODER_HPACK_BLOCK_COLLECTOR_H_
diff --git a/net/http2/hpack/decoder/hpack_block_decoder.cc b/net/http2/hpack/decoder/hpack_block_decoder.cc
new file mode 100644
index 0000000..29b6869
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_block_decoder.cc
@@ -0,0 +1,65 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/decoder/hpack_block_decoder.h"
+
+#include <stdint.h>
+
+#include <sstream>
+#include <string>
+
+#include "base/logging.h"
+
+namespace net {
+
+DecodeStatus HpackBlockDecoder::Decode(DecodeBuffer* db) {
+  if (!before_entry_) {
+    DVLOG(2) << "HpackBlockDecoder::Decode resume entry, db->Remaining="
+             << db->Remaining();
+    DecodeStatus status = entry_decoder_.Resume(db, listener_);
+    switch (status) {
+      case DecodeStatus::kDecodeDone:
+        before_entry_ = true;
+        break;
+      case DecodeStatus::kDecodeInProgress:
+        DCHECK_EQ(0u, db->Remaining());
+        return DecodeStatus::kDecodeInProgress;
+      case DecodeStatus::kDecodeError:
+        return DecodeStatus::kDecodeError;
+    }
+  }
+  DCHECK(before_entry_);
+  while (db->HasData()) {
+    DVLOG(2) << "HpackBlockDecoder::Decode start entry, db->Remaining="
+             << db->Remaining();
+    DecodeStatus status = entry_decoder_.Start(db, listener_);
+    switch (status) {
+      case DecodeStatus::kDecodeDone:
+        continue;
+      case DecodeStatus::kDecodeInProgress:
+        DCHECK_EQ(0u, db->Remaining());
+        before_entry_ = false;
+        return DecodeStatus::kDecodeInProgress;
+      case DecodeStatus::kDecodeError:
+        return DecodeStatus::kDecodeError;
+    }
+    DCHECK(false);
+  }
+  DCHECK(before_entry_);
+  return DecodeStatus::kDecodeDone;
+}
+
+std::string HpackBlockDecoder::DebugString() const {
+  std::stringstream ss;
+  ss << "HpackBlockDecoder(" << entry_decoder_.DebugString() << ", listener@"
+     << std::hex << reinterpret_cast<intptr_t>(listener_)
+     << (before_entry_ ? ", between entries)" : ", in an entry)");
+  return ss.str();
+}
+
+std::ostream& operator<<(std::ostream& out, const HpackBlockDecoder& v) {
+  return out << v.DebugString();
+}
+
+}  // namespace net
diff --git a/net/http2/hpack/decoder/hpack_block_decoder.h b/net/http2/hpack/decoder/hpack_block_decoder.h
new file mode 100644
index 0000000..49e8ae1
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_block_decoder.h
@@ -0,0 +1,69 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_HPACK_DECODER_HPACK_BLOCK_DECODER_H_
+#define NET_HTTP2_HPACK_DECODER_HPACK_BLOCK_DECODER_H_
+
+// HpackBlockDecoder decodes an entire HPACK block (or the available portion
+// thereof in the DecodeBuffer) into entries, but doesn't include HPACK static
+// or dynamic table support, so table indices remain indices at this level.
+// Reports the entries to an HpackEntryDecoderListener.
+
+#include <string>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "net/base/net_export.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/hpack/decoder/hpack_entry_decoder.h"
+#include "net/http2/hpack/decoder/hpack_entry_decoder_listener.h"
+
+namespace net {
+
+class NET_EXPORT_PRIVATE HpackBlockDecoder {
+ public:
+  explicit HpackBlockDecoder(HpackEntryDecoderListener* listener)
+      : listener_(listener) {
+    DCHECK_NE(listener_, nullptr);
+  }
+  ~HpackBlockDecoder() {}
+
+  // The listener may be changed at any time. The change takes effect on the
+  // next entry into the decode loop of the Decode() method below.
+  void set_listener(HpackEntryDecoderListener* listener) {
+    DCHECK_NE(nullptr, listener);
+    listener_ = listener;
+  }
+  HpackEntryDecoderListener* listener() { return listener_; }
+
+  // Prepares the decoder to start decoding a new HPACK block. Expected
+  // to be called from an implementation of Http2FrameDecoderListener's
+  // OnHeadersStart or OnPushPromiseStart methods.
+  void Reset() {
+    DVLOG(2) << "HpackBlockDecoder::Reset";
+    before_entry_ = true;
+  }
+
+  // Decode the fragment of the HPACK block contained in the decode buffer.
+  // Expected to be called from an implementation of Http2FrameDecoderListener's
+  // OnHpackFragment method.
+  DecodeStatus Decode(DecodeBuffer* db);
+
+  std::string DebugString() const;
+
+ private:
+  HpackEntryDecoder entry_decoder_;
+  HpackEntryDecoderListener* listener_;
+  bool before_entry_ = true;
+
+  DISALLOW_COPY_AND_ASSIGN(HpackBlockDecoder);
+};
+
+NET_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& out,
+                                            const HpackBlockDecoder& v);
+
+}  // namespace net
+
+#endif  // NET_HTTP2_HPACK_DECODER_HPACK_BLOCK_DECODER_H_
diff --git a/net/http2/hpack/decoder/hpack_block_decoder_test.cc b/net/http2/hpack/decoder/hpack_block_decoder_test.cc
new file mode 100644
index 0000000..421f0fc5
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_block_decoder_test.cc
@@ -0,0 +1,315 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/decoder/hpack_block_decoder.h"
+
+// Tests of HpackBlockDecoder.
+
+#include <sstream>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/hpack/decoder/hpack_block_collector.h"
+#include "net/http2/hpack/http2_hpack_constants.h"
+#include "net/http2/hpack/tools/hpack_block_builder.h"
+#include "net/http2/hpack/tools/hpack_example.h"
+#include "net/http2/tools/failure.h"
+#include "net/http2/tools/http2_random.h"
+#include "net/http2/tools/random_decoder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::AssertionSuccess;
+using std::string;
+using base::StringPiece;
+
+namespace net {
+namespace test {
+namespace {
+
+class HpackBlockDecoderTest : public RandomDecoderTest {
+ public:
+  AssertionResult VerifyExpected(const HpackBlockCollector& expected) {
+    VERIFY_AND_RETURN_SUCCESS(collector_.VerifyEq(expected));
+  }
+
+  AssertionResult ValidateForSpecExample_C_2_1() {
+    VERIFY_AND_RETURN_SUCCESS(collector_.ValidateSoleLiteralNameValueHeader(
+        HpackEntryType::kIndexedLiteralHeader, false, "custom-key", false,
+        "custom-header"));
+  }
+
+  AssertionResult ValidateForSpecExample_C_2_2() {
+    VERIFY_AND_RETURN_SUCCESS(collector_.ValidateSoleLiteralValueHeader(
+        HpackEntryType::kUnindexedLiteralHeader, 4, false, "/sample/path"));
+  }
+
+  AssertionResult ValidateForSpecExample_C_2_3() {
+    VERIFY_AND_RETURN_SUCCESS(collector_.ValidateSoleLiteralNameValueHeader(
+        HpackEntryType::kNeverIndexedLiteralHeader, false, "password", false,
+        "secret"));
+  }
+
+  AssertionResult ValidateForSpecExample_C_2_4() {
+    VERIFY_AND_RETURN_SUCCESS(collector_.ValidateSoleIndexedHeader(2));
+  }
+
+ protected:
+  HpackBlockDecoderTest() : listener_(&collector_), decoder_(&listener_) {
+    stop_decode_on_done_ = false;
+    decoder_.Reset();
+    // Make sure logging doesn't crash. Not examining the result.
+    std::ostringstream strm;
+    strm << decoder_;
+  }
+
+  DecodeStatus StartDecoding(DecodeBuffer* db) override {
+    collector_.Clear();
+    decoder_.Reset();
+    return ResumeDecoding(db);
+  }
+
+  DecodeStatus ResumeDecoding(DecodeBuffer* db) override {
+    DecodeStatus status = decoder_.Decode(db);
+
+    // Make sure logging doesn't crash. Not examining the result.
+    std::ostringstream strm;
+    strm << decoder_;
+
+    return status;
+  }
+
+  AssertionResult DecodeAndValidateSeveralWays(DecodeBuffer* db,
+                                               Validator validator) {
+    bool return_non_zero_on_first = false;
+    return RandomDecoderTest::DecodeAndValidateSeveralWays(
+        db, return_non_zero_on_first, validator);
+  }
+
+  AssertionResult DecodeAndValidateSeveralWays(const HpackBlockBuilder& hbb,
+                                               Validator validator) {
+    DecodeBuffer db(hbb.buffer());
+    return DecodeAndValidateSeveralWays(&db, validator);
+  }
+
+  AssertionResult DecodeHpackExampleAndValidateSeveralWays(
+      StringPiece hpack_example,
+      Validator validator) {
+    string input = HpackExampleToStringOrDie(hpack_example);
+    DecodeBuffer db(input);
+    return DecodeAndValidateSeveralWays(&db, validator);
+  }
+
+  uint8_t Rand8() { return Random().Rand8(); }
+
+  string Rand8String() { return Random().RandString(Rand8()); }
+
+  HpackBlockCollector collector_;
+  HpackEntryDecoderVLoggingListener listener_;
+  HpackBlockDecoder decoder_;
+};
+
+// http://httpwg.org/specs/rfc7541.html#rfc.section.C.2.1
+TEST_F(HpackBlockDecoderTest, SpecExample_C_2_1) {
+  NoArgValidator do_check =
+      base::Bind(&HpackBlockDecoderTest::ValidateForSpecExample_C_2_1,
+                 base::Unretained(this));
+  EXPECT_TRUE(
+      DecodeHpackExampleAndValidateSeveralWays(R"(
+      40                                      | == Literal indexed ==
+      0a                                      |   Literal name (len = 10)
+      6375 7374 6f6d 2d6b 6579                | custom-key
+      0d                                      |   Literal value (len = 13)
+      6375 7374 6f6d 2d68 6561 6465 72        | custom-header
+                                              | -> custom-key:
+                                              |   custom-header
+      )",
+                                               ValidateDoneAndEmpty(do_check)));
+  EXPECT_TRUE(do_check.Run());
+}
+
+// http://httpwg.org/specs/rfc7541.html#rfc.section.C.2.2
+TEST_F(HpackBlockDecoderTest, SpecExample_C_2_2) {
+  NoArgValidator do_check =
+      base::Bind(&HpackBlockDecoderTest::ValidateForSpecExample_C_2_2,
+                 base::Unretained(this));
+  EXPECT_TRUE(
+      DecodeHpackExampleAndValidateSeveralWays(R"(
+      04                                      | == Literal not indexed ==
+                                              |   Indexed name (idx = 4)
+                                              |     :path
+      0c                                      |   Literal value (len = 12)
+      2f73 616d 706c 652f 7061 7468           | /sample/path
+                                              | -> :path: /sample/path
+      )",
+                                               ValidateDoneAndEmpty(do_check)));
+  EXPECT_TRUE(do_check.Run());
+}
+
+// http://httpwg.org/specs/rfc7541.html#rfc.section.C.2.3
+TEST_F(HpackBlockDecoderTest, SpecExample_C_2_3) {
+  NoArgValidator do_check =
+      base::Bind(&HpackBlockDecoderTest::ValidateForSpecExample_C_2_3,
+                 base::Unretained(this));
+  EXPECT_TRUE(
+      DecodeHpackExampleAndValidateSeveralWays(R"(
+      10                                      | == Literal never indexed ==
+      08                                      |   Literal name (len = 8)
+      7061 7373 776f 7264                     | password
+      06                                      |   Literal value (len = 6)
+      7365 6372 6574                          | secret
+                                              | -> password: secret
+      )",
+                                               ValidateDoneAndEmpty(do_check)));
+  EXPECT_TRUE(do_check.Run());
+}
+
+// http://httpwg.org/specs/rfc7541.html#rfc.section.C.2.4
+TEST_F(HpackBlockDecoderTest, SpecExample_C_2_4) {
+  NoArgValidator do_check =
+      base::Bind(&HpackBlockDecoderTest::ValidateForSpecExample_C_2_4,
+                 base::Unretained(this));
+  EXPECT_TRUE(
+      DecodeHpackExampleAndValidateSeveralWays(R"(
+      82                                      | == Indexed - Add ==
+                                              |   idx = 2
+                                              | -> :method: GET
+      )",
+                                               ValidateDoneAndEmpty(do_check)));
+  EXPECT_TRUE(do_check.Run());
+}
+// http://httpwg.org/specs/rfc7541.html#rfc.section.C.3.1
+TEST_F(HpackBlockDecoderTest, SpecExample_C_3_1) {
+  string example = R"(
+      82                                      | == Indexed - Add ==
+                                              |   idx = 2
+                                              | -> :method: GET
+      86                                      | == Indexed - Add ==
+                                              |   idx = 6
+                                              | -> :scheme: http
+      84                                      | == Indexed - Add ==
+                                              |   idx = 4
+                                              | -> :path: /
+      41                                      | == Literal indexed ==
+                                              |   Indexed name (idx = 1)
+                                              |     :authority
+      0f                                      |   Literal value (len = 15)
+      7777 772e 6578 616d 706c 652e 636f 6d   | www.example.com
+                                              | -> :authority:
+                                              |   www.example.com
+      )";
+  HpackBlockCollector expected;
+  expected.ExpectIndexedHeader(2);
+  expected.ExpectIndexedHeader(6);
+  expected.ExpectIndexedHeader(4);
+  expected.ExpectNameIndexAndLiteralValue(HpackEntryType::kIndexedLiteralHeader,
+                                          1, false, "www.example.com");
+  NoArgValidator do_check = base::Bind(&HpackBlockDecoderTest::VerifyExpected,
+                                       base::Unretained(this), expected);
+  EXPECT_TRUE(DecodeHpackExampleAndValidateSeveralWays(
+      example, ValidateDoneAndEmpty(do_check)));
+  EXPECT_TRUE(do_check.Run());
+}
+
+// http://httpwg.org/specs/rfc7541.html#rfc.section.C.5.1
+TEST_F(HpackBlockDecoderTest, SpecExample_C_5_1) {
+  string example = R"(
+      48                                      | == Literal indexed ==
+                                              |   Indexed name (idx = 8)
+                                              |     :status
+      03                                      |   Literal value (len = 3)
+      3330 32                                 | 302
+                                              | -> :status: 302
+      58                                      | == Literal indexed ==
+                                              |   Indexed name (idx = 24)
+                                              |     cache-control
+      07                                      |   Literal value (len = 7)
+      7072 6976 6174 65                       | private
+                                              | -> cache-control: private
+      61                                      | == Literal indexed ==
+                                              |   Indexed name (idx = 33)
+                                              |     date
+      1d                                      |   Literal value (len = 29)
+      4d6f 6e2c 2032 3120 4f63 7420 3230 3133 | Mon, 21 Oct 2013
+      2032 303a 3133 3a32 3120 474d 54        |  20:13:21 GMT
+                                              | -> date: Mon, 21 Oct 2013
+                                              |   20:13:21 GMT
+      6e                                      | == Literal indexed ==
+                                              |   Indexed name (idx = 46)
+                                              |     location
+      17                                      |   Literal value (len = 23)
+      6874 7470 733a 2f2f 7777 772e 6578 616d | https://www.exam
+      706c 652e 636f 6d                       | ple.com
+                                              | -> location:
+                                              |   https://www.example.com
+      )";
+  HpackBlockCollector expected;
+  expected.ExpectNameIndexAndLiteralValue(HpackEntryType::kIndexedLiteralHeader,
+                                          8, false, "302");
+  expected.ExpectNameIndexAndLiteralValue(HpackEntryType::kIndexedLiteralHeader,
+                                          24, false, "private");
+  expected.ExpectNameIndexAndLiteralValue(HpackEntryType::kIndexedLiteralHeader,
+                                          33, false,
+                                          "Mon, 21 Oct 2013 20:13:21 GMT");
+  expected.ExpectNameIndexAndLiteralValue(HpackEntryType::kIndexedLiteralHeader,
+                                          46, false, "https://www.example.com");
+  NoArgValidator do_check = base::Bind(&HpackBlockDecoderTest::VerifyExpected,
+                                       base::Unretained(this), expected);
+  EXPECT_TRUE(DecodeHpackExampleAndValidateSeveralWays(
+      example, ValidateDoneAndEmpty(do_check)));
+  EXPECT_TRUE(do_check.Run());
+}
+
+// Generate a bunch of HPACK block entries to expect, use those expectations
+// to generate an HPACK block, then decode it and confirm it matches those
+// expectations. Some of these are invalid (such as Indexed, with index=0),
+// but well-formed, and the decoder doesn't check for validity, just
+// well-formedness. That includes the validity of the strings not being checked,
+// such as lower-case ascii for the names, and valid Huffman encodings.
+TEST_F(HpackBlockDecoderTest, Computed) {
+  HpackBlockCollector expected;
+  expected.ExpectIndexedHeader(0);
+  expected.ExpectIndexedHeader(1);
+  expected.ExpectIndexedHeader(126);
+  expected.ExpectIndexedHeader(127);
+  expected.ExpectIndexedHeader(128);
+  expected.ExpectDynamicTableSizeUpdate(0);
+  expected.ExpectDynamicTableSizeUpdate(1);
+  expected.ExpectDynamicTableSizeUpdate(14);
+  expected.ExpectDynamicTableSizeUpdate(15);
+  expected.ExpectDynamicTableSizeUpdate(30);
+  expected.ExpectDynamicTableSizeUpdate(31);
+  expected.ExpectDynamicTableSizeUpdate(4095);
+  expected.ExpectDynamicTableSizeUpdate(4096);
+  expected.ExpectDynamicTableSizeUpdate(8192);
+  for (auto type : {HpackEntryType::kIndexedLiteralHeader,
+                    HpackEntryType::kUnindexedLiteralHeader,
+                    HpackEntryType::kNeverIndexedLiteralHeader}) {
+    for (bool value_huffman : {false, true}) {
+      // An entry with an index for the name. Ensure the name index
+      // is not zero by adding one to the Rand8() result.
+      expected.ExpectNameIndexAndLiteralValue(type, Rand8() + 1, value_huffman,
+                                              Rand8String());
+      // And two entries with literal names, one plain, one huffman encoded.
+      expected.ExpectLiteralNameAndValue(type, false, Rand8String(),
+                                         value_huffman, Rand8String());
+      expected.ExpectLiteralNameAndValue(type, true, Rand8String(),
+                                         value_huffman, Rand8String());
+    }
+  }
+  // Shuffle the entries and serialize them to produce an HPACK block.
+  expected.ShuffleEntries(RandomPtr());
+  HpackBlockBuilder hbb;
+  expected.AppendToHpackBlockBuilder(&hbb);
+
+  NoArgValidator do_check = base::Bind(&HpackBlockDecoderTest::VerifyExpected,
+                                       base::Unretained(this), expected);
+  EXPECT_TRUE(
+      DecodeAndValidateSeveralWays(hbb, ValidateDoneAndEmpty(do_check)));
+  EXPECT_TRUE(do_check.Run());
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/hpack/decoder/hpack_decoder_string_buffer.cc b/net/http2/hpack/decoder/hpack_decoder_string_buffer.cc
new file mode 100644
index 0000000..90d0336
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_decoder_string_buffer.cc
@@ -0,0 +1,215 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/decoder/hpack_decoder_string_buffer.h"
+
+#include "base/logging.h"
+
+using base::StringPiece;
+
+namespace net {
+
+std::ostream& operator<<(std::ostream& out,
+                         const HpackDecoderStringBuffer::State v) {
+  switch (v) {
+    case HpackDecoderStringBuffer::State::RESET:
+      return out << "RESET";
+    case HpackDecoderStringBuffer::State::COLLECTING:
+      return out << "COLLECTING";
+    case HpackDecoderStringBuffer::State::COMPLETE:
+      return out << "COMPLETE";
+    default:
+      return out << "Unknown HpackDecoderStringBuffer::State!";
+  }
+}
+
+std::ostream& operator<<(std::ostream& out,
+                         const HpackDecoderStringBuffer::Backing v) {
+  switch (v) {
+    case HpackDecoderStringBuffer::Backing::RESET:
+      return out << "RESET";
+    case HpackDecoderStringBuffer::Backing::UNBUFFERED:
+      return out << "UNBUFFERED";
+    case HpackDecoderStringBuffer::Backing::BUFFERED:
+      return out << "BUFFERED";
+    case HpackDecoderStringBuffer::Backing::STATIC:
+      return out << "STATIC";
+    default:
+      return out << "Unknown HpackDecoderStringBuffer::Backing!";
+  }
+}
+
+HpackDecoderStringBuffer::HpackDecoderStringBuffer() {
+  Reset();
+}
+HpackDecoderStringBuffer::~HpackDecoderStringBuffer() {}
+
+// TODO(jamessynge): Consider eliminating most of Reset (i.e. do less); in
+// particular, if a variable won't be read again until after it is next set
+// (e.g. is_huffman_encoded_ or remaining_len_), then it doesn't need to be
+// cleared here. This will be easier when not supporting both HpackDecoder2
+// (in net/spdy/hpack) and HpackWholeEntryDecoder, so we can eliminate
+// the Set() and str() methods.
+void HpackDecoderStringBuffer::Reset() {
+  DVLOG(3) << "HpackDecoderStringBuffer::Reset";
+  buffer_.clear();
+  value_.clear();
+  remaining_len_ = 0;
+  is_huffman_encoded_ = false;
+  state_ = State::RESET;
+  backing_ = Backing::RESET;
+}
+
+void HpackDecoderStringBuffer::Set(StringPiece value, bool is_static) {
+  DVLOG(2) << "HpackDecoderStringBuffer::Set";
+  DCHECK_EQ(state_, State::RESET);
+  DCHECK_EQ(backing_, Backing::RESET);
+  value_ = value;
+  state_ = State::COMPLETE;
+  backing_ = is_static ? Backing::STATIC : Backing::UNBUFFERED;
+}
+
+void HpackDecoderStringBuffer::OnStart(bool huffman_encoded, size_t len) {
+  DVLOG(2) << "HpackDecoderStringBuffer::OnStart";
+  DCHECK_EQ(state_, State::RESET);
+  DCHECK_EQ(backing_, Backing::RESET);
+  buffer_.clear();
+  value_.clear();
+
+  remaining_len_ = len;
+  is_huffman_encoded_ = huffman_encoded;
+
+  state_ = State::COLLECTING;
+
+  if (huffman_encoded) {
+    decoder_.Reset();
+    backing_ = Backing::BUFFERED;
+
+    // Reserve space in buffer_ for the uncompressed string, assuming the
+    // maximum expansion. The shortest Huffman codes in the RFC are 5 bits long,
+    // which then expand to 8 bits during decoding (i.e. each code is for one
+    // plain text octet, aka byte), so the maximum size is 60% longer than the
+    // encoded size.
+    len = len * 8 / 5;
+    if (buffer_.capacity() < len) {
+      buffer_.reserve(len);
+    }
+  } else {
+    // Assume for now that we won't need to use buffer_, so don't reserve space
+    // in it.
+    backing_ = Backing::RESET;
+  }
+}
+
+bool HpackDecoderStringBuffer::OnData(const char* data, size_t len) {
+  DVLOG(2) << "HpackDecoderStringBuffer::OnData state=" << state_
+           << ", backing=" << backing_;
+  DCHECK_EQ(state_, State::COLLECTING);
+  DCHECK_LE(len, remaining_len_);
+  remaining_len_ -= len;
+
+  if (is_huffman_encoded_) {
+    DCHECK_EQ(backing_, Backing::BUFFERED);
+    // We don't set value_ for buffered strings until OnEnd,
+    // so it should be empty.
+    DCHECK_EQ(0u, value_.size());
+    return decoder_.Decode(StringPiece(data, len), &buffer_);
+  }
+
+  if (backing_ == Backing::RESET) {
+    // This is the first call to OnData.
+    DCHECK_EQ(0u, buffer_.size());
+    DCHECK_EQ(0u, value_.size());
+    // If data contains the entire string, don't copy the string. If we later
+    // find that the HPACK entry is split across input buffers, then we'll
+    // copy the string into buffer_.
+    if (remaining_len_ == 0) {
+      value_ = StringPiece(data, len);
+      backing_ = Backing::UNBUFFERED;
+      return true;
+    }
+
+    // We need to buffer the string because it is split across input buffers.
+    backing_ = Backing::BUFFERED;
+    buffer_.assign(data, len);
+    return true;
+  }
+
+  // This is not the first call to OnData for this string, so it should be
+  // buffered.
+  DCHECK_EQ(backing_, Backing::BUFFERED);
+  // We don't set value_ for buffered strings until OnEnd, so it should be
+  // empty.
+  DCHECK_EQ(0u, value_.size());
+
+  // Append to the current contents of the buffer.
+  buffer_.append(data, len);
+  return true;
+}
+
+bool HpackDecoderStringBuffer::OnEnd() {
+  DVLOG(2) << "HpackDecoderStringBuffer::OnEnd";
+  DCHECK_EQ(state_, State::COLLECTING);
+  DCHECK_EQ(0u, remaining_len_);
+
+  if (is_huffman_encoded_) {
+    DCHECK_EQ(backing_, Backing::BUFFERED);
+    // Did the Huffman encoding of the string end properly?
+    if (!decoder_.InputProperlyTerminated()) {
+      return false;  // No, it didn't.
+    }
+  }
+  state_ = State::COMPLETE;
+  if (backing_ == Backing::BUFFERED) {
+    value_ = buffer_;
+  }
+  return true;
+}
+
+void HpackDecoderStringBuffer::BufferStringIfUnbuffered() {
+  DVLOG(3) << "HpackDecoderStringBuffer::BufferStringIfUnbuffered state="
+           << state_ << ", backing=" << backing_;
+  if (state_ != State::RESET && backing_ == Backing::UNBUFFERED) {
+    DVLOG(2) << "HpackDecoderStringBuffer buffering string of length "
+             << value_.size();
+    value_.CopyToString(&buffer_);
+    if (state_ == State::COMPLETE) {
+      value_ = buffer_;
+    }
+    backing_ = Backing::BUFFERED;
+  }
+}
+
+size_t HpackDecoderStringBuffer::BufferedLength() const {
+  DVLOG(3) << "HpackDecoderStringBuffer::BufferedLength";
+  return backing_ == Backing::BUFFERED ? buffer_.size() : 0;
+}
+
+StringPiece HpackDecoderStringBuffer::str() const {
+  DVLOG(3) << "HpackDecoderStringBuffer::str";
+  DCHECK_EQ(state_, State::COMPLETE);
+  return value_;
+}
+
+void HpackDecoderStringBuffer::OutputDebugStringTo(std::ostream& out) const {
+  out << "{state=" << state_;
+  if (state_ != State::RESET) {
+    out << ", backing=" << backing_;
+    out << ", remaining_len=" << remaining_len_;
+    out << ", is_huffman_encoded=" << is_huffman_encoded_;
+    if (backing_ == Backing::BUFFERED) {
+      out << ", buffer: " << buffer_;
+    } else {
+      out << ", value: " << value_;
+    }
+  }
+  out << "}";
+}
+
+std::ostream& operator<<(std::ostream& out, const HpackDecoderStringBuffer& v) {
+  v.OutputDebugStringTo(out);
+  return out;
+}
+
+}  // namespace net
diff --git a/net/http2/hpack/decoder/hpack_decoder_string_buffer.h b/net/http2/hpack/decoder/hpack_decoder_string_buffer.h
new file mode 100644
index 0000000..a0917a2
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_decoder_string_buffer.h
@@ -0,0 +1,82 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_HPACK_DECODER_HPACK_DECODER_STRING_BUFFER_H_
+#define NET_HTTP2_HPACK_DECODER_HPACK_DECODER_STRING_BUFFER_H_
+
+// HpackDecoderStringBuffer helps an HPACK decoder to avoid copies of a string
+// literal (name or value) except when necessary (e.g. when split across two
+// or more HPACK block fragments).
+
+#include <stddef.h>
+
+#include <ostream>
+#include <string>
+
+#include "base/macros.h"
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+#include "net/http2/hpack/huffman/http2_hpack_huffman_decoder.h"
+
+namespace net {
+
+class NET_EXPORT_PRIVATE HpackDecoderStringBuffer {
+ public:
+  enum class State : uint8_t { RESET, COLLECTING, COMPLETE };
+  enum class Backing : uint8_t { RESET, UNBUFFERED, BUFFERED, STATIC };
+
+  HpackDecoderStringBuffer();
+  ~HpackDecoderStringBuffer();
+
+  void Reset();
+  void Set(base::StringPiece value, bool is_static);
+
+  // Note that for Huffman encoded strings the length of the string after
+  // decoding may be larger (expected), the same or even smaller; the latter
+  // are unlikely, but possible if the encoder makes odd choices.
+  void OnStart(bool huffman_encoded, size_t len);
+  bool OnData(const char* data, size_t len);
+  bool OnEnd();
+  void BufferStringIfUnbuffered();
+  size_t BufferedLength() const;
+
+  base::StringPiece str() const;
+
+  State state_for_testing() const { return state_; }
+  Backing backing_for_testing() const { return backing_; }
+  void OutputDebugStringTo(std::ostream& out) const;
+
+ private:
+  // Storage for the string being buffered, if buffering is necessary
+  // (e.g. if Huffman encoded, buffer_ is storage for the decoded string).
+  std::string buffer_;
+
+  // The StringPiece to be returned by HpackDecoderStringBuffer::str(). If a
+  // string has been collected, but not buffered, value_ points to that string.
+  base::StringPiece value_;
+
+  // The decoder to use if the string is Huffman encoded.
+  HpackHuffmanDecoder decoder_;
+
+  // Count of bytes not yet passed to OnData.
+  size_t remaining_len_ = 0;
+
+  // Is the HPACK string Huffman encoded?
+  bool is_huffman_encoded_ = false;
+
+  // State of the string decoding process.
+  State state_;
+
+  // Where is the string stored?
+  Backing backing_;
+
+  DISALLOW_COPY_AND_ASSIGN(HpackDecoderStringBuffer);
+};
+
+NET_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& out,
+                                            const HpackDecoderStringBuffer& v);
+
+}  // namespace net
+
+#endif  // NET_HTTP2_HPACK_DECODER_HPACK_DECODER_STRING_BUFFER_H_
diff --git a/net/http2/hpack/decoder/hpack_decoder_string_buffer_test.cc b/net/http2/hpack/decoder/hpack_decoder_string_buffer_test.cc
new file mode 100644
index 0000000..313a457
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_decoder_string_buffer_test.cc
@@ -0,0 +1,245 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/decoder/hpack_decoder_string_buffer.h"
+
+// Tests of HpackDecoderStringBuffer.
+
+#include <initializer_list>
+#include <sstream>
+#include <string>
+
+#include "base/logging.h"
+#include "base/strings/string_piece.h"
+#include "net/http2/tools/failure.h"
+#include "net/spdy/spdy_test_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::AssertionResult;
+using ::testing::AssertionSuccess;
+using ::testing::HasSubstr;
+using base::StringPiece;
+using std::string;
+
+namespace net {
+namespace test {
+namespace {
+
+class HpackDecoderStringBufferTest : public ::testing::Test {
+ protected:
+  typedef HpackDecoderStringBuffer::State State;
+  typedef HpackDecoderStringBuffer::Backing Backing;
+
+  State state() const { return buf_.state_for_testing(); }
+  Backing backing() const { return buf_.backing_for_testing(); }
+
+  // We want to know that LOG(x) << buf_ will work in production should that
+  // be needed, so we test that it outputs the expected values.
+  AssertionResult VerifyLogHasSubstrs(std::initializer_list<string> strs) {
+    VLOG(1) << buf_;
+    std::ostringstream ss;
+    buf_.OutputDebugStringTo(ss);
+    string dbg_str(ss.str());
+    for (const auto& expected : strs) {
+      VERIFY_THAT(dbg_str, HasSubstr(expected));
+    }
+    return AssertionSuccess();
+  }
+
+  HpackDecoderStringBuffer buf_;
+};
+
+TEST_F(HpackDecoderStringBufferTest, SetStatic) {
+  StringPiece data("static string");
+
+  EXPECT_EQ(state(), State::RESET);
+  EXPECT_TRUE(VerifyLogHasSubstrs({"state=RESET"}));
+
+  buf_.Set(data, /*is_static*/ true);
+  LOG(INFO) << buf_;
+  EXPECT_EQ(state(), State::COMPLETE);
+  EXPECT_EQ(backing(), Backing::STATIC);
+  EXPECT_EQ(data, buf_.str());
+  EXPECT_EQ(data.data(), buf_.str().data());
+  EXPECT_TRUE(VerifyLogHasSubstrs(
+      {"state=COMPLETE", "backing=STATIC", "value: static string"}));
+
+  // The string is static, so BufferStringIfUnbuffered won't change anything.
+  buf_.BufferStringIfUnbuffered();
+  EXPECT_EQ(state(), State::COMPLETE);
+  EXPECT_EQ(backing(), Backing::STATIC);
+  EXPECT_EQ(data, buf_.str());
+  EXPECT_EQ(data.data(), buf_.str().data());
+  EXPECT_TRUE(VerifyLogHasSubstrs(
+      {"state=COMPLETE", "backing=STATIC", "value: static string"}));
+}
+
+TEST_F(HpackDecoderStringBufferTest, PlainWhole) {
+  StringPiece data("some text.");
+
+  LOG(INFO) << buf_;
+  EXPECT_EQ(state(), State::RESET);
+
+  buf_.OnStart(/*huffman_encoded*/ false, data.size());
+  EXPECT_EQ(state(), State::COLLECTING);
+  EXPECT_EQ(backing(), Backing::RESET);
+  LOG(INFO) << buf_;
+
+  EXPECT_TRUE(buf_.OnData(data.data(), data.size()));
+  EXPECT_EQ(state(), State::COLLECTING);
+  EXPECT_EQ(backing(), Backing::UNBUFFERED);
+
+  EXPECT_TRUE(buf_.OnEnd());
+  EXPECT_EQ(state(), State::COMPLETE);
+  EXPECT_EQ(backing(), Backing::UNBUFFERED);
+  EXPECT_EQ(0u, buf_.BufferedLength());
+  EXPECT_TRUE(VerifyLogHasSubstrs(
+      {"state=COMPLETE", "backing=UNBUFFERED", "value: some text."}));
+
+  // We expect that the string buffer points to the passed in StringPiece's
+  // backing store.
+  EXPECT_EQ(data.data(), buf_.str().data());
+
+  // Now force it to buffer the string, after which it will still have the same
+  // string value, but the backing store will be different.
+  buf_.BufferStringIfUnbuffered();
+  LOG(INFO) << buf_;
+  EXPECT_EQ(backing(), Backing::BUFFERED);
+  EXPECT_EQ(buf_.BufferedLength(), data.size());
+  EXPECT_EQ(data, buf_.str());
+  EXPECT_NE(data.data(), buf_.str().data());
+  EXPECT_TRUE(VerifyLogHasSubstrs(
+      {"state=COMPLETE", "backing=BUFFERED", "buffer: some text."}));
+}
+
+TEST_F(HpackDecoderStringBufferTest, PlainSplit) {
+  StringPiece data("some text.");
+  StringPiece part1 = data.substr(0, 1);
+  StringPiece part2 = data.substr(1);
+
+  EXPECT_EQ(state(), State::RESET);
+  buf_.OnStart(/*huffman_encoded*/ false, data.size());
+  EXPECT_EQ(state(), State::COLLECTING);
+  EXPECT_EQ(backing(), Backing::RESET);
+
+  // OnData with only a part of the data, not the whole, so buf_ will buffer
+  // the data.
+  EXPECT_TRUE(buf_.OnData(part1.data(), part1.size()));
+  EXPECT_EQ(state(), State::COLLECTING);
+  EXPECT_EQ(backing(), Backing::BUFFERED);
+  EXPECT_EQ(buf_.BufferedLength(), part1.size());
+  LOG(INFO) << buf_;
+
+  EXPECT_TRUE(buf_.OnData(part2.data(), part2.size()));
+  EXPECT_EQ(state(), State::COLLECTING);
+  EXPECT_EQ(backing(), Backing::BUFFERED);
+  EXPECT_EQ(buf_.BufferedLength(), data.size());
+
+  EXPECT_TRUE(buf_.OnEnd());
+  EXPECT_EQ(state(), State::COMPLETE);
+  EXPECT_EQ(backing(), Backing::BUFFERED);
+  EXPECT_EQ(buf_.BufferedLength(), data.size());
+  LOG(INFO) << buf_;
+
+  StringPiece buffered = buf_.str();
+  EXPECT_EQ(data, buffered);
+  EXPECT_NE(data.data(), buffered.data());
+
+  // The string is already buffered, so BufferStringIfUnbuffered should not make
+  // any change.
+  buf_.BufferStringIfUnbuffered();
+  EXPECT_EQ(backing(), Backing::BUFFERED);
+  EXPECT_EQ(buf_.BufferedLength(), data.size());
+  EXPECT_EQ(buffered, buf_.str());
+  EXPECT_EQ(buffered.data(), buf_.str().data());
+}
+
+TEST_F(HpackDecoderStringBufferTest, HuffmanWhole) {
+  string encoded = a2b_hex("f1e3c2e5f23a6ba0ab90f4ff");
+  StringPiece decoded("www.example.com");
+
+  EXPECT_EQ(state(), State::RESET);
+  buf_.OnStart(/*huffman_encoded*/ true, encoded.size());
+  EXPECT_EQ(state(), State::COLLECTING);
+
+  EXPECT_TRUE(buf_.OnData(encoded.data(), encoded.size()));
+  EXPECT_EQ(state(), State::COLLECTING);
+  EXPECT_EQ(backing(), Backing::BUFFERED);
+
+  EXPECT_TRUE(buf_.OnEnd());
+  EXPECT_EQ(state(), State::COMPLETE);
+  EXPECT_EQ(backing(), Backing::BUFFERED);
+  EXPECT_EQ(buf_.BufferedLength(), decoded.size());
+  EXPECT_EQ(decoded, buf_.str());
+  EXPECT_TRUE(VerifyLogHasSubstrs(
+      {"{state=COMPLETE", "backing=BUFFERED", "buffer: www.example.com}"}));
+}
+
+TEST_F(HpackDecoderStringBufferTest, HuffmanSplit) {
+  string encoded = a2b_hex("f1e3c2e5f23a6ba0ab90f4ff");
+  string part1 = encoded.substr(0, 5);
+  string part2 = encoded.substr(5);
+  StringPiece decoded("www.example.com");
+
+  EXPECT_EQ(state(), State::RESET);
+  buf_.OnStart(/*huffman_encoded*/ true, encoded.size());
+  EXPECT_EQ(state(), State::COLLECTING);
+  EXPECT_EQ(backing(), Backing::BUFFERED);
+  EXPECT_EQ(0u, buf_.BufferedLength());
+  LOG(INFO) << buf_;
+
+  EXPECT_TRUE(buf_.OnData(part1.data(), part1.size()));
+  EXPECT_EQ(state(), State::COLLECTING);
+  EXPECT_EQ(backing(), Backing::BUFFERED);
+  EXPECT_GT(buf_.BufferedLength(), 0u);
+  EXPECT_LT(buf_.BufferedLength(), decoded.size());
+  LOG(INFO) << buf_;
+
+  EXPECT_TRUE(buf_.OnData(part2.data(), part2.size()));
+  EXPECT_EQ(state(), State::COLLECTING);
+  EXPECT_EQ(backing(), Backing::BUFFERED);
+  EXPECT_EQ(buf_.BufferedLength(), decoded.size());
+  LOG(INFO) << buf_;
+
+  EXPECT_TRUE(buf_.OnEnd());
+  EXPECT_EQ(state(), State::COMPLETE);
+  EXPECT_EQ(backing(), Backing::BUFFERED);
+  EXPECT_EQ(buf_.BufferedLength(), decoded.size());
+  EXPECT_EQ(decoded, buf_.str());
+  LOG(INFO) << buf_;
+}
+
+TEST_F(HpackDecoderStringBufferTest, InvalidHuffmanOnData) {
+  // Explicitly encode the End-of-String symbol, a no-no.
+  string encoded = a2b_hex("ffffffff");
+
+  buf_.OnStart(/*huffman_encoded*/ true, encoded.size());
+  EXPECT_EQ(state(), State::COLLECTING);
+
+  EXPECT_FALSE(buf_.OnData(encoded.data(), encoded.size()));
+  EXPECT_EQ(state(), State::COLLECTING);
+  EXPECT_EQ(backing(), Backing::BUFFERED);
+
+  LOG(INFO) << buf_;
+}
+
+TEST_F(HpackDecoderStringBufferTest, InvalidHuffmanOnEnd) {
+  // Last byte of string doesn't end with prefix of End-of-String symbol.
+  string encoded = a2b_hex("00");
+
+  buf_.OnStart(/*huffman_encoded*/ true, encoded.size());
+  EXPECT_EQ(state(), State::COLLECTING);
+
+  EXPECT_TRUE(buf_.OnData(encoded.data(), encoded.size()));
+  EXPECT_EQ(state(), State::COLLECTING);
+  EXPECT_EQ(backing(), Backing::BUFFERED);
+
+  EXPECT_FALSE(buf_.OnEnd());
+  LOG(INFO) << buf_;
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/hpack/decoder/hpack_entry_collector.cc b/net/http2/hpack/decoder/hpack_entry_collector.cc
new file mode 100644
index 0000000..ea4feb6
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_entry_collector.cc
@@ -0,0 +1,317 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/decoder/hpack_entry_collector.h"
+
+#include <sstream>
+#include <string>
+
+#include "base/logging.h"
+#include "net/http2/hpack/decoder/hpack_string_collector.h"
+#include "net/http2/hpack/http2_hpack_constants.h"
+#include "net/http2/tools/failure.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::AssertionResult;
+using std::string;
+using base::StringPiece;
+
+namespace net {
+namespace test {
+namespace {
+
+const HpackEntryType kInvalidHeaderType = static_cast<HpackEntryType>(99);
+const size_t kInvalidIndex = 99999999;
+
+}  // namespace
+
+HpackEntryCollector::HpackEntryCollector() {
+  Clear();
+}
+
+HpackEntryCollector::HpackEntryCollector(const HpackEntryCollector& other)
+    : header_type_(other.header_type_),
+      index_(other.index_),
+      name_(other.name_),
+      value_(other.value_),
+      started_(other.started_),
+      ended_(other.ended_) {}
+
+HpackEntryCollector::HpackEntryCollector(HpackEntryType type,
+                                         size_t index_or_size)
+    : header_type_(type), index_(index_or_size), started_(true), ended_(true) {}
+HpackEntryCollector::HpackEntryCollector(HpackEntryType type,
+                                         size_t index,
+                                         bool value_huffman,
+                                         const string& value)
+    : header_type_(type),
+      index_(index),
+      value_(value, value_huffman),
+      started_(true),
+      ended_(true) {}
+HpackEntryCollector::HpackEntryCollector(HpackEntryType type,
+                                         bool name_huffman,
+                                         const string& name,
+                                         bool value_huffman,
+                                         const string& value)
+    : header_type_(type),
+      index_(0),
+      name_(name, name_huffman),
+      value_(value, value_huffman),
+      started_(true),
+      ended_(true) {}
+
+HpackEntryCollector::~HpackEntryCollector() {}
+
+void HpackEntryCollector::OnIndexedHeader(size_t index) {
+  ASSERT_FALSE(started_);
+  ASSERT_TRUE(IsClear()) << ToString();
+  Init(HpackEntryType::kIndexedHeader, index);
+  ended_ = true;
+}
+void HpackEntryCollector::OnStartLiteralHeader(HpackEntryType header_type,
+                                               size_t maybe_name_index) {
+  ASSERT_FALSE(started_);
+  ASSERT_TRUE(IsClear()) << ToString();
+  Init(header_type, maybe_name_index);
+}
+void HpackEntryCollector::OnNameStart(bool huffman_encoded, size_t len) {
+  ASSERT_TRUE(started_);
+  ASSERT_FALSE(ended_);
+  ASSERT_FALSE(IsClear());
+  ASSERT_TRUE(LiteralNameExpected()) << ToString();
+  name_.OnStringStart(huffman_encoded, len);
+}
+void HpackEntryCollector::OnNameData(const char* data, size_t len) {
+  ASSERT_TRUE(started_);
+  ASSERT_FALSE(ended_);
+  ASSERT_TRUE(LiteralNameExpected()) << ToString();
+  ASSERT_TRUE(name_.IsInProgress());
+  name_.OnStringData(data, len);
+}
+void HpackEntryCollector::OnNameEnd() {
+  ASSERT_TRUE(started_);
+  ASSERT_FALSE(ended_);
+  ASSERT_TRUE(LiteralNameExpected()) << ToString();
+  ASSERT_TRUE(name_.IsInProgress());
+  name_.OnStringEnd();
+}
+void HpackEntryCollector::OnValueStart(bool huffman_encoded, size_t len) {
+  ASSERT_TRUE(started_);
+  ASSERT_FALSE(ended_);
+  if (LiteralNameExpected()) {
+    ASSERT_TRUE(name_.HasEnded());
+  }
+  ASSERT_TRUE(LiteralValueExpected()) << ToString();
+  ASSERT_TRUE(value_.IsClear()) << value_.ToString();
+  value_.OnStringStart(huffman_encoded, len);
+}
+void HpackEntryCollector::OnValueData(const char* data, size_t len) {
+  ASSERT_TRUE(started_);
+  ASSERT_FALSE(ended_);
+  ASSERT_TRUE(LiteralValueExpected()) << ToString();
+  ASSERT_TRUE(value_.IsInProgress());
+  value_.OnStringData(data, len);
+}
+void HpackEntryCollector::OnValueEnd() {
+  ASSERT_TRUE(started_);
+  ASSERT_FALSE(ended_);
+  ASSERT_TRUE(LiteralValueExpected()) << ToString();
+  ASSERT_TRUE(value_.IsInProgress());
+  value_.OnStringEnd();
+  ended_ = true;
+}
+void HpackEntryCollector::OnDynamicTableSizeUpdate(size_t size) {
+  ASSERT_FALSE(started_);
+  ASSERT_TRUE(IsClear()) << ToString();
+  Init(HpackEntryType::kDynamicTableSizeUpdate, size);
+  ended_ = true;
+}
+
+void HpackEntryCollector::Clear() {
+  header_type_ = kInvalidHeaderType;
+  index_ = kInvalidIndex;
+  name_.Clear();
+  value_.Clear();
+  started_ = ended_ = false;
+}
+bool HpackEntryCollector::IsClear() const {
+  return header_type_ == kInvalidHeaderType && index_ == kInvalidIndex &&
+         name_.IsClear() && value_.IsClear() && !started_ && !ended_;
+}
+bool HpackEntryCollector::IsComplete() const {
+  return started_ && ended_;
+}
+bool HpackEntryCollector::LiteralNameExpected() const {
+  switch (header_type_) {
+    case HpackEntryType::kIndexedLiteralHeader:
+    case HpackEntryType::kUnindexedLiteralHeader:
+    case HpackEntryType::kNeverIndexedLiteralHeader:
+      return index_ == 0;
+    default:
+      return false;
+  }
+}
+bool HpackEntryCollector::LiteralValueExpected() const {
+  switch (header_type_) {
+    case HpackEntryType::kIndexedLiteralHeader:
+    case HpackEntryType::kUnindexedLiteralHeader:
+    case HpackEntryType::kNeverIndexedLiteralHeader:
+      return true;
+    default:
+      return false;
+  }
+}
+AssertionResult HpackEntryCollector::ValidateIndexedHeader(
+    size_t expected_index) const {
+  VERIFY_TRUE(started_);
+  VERIFY_TRUE(ended_);
+  VERIFY_EQ(HpackEntryType::kIndexedHeader, header_type_);
+  VERIFY_EQ(expected_index, index_);
+  return ::testing::AssertionSuccess();
+}
+AssertionResult HpackEntryCollector::ValidateLiteralValueHeader(
+    HpackEntryType expected_type,
+    size_t expected_index,
+    bool expected_value_huffman,
+    StringPiece expected_value) const {
+  VERIFY_TRUE(started_);
+  VERIFY_TRUE(ended_);
+  VERIFY_EQ(expected_type, header_type_);
+  VERIFY_NE(0u, expected_index);
+  VERIFY_EQ(expected_index, index_);
+  VERIFY_TRUE(name_.IsClear());
+  VERIFY_SUCCESS(value_.Collected(expected_value, expected_value_huffman));
+  return ::testing::AssertionSuccess();
+}
+AssertionResult HpackEntryCollector::ValidateLiteralNameValueHeader(
+    HpackEntryType expected_type,
+    bool expected_name_huffman,
+    StringPiece expected_name,
+    bool expected_value_huffman,
+    StringPiece expected_value) const {
+  VERIFY_TRUE(started_);
+  VERIFY_TRUE(ended_);
+  VERIFY_EQ(expected_type, header_type_);
+  VERIFY_EQ(0u, index_);
+  VERIFY_SUCCESS(name_.Collected(expected_name, expected_name_huffman));
+  VERIFY_SUCCESS(value_.Collected(expected_value, expected_value_huffman));
+  return ::testing::AssertionSuccess();
+}
+AssertionResult HpackEntryCollector::ValidateDynamicTableSizeUpdate(
+    size_t size) const {
+  VERIFY_TRUE(started_);
+  VERIFY_TRUE(ended_);
+  VERIFY_EQ(HpackEntryType::kDynamicTableSizeUpdate, header_type_);
+  VERIFY_EQ(index_, size);
+  return ::testing::AssertionSuccess();
+}
+
+void HpackEntryCollector::AppendToHpackBlockBuilder(
+    HpackBlockBuilder* hbb) const {
+  ASSERT_TRUE(started_ && ended_) << *this;
+  switch (header_type_) {
+    case HpackEntryType::kIndexedHeader:
+      hbb->AppendIndexedHeader(index_);
+      return;
+
+    case HpackEntryType::kDynamicTableSizeUpdate:
+      hbb->AppendDynamicTableSizeUpdate(index_);
+      return;
+
+    case HpackEntryType::kIndexedLiteralHeader:
+    case HpackEntryType::kUnindexedLiteralHeader:
+    case HpackEntryType::kNeverIndexedLiteralHeader:
+      ASSERT_TRUE(value_.HasEnded()) << *this;
+      if (index_ != 0) {
+        CHECK(name_.IsClear());
+        hbb->AppendNameIndexAndLiteralValue(header_type_, index_,
+                                            value_.huffman_encoded, value_.s);
+      } else {
+        CHECK(name_.HasEnded()) << *this;
+        hbb->AppendLiteralNameAndValue(header_type_, name_.huffman_encoded,
+                                       name_.s, value_.huffman_encoded,
+                                       value_.s);
+      }
+      return;
+
+    default:
+      ADD_FAILURE() << *this;
+  }
+}
+
+string HpackEntryCollector::ToString() const {
+  string result("Type=");
+  switch (header_type_) {
+    case HpackEntryType::kIndexedHeader:
+      result += "IndexedHeader";
+      break;
+    case HpackEntryType::kDynamicTableSizeUpdate:
+      result += "DynamicTableSizeUpdate";
+      break;
+    case HpackEntryType::kIndexedLiteralHeader:
+      result += "IndexedLiteralHeader";
+      break;
+    case HpackEntryType::kUnindexedLiteralHeader:
+      result += "UnindexedLiteralHeader";
+      break;
+    case HpackEntryType::kNeverIndexedLiteralHeader:
+      result += "NeverIndexedLiteralHeader";
+      break;
+    default:
+      if (header_type_ == kInvalidHeaderType) {
+        result += "<unset>";
+      } else {
+        std::stringstream ss;
+        ss << header_type_;
+        result.append(ss.str());
+      }
+  }
+  if (index_ != 0) {
+    result.append(" Index=");
+    std::stringstream ss;
+    ss << index_;
+    result.append(ss.str());
+  }
+  if (!name_.IsClear()) {
+    result.append(" Name");
+    result.append(name_.ToString());
+  }
+  if (!value_.IsClear()) {
+    result.append(" Value");
+    result.append(value_.ToString());
+  }
+  if (!started_) {
+    EXPECT_FALSE(ended_);
+    result.append(" !started");
+  } else if (!ended_) {
+    result.append(" !ended");
+  } else {
+    result.append(" Complete");
+  }
+  return result;
+}
+
+void HpackEntryCollector::Init(HpackEntryType type, size_t maybe_index) {
+  ASSERT_TRUE(IsClear()) << ToString();
+  header_type_ = type;
+  index_ = maybe_index;
+  started_ = true;
+}
+
+bool operator==(const HpackEntryCollector& a, const HpackEntryCollector& b) {
+  return a.name() == b.name() && a.value() == b.value() &&
+         a.index() == b.index() && a.header_type() == b.header_type() &&
+         a.started() == b.started() && a.ended() == b.ended();
+}
+bool operator!=(const HpackEntryCollector& a, const HpackEntryCollector& b) {
+  return !(a == b);
+}
+
+std::ostream& operator<<(std::ostream& out, const HpackEntryCollector& v) {
+  return out << v.ToString();
+}
+
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/hpack/decoder/hpack_entry_collector.h b/net/http2/hpack/decoder/hpack_entry_collector.h
new file mode 100644
index 0000000..876e0b0
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_entry_collector.h
@@ -0,0 +1,154 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_HPACK_DECODER_HPACK_ENTRY_COLLECTOR_H_
+#define NET_HTTP2_HPACK_DECODER_HPACK_ENTRY_COLLECTOR_H_
+
+// HpackEntryCollector records calls to HpackEntryDecoderListener in support
+// of tests of HpackEntryDecoder, or which use it. Can only record the callbacks
+// for the decoding of a single entry; call Clear() between decoding successive
+// entries or use a distinct HpackEntryCollector for each entry.
+
+#include <stddef.h>
+
+#include <iosfwd>
+#include <string>
+
+#include "base/strings/string_piece.h"
+#include "net/http2/hpack/decoder/hpack_entry_decoder_listener.h"
+#include "net/http2/hpack/decoder/hpack_string_collector.h"
+#include "net/http2/hpack/http2_hpack_constants.h"
+#include "net/http2/hpack/tools/hpack_block_builder.h"
+
+namespace net {
+namespace test {
+
+class HpackEntryCollector : public HpackEntryDecoderListener {
+ public:
+  HpackEntryCollector();
+  HpackEntryCollector(const HpackEntryCollector& other);
+
+  // These next three constructors are intended for use in tests that create
+  // an HpackEntryCollector "manually", and then compare it against another
+  // that is populated via calls to the HpackEntryDecoderListener methods.
+  HpackEntryCollector(HpackEntryType type, size_t index_or_size);
+  HpackEntryCollector(HpackEntryType type,
+                      size_t index,
+                      bool value_huffman,
+                      const std::string& value);
+  HpackEntryCollector(HpackEntryType type,
+                      bool name_huffman,
+                      const std::string& name,
+                      bool value_huffman,
+                      const std::string& value);
+
+  ~HpackEntryCollector() override;
+
+  // Methods defined by HpackEntryDecoderListener.
+  void OnIndexedHeader(size_t index) override;
+  void OnStartLiteralHeader(HpackEntryType header_type,
+                            size_t maybe_name_index) override;
+  void OnNameStart(bool huffman_encoded, size_t len) override;
+  void OnNameData(const char* data, size_t len) override;
+  void OnNameEnd() override;
+  void OnValueStart(bool huffman_encoded, size_t len) override;
+  void OnValueData(const char* data, size_t len) override;
+  void OnValueEnd() override;
+  void OnDynamicTableSizeUpdate(size_t size) override;
+
+  // Clears the fields of the collector so that it is ready to start collecting
+  // another HPACK block entry.
+  void Clear();
+
+  // Is the collector ready to start collecting another HPACK block entry.
+  bool IsClear() const;
+
+  // Has a complete entry been collected?
+  bool IsComplete() const;
+
+  // Based on the HpackEntryType, is a literal name expected?
+  bool LiteralNameExpected() const;
+
+  // Based on the HpackEntryType, is a literal value expected?
+  bool LiteralValueExpected() const;
+
+  // Returns success if collected an Indexed Header (i.e. OnIndexedHeader was
+  // called).
+  ::testing::AssertionResult ValidateIndexedHeader(size_t expected_index) const;
+
+  // Returns success if collected a Header with an indexed name and literal
+  // value (i.e. OnStartLiteralHeader was called with a non-zero index for
+  // the name, which must match expected_index).
+  ::testing::AssertionResult ValidateLiteralValueHeader(
+      HpackEntryType expected_type,
+      size_t expected_index,
+      bool expected_value_huffman,
+      base::StringPiece expected_value) const;
+
+  // Returns success if collected a Header with an literal name and literal
+  // value.
+  ::testing::AssertionResult ValidateLiteralNameValueHeader(
+      HpackEntryType expected_type,
+      bool expected_name_huffman,
+      base::StringPiece expected_name,
+      bool expected_value_huffman,
+      base::StringPiece expected_value) const;
+
+  // Returns success if collected a Dynamic Table Size Update,
+  // with the specified size.
+  ::testing::AssertionResult ValidateDynamicTableSizeUpdate(
+      size_t expected_size) const;
+
+  void set_header_type(HpackEntryType v) { header_type_ = v; }
+  HpackEntryType header_type() const { return header_type_; }
+
+  void set_index(size_t v) { index_ = v; }
+  size_t index() const { return index_; }
+
+  void set_name(const HpackStringCollector& v) { name_ = v; }
+  const HpackStringCollector& name() const { return name_; }
+
+  void set_value(const HpackStringCollector& v) { value_ = v; }
+  const HpackStringCollector& value() const { return value_; }
+
+  void set_started(bool v) { started_ = v; }
+  bool started() const { return started_; }
+
+  void set_ended(bool v) { ended_ = v; }
+  bool ended() const { return ended_; }
+
+  void AppendToHpackBlockBuilder(HpackBlockBuilder* hbb) const;
+
+  // Returns a debug string.
+  std::string ToString() const;
+
+ private:
+  void Init(HpackEntryType type, size_t maybe_index);
+
+  HpackEntryType header_type_;
+  size_t index_;
+
+  HpackStringCollector name_;
+  HpackStringCollector value_;
+
+  // True if has received a call to an HpackEntryDecoderListener method
+  // indicating the start of decoding an HPACK entry; for example,
+  // OnIndexedHeader set it true, but OnNameStart does not change it.
+  bool started_ = false;
+
+  // True if has received a call to an HpackEntryDecoderListener method
+  // indicating the end of decoding an HPACK entry; for example,
+  // OnIndexedHeader and OnValueEnd both set it true, but OnNameEnd does
+  // not change it.
+  bool ended_ = false;
+};
+
+bool operator==(const HpackEntryCollector& a, const HpackEntryCollector& b);
+bool operator!=(const HpackEntryCollector& a, const HpackEntryCollector& b);
+std::ostream& operator<<(std::ostream& out, const HpackEntryCollector& v);
+
+}  // namespace test
+}  // namespace net
+
+#endif  // NET_HTTP2_HPACK_DECODER_HPACK_ENTRY_COLLECTOR_H_
diff --git a/net/http2/hpack/decoder/hpack_entry_decoder.cc b/net/http2/hpack/decoder/hpack_entry_decoder.cc
new file mode 100644
index 0000000..c47ef0c
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_entry_decoder.cc
@@ -0,0 +1,233 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/decoder/hpack_entry_decoder.h"
+
+#include <stddef.h>
+
+#include "base/logging.h"
+#include "base/macros.h"
+
+namespace net {
+namespace {
+// Converts calls from HpackStringDecoder when decoding a header name into the
+// appropriate HpackEntryDecoderListener::OnName* calls.
+class NameDecoderListener {
+ public:
+  explicit NameDecoderListener(HpackEntryDecoderListener* listener)
+      : listener_(listener) {}
+  bool OnStringStart(bool huffman_encoded, size_t len) {
+    listener_->OnNameStart(huffman_encoded, len);
+    return true;
+  }
+  void OnStringData(const char* data, size_t len) {
+    listener_->OnNameData(data, len);
+  }
+  void OnStringEnd() { listener_->OnNameEnd(); }
+
+ private:
+  HpackEntryDecoderListener* listener_;
+};
+
+// Converts calls from HpackStringDecoder when decoding a header value into
+// the appropriate HpackEntryDecoderListener::OnValue* calls.
+class ValueDecoderListener {
+ public:
+  explicit ValueDecoderListener(HpackEntryDecoderListener* listener)
+      : listener_(listener) {}
+  bool OnStringStart(bool huffman_encoded, size_t len) {
+    listener_->OnValueStart(huffman_encoded, len);
+    return true;
+  }
+  void OnStringData(const char* data, size_t len) {
+    listener_->OnValueData(data, len);
+  }
+  void OnStringEnd() { listener_->OnValueEnd(); }
+
+ private:
+  HpackEntryDecoderListener* listener_;
+};
+}  // namespace
+
+// Only call Resume if the previous call (Start or Resume) returned
+// kDecodeInProgress; Resume is also called from Start when it has succeeded
+// in decoding the entry type and its varint.
+DecodeStatus HpackEntryDecoder::Resume(DecodeBuffer* db,
+                                       HpackEntryDecoderListener* listener) {
+  DCHECK(db != nullptr);
+  DCHECK(listener != nullptr);
+
+  DecodeStatus status;
+
+  do {
+    switch (state_) {
+      case EntryDecoderState::kResumeDecodingType:
+        // entry_type_decoder_ returned kDecodeInProgress when last called.
+        DVLOG(1) << "kResumeDecodingType: db->Remaining=" << db->Remaining();
+        status = entry_type_decoder_.Resume(db);
+        if (status != DecodeStatus::kDecodeDone) {
+          return status;
+        }
+        state_ = EntryDecoderState::kDecodedType;
+      // FALLTHROUGH_INTENDED
+
+      case EntryDecoderState::kDecodedType:
+        // entry_type_decoder_ returned kDecodeDone, now need to decide how
+        // to proceed.
+        DVLOG(1) << "kDecodedType: db->Remaining=" << db->Remaining();
+        if (DispatchOnType(listener)) {
+          // All done.
+          return DecodeStatus::kDecodeDone;
+        }
+        continue;
+
+      case EntryDecoderState::kStartDecodingName:
+        DVLOG(1) << "kStartDecodingName: db->Remaining=" << db->Remaining();
+        {
+          NameDecoderListener ncb(listener);
+          status = string_decoder_.Start(db, &ncb);
+        }
+        if (status != DecodeStatus::kDecodeDone) {
+          // On the assumption that the status is kDecodeInProgress, set
+          // state_ accordingly; unnecessary if status is kDecodeError, but
+          // that will only happen if the varint encoding the name's length
+          // is too long.
+          state_ = EntryDecoderState::kResumeDecodingName;
+          return status;
+        }
+        state_ = EntryDecoderState::kStartDecodingValue;
+      // FALLTHROUGH_INTENDED
+
+      case EntryDecoderState::kStartDecodingValue:
+        DVLOG(1) << "kStartDecodingValue: db->Remaining=" << db->Remaining();
+        {
+          ValueDecoderListener vcb(listener);
+          status = string_decoder_.Start(db, &vcb);
+        }
+        if (status == DecodeStatus::kDecodeDone) {
+          // Done with decoding the literal value, so we've reached the
+          // end of the header entry.
+          return status;
+        }
+        // On the assumption that the status is kDecodeInProgress, set
+        // state_ accordingly; unnecessary if status is kDecodeError, but
+        // that will only happen if the varint encoding the value's length
+        // is too long.
+        state_ = EntryDecoderState::kResumeDecodingValue;
+        return status;
+
+      case EntryDecoderState::kResumeDecodingName:
+        // The literal name was split across decode buffers.
+        DVLOG(1) << "kResumeDecodingName: db->Remaining=" << db->Remaining();
+        {
+          NameDecoderListener ncb(listener);
+          status = string_decoder_.Resume(db, &ncb);
+        }
+        if (status != DecodeStatus::kDecodeDone) {
+          // On the assumption that the status is kDecodeInProgress, set
+          // state_ accordingly; unnecessary if status is kDecodeError, but
+          // that will only happen if the varint encoding the name's length
+          // is too long.
+          state_ = EntryDecoderState::kResumeDecodingName;
+          return status;
+        }
+        state_ = EntryDecoderState::kStartDecodingValue;
+        break;
+
+      case EntryDecoderState::kResumeDecodingValue:
+        // The literal value was split across decode buffers.
+        DVLOG(1) << "kResumeDecodingValue: db->Remaining=" << db->Remaining();
+        {
+          ValueDecoderListener vcb(listener);
+          status = string_decoder_.Resume(db, &vcb);
+        }
+        if (status == DecodeStatus::kDecodeDone) {
+          // Done with decoding the value, therefore the entry as a whole.
+          return status;
+        }
+        // On the assumption that the status is kDecodeInProgress, set
+        // state_ accordingly; unnecessary if status is kDecodeError, but
+        // that will only happen if the varint encoding the value's length
+        // is too long.
+        state_ = EntryDecoderState::kResumeDecodingValue;
+        return status;
+    }
+  } while (true);
+}
+
+bool HpackEntryDecoder::DispatchOnType(HpackEntryDecoderListener* listener) {
+  const HpackEntryType entry_type = entry_type_decoder_.entry_type();
+  const uint32_t varint = entry_type_decoder_.varint();
+  switch (entry_type) {
+    case HpackEntryType::kIndexedHeader:
+      // The entry consists solely of the entry type and varint. See:
+      // http://httpwg.org/specs/rfc7541.html#indexed.header.representation
+      listener->OnIndexedHeader(varint);
+      return true;
+
+    case HpackEntryType::kIndexedLiteralHeader:
+    case HpackEntryType::kUnindexedLiteralHeader:
+    case HpackEntryType::kNeverIndexedLiteralHeader:
+      // The entry has a literal value, and if the varint is zero also has a
+      // literal name preceding the value. See:
+      // http://httpwg.org/specs/rfc7541.html#literal.header.representation
+      listener->OnStartLiteralHeader(entry_type, varint);
+      if (varint == 0) {
+        state_ = EntryDecoderState::kStartDecodingName;
+      } else {
+        state_ = EntryDecoderState::kStartDecodingValue;
+      }
+      return false;
+
+    case HpackEntryType::kDynamicTableSizeUpdate:
+      // The entry consists solely of the entry type and varint. FWIW, I've
+      // never seen this type of entry in production (primarily browser
+      // traffic) so if you're designing an HPACK successor someday, consider
+      // dropping it or giving it a much longer prefix. See:
+      // http://httpwg.org/specs/rfc7541.html#encoding.context.update
+      listener->OnDynamicTableSizeUpdate(varint);
+      return true;
+  }
+
+  NOTREACHED();
+  return true;
+}
+
+void HpackEntryDecoder::OutputDebugString(std::ostream& out) const {
+  out << "HpackEntryDecoder(state=" << state_ << ", " << entry_type_decoder_
+      << ", " << string_decoder_ << ")";
+}
+
+std::string HpackEntryDecoder::DebugString() const {
+  std::stringstream s;
+  s << *this;
+  return s.str();
+}
+
+std::ostream& operator<<(std::ostream& out, const HpackEntryDecoder& v) {
+  v.OutputDebugString(out);
+  return out;
+}
+
+std::ostream& operator<<(std::ostream& out,
+                         HpackEntryDecoder::EntryDecoderState state) {
+  typedef HpackEntryDecoder::EntryDecoderState EntryDecoderState;
+  switch (state) {
+    case EntryDecoderState::kResumeDecodingType:
+      return out << "kResumeDecodingType";
+    case EntryDecoderState::kDecodedType:
+      return out << "kDecodedType";
+    case EntryDecoderState::kStartDecodingName:
+      return out << "kStartDecodingName";
+    case EntryDecoderState::kResumeDecodingName:
+      return out << "kResumeDecodingName";
+    case EntryDecoderState::kStartDecodingValue:
+      return out << "kStartDecodingValue";
+    case EntryDecoderState::kResumeDecodingValue:
+      return out << "kResumeDecodingValue";
+  }
+  return out << static_cast<int>(state);
+}
+
+}  // namespace net
diff --git a/net/http2/hpack/decoder/hpack_entry_decoder.h b/net/http2/hpack/decoder/hpack_entry_decoder.h
new file mode 100644
index 0000000..9b5f696
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_entry_decoder.h
@@ -0,0 +1,117 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_HPACK_DECODER_HPACK_ENTRY_DECODER_H_
+#define NET_HTTP2_HPACK_DECODER_HPACK_ENTRY_DECODER_H_
+
+// HpackEntryDecoder decodes a single HPACK entry (i.e. one header or one
+// dynamic table size update), in a resumable fashion. The first call, Start(),
+// must provide a non-empty decode buffer. Continue with calls to Resume() if
+// Start, and any subsequent calls to Resume, returns kDecodeInProgress.
+
+#include <string>
+
+#include "base/logging.h"
+#include "net/base/net_export.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/hpack/decoder/hpack_entry_decoder_listener.h"
+#include "net/http2/hpack/decoder/hpack_entry_type_decoder.h"
+#include "net/http2/hpack/decoder/hpack_string_decoder.h"
+#include "net/http2/hpack/http2_hpack_constants.h"
+
+namespace net {
+
+class NET_EXPORT_PRIVATE HpackEntryDecoder {
+ public:
+  enum class EntryDecoderState {
+    // Have started decoding the type/varint, but didn't finish on the previous
+    // attempt.  Next state is kResumeDecodingType or kDecodedType.
+    kResumeDecodingType,
+
+    // Have just finished decoding the type/varint. Final state if the type is
+    // kIndexedHeader or kDynamicTableSizeUpdate. Otherwise, the next state is
+    // kStartDecodingName (if the varint is 0), else kStartDecodingValue.
+    kDecodedType,
+
+    // Ready to start decoding the literal name of a header entry. Next state
+    // is kResumeDecodingName (if the name is split across decode buffers),
+    // else kStartDecodingValue.
+    kStartDecodingName,
+
+    // Resume decoding the literal name of a header that is split across decode
+    // buffers.
+    kResumeDecodingName,
+
+    // Ready to start decoding the literal value of a header entry. Final state
+    // if the value string is entirely in the decode buffer, else the next state
+    // is kResumeDecodingValue.
+    kStartDecodingValue,
+
+    // Resume decoding the literal value of a header that is split across decode
+    // buffers.
+    kResumeDecodingValue,
+  };
+
+  // Only call when the decode buffer has data (i.e. HpackBlockDecoder must
+  // not call until there is data).
+  DecodeStatus Start(DecodeBuffer* db, HpackEntryDecoderListener* listener) {
+    DCHECK(db != nullptr);
+    DCHECK(listener != nullptr);
+    DCHECK(db->HasData());
+    DecodeStatus status = entry_type_decoder_.Start(db);
+    switch (status) {
+      case DecodeStatus::kDecodeDone:
+        // The type of the entry and its varint fit into the current decode
+        // buffer.
+        if (entry_type_decoder_.entry_type() ==
+            HpackEntryType::kIndexedHeader) {
+          // The entry consists solely of the entry type and varint. This
+          // is by far the most common case in practice.
+          listener->OnIndexedHeader(entry_type_decoder_.varint());
+          return DecodeStatus::kDecodeDone;
+        }
+        state_ = EntryDecoderState::kDecodedType;
+        return Resume(db, listener);
+      case DecodeStatus::kDecodeInProgress:
+        // Hit the end of the decode buffer before fully decoding the entry
+        // type and varint.
+        DCHECK_EQ(0u, db->Remaining());
+        state_ = EntryDecoderState::kResumeDecodingType;
+        return status;
+      case DecodeStatus::kDecodeError:
+        // The varint must have been invalid (too long).
+        return status;
+    }
+
+    NOTREACHED();
+    return DecodeStatus::kDecodeError;
+  }
+
+  // Only call Resume if the previous call (Start or Resume) returned
+  // kDecodeInProgress; Resume is also called from Start when it has succeeded
+  // in decoding the entry type and its varint.
+  DecodeStatus Resume(DecodeBuffer* db, HpackEntryDecoderListener* listener);
+
+  std::string DebugString() const;
+  void OutputDebugString(std::ostream& out) const;
+
+ private:
+  // Implements handling state kDecodedType.
+  bool DispatchOnType(HpackEntryDecoderListener* listener);
+
+  HpackEntryTypeDecoder entry_type_decoder_;
+  HpackStringDecoder string_decoder_;
+  EntryDecoderState state_ = EntryDecoderState();
+};
+
+NET_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& out,
+                                            const HpackEntryDecoder& v);
+NET_EXPORT_PRIVATE std::ostream& operator<<(
+    std::ostream& out,
+    HpackEntryDecoder::EntryDecoderState state);
+
+}  // namespace net
+
+#endif  // NET_HTTP2_HPACK_DECODER_HPACK_ENTRY_DECODER_H_
diff --git a/net/http2/hpack/decoder/hpack_entry_decoder_listener.cc b/net/http2/hpack/decoder/hpack_entry_decoder_listener.cc
new file mode 100644
index 0000000..c7e991f
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_entry_decoder_listener.cc
@@ -0,0 +1,83 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/decoder/hpack_entry_decoder_listener.h"
+
+#include "base/logging.h"
+
+namespace net {
+
+void HpackEntryDecoderVLoggingListener::OnIndexedHeader(size_t index) {
+  VLOG(1) << "OnIndexedHeader, index=" << index;
+  if (wrapped_) {
+    wrapped_->OnIndexedHeader(index);
+  }
+}
+
+void HpackEntryDecoderVLoggingListener::OnStartLiteralHeader(
+    HpackEntryType entry_type,
+    size_t maybe_name_index) {
+  VLOG(1) << "OnStartLiteralHeader: entry_type=" << entry_type
+          << ", maybe_name_index=" << maybe_name_index;
+  if (wrapped_) {
+    wrapped_->OnStartLiteralHeader(entry_type, maybe_name_index);
+  }
+}
+
+void HpackEntryDecoderVLoggingListener::OnNameStart(bool huffman_encoded,
+                                                    size_t len) {
+  VLOG(1) << "OnNameStart: H=" << huffman_encoded << ", len=" << len;
+  if (wrapped_) {
+    wrapped_->OnNameStart(huffman_encoded, len);
+  }
+}
+
+void HpackEntryDecoderVLoggingListener::OnNameData(const char* data,
+                                                   size_t len) {
+  VLOG(1) << "OnNameData: len=" << len;
+  if (wrapped_) {
+    wrapped_->OnNameData(data, len);
+  }
+}
+
+void HpackEntryDecoderVLoggingListener::OnNameEnd() {
+  VLOG(1) << "OnNameEnd";
+  if (wrapped_) {
+    wrapped_->OnNameEnd();
+  }
+}
+
+void HpackEntryDecoderVLoggingListener::OnValueStart(bool huffman_encoded,
+                                                     size_t len) {
+  VLOG(1) << "OnValueStart: H=" << huffman_encoded << ", len=" << len;
+  if (wrapped_) {
+    wrapped_->OnValueStart(huffman_encoded, len);
+  }
+  return;
+}
+
+void HpackEntryDecoderVLoggingListener::OnValueData(const char* data,
+                                                    size_t len) {
+  VLOG(1) << "OnValueData: len=" << len;
+  if (wrapped_) {
+    wrapped_->OnValueData(data, len);
+  }
+}
+
+void HpackEntryDecoderVLoggingListener::OnValueEnd() {
+  VLOG(1) << "OnValueEnd";
+  if (wrapped_) {
+    wrapped_->OnValueEnd();
+  }
+}
+
+void HpackEntryDecoderVLoggingListener::OnDynamicTableSizeUpdate(size_t size) {
+  VLOG(1) << "OnDynamicTableSizeUpdate: size=" << size;
+  if (wrapped_) {
+    wrapped_->OnDynamicTableSizeUpdate(size);
+  }
+  return;
+}
+
+}  // namespace net
diff --git a/net/http2/hpack/decoder/hpack_entry_decoder_listener.h b/net/http2/hpack/decoder/hpack_entry_decoder_listener.h
new file mode 100644
index 0000000..09fbd54
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_entry_decoder_listener.h
@@ -0,0 +1,110 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_HPACK_DECODER_HPACK_ENTRY_DECODER_LISTENER_H_
+#define NET_HTTP2_HPACK_DECODER_HPACK_ENTRY_DECODER_LISTENER_H_
+
+// Defines HpackEntryDecoderListener, the base class of listeners that
+// HpackEntryDecoder calls. Also defines HpackEntryDecoderVLoggingListener
+// which logs before calling another HpackEntryDecoderListener implementation.
+
+#include <stddef.h>
+
+#include "net/base/net_export.h"
+#include "net/http2/hpack/http2_hpack_constants.h"
+
+namespace net {
+
+class NET_EXPORT_PRIVATE HpackEntryDecoderListener {
+ public:
+  virtual ~HpackEntryDecoderListener() {}
+
+  // Called when an indexed header (i.e. one in the static or dynamic table) has
+  // been decoded from an HPACK block. index is supposed to be non-zero, but
+  // that has not been checked by the caller.
+  virtual void OnIndexedHeader(size_t index) = 0;
+
+  // Called when the start of a header with a literal value, and maybe a literal
+  // name, has been decoded. maybe_name_index is zero if the header has a
+  // literal name, else it is a reference into the static or dynamic table, from
+  // which the name should be determined. When the name is literal, the next
+  // call will be to OnNameStart; else it will be to OnValueStart. entry_type
+  // indicates whether the peer has added the entry to its dynamic table, and
+  // whether a proxy is permitted to do so when forwarding the entry.
+  virtual void OnStartLiteralHeader(HpackEntryType entry_type,
+                                    size_t maybe_name_index) = 0;
+
+  // Called when the encoding (Huffman compressed or plain text) and the encoded
+  // length of a literal name has been decoded. OnNameData will be called next,
+  // and repeatedly until the sum of lengths passed to OnNameData is len.
+  virtual void OnNameStart(bool huffman_encoded, size_t len) = 0;
+
+  // Called when len bytes of an encoded header name have been decoded.
+  virtual void OnNameData(const char* data, size_t len) = 0;
+
+  // Called after the entire name has been passed to OnNameData.
+  // OnValueStart will be called next.
+  virtual void OnNameEnd() = 0;
+
+  // Called when the encoding (Huffman compressed or plain text) and the encoded
+  // length of a literal value has been decoded. OnValueData will be called
+  // next, and repeatedly until the sum of lengths passed to OnValueData is len.
+  virtual void OnValueStart(bool huffman_encoded, size_t len) = 0;
+
+  // Called when len bytes of an encoded header value have been decoded.
+  virtual void OnValueData(const char* data, size_t len) = 0;
+
+  // Called after the entire value has been passed to OnValueData, marking the
+  // end of a header entry with a literal value, and maybe a literal name.
+  virtual void OnValueEnd() = 0;
+
+  // Called when an update to the size of the peer's dynamic table has been
+  // decoded.
+  virtual void OnDynamicTableSizeUpdate(size_t size) = 0;
+};
+
+class NET_EXPORT_PRIVATE HpackEntryDecoderVLoggingListener
+    : public HpackEntryDecoderListener {
+ public:
+  HpackEntryDecoderVLoggingListener() : wrapped_(nullptr) {}
+  explicit HpackEntryDecoderVLoggingListener(HpackEntryDecoderListener* wrapped)
+      : wrapped_(wrapped) {}
+  ~HpackEntryDecoderVLoggingListener() override {}
+
+  void OnIndexedHeader(size_t index) override;
+  void OnStartLiteralHeader(HpackEntryType entry_type,
+                            size_t maybe_name_index) override;
+  void OnNameStart(bool huffman_encoded, size_t len) override;
+  void OnNameData(const char* data, size_t len) override;
+  void OnNameEnd() override;
+  void OnValueStart(bool huffman_encoded, size_t len) override;
+  void OnValueData(const char* data, size_t len) override;
+  void OnValueEnd() override;
+  void OnDynamicTableSizeUpdate(size_t size) override;
+
+ private:
+  HpackEntryDecoderListener* const wrapped_;
+};
+
+// A no-op implementation of HpackEntryDecoderListener.
+class NET_EXPORT_PRIVATE HpackEntryDecoderNoOpListener
+    : public HpackEntryDecoderListener {
+ public:
+  ~HpackEntryDecoderNoOpListener() override {}
+
+  void OnIndexedHeader(size_t index) override {}
+  void OnStartLiteralHeader(HpackEntryType entry_type,
+                            size_t maybe_name_index) override {}
+  void OnNameStart(bool huffman_encoded, size_t len) override {}
+  void OnNameData(const char* data, size_t len) override {}
+  void OnNameEnd() override {}
+  void OnValueStart(bool huffman_encoded, size_t len) override {}
+  void OnValueData(const char* data, size_t len) override {}
+  void OnValueEnd() override {}
+  void OnDynamicTableSizeUpdate(size_t size) override {}
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP2_HPACK_DECODER_HPACK_ENTRY_DECODER_LISTENER_H_
diff --git a/net/http2/hpack/decoder/hpack_entry_decoder_test.cc b/net/http2/hpack/decoder/hpack_entry_decoder_test.cc
new file mode 100644
index 0000000..34581fa
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_entry_decoder_test.cc
@@ -0,0 +1,244 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/decoder/hpack_entry_decoder.h"
+
+// Tests of HpackEntryDecoder.
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "net/http2/hpack/decoder/hpack_entry_collector.h"
+#include "net/http2/hpack/tools/hpack_block_builder.h"
+#include "net/http2/tools/failure.h"
+#include "net/http2/tools/http2_random.h"
+#include "net/http2/tools/random_decoder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::AssertionResult;
+using std::string;
+
+namespace net {
+namespace test {
+namespace {
+
+class HpackEntryDecoderTest : public RandomDecoderTest {
+ public:
+  AssertionResult ValidateIndexedHeader(uint32_t ndx) {
+    VERIFY_AND_RETURN_SUCCESS(collector_.ValidateIndexedHeader(ndx));
+  }
+
+  AssertionResult ValidateForIndexedLiteralValue_Literal() {
+    VERIFY_AND_RETURN_SUCCESS(collector_.ValidateLiteralValueHeader(
+        HpackEntryType::kIndexedLiteralHeader, 0x40, false, "custom-header"));
+  }
+
+  AssertionResult ValidateForIndexedLiteralNameValue_Literal() {
+    VERIFY_AND_RETURN_SUCCESS(collector_.ValidateLiteralNameValueHeader(
+        HpackEntryType::kIndexedLiteralHeader, false, "custom-key", false,
+        "custom-header"));
+  }
+
+  AssertionResult ValidateForDynamicTableSizeUpdate_Literal() {
+    VERIFY_AND_RETURN_SUCCESS(collector_.ValidateDynamicTableSizeUpdate(31));
+  }
+
+ protected:
+  HpackEntryDecoderTest() : listener_(&collector_) {}
+
+  DecodeStatus StartDecoding(DecodeBuffer* b) override {
+    collector_.Clear();
+    return decoder_.Start(b, &listener_);
+  }
+
+  DecodeStatus ResumeDecoding(DecodeBuffer* b) override {
+    return decoder_.Resume(b, &listener_);
+  }
+
+  AssertionResult DecodeAndValidateSeveralWays(DecodeBuffer* db,
+                                               Validator validator) {
+    // StartDecoding, above, requires the DecodeBuffer be non-empty so that it
+    // can call Start with the prefix byte.
+    bool return_non_zero_on_first = true;
+    return RandomDecoderTest::DecodeAndValidateSeveralWays(
+        db, return_non_zero_on_first, validator);
+  }
+
+  AssertionResult DecodeAndValidateSeveralWays(const HpackBlockBuilder& hbb,
+                                               Validator validator) {
+    DecodeBuffer db(hbb.buffer());
+    return DecodeAndValidateSeveralWays(&db, validator);
+  }
+
+  HpackEntryDecoder decoder_;
+  HpackEntryCollector collector_;
+  HpackEntryDecoderVLoggingListener listener_;
+};
+
+TEST_F(HpackEntryDecoderTest, IndexedHeader_Literals) {
+  {
+    const char input[] = {0x82u};  // == Index 2 ==
+    DecodeBuffer b(input);
+    NoArgValidator do_check =
+        base::Bind(&HpackEntryDecoderTest::ValidateIndexedHeader,
+                   base::Unretained(this), 2);
+    EXPECT_TRUE(
+        DecodeAndValidateSeveralWays(&b, ValidateDoneAndEmpty(do_check)));
+    EXPECT_TRUE(do_check.Run());
+  }
+  collector_.Clear();
+  {
+    const char input[] = {0xfeu};  // == Index 126 ==
+    DecodeBuffer b(input);
+    NoArgValidator do_check =
+        base::Bind(&HpackEntryDecoderTest::ValidateIndexedHeader,
+                   base::Unretained(this), 126);
+    EXPECT_TRUE(
+        DecodeAndValidateSeveralWays(&b, ValidateDoneAndEmpty(do_check)));
+    EXPECT_TRUE(do_check.Run());
+  }
+  collector_.Clear();
+  {
+    const char input[] = {0xffu, 0x00};  // == Index 127 ==
+    DecodeBuffer b(input);
+    NoArgValidator do_check =
+        base::Bind(&HpackEntryDecoderTest::ValidateIndexedHeader,
+                   base::Unretained(this), 127);
+    EXPECT_TRUE(
+        DecodeAndValidateSeveralWays(&b, ValidateDoneAndEmpty(do_check)));
+    EXPECT_TRUE(do_check.Run());
+  }
+}
+
+TEST_F(HpackEntryDecoderTest, IndexedHeader_Various) {
+  // Indices chosen to hit encoding and table boundaries.
+  for (const uint32_t ndx : {1, 2, 61, 62, 63, 126, 127, 254, 255, 256}) {
+    HpackBlockBuilder hbb;
+    hbb.AppendIndexedHeader(ndx);
+
+    NoArgValidator do_check =
+        base::Bind(&HpackEntryDecoderTest::ValidateIndexedHeader,
+                   base::Unretained(this), ndx);
+    EXPECT_TRUE(
+        DecodeAndValidateSeveralWays(hbb, ValidateDoneAndEmpty(do_check)));
+    EXPECT_TRUE(do_check.Run());
+  }
+}
+
+TEST_F(HpackEntryDecoderTest, IndexedLiteralValue_Literal) {
+  const char input[] =
+      "\x7f"            // == Literal indexed, name index 0x40 ==
+      "\x01"            // 2nd byte of name index (0x01 + 0x3f == 0x40)
+      "\x0d"            // Value length (13)
+      "custom-header";  // Value
+  DecodeBuffer b(input, sizeof input - 1);
+  NoArgValidator do_check =
+      base::Bind(&HpackEntryDecoderTest::ValidateForIndexedLiteralValue_Literal,
+                 base::Unretained(this));
+  EXPECT_TRUE(DecodeAndValidateSeveralWays(&b, ValidateDoneAndEmpty(do_check)));
+  EXPECT_TRUE(do_check.Run());
+}
+
+TEST_F(HpackEntryDecoderTest, IndexedLiteralNameValue_Literal) {
+  const char input[] =
+      "\x40"            // == Literal indexed ==
+      "\x0a"            // Name length (10)
+      "custom-key"      // Name
+      "\x0d"            // Value length (13)
+      "custom-header";  // Value
+
+  DecodeBuffer b(input, sizeof input - 1);
+  NoArgValidator do_check = base::Bind(
+      &HpackEntryDecoderTest::ValidateForIndexedLiteralNameValue_Literal,
+      base::Unretained(this));
+  EXPECT_TRUE(DecodeAndValidateSeveralWays(&b, ValidateDoneAndEmpty(do_check)));
+  EXPECT_TRUE(do_check.Run());
+}
+
+TEST_F(HpackEntryDecoderTest, DynamicTableSizeUpdate_Literal) {
+  // Size update, length 31.
+  const char input[] = "\x3f\x00";
+  DecodeBuffer b(input, 2);
+  NoArgValidator do_check = base::Bind(
+      &HpackEntryDecoderTest::ValidateForDynamicTableSizeUpdate_Literal,
+      base::Unretained(this));
+  EXPECT_TRUE(DecodeAndValidateSeveralWays(&b, ValidateDoneAndEmpty(do_check)));
+  EXPECT_TRUE(do_check.Run());
+}
+
+class HpackLiteralEntryDecoderTest
+    : public HpackEntryDecoderTest,
+      public ::testing::WithParamInterface<HpackEntryType> {
+ public:
+  AssertionResult ValidateForRandNameIndexAndLiteralValue(
+      uint32_t ndx,
+      bool value_is_huffman_encoded,
+      const string& value) {
+    VERIFY_AND_RETURN_SUCCESS(collector_.ValidateLiteralValueHeader(
+        entry_type_, ndx, value_is_huffman_encoded, value));
+  }
+
+  AssertionResult ValidateForRandLiteralNameAndValue(
+      bool name_is_huffman_encoded,
+      const string& name,
+      bool value_is_huffman_encoded,
+      const string& value) {
+    VERIFY_AND_RETURN_SUCCESS(collector_.ValidateLiteralNameValueHeader(
+        entry_type_, name_is_huffman_encoded, name, value_is_huffman_encoded,
+        value));
+  }
+
+ protected:
+  HpackLiteralEntryDecoderTest() : entry_type_(GetParam()) {}
+
+  const HpackEntryType entry_type_;
+};
+
+INSTANTIATE_TEST_CASE_P(
+    AllLiteralTypes,
+    HpackLiteralEntryDecoderTest,
+    testing::Values(HpackEntryType::kIndexedLiteralHeader,
+                    HpackEntryType::kUnindexedLiteralHeader,
+                    HpackEntryType::kNeverIndexedLiteralHeader));
+
+TEST_P(HpackLiteralEntryDecoderTest, RandNameIndexAndLiteralValue) {
+  for (int n = 0; n < 10; n++) {
+    const uint32_t ndx = 1 + Random().Rand8();
+    const bool value_is_huffman_encoded = (n % 2) == 0;
+    const string value = Random().RandString(Random().Rand8());
+    HpackBlockBuilder hbb;
+    hbb.AppendNameIndexAndLiteralValue(entry_type_, ndx,
+                                       value_is_huffman_encoded, value);
+    NoArgValidator do_check = base::Bind(
+        &HpackLiteralEntryDecoderTest::ValidateForRandNameIndexAndLiteralValue,
+        base::Unretained(this), ndx, value_is_huffman_encoded, value);
+    EXPECT_TRUE(
+        DecodeAndValidateSeveralWays(hbb, ValidateDoneAndEmpty(do_check)));
+    EXPECT_TRUE(do_check.Run());
+  }
+}
+
+TEST_P(HpackLiteralEntryDecoderTest, RandLiteralNameAndValue) {
+  for (int n = 0; n < 10; n++) {
+    const bool name_is_huffman_encoded = (n & 1) == 0;
+    const int name_len = 1 + Random().Rand8();
+    const string name = Random().RandString(name_len);
+    const bool value_is_huffman_encoded = (n & 2) == 0;
+    const int value_len = Random().Skewed(10);
+    const string value = Random().RandString(value_len);
+    HpackBlockBuilder hbb;
+    hbb.AppendLiteralNameAndValue(entry_type_, name_is_huffman_encoded, name,
+                                  value_is_huffman_encoded, value);
+    NoArgValidator do_check = base::Bind(
+        &HpackLiteralEntryDecoderTest::ValidateForRandLiteralNameAndValue,
+        base::Unretained(this), name_is_huffman_encoded, name,
+        value_is_huffman_encoded, value);
+    EXPECT_TRUE(
+        DecodeAndValidateSeveralWays(hbb, ValidateDoneAndEmpty(do_check)));
+    EXPECT_TRUE(do_check.Run());
+  }
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/hpack/decoder/hpack_entry_type_decoder.cc b/net/http2/hpack/decoder/hpack_entry_type_decoder.cc
new file mode 100644
index 0000000..a05d48b
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_entry_type_decoder.cc
@@ -0,0 +1,360 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/decoder/hpack_entry_type_decoder.h"
+
+#include <sstream>
+
+#include "base/logging.h"
+
+namespace net {
+
+std::string HpackEntryTypeDecoder::DebugString() const {
+  std::stringstream ss;
+  ss << "HpackEntryTypeDecoder(varint_decoder=" << varint_decoder_.DebugString()
+     << ", entry_type=" << entry_type_ << ")";
+  return ss.str();
+}
+
+std::ostream& operator<<(std::ostream& out, const HpackEntryTypeDecoder& v) {
+  return out << v.DebugString();
+}
+
+// This ridiculous looking function turned out to be the winner in benchmarking
+// of several very different alternative implementations. It would be even
+// faster (~7%) if inlined in the header file, but I'm not sure if that is
+// worth doing... yet.
+// TODO(jamessynge): Benchmark again at a higher level (e.g. at least at the
+// full HTTP/2 decoder level, but preferably still higher) to determine if the
+// alternatives that take less code/data space are preferable in that situation.
+DecodeStatus HpackEntryTypeDecoder::Start(DecodeBuffer* db) {
+  DCHECK(db != nullptr);
+  DCHECK(db->HasData());
+
+  // The high four bits (nibble) of first byte of the entry determine the type
+  // of the entry, and may also be the initial bits of the varint that
+  // represents an index or table size. Note the use of the word 'initial'
+  // rather than 'high'; the HPACK encoding of varints is not in network
+  // order (i.e. not big-endian, the high-order byte isn't first), nor in
+  // little-endian order. See:
+  // http://httpwg.org/specs/rfc7541.html#integer.representation
+  uint8_t byte = db->DecodeUInt8();
+  switch (byte) {
+    case 0b00000000:
+    case 0b00000001:
+    case 0b00000010:
+    case 0b00000011:
+    case 0b00000100:
+    case 0b00000101:
+    case 0b00000110:
+    case 0b00000111:
+    case 0b00001000:
+    case 0b00001001:
+    case 0b00001010:
+    case 0b00001011:
+    case 0b00001100:
+    case 0b00001101:
+    case 0b00001110:
+      // The low 4 bits of |byte| are the initial bits of the varint.
+      // One of those bits is 0, so the varint is only one byte long.
+      entry_type_ = HpackEntryType::kUnindexedLiteralHeader;
+      varint_decoder_.set_value(byte);
+      return DecodeStatus::kDecodeDone;
+
+    case 0b00001111:
+      // The low 4 bits of |byte| are the initial bits of the varint. All 4
+      // are 1, so the varint extends into another byte.
+      entry_type_ = HpackEntryType::kUnindexedLiteralHeader;
+      return varint_decoder_.StartExtended(0x0f, db);
+
+    case 0b00010000:
+    case 0b00010001:
+    case 0b00010010:
+    case 0b00010011:
+    case 0b00010100:
+    case 0b00010101:
+    case 0b00010110:
+    case 0b00010111:
+    case 0b00011000:
+    case 0b00011001:
+    case 0b00011010:
+    case 0b00011011:
+    case 0b00011100:
+    case 0b00011101:
+    case 0b00011110:
+      // The low 4 bits of |byte| are the initial bits of the varint.
+      // One of those bits is 0, so the varint is only one byte long.
+      entry_type_ = HpackEntryType::kNeverIndexedLiteralHeader;
+      varint_decoder_.set_value(byte & 0x0f);
+      return DecodeStatus::kDecodeDone;
+
+    case 0b00011111:
+      // The low 4 bits of |byte| are the initial bits of the varint.
+      // All of those bits are 1, so the varint extends into another byte.
+      entry_type_ = HpackEntryType::kNeverIndexedLiteralHeader;
+      return varint_decoder_.StartExtended(0x0f, db);
+
+    case 0b00100000:
+    case 0b00100001:
+    case 0b00100010:
+    case 0b00100011:
+    case 0b00100100:
+    case 0b00100101:
+    case 0b00100110:
+    case 0b00100111:
+    case 0b00101000:
+    case 0b00101001:
+    case 0b00101010:
+    case 0b00101011:
+    case 0b00101100:
+    case 0b00101101:
+    case 0b00101110:
+    case 0b00101111:
+    case 0b00110000:
+    case 0b00110001:
+    case 0b00110010:
+    case 0b00110011:
+    case 0b00110100:
+    case 0b00110101:
+    case 0b00110110:
+    case 0b00110111:
+    case 0b00111000:
+    case 0b00111001:
+    case 0b00111010:
+    case 0b00111011:
+    case 0b00111100:
+    case 0b00111101:
+    case 0b00111110:
+      entry_type_ = HpackEntryType::kDynamicTableSizeUpdate;
+      // The low 5 bits of |byte| are the initial bits of the varint.
+      // One of those bits is 0, so the varint is only one byte long.
+      varint_decoder_.set_value(byte & 0x01f);
+      return DecodeStatus::kDecodeDone;
+
+    case 0b00111111:
+      entry_type_ = HpackEntryType::kDynamicTableSizeUpdate;
+      // The low 5 bits of |byte| are the initial bits of the varint.
+      // All of those bits are 1, so the varint extends into another byte.
+      return varint_decoder_.StartExtended(0x1f, db);
+
+    case 0b01000000:
+    case 0b01000001:
+    case 0b01000010:
+    case 0b01000011:
+    case 0b01000100:
+    case 0b01000101:
+    case 0b01000110:
+    case 0b01000111:
+    case 0b01001000:
+    case 0b01001001:
+    case 0b01001010:
+    case 0b01001011:
+    case 0b01001100:
+    case 0b01001101:
+    case 0b01001110:
+    case 0b01001111:
+    case 0b01010000:
+    case 0b01010001:
+    case 0b01010010:
+    case 0b01010011:
+    case 0b01010100:
+    case 0b01010101:
+    case 0b01010110:
+    case 0b01010111:
+    case 0b01011000:
+    case 0b01011001:
+    case 0b01011010:
+    case 0b01011011:
+    case 0b01011100:
+    case 0b01011101:
+    case 0b01011110:
+    case 0b01011111:
+    case 0b01100000:
+    case 0b01100001:
+    case 0b01100010:
+    case 0b01100011:
+    case 0b01100100:
+    case 0b01100101:
+    case 0b01100110:
+    case 0b01100111:
+    case 0b01101000:
+    case 0b01101001:
+    case 0b01101010:
+    case 0b01101011:
+    case 0b01101100:
+    case 0b01101101:
+    case 0b01101110:
+    case 0b01101111:
+    case 0b01110000:
+    case 0b01110001:
+    case 0b01110010:
+    case 0b01110011:
+    case 0b01110100:
+    case 0b01110101:
+    case 0b01110110:
+    case 0b01110111:
+    case 0b01111000:
+    case 0b01111001:
+    case 0b01111010:
+    case 0b01111011:
+    case 0b01111100:
+    case 0b01111101:
+    case 0b01111110:
+      entry_type_ = HpackEntryType::kIndexedLiteralHeader;
+      // The low 6 bits of |byte| are the initial bits of the varint.
+      // One of those bits is 0, so the varint is only one byte long.
+      varint_decoder_.set_value(byte & 0x03f);
+      return DecodeStatus::kDecodeDone;
+
+    case 0b01111111:
+      entry_type_ = HpackEntryType::kIndexedLiteralHeader;
+      // The low 6 bits of |byte| are the initial bits of the varint.
+      // All of those bits are 1, so the varint extends into another byte.
+      return varint_decoder_.StartExtended(0x3f, db);
+
+    case 0b10000000:
+    case 0b10000001:
+    case 0b10000010:
+    case 0b10000011:
+    case 0b10000100:
+    case 0b10000101:
+    case 0b10000110:
+    case 0b10000111:
+    case 0b10001000:
+    case 0b10001001:
+    case 0b10001010:
+    case 0b10001011:
+    case 0b10001100:
+    case 0b10001101:
+    case 0b10001110:
+    case 0b10001111:
+    case 0b10010000:
+    case 0b10010001:
+    case 0b10010010:
+    case 0b10010011:
+    case 0b10010100:
+    case 0b10010101:
+    case 0b10010110:
+    case 0b10010111:
+    case 0b10011000:
+    case 0b10011001:
+    case 0b10011010:
+    case 0b10011011:
+    case 0b10011100:
+    case 0b10011101:
+    case 0b10011110:
+    case 0b10011111:
+    case 0b10100000:
+    case 0b10100001:
+    case 0b10100010:
+    case 0b10100011:
+    case 0b10100100:
+    case 0b10100101:
+    case 0b10100110:
+    case 0b10100111:
+    case 0b10101000:
+    case 0b10101001:
+    case 0b10101010:
+    case 0b10101011:
+    case 0b10101100:
+    case 0b10101101:
+    case 0b10101110:
+    case 0b10101111:
+    case 0b10110000:
+    case 0b10110001:
+    case 0b10110010:
+    case 0b10110011:
+    case 0b10110100:
+    case 0b10110101:
+    case 0b10110110:
+    case 0b10110111:
+    case 0b10111000:
+    case 0b10111001:
+    case 0b10111010:
+    case 0b10111011:
+    case 0b10111100:
+    case 0b10111101:
+    case 0b10111110:
+    case 0b10111111:
+    case 0b11000000:
+    case 0b11000001:
+    case 0b11000010:
+    case 0b11000011:
+    case 0b11000100:
+    case 0b11000101:
+    case 0b11000110:
+    case 0b11000111:
+    case 0b11001000:
+    case 0b11001001:
+    case 0b11001010:
+    case 0b11001011:
+    case 0b11001100:
+    case 0b11001101:
+    case 0b11001110:
+    case 0b11001111:
+    case 0b11010000:
+    case 0b11010001:
+    case 0b11010010:
+    case 0b11010011:
+    case 0b11010100:
+    case 0b11010101:
+    case 0b11010110:
+    case 0b11010111:
+    case 0b11011000:
+    case 0b11011001:
+    case 0b11011010:
+    case 0b11011011:
+    case 0b11011100:
+    case 0b11011101:
+    case 0b11011110:
+    case 0b11011111:
+    case 0b11100000:
+    case 0b11100001:
+    case 0b11100010:
+    case 0b11100011:
+    case 0b11100100:
+    case 0b11100101:
+    case 0b11100110:
+    case 0b11100111:
+    case 0b11101000:
+    case 0b11101001:
+    case 0b11101010:
+    case 0b11101011:
+    case 0b11101100:
+    case 0b11101101:
+    case 0b11101110:
+    case 0b11101111:
+    case 0b11110000:
+    case 0b11110001:
+    case 0b11110010:
+    case 0b11110011:
+    case 0b11110100:
+    case 0b11110101:
+    case 0b11110110:
+    case 0b11110111:
+    case 0b11111000:
+    case 0b11111001:
+    case 0b11111010:
+    case 0b11111011:
+    case 0b11111100:
+    case 0b11111101:
+    case 0b11111110:
+      entry_type_ = HpackEntryType::kIndexedHeader;
+      // The low 7 bits of |byte| are the initial bits of the varint.
+      // One of those bits is 0, so the varint is only one byte long.
+      varint_decoder_.set_value(byte & 0x07f);
+      return DecodeStatus::kDecodeDone;
+
+    case 0b11111111:
+      entry_type_ = HpackEntryType::kIndexedHeader;
+      // The low 7 bits of |byte| are the initial bits of the varint.
+      // All of those bits are 1, so the varint extends into another byte.
+      return varint_decoder_.StartExtended(0x7f, db);
+  }
+  CHECK(false) << "Unreachable, byte=" << std::hex
+               << static_cast<uint32_t>(byte);
+  return DecodeStatus::kDecodeError;
+}
+
+}  // namespace net
diff --git a/net/http2/hpack/decoder/hpack_entry_type_decoder.h b/net/http2/hpack/decoder/hpack_entry_type_decoder.h
new file mode 100644
index 0000000..d2c1f54
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_entry_type_decoder.h
@@ -0,0 +1,56 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_HPACK_DECODER_HPACK_ENTRY_TYPE_DECODER_H_
+#define NET_HTTP2_HPACK_DECODER_HPACK_ENTRY_TYPE_DECODER_H_
+
+// Decodes the type of an HPACK entry, and the variable length integer whose
+// prefix is in the low-order bits of the same byte, "below" the type bits.
+// The integer represents an index into static or dynamic table, which may be
+// zero, or is the new size limit of the dynamic table.
+
+#include <string>
+
+#include "base/logging.h"
+#include "net/base/net_export.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/hpack/decoder/hpack_varint_decoder.h"
+#include "net/http2/hpack/http2_hpack_constants.h"
+
+namespace net {
+
+class NET_EXPORT_PRIVATE HpackEntryTypeDecoder {
+ public:
+  // Only call when the decode buffer has data (i.e. HpackEntryDecoder must
+  // not call until there is data).
+  DecodeStatus Start(DecodeBuffer* db);
+
+  // Only call Resume if the previous call (Start or Resume) returned
+  // DecodeStatus::kDecodeInProgress.
+  DecodeStatus Resume(DecodeBuffer* db) { return varint_decoder_.Resume(db); }
+
+  // Returns the decoded entry type. Only call if the preceding call to Start
+  // or Resume returned kDecodeDone.
+  HpackEntryType entry_type() const { return entry_type_; }
+
+  // Returns the decoded variable length integer. Only call if the
+  // preceding call to Start or Resume returned kDecodeDone.
+  uint32_t varint() const { return varint_decoder_.value(); }
+
+  std::string DebugString() const;
+
+ private:
+  HpackVarintDecoder varint_decoder_;
+
+  // This field is initialized just to keep ASAN happy about reading it
+  // from DebugString().
+  HpackEntryType entry_type_ = HpackEntryType::kIndexedHeader;
+};
+
+NET_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& out,
+                                            const HpackEntryTypeDecoder& v);
+
+}  // namespace net
+#endif  // NET_HTTP2_HPACK_DECODER_HPACK_ENTRY_TYPE_DECODER_H_
diff --git a/net/http2/hpack/decoder/hpack_entry_type_decoder_test.cc b/net/http2/hpack/decoder/hpack_entry_type_decoder_test.cc
new file mode 100644
index 0000000..0abf0c9
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_entry_type_decoder_test.cc
@@ -0,0 +1,98 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/decoder/hpack_entry_type_decoder.h"
+
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/logging.h"
+#include "net/http2/hpack/tools/hpack_block_builder.h"
+#include "net/http2/tools/failure.h"
+#include "net/http2/tools/random_decoder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::AssertionFailure;
+using ::testing::AssertionResult;
+using ::testing::AssertionSuccess;
+
+namespace net {
+namespace test {
+namespace {
+const bool kReturnNonZeroOnFirst = true;
+
+class HpackEntryTypeDecoderTest : public RandomDecoderTest {
+ public:
+  AssertionResult ValidatorForDynamicTableSizeUpdate(uint32_t size) {
+    VERIFY_EQ(HpackEntryType::kDynamicTableSizeUpdate, decoder_.entry_type());
+    VERIFY_EQ(size, decoder_.varint());
+    return AssertionSuccess();
+  }
+
+  AssertionResult ValidatorForHeaderWithIndex(const HpackEntryType entry_type,
+                                              uint32_t index) {
+    VERIFY_EQ(entry_type, decoder_.entry_type());
+    VERIFY_EQ(index, decoder_.varint());
+    return AssertionSuccess();
+  }
+
+ protected:
+  DecodeStatus StartDecoding(DecodeBuffer* b) override {
+    CHECK_LT(0u, b->Remaining());
+    return decoder_.Start(b);
+  }
+
+  DecodeStatus ResumeDecoding(DecodeBuffer* b) override {
+    return decoder_.Resume(b);
+  }
+
+  HpackEntryTypeDecoder decoder_;
+};
+
+TEST_F(HpackEntryTypeDecoderTest, DynamicTableSizeUpdate) {
+  for (uint32_t size = 0; size < 1000 * 1000; size += 256) {
+    HpackBlockBuilder bb;
+    bb.AppendDynamicTableSizeUpdate(size);
+    DecodeBuffer db(bb.buffer());
+    NoArgValidator validator = base::Bind(
+        &HpackEntryTypeDecoderTest::ValidatorForDynamicTableSizeUpdate,
+        base::Unretained(this), size);
+    EXPECT_TRUE(DecodeAndValidateSeveralWays(&db, kReturnNonZeroOnFirst,
+                                             ValidateDoneAndEmpty(validator)))
+        << "\nentry_type=kDynamicTableSizeUpdate, size=" << size;
+    // Run the validator again to make sure that DecodeAndValidateSeveralWays
+    // did the right thing.
+    EXPECT_TRUE(validator.Run());
+  }
+}
+
+TEST_F(HpackEntryTypeDecoderTest, HeaderWithIndex) {
+  std::vector<HpackEntryType> entry_types = {
+      HpackEntryType::kIndexedHeader, HpackEntryType::kIndexedLiteralHeader,
+      HpackEntryType::kUnindexedLiteralHeader,
+      HpackEntryType::kNeverIndexedLiteralHeader,
+  };
+  for (const HpackEntryType entry_type : entry_types) {
+    const uint32_t first = entry_type == HpackEntryType::kIndexedHeader ? 1 : 0;
+    for (uint32_t index = first; index < 1000; ++index) {
+      HpackBlockBuilder bb;
+      bb.AppendEntryTypeAndVarint(entry_type, index);
+      DecodeBuffer db(bb.buffer());
+      NoArgValidator validator =
+          base::Bind(&HpackEntryTypeDecoderTest::ValidatorForHeaderWithIndex,
+                     base::Unretained(this), entry_type, index);
+      EXPECT_TRUE(DecodeAndValidateSeveralWays(&db, kReturnNonZeroOnFirst,
+                                               ValidateDoneAndEmpty(validator)))
+          << "\nentry_type=" << entry_type << ", index=" << index;
+      // Run the validator again to make sure that DecodeAndValidateSeveralWays
+      // did the right thing.
+      EXPECT_TRUE(validator.Run());
+    }
+  }
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/hpack/decoder/hpack_string_collector.cc b/net/http2/hpack/decoder/hpack_string_collector.cc
new file mode 100644
index 0000000..e7e8195
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_string_collector.cc
@@ -0,0 +1,127 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/decoder/hpack_string_collector.h"
+
+#include <stddef.h>
+
+#include <iosfwd>
+#include <ostream>
+#include <string>
+
+#include "net/base/escape.h"
+#include "net/http2/tools/failure.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::StringPiece;
+
+namespace net {
+namespace test {
+namespace {
+
+std::ostream& operator<<(std::ostream& out,
+                         HpackStringCollector::CollectorState v) {
+  switch (v) {
+    case HpackStringCollector::CollectorState::kGenesis:
+      return out << "kGenesis";
+    case HpackStringCollector::CollectorState::kStarted:
+      return out << "kStarted";
+    case HpackStringCollector::CollectorState::kEnded:
+      return out << "kEnded";
+  }
+  return out << "UnknownCollectorState";
+}
+
+}  // namespace
+
+HpackStringCollector::HpackStringCollector() {
+  Clear();
+}
+
+HpackStringCollector::HpackStringCollector(const std::string& str, bool huffman)
+    : s(str), len(str.size()), huffman_encoded(huffman), state(kEnded) {}
+
+void HpackStringCollector::Clear() {
+  s = "";
+  len = 0;
+  huffman_encoded = false;
+  state = kGenesis;
+}
+
+bool HpackStringCollector::IsClear() const {
+  return s == "" && len == 0 && huffman_encoded == false && state == kGenesis;
+}
+
+bool HpackStringCollector::IsInProgress() const {
+  return state == kStarted;
+}
+
+bool HpackStringCollector::HasEnded() const {
+  return state == kEnded;
+}
+
+void HpackStringCollector::OnStringStart(bool huffman, size_t length) {
+  EXPECT_TRUE(IsClear()) << ToString();
+  state = kStarted;
+  huffman_encoded = huffman;
+  len = length;
+  return;
+}
+
+void HpackStringCollector::OnStringData(const char* data, size_t length) {
+  StringPiece sp(data, length);
+  EXPECT_TRUE(IsInProgress()) << ToString();
+  EXPECT_LE(sp.size(), len) << ToString();
+  sp.AppendToString(&s);
+  EXPECT_LE(s.size(), len) << ToString();
+}
+
+void HpackStringCollector::OnStringEnd() {
+  EXPECT_TRUE(IsInProgress()) << ToString();
+  EXPECT_EQ(s.size(), len) << ToString();
+  state = kEnded;
+}
+
+::testing::AssertionResult HpackStringCollector::Collected(
+    StringPiece str,
+    bool is_huffman_encoded) const {
+  VERIFY_TRUE(HasEnded());
+  VERIFY_EQ(str.size(), len);
+  VERIFY_EQ(is_huffman_encoded, huffman_encoded);
+  VERIFY_EQ(str, s);
+  return ::testing::AssertionSuccess();
+}
+
+std::string HpackStringCollector::ToString() const {
+  std::stringstream ss;
+  ss << *this;
+  return ss.str();
+}
+
+bool operator==(const HpackStringCollector& a, const HpackStringCollector& b) {
+  return a.s == b.s && a.len == b.len &&
+         a.huffman_encoded == b.huffman_encoded && a.state == b.state;
+}
+
+bool operator!=(const HpackStringCollector& a, const HpackStringCollector& b) {
+  return !(a == b);
+}
+
+std::ostream& operator<<(std::ostream& out, const HpackStringCollector& v) {
+  out << "HpackStringCollector(state=" << v.state;
+  if (v.state == HpackStringCollector::kGenesis) {
+    return out << ")";
+  }
+  if (v.huffman_encoded) {
+    out << ", Huffman Encoded";
+  }
+  out << ", Length=" << v.len;
+  if (!v.s.empty() && v.len != v.s.size()) {
+    out << " (" << v.s.size() << ")";
+  }
+  return out << ", String=\"" << EscapeQueryParamValue(v.s, false) << "\")";
+}
+
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/hpack/decoder/hpack_string_collector.h b/net/http2/hpack/decoder/hpack_string_collector.h
new file mode 100644
index 0000000..1bc5831
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_string_collector.h
@@ -0,0 +1,63 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_HPACK_DECODER_HPACK_STRING_COLLECTOR_H_
+#define NET_HTTP2_HPACK_DECODER_HPACK_STRING_COLLECTOR_H_
+
+// Supports tests of decoding HPACK strings.
+
+#include <stddef.h>
+
+#include <iosfwd>
+#include <string>
+
+#include "base/strings/string_piece.h"
+#include "net/http2/hpack/decoder/hpack_string_decoder_listener.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+// Records the callbacks associated with a decoding a string; must
+// call Clear() between decoding successive strings.
+struct HpackStringCollector : public HpackStringDecoderListener {
+  enum CollectorState {
+    kGenesis,
+    kStarted,
+    kEnded,
+  };
+
+  HpackStringCollector();
+  HpackStringCollector(const std::string& str, bool huffman);
+
+  void Clear();
+  bool IsClear() const;
+  bool IsInProgress() const;
+  bool HasEnded() const;
+
+  void OnStringStart(bool huffman, size_t length) override;
+  void OnStringData(const char* data, size_t length) override;
+  void OnStringEnd() override;
+
+  ::testing::AssertionResult Collected(base::StringPiece str,
+                                       bool is_huffman_encoded) const;
+
+  std::string ToString() const;
+
+  std::string s;
+  size_t len;
+  bool huffman_encoded;
+  CollectorState state;
+};
+
+bool operator==(const HpackStringCollector& a, const HpackStringCollector& b);
+
+bool operator!=(const HpackStringCollector& a, const HpackStringCollector& b);
+
+std::ostream& operator<<(std::ostream& out, const HpackStringCollector& v);
+
+}  // namespace test
+}  // namespace net
+
+#endif  // NET_HTTP2_HPACK_DECODER_HPACK_STRING_COLLECTOR_H_
diff --git a/net/http2/hpack/decoder/hpack_string_decoder.cc b/net/http2/hpack/decoder/hpack_string_decoder.cc
new file mode 100644
index 0000000..4131e01
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_string_decoder.cc
@@ -0,0 +1,39 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/decoder/hpack_string_decoder.h"
+
+#include <sstream>
+
+namespace net {
+
+std::string HpackStringDecoder::DebugString() const {
+  std::stringstream ss;
+  ss << "HpackStringDecoder(state=" << StateToString(state_)
+     << ", length=" << length_decoder_.DebugString()
+     << ", remaining=" << remaining_
+     << ", huffman=" << (huffman_encoded_ ? "true)" : "false)");
+  return ss.str();
+}
+
+// static
+std::string HpackStringDecoder::StateToString(StringDecoderState v) {
+  switch (v) {
+    case kStartDecodingLength:
+      return "kStartDecodingLength";
+    case kDecodingString:
+      return "kDecodingString";
+    case kResumeDecodingLength:
+      return "kResumeDecodingLength";
+  }
+  std::stringstream ss;
+  ss << "UNKNOWN_STATE(" << static_cast<uint32_t>(v) << ")";
+  return ss.str();
+}
+
+std::ostream& operator<<(std::ostream& out, const HpackStringDecoder& v) {
+  return out << v.DebugString();
+}
+
+}  // namespace net
diff --git a/net/http2/hpack/decoder/hpack_string_decoder.h b/net/http2/hpack/decoder/hpack_string_decoder.h
new file mode 100644
index 0000000..baea422
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_string_decoder.h
@@ -0,0 +1,236 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_HPACK_DECODER_HPACK_STRING_DECODER_H_
+#define NET_HTTP2_HPACK_DECODER_HPACK_STRING_DECODER_H_
+
+// HpackStringDecoder decodes strings encoded per the HPACK spec; this does
+// not mean decompressing Huffman encoded strings, just identifying the length,
+// encoding and contents for a listener.
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <string>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "net/base/net_export.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/hpack/decoder/hpack_varint_decoder.h"
+
+namespace net {
+
+// Decodes a single string in an HPACK header entry. The high order bit of
+// the first byte of the length is the H (Huffman) bit indicating whether
+// the value is Huffman encoded, and the remainder of the byte is the first
+// 7 bits of an HPACK varint.
+//
+// Call Start() to begin decoding; if it returns kDecodeInProgress, then call
+// Resume() when more input is available, repeating until kDecodeInProgress is
+// not returned. If kDecodeDone or kDecodeError is returned, then Resume() must
+// not be called until Start() has been called to start decoding a new string.
+//
+// There are 3 variants of Start in this class, participants in a performance
+// experiment. Perflab experiments show it is generally fastest to call
+// StartSpecialCaseShort rather than StartOnly (~9% slower) or
+// StartAndDecodeLength (~10% slower).
+class NET_EXPORT_PRIVATE HpackStringDecoder {
+ public:
+  enum StringDecoderState {
+    kStartDecodingLength,
+    kDecodingString,
+    kResumeDecodingLength,
+  };
+
+  // TODO(jamessynge): Get rid of all but one of the Start and Resume methods
+  // after all of the HPACK decoder is checked in and has been perf tested.
+  template <class Listener>
+  DecodeStatus Start(DecodeBuffer* db, Listener* cb) {
+    return StartSpecialCaseShort(db, cb);
+  }
+
+  template <class Listener>
+  DecodeStatus StartOnly(DecodeBuffer* db, Listener* cb) {
+    state_ = kStartDecodingLength;
+    return Resume(db, cb);
+  }
+
+  template <class Listener>
+  DecodeStatus StartAndDecodeLength(DecodeBuffer* db, Listener* cb) {
+    DecodeStatus status;
+    if (StartDecodingLength(db, cb, &status)) {
+      state_ = kDecodingString;
+      return DecodeString(db, cb);
+    }
+    return status;
+  }
+
+  template <class Listener>
+  DecodeStatus StartSpecialCaseShort(DecodeBuffer* db, Listener* cb) {
+    // Fast decode path is used if the string is under 127 bytes and the
+    // entire length of the string is in the decode buffer. More than 83% of
+    // string lengths are encoded in just one byte.
+    if (db->HasData() && (*db->cursor() & 0x7f) != 0x7f) {
+      // The string is short.
+      uint8_t h_and_prefix = db->DecodeUInt8();
+      uint8_t length = h_and_prefix & 0x7f;
+      bool huffman_encoded = (h_and_prefix & 0x80) == 0x80;
+      cb->OnStringStart(huffman_encoded, length);
+      if (length <= db->Remaining()) {
+        // Yeah, we've got the whole thing in the decode buffer.
+        // Ideally this will be the common case. Note that we don't
+        // update any of the member variables in this path.
+        cb->OnStringData(db->cursor(), length);
+        db->AdvanceCursor(length);
+        cb->OnStringEnd();
+        return DecodeStatus::kDecodeDone;
+      }
+      // Not all in the buffer.
+      huffman_encoded_ = huffman_encoded;
+      remaining_ = length;
+      // Call Resume to decode the string body, which is only partially
+      // in the decode buffer (or not at all).
+      state_ = kDecodingString;
+      return Resume(db, cb);
+    }
+    // Call Resume to decode the string length, which is either not in
+    // the decode buffer, or spans multiple bytes.
+    state_ = kStartDecodingLength;
+    return Resume(db, cb);
+  }
+
+  template <class Listener>
+  DecodeStatus Resume(DecodeBuffer* db, Listener* cb) {
+    DecodeStatus status;
+    while (true) {
+      switch (state_) {
+        case kStartDecodingLength:
+          DVLOG(2) << "kStartDecodingLength: db->Remaining=" << db->Remaining();
+          if (!StartDecodingLength(db, cb, &status)) {
+            // The length is split across decode buffers.
+            return status;
+          }
+        // We've finished decoding the length, which spanned one or more
+        // bytes. Approximately 17% of strings have a length that is greater
+        // than 126 bytes, and thus the length is encoded in more than one
+        // byte, and so doesn't get the benefit of the optimization in
+        // Start() for single byte lengths. But, we still expect that most
+        // of such strings will be contained entirely in a single decode
+        // buffer, and hence this fall through skips another trip through the
+        // switch above and more importantly skips setting the state_ variable
+        // again in those cases where we don't need it.
+
+        // FALLTHROUGH_INTENDED
+
+        case kDecodingString:
+          DVLOG(2) << "kDecodingString: db->Remaining=" << db->Remaining()
+                   << "    remaining_=" << remaining_;
+          return DecodeString(db, cb);
+
+        case kResumeDecodingLength:
+          DVLOG(2) << "kResumeDecodingLength: db->Remaining="
+                   << db->Remaining();
+          if (!ResumeDecodingLength(db, cb, &status)) {
+            return status;
+          }
+      }
+    }
+  }
+
+  std::string DebugString() const;
+
+ private:
+  static std::string StateToString(StringDecoderState v);
+
+  // Returns true if the length is fully decoded and the listener wants the
+  // decoding to continue, false otherwise; status is set to the status from
+  // the varint decoder.
+  // If the length is not fully decoded, case state_ is set appropriately
+  // for the next call to Resume.
+  template <class Listener>
+  bool StartDecodingLength(DecodeBuffer* db,
+                           Listener* cb,
+                           DecodeStatus* status) {
+    if (db->Empty()) {
+      *status = DecodeStatus::kDecodeInProgress;
+      state_ = kStartDecodingLength;
+      return false;
+    }
+    uint8_t h_and_prefix = db->DecodeUInt8();
+    huffman_encoded_ = (h_and_prefix & 0x80) == 0x80;
+    *status = length_decoder_.Start(h_and_prefix, 0x7f, db);
+    if (*status == DecodeStatus::kDecodeDone) {
+      OnStringStart(cb, status);
+      return true;
+    }
+    // Set the state to cover the DecodeStatus::kDecodeInProgress case.
+    // Won't be needed if the status is kDecodeError.
+    state_ = kResumeDecodingLength;
+    return false;
+  }
+
+  // Returns true if the length is fully decoded and the listener wants the
+  // decoding to continue, false otherwise; status is set to the status from
+  // the varint decoder; state_ is updated when fully decoded.
+  // If the length is not fully decoded, case state_ is set appropriately
+  // for the next call to Resume.
+  template <class Listener>
+  bool ResumeDecodingLength(DecodeBuffer* db,
+                            Listener* cb,
+                            DecodeStatus* status) {
+    DCHECK_EQ(state_, kResumeDecodingLength);
+    *status = length_decoder_.Resume(db);
+    if (*status == DecodeStatus::kDecodeDone) {
+      state_ = kDecodingString;
+      OnStringStart(cb, status);
+      return true;
+    }
+    return false;
+  }
+
+  // Returns true if the listener wants the decoding to continue, and
+  // false otherwise, in which case status set.
+  template <class Listener>
+  void OnStringStart(Listener* cb, DecodeStatus* status) {
+    remaining_ = length_decoder_.value();
+    // Make callback so consumer knows what is coming.
+    cb->OnStringStart(huffman_encoded_, remaining_);
+    return;
+  }
+
+  // Passes the available portion of the string to the listener, and signals
+  // the end of the string when it is reached. Returns kDecodeDone or
+  // kDecodeInProgress as appropriate.
+  template <class Listener>
+  DecodeStatus DecodeString(DecodeBuffer* db, Listener* cb) {
+    size_t len = std::min(remaining_, db->Remaining());
+    if (len > 0) {
+      cb->OnStringData(db->cursor(), len);
+      db->AdvanceCursor(len);
+      remaining_ -= len;
+    }
+    if (remaining_ == 0) {
+      cb->OnStringEnd();
+      return DecodeStatus::kDecodeDone;
+    }
+    state_ = kDecodingString;
+    return DecodeStatus::kDecodeInProgress;
+  }
+
+  HpackVarintDecoder length_decoder_;
+
+  // These fields are initialized just to keep ASAN happy about reading
+  // them from DebugString().
+  size_t remaining_ = 0;
+  StringDecoderState state_ = kStartDecodingLength;
+  bool huffman_encoded_ = false;
+};
+
+NET_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& out,
+                                            const HpackStringDecoder& v);
+
+}  // namespace net
+#endif  // NET_HTTP2_HPACK_DECODER_HPACK_STRING_DECODER_H_
diff --git a/net/http2/hpack/decoder/hpack_string_decoder_listener.cc b/net/http2/hpack/decoder/hpack_string_decoder_listener.cc
new file mode 100644
index 0000000..97ec985
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_string_decoder_listener.cc
@@ -0,0 +1,36 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/decoder/hpack_string_decoder_listener.h"
+
+#include "base/logging.h"
+
+namespace net {
+namespace test {
+
+void HpackStringDecoderVLoggingListener::OnStringStart(bool huffman_encoded,
+                                                       size_t len) {
+  VLOG(1) << "OnStringStart: H=" << huffman_encoded << ", len=" << len;
+  if (wrapped_) {
+    wrapped_->OnStringStart(huffman_encoded, len);
+  }
+}
+
+void HpackStringDecoderVLoggingListener::OnStringData(const char* data,
+                                                      size_t len) {
+  VLOG(1) << "OnStringData: len=" << len;
+  if (wrapped_) {
+    return wrapped_->OnStringData(data, len);
+  }
+}
+
+void HpackStringDecoderVLoggingListener::OnStringEnd() {
+  VLOG(1) << "OnStringEnd";
+  if (wrapped_) {
+    return wrapped_->OnStringEnd();
+  }
+}
+
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/hpack/decoder/hpack_string_decoder_listener.h b/net/http2/hpack/decoder/hpack_string_decoder_listener.h
new file mode 100644
index 0000000..fbeeeb4
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_string_decoder_listener.h
@@ -0,0 +1,62 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_HPACK_DECODER_HPACK_STRING_DECODER_LISTENER_H_
+#define NET_HTTP2_HPACK_DECODER_HPACK_STRING_DECODER_LISTENER_H_
+
+// Defines HpackStringDecoderListener which defines the methods required by an
+// HpackStringDecoder. Also defines HpackStringDecoderVLoggingListener which
+// logs before calling another HpackStringDecoderListener implementation.
+// For now these are only used by tests, so placed in the test namespace.
+
+#include <stddef.h>
+
+#include "net/base/net_export.h"
+
+namespace net {
+namespace test {
+
+// HpackStringDecoder methods require a listener that implements the methods
+// below, but it is NOT necessary to extend this class because the methods
+// are templates.
+class NET_EXPORT_PRIVATE HpackStringDecoderListener {
+ public:
+  virtual ~HpackStringDecoderListener() {}
+
+  // Called at the start of decoding an HPACK string. The encoded length of the
+  // string is |len| bytes, which may be zero. The string is Huffman encoded
+  // if huffman_encoded is true, else it is plain text (i.e. the encoded length
+  // is then the plain text length).
+  virtual void OnStringStart(bool huffman_encoded, size_t len) = 0;
+
+  // Called when some data is available, or once when the string length is zero
+  // (to simplify the decoder, it doesn't have a special case for len==0).
+  virtual void OnStringData(const char* data, size_t len) = 0;
+
+  // Called after OnStringData has provided all of the encoded bytes of the
+  // string.
+  virtual void OnStringEnd() = 0;
+};
+
+class NET_EXPORT_PRIVATE HpackStringDecoderVLoggingListener
+    : public HpackStringDecoderListener {
+ public:
+  HpackStringDecoderVLoggingListener() : wrapped_(nullptr) {}
+  explicit HpackStringDecoderVLoggingListener(
+      HpackStringDecoderListener* wrapped)
+      : wrapped_(wrapped) {}
+  ~HpackStringDecoderVLoggingListener() override {}
+
+  void OnStringStart(bool huffman_encoded, size_t len) override;
+  void OnStringData(const char* data, size_t len) override;
+  void OnStringEnd() override;
+
+ private:
+  HpackStringDecoderListener* const wrapped_;
+};
+
+}  // namespace test
+}  // namespace net
+
+#endif  // NET_HTTP2_HPACK_DECODER_HPACK_STRING_DECODER_LISTENER_H_
diff --git a/net/http2/hpack/decoder/hpack_string_decoder_test.cc b/net/http2/hpack/decoder/hpack_string_decoder_test.cc
new file mode 100644
index 0000000..e4afc61
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_string_decoder_test.cc
@@ -0,0 +1,194 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/decoder/hpack_string_decoder.h"
+
+// Tests of HpackStringDecoder.
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/strings/string_piece.h"
+#include "net/http2/hpack/decoder/hpack_string_collector.h"
+#include "net/http2/hpack/decoder/hpack_string_decoder_listener.h"
+#include "net/http2/hpack/tools/hpack_block_builder.h"
+#include "net/http2/tools/failure.h"
+#include "net/http2/tools/http2_random.h"
+#include "net/http2/tools/random_decoder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::AssertionResult;
+using ::testing::AssertionSuccess;
+using base::StringPiece;
+using std::string;
+
+namespace net {
+namespace test {
+namespace {
+
+const bool kMayReturnZeroOnFirst = false;
+const bool kCompressed = true;
+const bool kUncompressed = false;
+
+enum StartMethod {
+  kStart,
+  kStartOnly,
+  kStartAndDecodeLength,
+  kStartSpecialCaseShort,
+};
+
+class HpackStringDecoderTest
+    : public RandomDecoderTest,
+      public ::testing::WithParamInterface<StartMethod> {
+ protected:
+  HpackStringDecoderTest()
+      : start_method_(GetParam()), listener_(&collector_) {}
+
+  DecodeStatus StartDecoding(DecodeBuffer* b) override {
+    ++start_decoding_calls_;
+    collector_.Clear();
+    switch (start_method_) {
+      case kStart:
+        return decoder_.Start(b, &listener_);
+      case kStartOnly:
+        return decoder_.StartOnly(b, &listener_);
+      case kStartAndDecodeLength:
+        return decoder_.StartAndDecodeLength(b, &listener_);
+      case kStartSpecialCaseShort:
+        return decoder_.StartSpecialCaseShort(b, &listener_);
+      default:
+        return DecodeStatus::kDecodeError;
+    }
+  }
+
+  DecodeStatus ResumeDecoding(DecodeBuffer* b) override {
+    // Provides coverage of DebugString and StateToString.
+    // Not validating output.
+    VLOG(1) << decoder_.DebugString();
+    VLOG(2) << collector_;
+    return decoder_.Resume(b, &listener_);
+  }
+
+  AssertionResult Collected(StringPiece s, bool huffman_encoded) {
+    VLOG(1) << collector_;
+    return collector_.Collected(s, huffman_encoded);
+  }
+
+  // Note that base::Bind() makes a copy of |expected_str| even though it is
+  // taken as a constant reference, so even if MakeValidator is called with a
+  // C-style string that is cast to a temporary std::string that gets destroyed
+  // after the call to MakeValidator, |expected_str| is still valid later when
+  // the Validator is run.
+  AssertionResult StringValidator(const string& expected_str,
+                                  bool expected_huffman,
+                                  const DecodeBuffer& input,
+                                  DecodeStatus status) {
+    AssertionResult result = Collected(expected_str, expected_huffman);
+    if (result) {
+      VERIFY_EQ(collector_,
+                HpackStringCollector(expected_str, expected_huffman));
+    } else {
+      VERIFY_NE(collector_,
+                HpackStringCollector(expected_str, expected_huffman));
+    }
+    VLOG(2) << collector_.ToString();
+    collector_.Clear();
+    VLOG(2) << collector_;
+    return result;
+  }
+
+  Validator MakeValidator(const string& expected_str, bool expected_huffman) {
+    return base::Bind(&HpackStringDecoderTest::StringValidator,
+                      base::Unretained(this), expected_str, expected_huffman);
+  }
+
+  const StartMethod start_method_;
+  HpackStringDecoder decoder_;
+  HpackStringCollector collector_;
+  HpackStringDecoderVLoggingListener listener_;
+  size_t start_decoding_calls_ = 0;
+};
+
+TEST_P(HpackStringDecoderTest, DecodeEmptyString) {
+  {
+    Validator validator = ValidateDoneAndEmpty(MakeValidator("", kCompressed));
+    const char kData[] = {0x80u};
+    DecodeBuffer b(kData);
+    EXPECT_TRUE(
+        DecodeAndValidateSeveralWays(&b, kMayReturnZeroOnFirst, validator));
+  }
+  {
+    // Make sure it stops after decoding the empty string.
+    Validator validator =
+        ValidateDoneAndOffset(1, MakeValidator("", kUncompressed));
+    const char kData[] = {0x00, 0xffu};
+    DecodeBuffer b(kData);
+    EXPECT_EQ(2u, b.Remaining());
+    EXPECT_TRUE(
+        DecodeAndValidateSeveralWays(&b, kMayReturnZeroOnFirst, validator));
+    EXPECT_EQ(1u, b.Remaining());
+  }
+}
+
+TEST_P(HpackStringDecoderTest, DecodeShortString) {
+  {
+    // Make sure it stops after decoding the non-empty string.
+    Validator validator =
+        ValidateDoneAndOffset(11, MakeValidator("start end.", kCompressed));
+    const char kData[] = "\x8astart end.Don't peek at this.";
+    DecodeBuffer b(kData);
+    EXPECT_TRUE(
+        DecodeAndValidateSeveralWays(&b, kMayReturnZeroOnFirst, validator));
+  }
+  {
+    Validator validator =
+        ValidateDoneAndOffset(11, MakeValidator("start end.", kUncompressed));
+    StringPiece data("\x0astart end.");
+    DecodeBuffer b(data);
+    EXPECT_TRUE(
+        DecodeAndValidateSeveralWays(&b, kMayReturnZeroOnFirst, validator));
+  }
+}
+
+TEST_P(HpackStringDecoderTest, DecodeLongStrings) {
+  string name = Random().RandString(1024);
+  string value = Random().RandString(65536);
+  HpackBlockBuilder hbb;
+
+  hbb.AppendString(false, name);
+  uint32_t offset_after_name = hbb.size();
+  EXPECT_EQ(3 + name.size(), offset_after_name);
+
+  hbb.AppendString(true, value);
+  uint32_t offset_after_value = hbb.size();
+  EXPECT_EQ(3 + name.size() + 4 + value.size(), offset_after_value);
+
+  DecodeBuffer b(hbb.buffer());
+
+  // Decode the name...
+  EXPECT_TRUE(DecodeAndValidateSeveralWays(
+      &b, kMayReturnZeroOnFirst,
+      ValidateDoneAndOffset(offset_after_name,
+                            MakeValidator(name, kUncompressed))));
+  EXPECT_EQ(offset_after_name, b.Offset());
+  EXPECT_EQ(offset_after_value - offset_after_name, b.Remaining());
+
+  // Decode the value...
+  EXPECT_TRUE(DecodeAndValidateSeveralWays(
+      &b, kMayReturnZeroOnFirst,
+      ValidateDoneAndOffset(offset_after_value - offset_after_name,
+                            MakeValidator(value, kCompressed))));
+  EXPECT_EQ(offset_after_value, b.Offset());
+  EXPECT_EQ(0u, b.Remaining());
+}
+
+INSTANTIATE_TEST_CASE_P(AllStartMethods,
+                        HpackStringDecoderTest,
+                        ::testing::Values(kStart,
+                                          kStartOnly,
+                                          kStartAndDecodeLength,
+                                          kStartSpecialCaseShort));
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/hpack/decoder/hpack_varint_decoder.cc b/net/http2/hpack/decoder/hpack_varint_decoder.cc
new file mode 100644
index 0000000..f04bd89d
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_varint_decoder.cc
@@ -0,0 +1,36 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/decoder/hpack_varint_decoder.h"
+
+#include <sstream>
+
+namespace net {
+
+std::string HpackVarintDecoder::DebugString() const {
+  std::stringstream ss;
+  ss << "HpackVarintDecoder(value=" << value_ << ", offset=" << offset_ << ")";
+  return ss.str();
+}
+
+DecodeStatus HpackVarintDecoder::StartForTest(uint8_t prefix_value,
+                                              uint8_t prefix_mask,
+                                              DecodeBuffer* db) {
+  return Start(prefix_value, prefix_mask, db);
+}
+
+DecodeStatus HpackVarintDecoder::StartExtendedForTest(uint8_t prefix_mask,
+                                                      DecodeBuffer* db) {
+  return StartExtended(prefix_mask, db);
+}
+
+DecodeStatus HpackVarintDecoder::ResumeForTest(DecodeBuffer* db) {
+  return Resume(db);
+}
+
+std::ostream& operator<<(std::ostream& out, const HpackVarintDecoder& v) {
+  return out << v.DebugString();
+}
+
+}  // namespace net
diff --git a/net/http2/hpack/decoder/hpack_varint_decoder.h b/net/http2/hpack/decoder/hpack_varint_decoder.h
new file mode 100644
index 0000000..b110fad
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_varint_decoder.h
@@ -0,0 +1,181 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// HpackVarintDecoder decodes HPACK variable length unsigned integers. These
+// integers are used to identify static or dynamic table index entries, to
+// specify string lengths, and to update the size limit of the dynamic table.
+//
+// The caller will need to validate that the decoded value is in an acceptable
+// range.
+//
+// In order to support naive encoders (i.e. which always output 5 extension
+// bytes for a uint32 that is >= prefix_mask), the decoder supports an an
+// encoding with up to 5 extension bytes, and a maximum value of 268,435,582
+// (4 "full" extension bytes plus the maximum for a prefix, 127). It could be
+// modified to support a lower maximum value (by requiring that extensions bytes
+// be "empty"), or a larger value if valuable for some reason I can't see.
+//
+// For details of the encoding, see:
+//        http://httpwg.org/specs/rfc7541.html#integer.representation
+//
+// TODO(jamessynge): Consider dropping support for encodings of more than 4
+// bytes, including the prefix byte, as in practice we only see at most 3 bytes,
+// and 4 bytes would cover any desire to support large (but not ridiculously
+// large) header values.
+
+#ifndef NET_HTTP2_HPACK_DECODER_HPACK_VARINT_DECODER_H_
+#define NET_HTTP2_HPACK_DECODER_HPACK_VARINT_DECODER_H_
+
+#include <string>
+
+#include "base/logging.h"
+#include "net/base/net_export.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+
+namespace net {
+// Decodes an HPACK variable length unsigned integer, in a resumable fashion
+// so it can handle running out of input in the DecodeBuffer. Call Start or
+// StartExtended the first time (when decoding the byte that contains the
+// prefix), then call Resume later if it is necessary to resume. When done,
+// call value() to retrieve the decoded value.
+//
+// No constructor or destructor. Holds no resources, so destruction isn't
+// needed. Start and StartExtended handles the initialization of member
+// variables. This is necessary in order for HpackVarintDecoder to be part
+// of a union.
+class NET_EXPORT_PRIVATE HpackVarintDecoder {
+ public:
+  // |prefix_value| is the first byte of the encoded varint.
+  // |prefix_mask| is the mask of the valid bits, i.e. without the top 1 to 4
+  // high-bits set, as appropriate for the item being decoded; must be a
+  // contiguous sequence of set bits, starting with the low-order bits.
+  DecodeStatus Start(uint8_t prefix_value,
+                     uint8_t prefix_mask,
+                     DecodeBuffer* db) {
+    DCHECK_LE(15, prefix_mask) << std::hex << prefix_mask;
+    DCHECK_LE(prefix_mask, 127) << std::hex << prefix_mask;
+    // Confirm that |prefix_mask| is a contiguous sequence of bits.
+    DCHECK_EQ(0, (prefix_mask + 1) & prefix_mask) << std::hex << prefix_mask;
+
+    // Ignore the bits that aren't a part of the prefix of the varint.
+    value_ = prefix_value & prefix_mask;
+
+    if (value_ < prefix_mask) {
+      MarkDone();
+      return DecodeStatus::kDecodeDone;
+    }
+
+    offset_ = 0;
+    return Resume(db);
+  }
+
+  // The caller has already determined that the encoding requires multiple
+  // bytes, i.e. that the 4 to 7 low-order bits (the number determined by the
+  // prefix length, a value not passed into this function) of the first byte are
+  // are all 1. The caller passes in |prefix_mask|, which is 2^prefix_length-1.
+  DecodeStatus StartExtended(uint8_t prefix_mask, DecodeBuffer* db) {
+    DCHECK_LE(15, prefix_mask) << std::hex << prefix_mask;
+    DCHECK_LE(prefix_mask, 127) << std::hex << prefix_mask;
+    // Confirm that |prefix_mask| is a contiguous sequence of bits.
+    DCHECK_EQ(0, prefix_mask & (prefix_mask + 1)) << std::hex << prefix_mask;
+
+    value_ = prefix_mask;
+    offset_ = 0;
+    return Resume(db);
+  }
+
+  // Resume decoding a variable length integer after an earlier
+  // call to Start or StartExtended returned kDecodeInProgress.
+  DecodeStatus Resume(DecodeBuffer* db) {
+    CheckNotDone();
+    do {
+      if (db->Empty()) {
+        return DecodeStatus::kDecodeInProgress;
+      }
+      uint8_t byte = db->DecodeUInt8();
+      value_ += (byte & 0x7f) << offset_;
+      if ((byte & 0x80) == 0) {
+        if (offset_ < MaxOffset() || byte == 0) {
+          MarkDone();
+          return DecodeStatus::kDecodeDone;
+        }
+        break;
+      }
+      offset_ += 7;
+    } while (offset_ <= MaxOffset());
+    DLOG(WARNING) << "Variable length int encoding is too large or too long. "
+                  << DebugString();
+    MarkDone();
+    return DecodeStatus::kDecodeError;
+  }
+
+  uint32_t value() const {
+    CheckDone();
+    return value_;
+  }
+
+  // This supports optimizations for the case of a varint with zero extension
+  // bytes, where the handling of the prefix is done by the caller.
+  void set_value(uint32_t v) {
+    MarkDone();
+    value_ = v;
+  }
+
+  // All the public methods below are for supporting assertions and tests.
+
+  std::string DebugString() const;
+
+  // For benchmarking, these methods ensure the decoder
+  // is NOT inlined into the caller.
+  DecodeStatus StartForTest(uint8_t prefix_value,
+                            uint8_t prefix_mask,
+                            DecodeBuffer* db);
+  DecodeStatus StartExtendedForTest(uint8_t prefix_mask, DecodeBuffer* db);
+  DecodeStatus ResumeForTest(DecodeBuffer* db);
+
+  static constexpr uint32_t MaxExtensionBytes() { return 5; }
+
+  // Returns the highest value with the specified number of extension bytes and
+  // the specified prefix length (bits).
+  static uint64_t constexpr HiValueOfExtensionBytes(uint32_t extension_bytes,
+                                                    uint32_t prefix_length) {
+    return (1 << prefix_length) - 2 +
+           (extension_bytes == 0 ? 0 : (1LLU << (extension_bytes * 7)));
+  }
+
+ private:
+  // Protection in case Resume is called when it shouldn't be.
+  void MarkDone() {
+#ifndef NDEBUG
+    // We support up to 5 extension bytes, so offset_ should never be > 28 when
+    // it makes sense to call Resume().
+    offset_ = MaxOffset() + 7;
+#endif
+  }
+  void CheckNotDone() const {
+#ifndef NDEBUG
+    DCHECK_LE(offset_, MaxOffset());
+#endif
+  }
+  void CheckDone() const {
+#ifndef NDEBUG
+    DCHECK_GT(offset_, MaxOffset());
+#endif
+  }
+  static constexpr uint32_t MaxOffset() {
+    return 7 * (MaxExtensionBytes() - 1);
+  }
+
+  // These fields are initialized just to keep ASAN happy about reading
+  // them from DebugString().
+  uint32_t value_ = 0;
+  uint32_t offset_ = 0;
+};
+
+std::ostream& operator<<(std::ostream& out, const HpackVarintDecoder& v);
+
+}  // namespace net
+
+#endif  // NET_HTTP2_HPACK_DECODER_HPACK_VARINT_DECODER_H_
diff --git a/net/http2/hpack/decoder/hpack_varint_decoder_test.cc b/net/http2/hpack/decoder/hpack_varint_decoder_test.cc
new file mode 100644
index 0000000..e63e9f0
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_varint_decoder_test.cc
@@ -0,0 +1,395 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/decoder/hpack_varint_decoder.h"
+
+// Tests of HpackVarintDecoder.
+
+#include <stddef.h>
+
+#include <ios>
+#include <iterator>
+#include <ostream>
+#include <set>
+#include <sstream>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/stringprintf.h"
+#include "net/http2/hpack/tools/hpack_block_builder.h"
+#include "net/http2/tools/failure.h"
+#include "net/http2/tools/http2_random.h"
+#include "net/http2/tools/random_decoder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::AssertionFailure;
+using ::testing::AssertionSuccess;
+using base::StringPiece;
+using base::StringPrintf;
+using std::string;
+
+namespace net {
+namespace test {
+namespace {
+
+class HpackVarintDecoderTest : public RandomDecoderTest {
+ public:
+  AssertionResult ValidatorForValueTooLarge(bool* validated,
+                                            uint32_t expected_offset,
+                                            const DecodeBuffer& db,
+                                            DecodeStatus status) {
+    *validated = true;
+    VERIFY_EQ(DecodeStatus::kDecodeError, status);
+    VERIFY_EQ(expected_offset, db.Offset());
+    return AssertionSuccess();
+  }
+
+ protected:
+  DecodeStatus StartDecoding(DecodeBuffer* b) override {
+    CHECK_LT(0u, b->Remaining());
+    CHECK_NE(0, prefix_mask_);
+    uint8_t prefix = b->DecodeUInt8();
+    return decoder_.Start(prefix, prefix_mask_, b);
+  }
+
+  DecodeStatus ResumeDecoding(DecodeBuffer* b) override {
+    return decoder_.Resume(b);
+  }
+
+  AssertionResult ValidatorForDecodeSeveralWays(uint32_t expected_value,
+                                                const DecodeBuffer& db,
+                                                DecodeStatus status) {
+    if (decoder_.value() != expected_value) {
+      return AssertionFailure()
+             << "Value doesn't match expected: " << decoder_.value()
+             << " != " << expected_value;
+    }
+    return AssertionSuccess();
+  }
+
+  void DecodeSeveralWays(uint32_t expected_value, uint32_t expected_offset) {
+    // The validator is called after each of the several times that the input
+    // DecodeBuffer is decoded, each with a different segmentation of the input.
+    // Validate that decoder_.value() matches the expected value.
+    Validator validator =
+        base::Bind(&HpackVarintDecoderTest::ValidatorForDecodeSeveralWays,
+                   base::Unretained(this), expected_value);
+
+    // First validate that decoding is done and that we've advanced the cursor
+    // the expected amount.
+    validator = ValidateDoneAndOffset(expected_offset, validator);
+
+    // StartDecoding, above, requires the DecodeBuffer be non-empty so that it
+    // can call Start with the prefix byte.
+    bool return_non_zero_on_first = true;
+
+    DecodeBuffer b(buffer_);
+    EXPECT_TRUE(
+        DecodeAndValidateSeveralWays(&b, return_non_zero_on_first, validator));
+
+    EXPECT_EQ(expected_value, decoder_.value());
+    EXPECT_EQ(expected_offset, b.Offset());
+  }
+
+  void EncodeNoRandom(uint32_t value, uint8_t prefix_length) {
+    DCHECK_LE(4, prefix_length);
+    DCHECK_LE(prefix_length, 7);
+    prefix_length_ = prefix_length;
+
+    HpackBlockBuilder bb;
+    bb.AppendHighBitsAndVarint(0, prefix_length_, value);
+    buffer_ = bb.buffer();
+    ASSERT_LT(0u, buffer_.size());
+
+    // Note: setting member variable prefix_mask_ here, which will be read
+    // in StartDecoding above.
+    prefix_mask_ = (1 << prefix_length_) - 1;
+    ASSERT_EQ(buffer_[0], buffer_[0] & prefix_mask_);
+  }
+
+  void Encode(uint32_t value, uint8_t prefix_length) {
+    EncodeNoRandom(value, prefix_length);
+    // Add some random bits to the prefix (the first byte) above the mask.
+    uint8_t prefix = buffer_[0];
+    buffer_[0] = prefix | (Random().Rand8() << prefix_length);
+    ASSERT_EQ(prefix, buffer_[0] & prefix_mask_);
+  }
+
+  // This is really a test of HpackBlockBuilder, making sure that the input to
+  // HpackVarintDecoder is as expected, which also acts as confirmation that
+  // my thinking about the encodings being used by the tests, i.e. cover the
+  // range desired.
+  void ValidateEncoding(uint32_t value,
+                        uint32_t minimum,
+                        uint32_t maximum,
+                        size_t expected_bytes) {
+    ASSERT_EQ(expected_bytes, buffer_.size());
+    if (expected_bytes > 1) {
+      EXPECT_EQ(prefix_mask_, buffer_[0] & prefix_mask_);
+      size_t last = expected_bytes - 1;
+      for (size_t ndx = 1; ndx < last; ++ndx) {
+        // Before the last extension byte, we expect the high-bit set.
+        uint8_t byte = buffer_[ndx];
+        if (value == minimum) {
+          EXPECT_EQ(0x80, byte) << "ndx=" << ndx;
+        } else if (value == maximum) {
+          EXPECT_EQ(0xff, byte) << "ndx=" << ndx;
+        } else {
+          EXPECT_EQ(0x80, byte & 0x80) << "ndx=" << ndx;
+        }
+      }
+      // The last extension byte should not have the high-bit set.
+      uint8_t byte = buffer_[last];
+      if (value == minimum) {
+        if (expected_bytes == 2) {
+          EXPECT_EQ(0x00, byte);
+        } else {
+          EXPECT_EQ(0x01, byte);
+        }
+      } else if (value == maximum) {
+        EXPECT_EQ(0x7f, byte);
+      } else {
+        EXPECT_EQ(0x00, byte & 0x80);
+      }
+    } else {
+      EXPECT_EQ(value, static_cast<uint32_t>(buffer_[0] & prefix_mask_));
+      EXPECT_LT(value, static_cast<uint32_t>(prefix_mask_));
+    }
+  }
+
+  void EncodeAndDecodeValues(const std::set<uint32_t>& values,
+                             uint8_t prefix_length,
+                             size_t expected_bytes) {
+    CHECK(!values.empty());
+    const uint32_t minimum = *values.begin();
+    const uint32_t maximum = *values.rbegin();
+    for (const uint32_t value : values) {
+      Encode(value, prefix_length);  // Sets prefix_mask_ and buffer_
+
+      std::stringstream ss;
+      ss << "value=" << value << " (0x" << std::hex << value
+         << "), prefix_length=" << std::dec << prefix_length
+         << ", expected_bytes=" << expected_bytes << std::endl
+         << HexEncode(buffer_);
+      string msg(ss.str());
+
+      if (value == minimum) {
+        LOG(INFO) << "Checking minimum; " << msg;
+      } else if (value == maximum) {
+        LOG(INFO) << "Checking maximum; " << msg;
+      }
+
+      SCOPED_TRACE(msg);
+      ValidateEncoding(value, minimum, maximum, expected_bytes);
+      DecodeSeveralWays(value, expected_bytes);
+
+      // Append some random data to the end of buffer_ and repeat. That random
+      // data should be ignored.
+      buffer_.append(Random().RandString(1 + Random().Uniform(10)));
+      DecodeSeveralWays(value, expected_bytes);
+
+      // If possible, add extension bytes that don't change the value.
+      if (1 < expected_bytes) {
+        buffer_.resize(expected_bytes);
+        for (uint8_t total_bytes = expected_bytes + 1; total_bytes <= 6;
+             ++total_bytes) {
+          // Mark the current last byte as not being the last one.
+          EXPECT_EQ(0x00, 0x80 & buffer_.back());
+          buffer_.back() |= 0x80;
+          buffer_.push_back('\0');
+          DecodeSeveralWays(value, total_bytes);
+        }
+      }
+    }
+  }
+
+  void EncodeAndDecodeValuesInRange(uint32_t start,
+                                    uint32_t range,
+                                    uint8_t prefix_length,
+                                    size_t expected_bytes) {
+    const uint8_t prefix_mask = (1 << prefix_length) - 1;
+    const uint32_t beyond = start + range;
+
+    LOG(INFO) << "############################################################";
+    LOG(INFO) << "prefix_length=" << static_cast<int>(prefix_length);
+    LOG(INFO) << "prefix_mask=" << std::hex << static_cast<int>(prefix_mask);
+    LOG(INFO) << "start=" << start << " (" << std::hex << start << ")";
+    LOG(INFO) << "range=" << range << " (" << std::hex << range << ")";
+    LOG(INFO) << "beyond=" << beyond << " (" << std::hex << beyond << ")";
+    LOG(INFO) << "expected_bytes=" << expected_bytes;
+
+    // Confirm the claim that beyond requires more bytes.
+    Encode(beyond, prefix_length);
+    EXPECT_EQ(expected_bytes + 1, buffer_.size()) << HexEncode(buffer_);
+
+    std::set<uint32_t> values;
+    if (range < 200) {
+      // Select all values in the range.
+      for (uint32_t offset = 0; offset < range; ++offset) {
+        values.insert(start + offset);
+      }
+    } else {
+      // Select some values in this range, including the minimum and maximum
+      // values that require exactly |expected_bytes| extension bytes.
+      values.insert({start, start + 1, beyond - 2, beyond - 1});
+      while (values.size() < 100) {
+        values.insert(start + Random().Rand32() % range);
+      }
+    }
+
+    EncodeAndDecodeValues(values, prefix_length, expected_bytes);
+  }
+
+  HpackVarintDecoder decoder_;
+  string buffer_;
+  uint8_t prefix_mask_ = 0;
+  uint8_t prefix_length_ = 0;
+};
+
+// To help me and future debuggers of varint encodings, this LOGs out the
+// transition points where a new extension byte is added.
+TEST_F(HpackVarintDecoderTest, Encode) {
+  for (int prefix_length = 4; prefix_length <= 7; ++prefix_length) {
+    const uint32_t a = (1 << prefix_length) - 1;
+    const uint32_t b = a + 128;
+    const uint32_t c = b + (127 << 7);
+    const uint32_t d = c + (127 << 14);
+    const uint32_t e = d + (127 << 21);
+
+    LOG(INFO) << "############################################################";
+    LOG(INFO) << "prefix_length=" << prefix_length << "   a=" << a
+              << "   b=" << b << "   c=" << c;
+
+    EXPECT_EQ(a - 1,
+              HpackVarintDecoder::HiValueOfExtensionBytes(0, prefix_length));
+    EXPECT_EQ(b - 1,
+              HpackVarintDecoder::HiValueOfExtensionBytes(1, prefix_length));
+    EXPECT_EQ(c - 1,
+              HpackVarintDecoder::HiValueOfExtensionBytes(2, prefix_length));
+    EXPECT_EQ(d - 1,
+              HpackVarintDecoder::HiValueOfExtensionBytes(3, prefix_length));
+    EXPECT_EQ(e - 1,
+              HpackVarintDecoder::HiValueOfExtensionBytes(4, prefix_length));
+
+    std::vector<uint32_t> values = {
+        0,     1,                       // Force line break.
+        a - 2, a - 1, a, a + 1, a + 2,  // Force line break.
+        b - 2, b - 1, b, b + 1, b + 2,  // Force line break.
+        c - 2, c - 1, c, c + 1, c + 2,  // Force line break.
+        d - 2, d - 1, d, d + 1, d + 2,  // Force line break.
+        e - 2, e - 1, e, e + 1, e + 2   // Force line break.
+    };
+
+    for (uint32_t value : values) {
+      EncodeNoRandom(value, prefix_length);
+      string dump = HexEncode(buffer_);
+      LOG(INFO) << StringPrintf("%10u %0#10x ", value, value)
+                << HexEncode(buffer_);
+    }
+  }
+}
+
+TEST_F(HpackVarintDecoderTest, FromSpec1337) {
+  DecodeBuffer b(StringPiece("\x1f\x9a\x0a"));
+  uint32_t prefix_length = 5;
+  uint32_t prefix_mask = (1 << prefix_length) - 1;
+  uint8_t p = b.DecodeUInt8();
+  EXPECT_EQ(1u, b.Offset());
+  EXPECT_EQ(DecodeStatus::kDecodeDone, decoder_.Start(p, prefix_mask, &b));
+  EXPECT_EQ(3u, b.Offset());
+  EXPECT_EQ(1337u, decoder_.value());
+
+  EncodeNoRandom(1337, prefix_length);
+  EXPECT_EQ(3u, buffer_.size());
+  EXPECT_EQ('\x1f', buffer_[0]);
+  EXPECT_EQ('\x9a', buffer_[1]);
+  EXPECT_EQ('\x0a', buffer_[2]);
+}
+
+// Test all the values that fit into the prefix (one less than the mask).
+TEST_F(HpackVarintDecoderTest, ValidatePrefixOnly) {
+  for (int prefix_length = 4; prefix_length <= 7; ++prefix_length) {
+    const uint8_t prefix_mask = (1 << prefix_length) - 1;
+    EncodeAndDecodeValuesInRange(0, prefix_mask, prefix_length, 1);
+  }
+}
+
+// Test all values that require exactly 1 extension byte.
+TEST_F(HpackVarintDecoderTest, ValidateOneExtensionByte) {
+  for (int prefix_length = 4; prefix_length <= 7; ++prefix_length) {
+    const uint32_t start = (1 << prefix_length) - 1;
+    EncodeAndDecodeValuesInRange(start, 128, prefix_length, 2);
+  }
+}
+
+// Test *some* values that require exactly 2 extension bytes.
+TEST_F(HpackVarintDecoderTest, ValidateTwoExtensionBytes) {
+  for (int prefix_length = 4; prefix_length <= 7; ++prefix_length) {
+    const uint8_t prefix_mask = (1 << prefix_length) - 1;
+    const uint32_t start = prefix_mask + 128;
+    const uint32_t range = 127 << 7;
+
+    EncodeAndDecodeValuesInRange(start, range, prefix_length, 3);
+  }
+}
+
+// Test *some* values that require 3 extension bytes.
+TEST_F(HpackVarintDecoderTest, ValidateThreeExtensionBytes) {
+  for (int prefix_length = 4; prefix_length <= 7; ++prefix_length) {
+    const uint8_t prefix_mask = (1 << prefix_length) - 1;
+    const uint32_t start = prefix_mask + 128 + (127 << 7);
+    const uint32_t range = 127 << 14;
+
+    EncodeAndDecodeValuesInRange(start, range, prefix_length, 4);
+  }
+}
+
+// Test *some* values that require 4 extension bytes.
+TEST_F(HpackVarintDecoderTest, ValidateFourExtensionBytes) {
+  for (int prefix_length = 4; prefix_length <= 7; ++prefix_length) {
+    const uint8_t prefix_mask = (1 << prefix_length) - 1;
+    const uint32_t start = prefix_mask + 128 + (127 << 7) + (127 << 14);
+    const uint32_t range = 127 << 21;
+
+    EncodeAndDecodeValuesInRange(start, range, prefix_length, 5);
+  }
+}
+
+// Test *some* values that require too many extension bytes.
+TEST_F(HpackVarintDecoderTest, ValueTooLarge) {
+  const uint32_t expected_offset = HpackVarintDecoder::MaxExtensionBytes() + 1;
+  for (prefix_length_ = 4; prefix_length_ <= 7; ++prefix_length_) {
+    prefix_mask_ = (1 << prefix_length_) - 1;
+    uint64_t too_large = HpackVarintDecoder::HiValueOfExtensionBytes(
+        HpackVarintDecoder::MaxExtensionBytes() + 3, prefix_length_);
+    HpackBlockBuilder bb;
+    bb.AppendHighBitsAndVarint(0, prefix_length_, too_large);
+    buffer_ = bb.buffer();
+
+    // The validator is called after each of the several times that the input
+    // DecodeBuffer is decoded, each with a different segmentation of the input.
+    // Validate that decoder_.value() matches the expected value.
+    bool validated = false;
+    Validator validator =
+        base::Bind(&HpackVarintDecoderTest::ValidatorForValueTooLarge,
+                   base::Unretained(this), &validated, expected_offset);
+
+    // StartDecoding, above, requires the DecodeBuffer be non-empty so that it
+    // can call Start with the prefix byte.
+    bool return_non_zero_on_first = true;
+    DecodeBuffer b(buffer_);
+    EXPECT_TRUE(
+        DecodeAndValidateSeveralWays(&b, return_non_zero_on_first, validator));
+    EXPECT_EQ(expected_offset, b.Offset());
+    EXPECT_TRUE(validated);
+  }
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/hpack/http2_hpack_constants.cc b/net/http2/hpack/http2_hpack_constants.cc
new file mode 100644
index 0000000..bc0ee9e
--- /dev/null
+++ b/net/http2/hpack/http2_hpack_constants.cc
@@ -0,0 +1,33 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/http2_hpack_constants.h"
+
+#include <sstream>
+
+namespace net {
+
+std::string HpackEntryTypeToString(HpackEntryType v) {
+  switch (v) {
+    case HpackEntryType::kIndexedHeader:
+      return "kIndexedHeader";
+    case HpackEntryType::kDynamicTableSizeUpdate:
+      return "kDynamicTableSizeUpdate";
+    case HpackEntryType::kIndexedLiteralHeader:
+      return "kIndexedLiteralHeader";
+    case HpackEntryType::kUnindexedLiteralHeader:
+      return "kUnindexedLiteralHeader";
+    case HpackEntryType::kNeverIndexedLiteralHeader:
+      return "kNeverIndexedLiteralHeader";
+  }
+  std::stringstream ss;
+  ss << "UnknownHpackEntryType(" << static_cast<int>(v) << ")";
+  return ss.str();
+}
+
+std::ostream& operator<<(std::ostream& out, HpackEntryType v) {
+  return out << HpackEntryTypeToString(v);
+}
+
+}  // namespace net
diff --git a/net/http2/hpack/http2_hpack_constants.h b/net/http2/hpack/http2_hpack_constants.h
new file mode 100644
index 0000000..b9883a0
--- /dev/null
+++ b/net/http2/hpack/http2_hpack_constants.h
@@ -0,0 +1,61 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_HPACK_HTTP2_HPACK_CONSTANTS_H_
+#define NET_HTTP2_HPACK_HTTP2_HPACK_CONSTANTS_H_
+
+// Enum HpackEntryType identifies the 5 basic types of HPACK Block Entries.
+//
+// See the spec for details:
+// https://http2.github.io/http2-spec/compression.html#rfc.section.6
+
+#include <ostream>
+#include <string>
+
+#include "net/base/net_export.h"
+
+namespace net {
+
+enum class HpackEntryType {
+  // Entry is an index into the static or dynamic table. Decoding it has no
+  // effect on the dynamic table.
+  kIndexedHeader,
+
+  // The entry contains a literal value. The name may be either a literal or a
+  // reference to an entry in the static or dynamic table.
+  // The entry is added to the dynamic table after decoding.
+  kIndexedLiteralHeader,
+
+  // The entry contains a literal value. The name may be either a literal or a
+  // reference to an entry in the static or dynamic table.
+  // The entry is not added to the dynamic table after decoding, but a proxy
+  // may choose to insert the entry into its dynamic table when forwarding
+  // to another endpoint.
+  kUnindexedLiteralHeader,
+
+  // The entry contains a literal value. The name may be either a literal or a
+  // reference to an entry in the static or dynamic table.
+  // The entry is not added to the dynamic table after decoding, and a proxy
+  // must NOT insert the entry into its dynamic table when forwarding to another
+  // endpoint.
+  kNeverIndexedLiteralHeader,
+
+  // Entry conveys the size limit of the dynamic table of the encoder to
+  // the decoder. May be used to flush the table by sending a zero and then
+  // resetting the size back up to the maximum that the encoder will use
+  // (within the limits of SETTINGS_HEADER_TABLE_SIZE sent by the
+  // decoder to the encoder, with the default of 4096 assumed).
+  kDynamicTableSizeUpdate,
+};
+
+// Returns the name of the enum member.
+NET_EXPORT_PRIVATE std::string HpackEntryTypeToString(HpackEntryType v);
+
+// Inserts the name of the enum member into |out|.
+NET_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& out,
+                                            HpackEntryType v);
+
+}  // namespace net
+
+#endif  // NET_HTTP2_HPACK_HTTP2_HPACK_CONSTANTS_H_
diff --git a/net/http2/hpack/http2_hpack_constants_test.cc b/net/http2/hpack/http2_hpack_constants_test.cc
new file mode 100644
index 0000000..217b64d
--- /dev/null
+++ b/net/http2/hpack/http2_hpack_constants_test.cc
@@ -0,0 +1,34 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/http2_hpack_constants.h"
+
+#include "base/logging.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+namespace {
+
+class HpackEntryTypeTest : public testing::Test {};
+
+TEST(HpackEntryTypeTest, HpackEntryTypeToString) {
+  EXPECT_EQ("kIndexedHeader",
+            HpackEntryTypeToString(HpackEntryType::kIndexedHeader));
+  EXPECT_EQ("kDynamicTableSizeUpdate",
+            HpackEntryTypeToString(HpackEntryType::kDynamicTableSizeUpdate));
+  EXPECT_EQ("kIndexedLiteralHeader",
+            HpackEntryTypeToString(HpackEntryType::kIndexedLiteralHeader));
+  EXPECT_EQ("kUnindexedLiteralHeader",
+            HpackEntryTypeToString(HpackEntryType::kUnindexedLiteralHeader));
+  EXPECT_EQ("kNeverIndexedLiteralHeader",
+            HpackEntryTypeToString(HpackEntryType::kNeverIndexedLiteralHeader));
+  EXPECT_EQ("UnknownHpackEntryType(12321)",
+            HpackEntryTypeToString(static_cast<HpackEntryType>(12321)));
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/hpack/huffman/http2_hpack_huffman_decoder.cc b/net/http2/hpack/huffman/http2_hpack_huffman_decoder.cc
new file mode 100644
index 0000000..45c32c08
--- /dev/null
+++ b/net/http2/hpack/huffman/http2_hpack_huffman_decoder.cc
@@ -0,0 +1,542 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/huffman/http2_hpack_huffman_decoder.h"
+
+#include <bitset>
+#include <limits>
+
+#include "base/logging.h"
+
+using base::StringPiece;
+using std::string;
+
+// Terminology:
+//
+// Symbol - a plain text (unencoded) character (uint8), or the End-of-String
+//          (EOS) symbol, 256.
+//
+// Code - the sequence of bits used to encode a symbol, varying in length from
+//        5 bits for the most common symbols (e.g. '0', '1', and 'a'), to
+//        30 bits for the least common (e.g. the EOS symbol).
+//        For those symbols whose codes have the same length, their code values
+//        are sorted such that the lower symbol value has a lower code value.
+//
+// Canonical - a symbol's cardinal value when sorted first by code length, and
+//             then by symbol value. For example, canonical 0 is for ASCII '0'
+//             (uint8 value 0x30), which is the first of the symbols whose code
+//             is 5 bits long, and the last canonical is EOS, which is the last
+//             of the symbols whose code is 30 bits long.
+
+// TODO(jamessynge): Remove use of binary literals, that is a C++ 14 feature.
+
+namespace net {
+namespace {
+
+// HuffmanCode is used to store the codes associated with symbols (a pattern of
+// from 5 to 30 bits).
+typedef uint32_t HuffmanCode;
+
+// HuffmanCodeBitCount is used to store a count of bits in a code.
+typedef uint16_t HuffmanCodeBitCount;
+
+// HuffmanCodeBitSet is used for producing a string version of a code because
+// std::bitset logs nicely.
+typedef std::bitset<32> HuffmanCodeBitSet;
+typedef std::bitset<64> HuffmanAccumulatorBitSet;
+
+static constexpr HuffmanCodeBitCount kMinCodeBitCount = 5;
+static constexpr HuffmanCodeBitCount kMaxCodeBitCount = 30;
+static constexpr HuffmanCodeBitCount kHuffmanCodeBitCount =
+    std::numeric_limits<HuffmanCode>::digits;
+
+static_assert(std::numeric_limits<HuffmanCode>::digits >= kMaxCodeBitCount,
+              "HuffmanCode isn't big enough.");
+
+static_assert(std::numeric_limits<HuffmanAccumulator>::digits >=
+                  kMaxCodeBitCount,
+              "HuffmanAccumulator isn't big enough.");
+
+static constexpr HuffmanAccumulatorBitCount kHuffmanAccumulatorBitCount =
+    std::numeric_limits<HuffmanAccumulator>::digits;
+static constexpr HuffmanAccumulatorBitCount kExtraAccumulatorBitCount =
+    kHuffmanAccumulatorBitCount - kHuffmanCodeBitCount;
+
+// PrefixInfo holds info about a group of codes that are all of the same length.
+struct PrefixInfo {
+  // Given the leading bits (32 in this case) of the encoded string, and that
+  // they start with a code of length |code_length|, return the corresponding
+  // canonical for that leading code.
+  uint32_t DecodeToCanonical(HuffmanCode bits) const {
+    // What is the position of the canonical symbol being decoded within
+    // the canonical symbols of |length|?
+    HuffmanCode ordinal_in_length =
+        ((bits - first_code) >> (kHuffmanCodeBitCount - code_length));
+
+    // Combined with |canonical| to produce the position of the canonical symbol
+    // being decoded within all of the canonical symbols.
+    return first_canonical + ordinal_in_length;
+  }
+
+  const HuffmanCode first_code;  // First code of this length, left justified in
+                                 // the field (i.e. the first bit of the code is
+                                 // the high-order bit).
+  const uint16_t code_length;    // Length of the prefix code |base|.
+  const uint16_t first_canonical;  // First canonical symbol of this length.
+};
+
+inline std::ostream& operator<<(std::ostream& out, const PrefixInfo& v) {
+  return out << "{first_code: " << HuffmanCodeBitSet(v.first_code)
+             << ", code_length: " << v.code_length
+             << ", first_canonical: " << v.first_canonical << "}";
+}
+
+// Given |value|, a sequence of the leading bits remaining to be decoded,
+// figure out which group of canonicals (by code length) that value starts
+// with. This function was generated.
+PrefixInfo PrefixToInfo(HuffmanCode value) {
+  if (value < 0b10111000000000000000000000000000) {
+    if (value < 0b01010000000000000000000000000000) {
+      return {0b00000000000000000000000000000000, 5, 0};
+    } else {
+      return {0b01010000000000000000000000000000, 6, 10};
+    }
+  } else {
+    if (value < 0b11111110000000000000000000000000) {
+      if (value < 0b11111000000000000000000000000000) {
+        return {0b10111000000000000000000000000000, 7, 36};
+      } else {
+        return {0b11111000000000000000000000000000, 8, 68};
+      }
+    } else {
+      if (value < 0b11111111110000000000000000000000) {
+        if (value < 0b11111111101000000000000000000000) {
+          if (value < 0b11111111010000000000000000000000) {
+            return {0b11111110000000000000000000000000, 10, 74};
+          } else {
+            return {0b11111111010000000000000000000000, 11, 79};
+          }
+        } else {
+          return {0b11111111101000000000000000000000, 12, 82};
+        }
+      } else {
+        if (value < 0b11111111111111100000000000000000) {
+          if (value < 0b11111111111110000000000000000000) {
+            if (value < 0b11111111111100000000000000000000) {
+              return {0b11111111110000000000000000000000, 13, 84};
+            } else {
+              return {0b11111111111100000000000000000000, 14, 90};
+            }
+          } else {
+            return {0b11111111111110000000000000000000, 15, 92};
+          }
+        } else {
+          if (value < 0b11111111111111110100100000000000) {
+            if (value < 0b11111111111111101110000000000000) {
+              if (value < 0b11111111111111100110000000000000) {
+                return {0b11111111111111100000000000000000, 19, 95};
+              } else {
+                return {0b11111111111111100110000000000000, 20, 98};
+              }
+            } else {
+              return {0b11111111111111101110000000000000, 21, 106};
+            }
+          } else {
+            if (value < 0b11111111111111111110101000000000) {
+              if (value < 0b11111111111111111011000000000000) {
+                return {0b11111111111111110100100000000000, 22, 119};
+              } else {
+                return {0b11111111111111111011000000000000, 23, 145};
+              }
+            } else {
+              if (value < 0b11111111111111111111101111000000) {
+                if (value < 0b11111111111111111111100000000000) {
+                  if (value < 0b11111111111111111111011000000000) {
+                    return {0b11111111111111111110101000000000, 24, 174};
+                  } else {
+                    return {0b11111111111111111111011000000000, 25, 186};
+                  }
+                } else {
+                  return {0b11111111111111111111100000000000, 26, 190};
+                }
+              } else {
+                if (value < 0b11111111111111111111111111110000) {
+                  if (value < 0b11111111111111111111111000100000) {
+                    return {0b11111111111111111111101111000000, 27, 205};
+                  } else {
+                    return {0b11111111111111111111111000100000, 28, 224};
+                  }
+                } else {
+                  return {0b11111111111111111111111111110000, 30, 253};
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+// Mapping from canonical symbol (0 to 255) to actual symbol.
+// clang-format off
+constexpr unsigned char kCanonicalToSymbol[] = {
+    '0',  '1',  '2',  'a',  'c',  'e',  'i',  'o',
+    's',  't',  0x20, '%',  '-',  '.',  '/',  '3',
+    '4',  '5',  '6',  '7',  '8',  '9',  '=',  'A',
+    '_',  'b',  'd',  'f',  'g',  'h',  'l',  'm',
+    'n',  'p',  'r',  'u',  ':',  'B',  'C',  'D',
+    'E',  'F',  'G',  'H',  'I',  'J',  'K',  'L',
+    'M',  'N',  'O',  'P',  'Q',  'R',  'S',  'T',
+    'U',  'V',  'W',  'Y',  'j',  'k',  'q',  'v',
+    'w',  'x',  'y',  'z',  '&',  '*',  ',',  ';',
+    'X',  'Z',  '!',  '\"', '(',  ')',  '?',  '\'',
+    '+',  '|',  '#',  '>',  0x00, '$',  '@',  '[',
+    ']',  '~',  '^',  '}',  '<',  '`',  '{',  '\\',
+    0xc3, 0xd0, 0x80, 0x82, 0x83, 0xa2, 0xb8, 0xc2,
+    0xe0, 0xe2, 0x99, 0xa1, 0xa7, 0xac, 0xb0, 0xb1,
+    0xb3, 0xd1, 0xd8, 0xd9, 0xe3, 0xe5, 0xe6, 0x81,
+    0x84, 0x85, 0x86, 0x88, 0x92, 0x9a, 0x9c, 0xa0,
+    0xa3, 0xa4, 0xa9, 0xaa, 0xad, 0xb2, 0xb5, 0xb9,
+    0xba, 0xbb, 0xbd, 0xbe, 0xc4, 0xc6, 0xe4, 0xe8,
+    0xe9, 0x01, 0x87, 0x89, 0x8a, 0x8b, 0x8c, 0x8d,
+    0x8f, 0x93, 0x95, 0x96, 0x97, 0x98, 0x9b, 0x9d,
+    0x9e, 0xa5, 0xa6, 0xa8, 0xae, 0xaf, 0xb4, 0xb6,
+    0xb7, 0xbc, 0xbf, 0xc5, 0xe7, 0xef, 0x09, 0x8e,
+    0x90, 0x91, 0x94, 0x9f, 0xab, 0xce, 0xd7, 0xe1,
+    0xec, 0xed, 0xc7, 0xcf, 0xea, 0xeb, 0xc0, 0xc1,
+    0xc8, 0xc9, 0xca, 0xcd, 0xd2, 0xd5, 0xda, 0xdb,
+    0xee, 0xf0, 0xf2, 0xf3, 0xff, 0xcb, 0xcc, 0xd3,
+    0xd4, 0xd6, 0xdd, 0xde, 0xdf, 0xf1, 0xf4, 0xf5,
+    0xf6, 0xf7, 0xf8, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe,
+    0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x0b,
+    0x0c, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
+    0x15, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d,
+    0x1e, 0x1f, 0x7f, 0xdc, 0xf9, 0x0a, 0x0d, 0x16,
+};
+// clang-format on
+
+constexpr size_t kShortCodeTableSize = 124;
+struct ShortCodeInfo {
+  uint8_t symbol;
+  uint8_t length;
+} kShortCodeTable[kShortCodeTableSize] = {
+    {0x30, 5},  // Match: 0b0000000, Symbol: 0
+    {0x30, 5},  // Match: 0b0000001, Symbol: 0
+    {0x30, 5},  // Match: 0b0000010, Symbol: 0
+    {0x30, 5},  // Match: 0b0000011, Symbol: 0
+    {0x31, 5},  // Match: 0b0000100, Symbol: 1
+    {0x31, 5},  // Match: 0b0000101, Symbol: 1
+    {0x31, 5},  // Match: 0b0000110, Symbol: 1
+    {0x31, 5},  // Match: 0b0000111, Symbol: 1
+    {0x32, 5},  // Match: 0b0001000, Symbol: 2
+    {0x32, 5},  // Match: 0b0001001, Symbol: 2
+    {0x32, 5},  // Match: 0b0001010, Symbol: 2
+    {0x32, 5},  // Match: 0b0001011, Symbol: 2
+    {0x61, 5},  // Match: 0b0001100, Symbol: a
+    {0x61, 5},  // Match: 0b0001101, Symbol: a
+    {0x61, 5},  // Match: 0b0001110, Symbol: a
+    {0x61, 5},  // Match: 0b0001111, Symbol: a
+    {0x63, 5},  // Match: 0b0010000, Symbol: c
+    {0x63, 5},  // Match: 0b0010001, Symbol: c
+    {0x63, 5},  // Match: 0b0010010, Symbol: c
+    {0x63, 5},  // Match: 0b0010011, Symbol: c
+    {0x65, 5},  // Match: 0b0010100, Symbol: e
+    {0x65, 5},  // Match: 0b0010101, Symbol: e
+    {0x65, 5},  // Match: 0b0010110, Symbol: e
+    {0x65, 5},  // Match: 0b0010111, Symbol: e
+    {0x69, 5},  // Match: 0b0011000, Symbol: i
+    {0x69, 5},  // Match: 0b0011001, Symbol: i
+    {0x69, 5},  // Match: 0b0011010, Symbol: i
+    {0x69, 5},  // Match: 0b0011011, Symbol: i
+    {0x6f, 5},  // Match: 0b0011100, Symbol: o
+    {0x6f, 5},  // Match: 0b0011101, Symbol: o
+    {0x6f, 5},  // Match: 0b0011110, Symbol: o
+    {0x6f, 5},  // Match: 0b0011111, Symbol: o
+    {0x73, 5},  // Match: 0b0100000, Symbol: s
+    {0x73, 5},  // Match: 0b0100001, Symbol: s
+    {0x73, 5},  // Match: 0b0100010, Symbol: s
+    {0x73, 5},  // Match: 0b0100011, Symbol: s
+    {0x74, 5},  // Match: 0b0100100, Symbol: t
+    {0x74, 5},  // Match: 0b0100101, Symbol: t
+    {0x74, 5},  // Match: 0b0100110, Symbol: t
+    {0x74, 5},  // Match: 0b0100111, Symbol: t
+    {0x20, 6},  // Match: 0b0101000, Symbol: (space)
+    {0x20, 6},  // Match: 0b0101001, Symbol: (space)
+    {0x25, 6},  // Match: 0b0101010, Symbol: %
+    {0x25, 6},  // Match: 0b0101011, Symbol: %
+    {0x2d, 6},  // Match: 0b0101100, Symbol: -
+    {0x2d, 6},  // Match: 0b0101101, Symbol: -
+    {0x2e, 6},  // Match: 0b0101110, Symbol: .
+    {0x2e, 6},  // Match: 0b0101111, Symbol: .
+    {0x2f, 6},  // Match: 0b0110000, Symbol: /
+    {0x2f, 6},  // Match: 0b0110001, Symbol: /
+    {0x33, 6},  // Match: 0b0110010, Symbol: 3
+    {0x33, 6},  // Match: 0b0110011, Symbol: 3
+    {0x34, 6},  // Match: 0b0110100, Symbol: 4
+    {0x34, 6},  // Match: 0b0110101, Symbol: 4
+    {0x35, 6},  // Match: 0b0110110, Symbol: 5
+    {0x35, 6},  // Match: 0b0110111, Symbol: 5
+    {0x36, 6},  // Match: 0b0111000, Symbol: 6
+    {0x36, 6},  // Match: 0b0111001, Symbol: 6
+    {0x37, 6},  // Match: 0b0111010, Symbol: 7
+    {0x37, 6},  // Match: 0b0111011, Symbol: 7
+    {0x38, 6},  // Match: 0b0111100, Symbol: 8
+    {0x38, 6},  // Match: 0b0111101, Symbol: 8
+    {0x39, 6},  // Match: 0b0111110, Symbol: 9
+    {0x39, 6},  // Match: 0b0111111, Symbol: 9
+    {0x3d, 6},  // Match: 0b1000000, Symbol: =
+    {0x3d, 6},  // Match: 0b1000001, Symbol: =
+    {0x41, 6},  // Match: 0b1000010, Symbol: A
+    {0x41, 6},  // Match: 0b1000011, Symbol: A
+    {0x5f, 6},  // Match: 0b1000100, Symbol: _
+    {0x5f, 6},  // Match: 0b1000101, Symbol: _
+    {0x62, 6},  // Match: 0b1000110, Symbol: b
+    {0x62, 6},  // Match: 0b1000111, Symbol: b
+    {0x64, 6},  // Match: 0b1001000, Symbol: d
+    {0x64, 6},  // Match: 0b1001001, Symbol: d
+    {0x66, 6},  // Match: 0b1001010, Symbol: f
+    {0x66, 6},  // Match: 0b1001011, Symbol: f
+    {0x67, 6},  // Match: 0b1001100, Symbol: g
+    {0x67, 6},  // Match: 0b1001101, Symbol: g
+    {0x68, 6},  // Match: 0b1001110, Symbol: h
+    {0x68, 6},  // Match: 0b1001111, Symbol: h
+    {0x6c, 6},  // Match: 0b1010000, Symbol: l
+    {0x6c, 6},  // Match: 0b1010001, Symbol: l
+    {0x6d, 6},  // Match: 0b1010010, Symbol: m
+    {0x6d, 6},  // Match: 0b1010011, Symbol: m
+    {0x6e, 6},  // Match: 0b1010100, Symbol: n
+    {0x6e, 6},  // Match: 0b1010101, Symbol: n
+    {0x70, 6},  // Match: 0b1010110, Symbol: p
+    {0x70, 6},  // Match: 0b1010111, Symbol: p
+    {0x72, 6},  // Match: 0b1011000, Symbol: r
+    {0x72, 6},  // Match: 0b1011001, Symbol: r
+    {0x75, 6},  // Match: 0b1011010, Symbol: u
+    {0x75, 6},  // Match: 0b1011011, Symbol: u
+    {0x3a, 7},  // Match: 0b1011100, Symbol: :
+    {0x42, 7},  // Match: 0b1011101, Symbol: B
+    {0x43, 7},  // Match: 0b1011110, Symbol: C
+    {0x44, 7},  // Match: 0b1011111, Symbol: D
+    {0x45, 7},  // Match: 0b1100000, Symbol: E
+    {0x46, 7},  // Match: 0b1100001, Symbol: F
+    {0x47, 7},  // Match: 0b1100010, Symbol: G
+    {0x48, 7},  // Match: 0b1100011, Symbol: H
+    {0x49, 7},  // Match: 0b1100100, Symbol: I
+    {0x4a, 7},  // Match: 0b1100101, Symbol: J
+    {0x4b, 7},  // Match: 0b1100110, Symbol: K
+    {0x4c, 7},  // Match: 0b1100111, Symbol: L
+    {0x4d, 7},  // Match: 0b1101000, Symbol: M
+    {0x4e, 7},  // Match: 0b1101001, Symbol: N
+    {0x4f, 7},  // Match: 0b1101010, Symbol: O
+    {0x50, 7},  // Match: 0b1101011, Symbol: P
+    {0x51, 7},  // Match: 0b1101100, Symbol: Q
+    {0x52, 7},  // Match: 0b1101101, Symbol: R
+    {0x53, 7},  // Match: 0b1101110, Symbol: S
+    {0x54, 7},  // Match: 0b1101111, Symbol: T
+    {0x55, 7},  // Match: 0b1110000, Symbol: U
+    {0x56, 7},  // Match: 0b1110001, Symbol: V
+    {0x57, 7},  // Match: 0b1110010, Symbol: W
+    {0x59, 7},  // Match: 0b1110011, Symbol: Y
+    {0x6a, 7},  // Match: 0b1110100, Symbol: j
+    {0x6b, 7},  // Match: 0b1110101, Symbol: k
+    {0x71, 7},  // Match: 0b1110110, Symbol: q
+    {0x76, 7},  // Match: 0b1110111, Symbol: v
+    {0x77, 7},  // Match: 0b1111000, Symbol: w
+    {0x78, 7},  // Match: 0b1111001, Symbol: x
+    {0x79, 7},  // Match: 0b1111010, Symbol: y
+    {0x7a, 7},  // Match: 0b1111011, Symbol: z
+};
+
+}  // namespace
+
+HuffmanBitBuffer::HuffmanBitBuffer() {
+  Reset();
+}
+
+void HuffmanBitBuffer::Reset() {
+  accumulator_ = 0;
+  count_ = 0;
+}
+
+size_t HuffmanBitBuffer::AppendBytes(StringPiece input) {
+  HuffmanAccumulatorBitCount free_cnt = free_count();
+  size_t bytes_available = input.size();
+  if (free_cnt < 8 || bytes_available == 0) {
+    return 0;
+  }
+
+  // Top up |accumulator_| until there isn't room for a whole byte.
+  size_t bytes_used = 0;
+  auto ptr = reinterpret_cast<const uint8_t*>(input.data());
+  do {
+    auto b = static_cast<HuffmanAccumulator>(*ptr++);
+    free_cnt -= 8;
+    accumulator_ |= (b << free_cnt);
+    ++bytes_used;
+  } while (free_cnt >= 8 && bytes_used < bytes_available);
+  count_ += (bytes_used * 8);
+  return bytes_used;
+}
+
+HuffmanAccumulatorBitCount HuffmanBitBuffer::free_count() const {
+  return kHuffmanAccumulatorBitCount - count_;
+}
+
+void HuffmanBitBuffer::ConsumeBits(HuffmanAccumulatorBitCount code_length) {
+  DCHECK_LE(code_length, count_);
+  accumulator_ <<= code_length;
+  count_ -= code_length;
+}
+
+bool HuffmanBitBuffer::InputProperlyTerminated() const {
+  auto cnt = count();
+  if (cnt < 8) {
+    if (cnt == 0) {
+      return true;
+    }
+    HuffmanAccumulator expected = ~(~HuffmanAccumulator() >> cnt);
+    // We expect all the bits below the high order |cnt| bits of accumulator_
+    // to be cleared as we perform left shift operations while decoding.
+    DCHECK_EQ(accumulator_ & ~expected, 0u)
+        << "\n  expected: " << HuffmanAccumulatorBitSet(expected) << "\n  "
+        << *this;
+    return accumulator_ == expected;
+  }
+  return false;
+}
+
+string HuffmanBitBuffer::DebugString() const {
+  std::stringstream ss;
+  ss << "{accumulator: " << HuffmanAccumulatorBitSet(accumulator_)
+     << "; count: " << count_ << "}";
+  return ss.str();
+}
+
+HpackHuffmanDecoder::HpackHuffmanDecoder() {}
+
+HpackHuffmanDecoder::~HpackHuffmanDecoder() {}
+
+bool HpackHuffmanDecoder::Decode(StringPiece input, string* output) {
+  return DecodeShortCodesFirst(input, output);
+}
+
+// "Legacy" decoder, used until cl/129771019 submitted, which added
+// DecodeShortCodesFirst() as primary decoder method.
+// TODO(jamessynge): Remove this once satisfied that there is no going back.
+bool HpackHuffmanDecoder::DecodeWithIfTreeAndStruct(StringPiece input,
+                                                    string* output) {
+  DVLOG(1) << "HpackHuffmanDecoder::DecodeWithIfTreeAndStruct";
+
+  // Fill bit_buffer_ from input.
+  input.remove_prefix(bit_buffer_.AppendBytes(input));
+
+  while (true) {
+    DVLOG(3) << "Enter Decode Loop, bit_buffer_: " << bit_buffer_;
+
+    HuffmanCode code_prefix = bit_buffer_.value() >> kExtraAccumulatorBitCount;
+    DVLOG(3) << "code_prefix: " << HuffmanCodeBitSet(code_prefix);
+
+    PrefixInfo prefix_info = PrefixToInfo(code_prefix);
+    DVLOG(3) << "prefix_info: " << prefix_info;
+    DCHECK_LE(kMinCodeBitCount, prefix_info.code_length);
+    DCHECK_LE(prefix_info.code_length, kMaxCodeBitCount);
+
+    if (prefix_info.code_length <= bit_buffer_.count()) {
+      // We have enough bits for one code.
+      uint32_t canonical = prefix_info.DecodeToCanonical(code_prefix);
+      if (canonical < 256) {
+        // Valid code.
+        char c = kCanonicalToSymbol[canonical];
+        output->push_back(c);
+        bit_buffer_.ConsumeBits(prefix_info.code_length);
+        continue;
+      }
+      // Encoder is not supposed to explicity encode the EOS symbol.
+      DLOG(ERROR) << "EOS explicitly encoded!\n " << bit_buffer_ << "\n "
+                  << prefix_info;
+      return false;
+    }
+    // bit_buffer_ doesn't have enough bits in it to decode the next symbol.
+    // Append to it as many bytes as are available AND fit.
+    size_t byte_count = bit_buffer_.AppendBytes(input);
+    if (byte_count == 0) {
+      DCHECK_EQ(input.size(), 0u);
+      return true;
+    }
+    input.remove_prefix(byte_count);
+  }
+}
+
+bool HpackHuffmanDecoder::DecodeShortCodesFirst(StringPiece input,
+                                                string* output) {
+  DVLOG(1) << "HpackHuffmanDecoder::DecodeShortCodesFirst";
+
+  // Fill bit_buffer_ from input.
+  input.remove_prefix(bit_buffer_.AppendBytes(input));
+
+  while (true) {
+    DVLOG(3) << "Enter Decode Loop, bit_buffer_: " << bit_buffer_;
+    if (bit_buffer_.count() >= 7) {
+      // Get high 7 bits of the bit buffer, see if that contains a complete
+      // code of 5, 6 or 7 bits.
+      uint8_t short_code =
+          bit_buffer_.value() >> (kHuffmanAccumulatorBitCount - 7);
+      DCHECK_LT(short_code, 128);
+      if (short_code < kShortCodeTableSize) {
+        ShortCodeInfo info = kShortCodeTable[short_code];
+        bit_buffer_.ConsumeBits(info.length);
+        output->push_back(static_cast<char>(info.symbol));
+        continue;
+      }
+      // The code is more than 7 bits long. Use PrefixToInfo, etc. to decode
+      // longer codes.
+    } else {
+      // We may have (mostly) drained bit_buffer_. If we can top it up, try
+      // using the table decoder above.
+      size_t byte_count = bit_buffer_.AppendBytes(input);
+      if (byte_count > 0) {
+        input.remove_prefix(byte_count);
+        continue;
+      }
+    }
+
+    HuffmanCode code_prefix = bit_buffer_.value() >> kExtraAccumulatorBitCount;
+    DVLOG(3) << "code_prefix: " << HuffmanCodeBitSet(code_prefix);
+
+    PrefixInfo prefix_info = PrefixToInfo(code_prefix);
+    DVLOG(3) << "prefix_info: " << prefix_info;
+    DCHECK_LE(kMinCodeBitCount, prefix_info.code_length);
+    DCHECK_LE(prefix_info.code_length, kMaxCodeBitCount);
+
+    if (prefix_info.code_length <= bit_buffer_.count()) {
+      // We have enough bits for one code.
+      uint32_t canonical = prefix_info.DecodeToCanonical(code_prefix);
+      if (canonical < 256) {
+        // Valid code.
+        char c = kCanonicalToSymbol[canonical];
+        output->push_back(c);
+        bit_buffer_.ConsumeBits(prefix_info.code_length);
+        continue;
+      }
+      // Encoder is not supposed to explicity encode the EOS symbol.
+      DLOG(ERROR) << "EOS explicitly encoded!\n " << bit_buffer_ << "\n "
+                  << prefix_info;
+      return false;
+    }
+    // bit_buffer_ doesn't have enough bits in it to decode the next symbol.
+    // Append to it as many bytes as are available AND fit.
+    size_t byte_count = bit_buffer_.AppendBytes(input);
+    if (byte_count == 0) {
+      DCHECK_EQ(input.size(), 0u);
+      return true;
+    }
+    input.remove_prefix(byte_count);
+  }
+}
+
+string HpackHuffmanDecoder::DebugString() const {
+  return bit_buffer_.DebugString();
+}
+
+}  // namespace net
diff --git a/net/http2/hpack/huffman/http2_hpack_huffman_decoder.h b/net/http2/hpack/huffman/http2_hpack_huffman_decoder.h
new file mode 100644
index 0000000..7b8edd10
--- /dev/null
+++ b/net/http2/hpack/huffman/http2_hpack_huffman_decoder.h
@@ -0,0 +1,149 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_HPACK_HUFFMAN_HTTP2_HPACK_HUFFMAN_DECODER_H_
+#define NET_HTTP2_HPACK_HUFFMAN_HTTP2_HPACK_HUFFMAN_DECODER_H_
+
+// HpackHuffmanDecoder is an incremental decoder of strings that have been
+// encoded using the Huffman table defined in the HPACK spec.
+// By incremental, we mean that the HpackHuffmanDecoder::Decode method does
+// not require the entire string to be provided, and can instead decode the
+// string as fragments of it become available (e.g. as HPACK block fragments
+// are received for decoding by HpackEntryDecoder).
+
+#include <stddef.h>
+
+#include <iosfwd>
+#include <string>
+
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+
+namespace net {
+
+// HuffmanAccumulator is used to store bits during decoding, e.g. next N bits
+// that have not yet been decoded, but have been extracted from the encoded
+// string).  An advantage of using a uint64 for the accumulator
+// is that it has room for the bits of the longest code plus the bits of a full
+// byte; that means that when adding more bits to the accumulator, it can always
+// be done in whole bytes. For example, if we currently have 26 bits in the
+// accumulator, and need more to decode the current symbol, we can add a whole
+// byte to the accumulator, and not have to do juggling with adding 6 bits (to
+// reach 30), and then keep track of the last two bits we've not been able to
+// add to the accumulator.
+typedef uint64_t HuffmanAccumulator;
+typedef size_t HuffmanAccumulatorBitCount;
+
+// HuffmanBitBuffer stores the leading edge of bits to be decoded. The high
+// order bit of accumulator_ is the next bit to be decoded.
+class NET_EXPORT_PRIVATE HuffmanBitBuffer {
+ public:
+  HuffmanBitBuffer();
+
+  // Prepare for decoding a new Huffman encoded string.
+  void Reset();
+
+  // Add as many whole bytes to the accumulator (accumulator_) as possible,
+  // returning the number of bytes added.
+  size_t AppendBytes(base::StringPiece input);
+
+  // Get the bits of the accumulator.
+  HuffmanAccumulator value() const { return accumulator_; }
+
+  // Number of bits of the encoded string that are in the accumulator
+  // (accumulator_).
+  HuffmanAccumulatorBitCount count() const { return count_; }
+
+  // Are there no bits in the accumulator?
+  bool IsEmpty() const { return count_ == 0; }
+
+  // Number of additional bits that can be added to the accumulator.
+  HuffmanAccumulatorBitCount free_count() const;
+
+  // Consume the leading |code_length| bits of the accumulator.
+  void ConsumeBits(HuffmanAccumulatorBitCount code_length);
+
+  // Are the contents valid for the end of a Huffman encoded string? The RFC
+  // states that EOS (end-of-string) symbol must not be explicitly encoded in
+  // the bit stream, but any unused bits in the final byte must be set to the
+  // prefix of the EOS symbol, which is all 1 bits. So there can be at most 7
+  // such bits.
+  // Returns true if the bit buffer is empty, or contains at most 7 bits, all
+  // of them 1. Otherwise returns false.
+  bool InputProperlyTerminated() const;
+
+  std::string DebugString() const;
+
+ private:
+  HuffmanAccumulator accumulator_;
+  HuffmanAccumulatorBitCount count_;
+};
+
+inline std::ostream& operator<<(std::ostream& out, const HuffmanBitBuffer& v) {
+  return out << v.DebugString();
+}
+
+class NET_EXPORT_PRIVATE HpackHuffmanDecoder {
+ public:
+  HpackHuffmanDecoder();
+  ~HpackHuffmanDecoder();
+
+  // Prepare for decoding a new Huffman encoded string.
+  void Reset() { bit_buffer_.Reset(); }
+
+  // Decode the portion of a HPACK Huffman encoded string that is in |input|,
+  // appending the decoded symbols into |*output|, stopping when more bits are
+  // needed to determine the next symbol, which/ means that the input has been
+  // drained, and also that the bit_buffer_ is empty or that the bits that are
+  // in it are not a whole symbol.
+  // If |input| is the start of a string, the caller must first call Reset.
+  // If |input| includes the end of the encoded string, the caller must call
+  // InputProperlyTerminated after Decode has returned true in order to
+  // determine if the encoded string was properly terminated.
+  // Returns false if something went wrong (e.g. the encoding contains the code
+  // EOS symbol). Otherwise returns true, in which case input has been fully
+  // decoded or buffered; in particular, if the low-order bit of the final byte
+  // of the input is not the last bit of an encoded symbol, then bit_buffer_
+  // will contain the leading bits of the code for that symbol, but not the
+  // final bits of that code.
+  // Note that output should be empty, but that it is not cleared by Decode().
+  bool Decode(base::StringPiece input, std::string* output);
+
+  // Is what remains in the bit_buffer_ valid at the end of an encoded string?
+  // Call after passing the the final portion of a Huffman string to Decode,
+  // and getting true as the result.
+  bool InputProperlyTerminated() const {
+    return bit_buffer_.InputProperlyTerminated();
+  }
+
+  bool IsEmptyForTest() const { return bit_buffer_.IsEmpty(); }
+
+  std::string DebugString() const;
+
+  //////////////////////////////////////////////////////////////////////////////
+  // Alternate implementations of Decode:
+
+  // As above, implemented using a tree of if statements to determine the code
+  // length, etc., which are returned as a tree. See PrefixToInfo. This is the
+  // original implementation, current as of 2016-8-8.
+  bool DecodeWithIfTreeAndStruct(base::StringPiece input, std::string* output);
+
+  // Based on DecodeWithIfTreeAndStruct, but adds an optimization for the common
+  // case of short codes (5, 6 or 7), which make up a large fraction of the
+  // frequency distribution on which the HPACK table was based.
+  // TODO(jamessynge): Be precise about that fraction.
+  bool DecodeShortCodesFirst(base::StringPiece input, std::string* output);
+
+ private:
+  HuffmanBitBuffer bit_buffer_;
+};
+
+inline std::ostream& operator<<(std::ostream& out,
+                                const HpackHuffmanDecoder& v) {
+  return out << v.DebugString();
+}
+
+}  // namespace net
+
+#endif  // NET_HTTP2_HPACK_HUFFMAN_HTTP2_HPACK_HUFFMAN_DECODER_H_
diff --git a/net/http2/hpack/huffman/http2_hpack_huffman_decoder_test.cc b/net/http2/hpack/huffman/http2_hpack_huffman_decoder_test.cc
new file mode 100644
index 0000000..3ad47351
--- /dev/null
+++ b/net/http2/hpack/huffman/http2_hpack_huffman_decoder_test.cc
@@ -0,0 +1,292 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/huffman/http2_hpack_huffman_decoder.h"
+
+// Tests of HpackHuffmanDecoder and HuffmanBitBuffer.
+
+#include <iostream>
+#include <string>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/macros.h"
+#include "base/strings/string_piece.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/tools/failure.h"
+#include "net/http2/tools/random_decoder_test.h"
+#include "net/spdy/spdy_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::AssertionResult;
+using ::testing::AssertionSuccess;
+using base::StringPiece;
+using std::string;
+
+namespace net {
+namespace test {
+namespace {
+
+TEST(HuffmanBitBufferTest, Reset) {
+  HuffmanBitBuffer bb;
+  EXPECT_TRUE(bb.IsEmpty());
+  EXPECT_TRUE(bb.InputProperlyTerminated());
+  EXPECT_EQ(bb.count(), 0u);
+  EXPECT_EQ(bb.free_count(), 64u);
+  EXPECT_EQ(bb.value(), 0u);
+}
+
+TEST(HuffmanBitBufferTest, AppendBytesAligned) {
+  string s;
+  s.push_back('\x11');
+  s.push_back('\x22');
+  s.push_back('\x33');
+  StringPiece sp(s);
+
+  HuffmanBitBuffer bb;
+  sp.remove_prefix(bb.AppendBytes(sp));
+  EXPECT_TRUE(sp.empty());
+  EXPECT_FALSE(bb.IsEmpty()) << bb;
+  EXPECT_FALSE(bb.InputProperlyTerminated());
+  EXPECT_EQ(bb.count(), 24u) << bb;
+  EXPECT_EQ(bb.free_count(), 40u) << bb;
+  EXPECT_EQ(bb.value(), HuffmanAccumulator(0x112233) << 40) << bb;
+
+  s.clear();
+  s.push_back('\x44');
+  sp = s;
+
+  sp.remove_prefix(bb.AppendBytes(sp));
+  EXPECT_TRUE(sp.empty());
+  EXPECT_EQ(bb.count(), 32u) << bb;
+  EXPECT_EQ(bb.free_count(), 32u) << bb;
+  EXPECT_EQ(bb.value(), HuffmanAccumulator(0x11223344) << 32) << bb;
+
+  s.clear();
+  s.push_back('\x55');
+  s.push_back('\x66');
+  s.push_back('\x77');
+  s.push_back('\x88');
+  s.push_back('\x99');
+  sp = s;
+
+  sp.remove_prefix(bb.AppendBytes(sp));
+  EXPECT_EQ(sp.size(), 1u);
+  EXPECT_EQ('\x99', sp[0]);
+  EXPECT_EQ(bb.count(), 64u) << bb;
+  EXPECT_EQ(bb.free_count(), 0u) << bb;
+  EXPECT_EQ(bb.value(), HuffmanAccumulator(0x1122334455667788LL)) << bb;
+
+  sp.remove_prefix(bb.AppendBytes(sp));
+  EXPECT_EQ(sp.size(), 1u);
+  EXPECT_EQ('\x99', sp[0]);
+  EXPECT_EQ(bb.count(), 64u) << bb;
+  EXPECT_EQ(bb.free_count(), 0u) << bb;
+  EXPECT_EQ(bb.value(), HuffmanAccumulator(0x1122334455667788LL)) << bb;
+}
+
+TEST(HuffmanBitBufferTest, ConsumeBits) {
+  string s;
+  s.push_back('\x11');
+  s.push_back('\x22');
+  s.push_back('\x33');
+  StringPiece sp(s);
+
+  HuffmanBitBuffer bb;
+  sp.remove_prefix(bb.AppendBytes(sp));
+  EXPECT_TRUE(sp.empty());
+
+  bb.ConsumeBits(1);
+  EXPECT_EQ(bb.count(), 23u) << bb;
+  EXPECT_EQ(bb.free_count(), 41u) << bb;
+  EXPECT_EQ(bb.value(), HuffmanAccumulator(0x112233) << 41) << bb;
+
+  bb.ConsumeBits(20);
+  EXPECT_EQ(bb.count(), 3u) << bb;
+  EXPECT_EQ(bb.free_count(), 61u) << bb;
+  EXPECT_EQ(bb.value(), HuffmanAccumulator(0x3) << 61) << bb;
+}
+
+TEST(HuffmanBitBufferTest, AppendBytesUnaligned) {
+  string s;
+  s.push_back('\x11');
+  s.push_back('\x22');
+  s.push_back('\x33');
+  s.push_back('\x44');
+  s.push_back('\x55');
+  s.push_back('\x66');
+  s.push_back('\x77');
+  s.push_back('\x88');
+  s.push_back('\x99');
+  s.push_back('\xaa');
+  s.push_back('\xbb');
+  s.push_back('\xcc');
+  s.push_back('\xdd');
+  StringPiece sp(s);
+
+  HuffmanBitBuffer bb;
+  sp.remove_prefix(bb.AppendBytes(sp));
+  EXPECT_EQ(sp.size(), 5u);
+  EXPECT_FALSE(bb.InputProperlyTerminated());
+
+  bb.ConsumeBits(15);
+  EXPECT_EQ(bb.count(), 49u) << bb;
+  EXPECT_EQ(bb.free_count(), 15u) << bb;
+
+  HuffmanAccumulator expected(0x1122334455667788);
+  expected <<= 15;
+  EXPECT_EQ(bb.value(), expected);
+
+  sp.remove_prefix(bb.AppendBytes(sp));
+  EXPECT_EQ(sp.size(), 4u);
+  EXPECT_EQ(bb.count(), 57u) << bb;
+  EXPECT_EQ(bb.free_count(), 7u) << bb;
+
+  expected |= (HuffmanAccumulator(0x99) << 7);
+  EXPECT_EQ(bb.value(), expected) << bb << std::hex
+                                  << "\n   actual: " << bb.value()
+                                  << "\n expected: " << expected;
+}
+
+enum class DecoderChoice { IF_TREE, SHORT_CODE };
+
+class HpackHuffmanDecoderTest
+    : public RandomDecoderTest,
+      public ::testing::WithParamInterface<DecoderChoice> {
+ protected:
+  HpackHuffmanDecoderTest() {
+    // The decoder may return true, and its accumulator may be empty, at
+    // many boundaries while decoding, and yet the whole string hasn't
+    // been decoded.
+    stop_decode_on_done_ = false;
+  }
+
+  DecodeStatus StartDecoding(DecodeBuffer* b) override {
+    input_bytes_seen_ = 0;
+    output_buffer_.clear();
+    decoder_.Reset();
+    return ResumeDecoding(b);
+  }
+
+  DecodeStatus ResumeDecoding(DecodeBuffer* b) override {
+    input_bytes_seen_ += b->Remaining();
+    StringPiece sp(b->cursor(), b->Remaining());
+    if (DecodeFragment(sp)) {
+      b->AdvanceCursor(b->Remaining());
+      // Successfully decoded (or buffered) the bytes in StringPiece.
+      EXPECT_LE(input_bytes_seen_, input_bytes_expected_);
+      // Have we reached the end of the encoded string?
+      if (input_bytes_expected_ == input_bytes_seen_) {
+        if (decoder_.InputProperlyTerminated()) {
+          return DecodeStatus::kDecodeDone;
+        } else {
+          return DecodeStatus::kDecodeError;
+        }
+      }
+      return DecodeStatus::kDecodeInProgress;
+    }
+    return DecodeStatus::kDecodeError;
+  }
+
+  bool DecodeFragment(StringPiece sp) {
+    switch (GetParam()) {
+      case DecoderChoice::IF_TREE:
+        return decoder_.DecodeWithIfTreeAndStruct(sp, &output_buffer_);
+      case DecoderChoice::SHORT_CODE:
+        return decoder_.DecodeShortCodesFirst(sp, &output_buffer_);
+    }
+
+    NOTREACHED();
+    return false;
+  }
+
+  AssertionResult ValidatorForHuffmanDecodeAndValidateSeveralWays(
+      StringPiece expected_plain) {
+    VERIFY_EQ(output_buffer_.size(), expected_plain.size());
+    VERIFY_EQ(output_buffer_, expected_plain);
+    return AssertionSuccess();
+  }
+
+  AssertionResult HuffmanDecodeAndValidateSeveralWays(
+      StringPiece encoded,
+      StringPiece expected_plain) {
+    input_bytes_expected_ = encoded.size();
+    DecodeBuffer db(encoded);
+    bool return_non_zero_on_first = false;
+    return DecodeAndValidateSeveralWays(
+        &db, return_non_zero_on_first,
+        ValidateDoneAndEmpty(
+            base::Bind(&HpackHuffmanDecoderTest::
+                           ValidatorForHuffmanDecodeAndValidateSeveralWays,
+                       base::Unretained(this), expected_plain)));
+  }
+
+  HpackHuffmanDecoder decoder_;
+  string output_buffer_;
+  size_t input_bytes_seen_;
+  size_t input_bytes_expected_;
+};
+INSTANTIATE_TEST_CASE_P(AllDecoders,
+                        HpackHuffmanDecoderTest,
+                        ::testing::Values(DecoderChoice::IF_TREE,
+                                          DecoderChoice::SHORT_CODE));
+
+TEST_P(HpackHuffmanDecoderTest, SpecRequestExamples) {
+  HpackHuffmanDecoder decoder;
+  string test_table[] = {
+      a2b_hex("f1e3c2e5f23a6ba0ab90f4ff"),
+      "www.example.com",
+      a2b_hex("a8eb10649cbf"),
+      "no-cache",
+      a2b_hex("25a849e95ba97d7f"),
+      "custom-key",
+      a2b_hex("25a849e95bb8e8b4bf"),
+      "custom-value",
+  };
+  for (size_t i = 0; i != arraysize(test_table); i += 2) {
+    const string& huffman_encoded(test_table[i]);
+    const string& plain_string(test_table[i + 1]);
+    string buffer;
+    decoder.Reset();
+    EXPECT_TRUE(decoder.Decode(huffman_encoded, &buffer)) << decoder;
+    EXPECT_TRUE(decoder.InputProperlyTerminated()) << decoder;
+    EXPECT_EQ(buffer, plain_string);
+  }
+}
+
+TEST_P(HpackHuffmanDecoderTest, SpecResponseExamples) {
+  HpackHuffmanDecoder decoder;
+  // clang-format off
+  string test_table[] = {
+    a2b_hex("6402"),
+    "302",
+    a2b_hex("aec3771a4b"),
+    "private",
+    a2b_hex("d07abe941054d444a8200595040b8166"
+            "e082a62d1bff"),
+    "Mon, 21 Oct 2013 20:13:21 GMT",
+    a2b_hex("9d29ad171863c78f0b97c8e9ae82ae43"
+            "d3"),
+    "https://www.example.com",
+    a2b_hex("94e7821dd7f2e6c7b335dfdfcd5b3960"
+            "d5af27087f3672c1ab270fb5291f9587"
+            "316065c003ed4ee5b1063d5007"),
+    "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1",
+  };
+  // clang-format on
+  for (size_t i = 0; i != arraysize(test_table); i += 2) {
+    const string& huffman_encoded(test_table[i]);
+    const string& plain_string(test_table[i + 1]);
+    string buffer;
+    decoder.Reset();
+    EXPECT_TRUE(decoder.Decode(huffman_encoded, &buffer)) << decoder;
+    EXPECT_TRUE(decoder.InputProperlyTerminated()) << decoder;
+    EXPECT_EQ(buffer, plain_string);
+  }
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/hpack/tools/hpack_block_builder.cc b/net/http2/hpack/tools/hpack_block_builder.cc
new file mode 100644
index 0000000..49eba0d
--- /dev/null
+++ b/net/http2/hpack/tools/hpack_block_builder.cc
@@ -0,0 +1,84 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/tools/hpack_block_builder.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+void HpackBlockBuilder::AppendHighBitsAndVarint(uint8_t high_bits,
+                                                uint8_t prefix_length,
+                                                uint64_t varint) {
+  EXPECT_LE(4, prefix_length);
+  EXPECT_LE(prefix_length, 7);
+
+  // prefix_mask defines the sequence of low-order bits of the first byte
+  // that encode the prefix of the value. It is also the marker in those bits
+  // of the first byte indicating that at least one extension byte is needed.
+  uint8_t prefix_mask = (1 << prefix_length) - 1;
+  EXPECT_EQ(0, high_bits & prefix_mask);
+
+  if (varint < prefix_mask) {
+    uint8_t b = high_bits | static_cast<uint8_t>(varint);
+    buffer_.push_back(static_cast<char>(b));
+    return;
+  }
+
+  // We need extension bytes.
+  buffer_.push_back(static_cast<char>(high_bits | prefix_mask));
+  varint -= prefix_mask;
+  while (varint >= 128) {
+    uint8_t b = static_cast<uint8_t>((varint % 128) + 128);
+    buffer_.push_back(static_cast<char>(b));
+    varint = varint / 128;
+  }
+  char c = static_cast<char>(varint);
+  buffer_.push_back(c);
+}
+
+void HpackBlockBuilder::AppendEntryTypeAndVarint(HpackEntryType entry_type,
+                                                 uint64_t varint) {
+  uint8_t high_bits;
+  uint8_t prefix_length;  // Bits of the varint prefix in the first byte.
+  switch (entry_type) {
+    case HpackEntryType::kIndexedHeader:
+      high_bits = 0x80;
+      prefix_length = 7;
+      break;
+    case HpackEntryType::kDynamicTableSizeUpdate:
+      high_bits = 0x20;
+      prefix_length = 5;
+      break;
+    case HpackEntryType::kIndexedLiteralHeader:
+      high_bits = 0x40;
+      prefix_length = 6;
+      break;
+    case HpackEntryType::kUnindexedLiteralHeader:
+      high_bits = 0x00;
+      prefix_length = 4;
+      break;
+    case HpackEntryType::kNeverIndexedLiteralHeader:
+      high_bits = 0x10;
+      prefix_length = 4;
+      break;
+    default:
+      NOTREACHED();
+      high_bits = 0;
+      prefix_length = 0;
+  }
+  AppendHighBitsAndVarint(high_bits, prefix_length, varint);
+}
+
+void HpackBlockBuilder::AppendString(bool is_huffman_encoded,
+                                     base::StringPiece str) {
+  uint8_t high_bits = is_huffman_encoded ? 0x80 : 0;
+  uint8_t prefix_length = 7;
+  AppendHighBitsAndVarint(high_bits, prefix_length, str.size());
+  str.AppendToString(&buffer_);
+}
+
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/hpack/tools/hpack_block_builder.h b/net/http2/hpack/tools/hpack_block_builder.h
new file mode 100644
index 0000000..06502b2a
--- /dev/null
+++ b/net/http2/hpack/tools/hpack_block_builder.h
@@ -0,0 +1,95 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_HPACK_TOOLS_HPACK_BLOCK_BUILDER_H_
+#define NET_HTTP2_HPACK_TOOLS_HPACK_BLOCK_BUILDER_H_
+
+// HpackBlockBuilder builds wire-format HPACK blocks (or fragments thereof)
+// from components.
+
+// Supports very large varints to enable tests to create HPACK blocks with
+// values that the decoder should reject. For now, this is only intended for
+// use in tests, and thus has EXPECT* in the code. If desired to use it in an
+// encoder, it will need optimization work, especially w.r.t memory mgmt, and
+// the EXPECT* will need to be removed or replaced with DCHECKs. And of course
+// the support for very large varints will not be needed in production code.
+
+#include <stddef.h>
+#include <string>
+
+#include "base/strings/string_piece.h"
+#include "net/http2/hpack/http2_hpack_constants.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+class HpackBlockBuilder {
+ public:
+  explicit HpackBlockBuilder(base::StringPiece initial_contents) {
+    initial_contents.AppendToString(&buffer_);
+  }
+  HpackBlockBuilder() {}
+  ~HpackBlockBuilder() {}
+
+  size_t size() const { return buffer_.size(); }
+  const std::string& buffer() const { return buffer_; }
+
+  //----------------------------------------------------------------------------
+  // Methods for appending a valid HPACK entry.
+
+  void AppendIndexedHeader(uint64_t index) {
+    AppendEntryTypeAndVarint(HpackEntryType::kIndexedHeader, index);
+  }
+
+  void AppendDynamicTableSizeUpdate(uint64_t size) {
+    AppendEntryTypeAndVarint(HpackEntryType::kDynamicTableSizeUpdate, size);
+  }
+
+  void AppendNameIndexAndLiteralValue(HpackEntryType entry_type,
+                                      uint64_t name_index,
+                                      bool value_is_huffman_encoded,
+                                      base::StringPiece value) {
+    // name_index==0 would indicate that the entry includes a literal name.
+    // Call AppendLiteralNameAndValue in that case.
+    EXPECT_NE(0u, name_index);
+    AppendEntryTypeAndVarint(entry_type, name_index);
+    AppendString(value_is_huffman_encoded, value);
+  }
+
+  void AppendLiteralNameAndValue(HpackEntryType entry_type,
+                                 bool name_is_huffman_encoded,
+                                 base::StringPiece name,
+                                 bool value_is_huffman_encoded,
+                                 base::StringPiece value) {
+    AppendEntryTypeAndVarint(entry_type, 0);
+    AppendString(name_is_huffman_encoded, name);
+    AppendString(value_is_huffman_encoded, value);
+  }
+
+  //----------------------------------------------------------------------------
+  // Primitive methods that are not guaranteed to write a valid HPACK entry.
+
+  // Appends a varint, with the specified high_bits above the prefix of the
+  // varint.
+  void AppendHighBitsAndVarint(uint8_t high_bits,
+                               uint8_t prefix_length,
+                               uint64_t varint);
+
+  // Append the start of an HPACK entry for the specified type, with the
+  // specified varint.
+  void AppendEntryTypeAndVarint(HpackEntryType entry_type, uint64_t varint);
+
+  // Append a header string (i.e. a header name or value) in HPACK format.
+  // Does NOT perform Huffman encoding.
+  void AppendString(bool is_huffman_encoded, base::StringPiece str);
+
+ private:
+  std::string buffer_;
+};
+
+}  // namespace test
+}  // namespace net
+
+#endif  // NET_HTTP2_HPACK_TOOLS_HPACK_BLOCK_BUILDER_H_
diff --git a/net/http2/hpack/tools/hpack_block_builder_test.cc b/net/http2/hpack/tools/hpack_block_builder_test.cc
new file mode 100644
index 0000000..e591049
--- /dev/null
+++ b/net/http2/hpack/tools/hpack_block_builder_test.cc
@@ -0,0 +1,169 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/tools/hpack_block_builder.h"
+
+#include "net/spdy/spdy_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::StringPiece;
+
+namespace net {
+namespace test {
+namespace {
+const bool kUncompressed = false;
+const bool kCompressed = true;
+
+// TODO(jamessynge): Once static table code is checked in, switch to using
+// constants from there.
+const uint32_t kStaticTableMethodGET = 2;
+const uint32_t kStaticTablePathSlash = 4;
+const uint32_t kStaticTableSchemeHttp = 6;
+
+// Tests of encoding per the RFC. See:
+//   http://httpwg.org/specs/rfc7541.html#header.field.representation.examples
+// The expected values have been copied from the RFC.
+TEST(HpackBlockBuilderTest, ExamplesFromSpecC2) {
+  {
+    HpackBlockBuilder b;
+    b.AppendLiteralNameAndValue(HpackEntryType::kIndexedLiteralHeader,
+                                kUncompressed, "custom-key", kUncompressed,
+                                "custom-header");
+    EXPECT_EQ(26u, b.size());
+
+    const char kExpected[] =
+        "\x40"            // == Literal indexed ==
+        "\x0a"            // Name length (10)
+        "custom-key"      // Name
+        "\x0d"            // Value length (13)
+        "custom-header";  // Value
+    EXPECT_EQ(kExpected, b.buffer());
+  }
+  {
+    HpackBlockBuilder b;
+    b.AppendNameIndexAndLiteralValue(HpackEntryType::kUnindexedLiteralHeader, 4,
+                                     kUncompressed, "/sample/path");
+    EXPECT_EQ(14u, b.size());
+
+    const char kExpected[] =
+        "\x04"           // == Literal unindexed, name index 0x04 ==
+        "\x0c"           // Value length (12)
+        "/sample/path";  // Value
+    EXPECT_EQ(kExpected, b.buffer());
+  }
+  {
+    HpackBlockBuilder b;
+    b.AppendLiteralNameAndValue(HpackEntryType::kNeverIndexedLiteralHeader,
+                                kUncompressed, "password", kUncompressed,
+                                "secret");
+    EXPECT_EQ(17u, b.size());
+
+    const char kExpected[] =
+        "\x10"      // == Literal never indexed ==
+        "\x08"      // Name length (8)
+        "password"  // Name
+        "\x06"      // Value length (6)
+        "secret";   // Value
+    EXPECT_EQ(kExpected, b.buffer());
+  }
+  {
+    HpackBlockBuilder b;
+    b.AppendIndexedHeader(2);
+    EXPECT_EQ(1u, b.size());
+
+    const char kExpected[] = "\x82";  // == Indexed (2) ==
+    EXPECT_EQ(kExpected, b.buffer());
+  }
+}
+
+// Tests of encoding per the RFC. See:
+//  http://httpwg.org/specs/rfc7541.html#request.examples.without.huffman.coding
+TEST(HpackBlockBuilderTest, ExamplesFromSpecC3) {
+  {
+    // Header block to encode:
+    //   :method: GET
+    //   :scheme: http
+    //   :path: /
+    //   :authority: www.example.com
+    HpackBlockBuilder b;
+    b.AppendIndexedHeader(2);  // :method: GET
+    b.AppendIndexedHeader(6);  // :scheme: http
+    b.AppendIndexedHeader(4);  // :path: /
+    b.AppendNameIndexAndLiteralValue(HpackEntryType::kIndexedLiteralHeader, 1,
+                                     kUncompressed, "www.example.com");
+    EXPECT_EQ(20u, b.size());
+
+    // Hex dump of encoded data (copied from RFC):
+    // 0x0000:  8286 8441 0f77 7777 2e65 7861 6d70 6c65  ...A.www.example
+    // 0x0010:  2e63 6f6d                                .com
+
+    const std::string expected =
+        a2b_hex("828684410f7777772e6578616d706c652e636f6d");
+    EXPECT_EQ(expected, b.buffer());
+  }
+}
+
+// Tests of encoding per the RFC. See:
+//   http://httpwg.org/specs/rfc7541.html#request.examples.with.huffman.coding
+TEST(HpackBlockBuilderTest, ExamplesFromSpecC4) {
+  {
+    // Header block to encode:
+    //   :method: GET
+    //   :scheme: http
+    //   :path: /
+    //   :authority: www.example.com  (Huffman encoded)
+    HpackBlockBuilder b;
+    b.AppendIndexedHeader(kStaticTableMethodGET);
+    b.AppendIndexedHeader(kStaticTableSchemeHttp);
+    b.AppendIndexedHeader(kStaticTablePathSlash);
+    const char kHuffmanWwwExampleCom[] = {0xf1u, 0xe3u, 0xc2u, 0xe5u,
+                                          0xf2u, 0x3au, 0x6bu, 0xa0u,
+                                          0xabu, 0x90u, 0xf4u, 0xffu};
+    b.AppendNameIndexAndLiteralValue(
+        HpackEntryType::kIndexedLiteralHeader, 1, kCompressed,
+        StringPiece(kHuffmanWwwExampleCom, sizeof kHuffmanWwwExampleCom));
+    EXPECT_EQ(17u, b.size());
+
+    // Hex dump of encoded data (copied from RFC):
+    // 0x0000:  8286 8441 8cf1 e3c2 e5f2 3a6b a0ab 90f4  ...A......:k....
+    // 0x0010:  ff                                       .
+
+    const std::string expected = a2b_hex("828684418cf1e3c2e5f23a6ba0ab90f4ff");
+    EXPECT_EQ(expected, b.buffer());
+  }
+}
+
+TEST(HpackBlockBuilderTest, DynamicTableSizeUpdate) {
+  {
+    HpackBlockBuilder b;
+    b.AppendDynamicTableSizeUpdate(0);
+    EXPECT_EQ(1u, b.size());
+
+    const char kData[] = {0x20};
+    StringPiece expected(kData, sizeof kData);
+    EXPECT_EQ(expected, b.buffer());
+  }
+  {
+    HpackBlockBuilder b;
+    b.AppendDynamicTableSizeUpdate(4096);  // The default size.
+    EXPECT_EQ(3u, b.size());
+
+    const char kData[] = {0x3f, 0xe1u, 0x1f};
+    StringPiece expected(kData, sizeof kData);
+    EXPECT_EQ(expected, b.buffer());
+  }
+  {
+    HpackBlockBuilder b;
+    b.AppendDynamicTableSizeUpdate(1000000000000);  // A very large value.
+    EXPECT_EQ(7u, b.size());
+
+    const char kData[] = {0x3fu, 0xe1u, 0x9fu, 0x94u, 0xa5u, 0x8du, 0x1du};
+    StringPiece expected(kData, sizeof kData);
+    EXPECT_EQ(expected, b.buffer());
+  }
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/hpack/tools/hpack_example.cc b/net/http2/hpack/tools/hpack_example.cc
new file mode 100644
index 0000000..72fd581
--- /dev/null
+++ b/net/http2/hpack/tools/hpack_example.cc
@@ -0,0 +1,61 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/tools/hpack_example.h"
+
+#include <ctype.h>
+
+#include "base/logging.h"
+#include "net/spdy/spdy_test_utils.h"
+
+using base::StringPiece;
+using std::string;
+
+namespace net {
+namespace test {
+namespace {
+
+void HpackExampleToStringOrDie(StringPiece example, string* output) {
+  while (!example.empty()) {
+    const char c0 = example[0];
+    if (isxdigit(c0)) {
+      CHECK_GT(example.size(), 1u) << "Truncated hex byte?";
+      const char c1 = example[1];
+      CHECK(isxdigit(c1)) << "Found half a byte?";
+      *output += a2b_hex(example.substr(0, 2).as_string().c_str());
+      example.remove_prefix(2);
+      continue;
+    }
+    if (isspace(c0)) {
+      example.remove_prefix(1);
+      continue;
+    }
+    if (example.starts_with("|")) {
+      // Start of a comment. Skip to end of line or of input.
+      auto pos = example.find('\n');
+      if (pos == StringPiece::npos) {
+        // End of input.
+        break;
+      }
+      example.remove_prefix(pos + 1);
+      continue;
+    }
+    CHECK(false) << "Can't parse byte " << static_cast<int>(c0) << " (0x"
+                 << std::hex << c0 << ")"
+                 << "\nExample: " << example;
+  }
+  CHECK_LT(0u, output->size()) << "Example is empty.";
+  return;
+}
+
+}  // namespace
+
+string HpackExampleToStringOrDie(StringPiece example) {
+  string output;
+  HpackExampleToStringOrDie(example, &output);
+  return output;
+}
+
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/hpack/tools/hpack_example.h b/net/http2/hpack/tools/hpack_example.h
new file mode 100644
index 0000000..1567cbf
--- /dev/null
+++ b/net/http2/hpack/tools/hpack_example.h
@@ -0,0 +1,32 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_HPACK_TOOLS_HPACK_EXAMPLE_H_
+#define NET_HTTP2_HPACK_TOOLS_HPACK_EXAMPLE_H_
+
+// Parses HPACK examples in the format seen in the HPACK specification,
+// RFC 7541. For example:
+//
+//       10                                      | == Literal never indexed ==
+//       08                                      |   Literal name (len = 8)
+//       7061 7373 776f 7264                     | password
+//       06                                      |   Literal value (len = 6)
+//       7365 6372 6574                          | secret
+//                                               | -> password: secret
+//
+// (excluding the leading "//").
+
+#include <string>
+
+#include "base/strings/string_piece.h"
+
+namespace net {
+namespace test {
+
+std::string HpackExampleToStringOrDie(base::StringPiece example);
+
+}  // namespace test
+}  // namespace net
+
+#endif  // NET_HTTP2_HPACK_TOOLS_HPACK_EXAMPLE_H_
diff --git a/net/http2/http2_constants.cc b/net/http2/http2_constants.cc
new file mode 100644
index 0000000..ca28f15e
--- /dev/null
+++ b/net/http2/http2_constants.cc
@@ -0,0 +1,161 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/http2_constants.h"
+
+#include <ios>
+#include <sstream>
+
+#include "base/logging.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/stringprintf.h"
+
+using base::StringPrintf;
+using std::string;
+
+namespace net {
+
+string Http2FrameTypeToString(Http2FrameType v) {
+  switch (v) {
+    case Http2FrameType::DATA:
+      return "DATA";
+    case Http2FrameType::HEADERS:
+      return "HEADERS";
+    case Http2FrameType::PRIORITY:
+      return "PRIORITY";
+    case Http2FrameType::RST_STREAM:
+      return "RST_STREAM";
+    case Http2FrameType::SETTINGS:
+      return "SETTINGS";
+    case Http2FrameType::PUSH_PROMISE:
+      return "PUSH_PROMISE";
+    case Http2FrameType::PING:
+      return "PING";
+    case Http2FrameType::GOAWAY:
+      return "GOAWAY";
+    case Http2FrameType::WINDOW_UPDATE:
+      return "WINDOW_UPDATE";
+    case Http2FrameType::CONTINUATION:
+      return "CONTINUATION";
+    case Http2FrameType::ALTSVC:
+      return "ALTSVC";
+  }
+  std::stringstream ss;
+  ss << "UnknownFrameType(" << static_cast<int>(v) << ")";
+  return ss.str();
+}
+string Http2FrameTypeToString(uint8_t v) {
+  return Http2FrameTypeToString(static_cast<Http2FrameType>(v));
+}
+
+string Http2FrameFlagsToString(Http2FrameType type, uint8_t flags) {
+  string s;
+  // Closure to append flag name |v| to the string |s|, and to clear
+  // |bit| from |flags|.
+  auto append_and_clear = [&s, &flags](base::StringPiece v, uint8_t bit) {
+    if (!s.empty()) {
+      s.push_back('|');
+    }
+    v.AppendToString(&s);
+    flags ^= bit;
+  };
+  if (flags & 0x01) {
+    if (type == Http2FrameType::DATA || type == Http2FrameType::HEADERS) {
+      append_and_clear("END_STREAM", Http2FrameFlag::FLAG_END_STREAM);
+    } else if (type == Http2FrameType::SETTINGS ||
+               type == Http2FrameType::PING) {
+      append_and_clear("ACK", Http2FrameFlag::FLAG_ACK);
+    }
+  }
+  if (flags & 0x04) {
+    if (type == Http2FrameType::HEADERS ||
+        type == Http2FrameType::PUSH_PROMISE ||
+        type == Http2FrameType::CONTINUATION) {
+      append_and_clear("END_HEADERS", Http2FrameFlag::FLAG_END_HEADERS);
+    }
+  }
+  if (flags & 0x08) {
+    if (type == Http2FrameType::DATA || type == Http2FrameType::HEADERS ||
+        type == Http2FrameType::PUSH_PROMISE) {
+      append_and_clear("PADDED", Http2FrameFlag::FLAG_PADDED);
+    }
+  }
+  if (flags & 0x20) {
+    if (type == Http2FrameType::HEADERS) {
+      append_and_clear("PRIORITY", Http2FrameFlag::FLAG_PRIORITY);
+    }
+  }
+  if (flags != 0) {
+    append_and_clear(StringPrintf("0x%02x", flags), flags);
+  }
+  DCHECK_EQ(0, flags);
+  return s;
+}
+string Http2FrameFlagsToString(uint8_t type, uint8_t flags) {
+  return Http2FrameFlagsToString(static_cast<Http2FrameType>(type), flags);
+}
+
+string Http2ErrorCodeToString(uint32_t v) {
+  switch (v) {
+    case 0x0:
+      return "NO_ERROR";
+    case 0x1:
+      return "PROTOCOL_ERROR";
+    case 0x2:
+      return "INTERNAL_ERROR";
+    case 0x3:
+      return "FLOW_CONTROL_ERROR";
+    case 0x4:
+      return "SETTINGS_TIMEOUT";
+    case 0x5:
+      return "STREAM_CLOSED";
+    case 0x6:
+      return "FRAME_SIZE_ERROR";
+    case 0x7:
+      return "REFUSED_STREAM";
+    case 0x8:
+      return "CANCEL";
+    case 0x9:
+      return "COMPRESSION_ERROR";
+    case 0xa:
+      return "CONNECT_ERROR";
+    case 0xb:
+      return "ENHANCE_YOUR_CALM";
+    case 0xc:
+      return "INADEQUATE_SECURITY";
+    case 0xd:
+      return "HTTP_1_1_REQUIRED";
+  }
+  std::stringstream ss;
+  ss << "UnknownErrorCode(0x" << std::hex << v << ")";
+  return ss.str();
+}
+string Http2ErrorCodeToString(Http2ErrorCode v) {
+  return Http2ErrorCodeToString(static_cast<uint32_t>(v));
+}
+
+string Http2SettingsParameterToString(uint32_t v) {
+  switch (v) {
+    case 0x1:
+      return "HEADER_TABLE_SIZE";
+    case 0x2:
+      return "ENABLE_PUSH";
+    case 0x3:
+      return "MAX_CONCURRENT_STREAMS";
+    case 0x4:
+      return "INITIAL_WINDOW_SIZE";
+    case 0x5:
+      return "MAX_FRAME_SIZE";
+    case 0x6:
+      return "MAX_HEADER_LIST_SIZE";
+  }
+  std::stringstream ss;
+  ss << "UnknownSettingsParameter(0x" << std::hex << v << ")";
+  return ss.str();
+}
+string Http2SettingsParameterToString(Http2SettingsParameter v) {
+  return Http2SettingsParameterToString(static_cast<uint32_t>(v));
+}
+
+}  // namespace net
diff --git a/net/http2/http2_constants.h b/net/http2/http2_constants.h
new file mode 100644
index 0000000..61c63079
--- /dev/null
+++ b/net/http2/http2_constants.h
@@ -0,0 +1,265 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_HTTP2_CONSTANTS_H_
+#define NET_HTTP2_HTTP2_CONSTANTS_H_
+
+// Constants from the HTTP/2 spec, RFC 7540, and associated helper functions.
+
+#include <stdint.h>
+
+#include <iosfwd>
+#include <ostream>
+#include <string>
+
+#include "net/base/net_export.h"
+
+namespace net {
+
+// TODO(jamessynge): create http2_simple_types for types similar to
+// SpdyStreamId, but not for structures like Http2FrameHeader. Then will be
+// able to move these stream id functions there.
+constexpr uint32_t UInt31Mask() {
+  return 0x7fffffff;
+}
+constexpr uint32_t StreamIdMask() {
+  return UInt31Mask();
+}
+
+// The value used to identify types of frames. Upper case to match the RFC.
+// The comments indicate which flags are valid for that frame type.
+// ALTSVC is defined in http://httpwg.org/http-extensions/alt-svc.html
+// (not yet final standard as of March 2016, but close).
+enum class Http2FrameType : uint8_t {
+  DATA = 0,           // END_STREAM | PADDED
+  HEADERS = 1,        // END_STREAM | END_HEADERS | PADDED | PRIORITY
+  PRIORITY = 2,       //
+  RST_STREAM = 3,     //
+  SETTINGS = 4,       // ACK
+  PUSH_PROMISE = 5,   // END_HEADERS | PADDED
+  PING = 6,           // ACK
+  GOAWAY = 7,         //
+  WINDOW_UPDATE = 8,  //
+  CONTINUATION = 9,   // END_HEADERS
+  ALTSVC = 10,        //
+};
+
+// Is the frame type known/supported?
+inline bool IsSupportedHttp2FrameType(uint32_t v) {
+  return v <= static_cast<uint32_t>(Http2FrameType::ALTSVC);
+}
+inline bool IsSupportedHttp2FrameType(Http2FrameType v) {
+  return IsSupportedHttp2FrameType(static_cast<uint32_t>(v));
+}
+
+// The return type is 'string' so that they can generate a unique string for
+// each unsupported value. Since these are just used for debugging/error
+// messages, that isn't a cost to we need to worry about.
+// The same applies to the functions later in this file.
+NET_EXPORT_PRIVATE std::string Http2FrameTypeToString(Http2FrameType v);
+NET_EXPORT_PRIVATE std::string Http2FrameTypeToString(uint8_t v);
+NET_EXPORT_PRIVATE inline std::ostream& operator<<(std::ostream& out,
+                                                   Http2FrameType v) {
+  return out << Http2FrameTypeToString(v);
+}
+
+// Flags that appear in supported frame types. These are treated as bit masks.
+// The comments indicate for which frame types the flag is valid.
+// TODO(bnc): Remove FLAG_ prefix once enum SpdyFrameType is removed
+// (both enums have a PRIORITY member).
+enum Http2FrameFlag {
+  FLAG_END_STREAM = 0x01,   // DATA, HEADERS
+  FLAG_ACK = 0x01,          // SETTINGS, PING
+  FLAG_END_HEADERS = 0x04,  // HEADERS, PUSH_PROMISE, CONTINUATION
+  FLAG_PADDED = 0x08,       // DATA, HEADERS, PUSH_PROMISE
+  FLAG_PRIORITY = 0x20,     // HEADERS
+};
+
+// Formats zero or more flags for the specified type of frame. Returns an
+// empty string if flags==0.
+NET_EXPORT_PRIVATE std::string Http2FrameFlagsToString(Http2FrameType type,
+                                                       uint8_t flags);
+NET_EXPORT_PRIVATE std::string Http2FrameFlagsToString(uint8_t type,
+                                                       uint8_t flags);
+
+// Error codes for GOAWAY and RST_STREAM frames.
+enum class Http2ErrorCode : uint32_t {
+  // The associated condition is not a result of an error. For example, a GOAWAY
+  // might include this code to indicate graceful shutdown of a connection.
+  HTTP2_NO_ERROR = 0x0,
+
+  // The endpoint detected an unspecific protocol error. This error is for use
+  // when a more specific error code is not available.
+  PROTOCOL_ERROR = 0x1,
+
+  // The endpoint encountered an unexpected internal error.
+  INTERNAL_ERROR = 0x2,
+
+  // The endpoint detected that its peer violated the flow-control protocol.
+  FLOW_CONTROL_ERROR = 0x3,
+
+  // The endpoint sent a SETTINGS frame but did not receive a response in a
+  // timely manner. See Section 6.5.3 ("Settings Synchronization").
+  SETTINGS_TIMEOUT = 0x4,
+
+  // The endpoint received a frame after a stream was half-closed.
+  STREAM_CLOSED = 0x5,
+
+  // The endpoint received a frame with an invalid size.
+  FRAME_SIZE_ERROR = 0x6,
+
+  // The endpoint refused the stream prior to performing any application
+  // processing (see Section 8.1.4 for details).
+  REFUSED_STREAM = 0x7,
+
+  // Used by the endpoint to indicate that the stream is no longer needed.
+  CANCEL = 0x8,
+
+  // The endpoint is unable to maintain the header compression context
+  // for the connection.
+  COMPRESSION_ERROR = 0x9,
+
+  // The connection established in response to a CONNECT request (Section 8.3)
+  // was reset or abnormally closed.
+  CONNECT_ERROR = 0xa,
+
+  // The endpoint detected that its peer is exhibiting a behavior that might
+  // be generating excessive load.
+  ENHANCE_YOUR_CALM = 0xb,
+
+  // The underlying transport has properties that do not meet minimum
+  // security requirements (see Section 9.2).
+  INADEQUATE_SECURITY = 0xc,
+
+  // The endpoint requires that HTTP/1.1 be used instead of HTTP/2.
+  HTTP_1_1_REQUIRED = 0xd,
+};
+
+// Is the error code supported? (So far that means it is in RFC 7540.)
+inline bool IsSupportedHttp2ErrorCode(uint32_t v) {
+  return v <= static_cast<uint32_t>(Http2ErrorCode::HTTP_1_1_REQUIRED);
+}
+inline bool IsSupportedHttp2ErrorCode(Http2ErrorCode v) {
+  return IsSupportedHttp2ErrorCode(static_cast<uint32_t>(v));
+}
+
+// Format the specified error code.
+NET_EXPORT_PRIVATE std::string Http2ErrorCodeToString(uint32_t v);
+NET_EXPORT_PRIVATE std::string Http2ErrorCodeToString(Http2ErrorCode v);
+NET_EXPORT_PRIVATE inline std::ostream& operator<<(std::ostream& out,
+                                                   Http2ErrorCode v) {
+  return out << Http2ErrorCodeToString(v);
+}
+
+// Supported parameters in SETTINGS frames; so far just those in RFC 7540.
+enum class Http2SettingsParameter : uint16_t {
+  // Allows the sender to inform the remote endpoint of the maximum size of the
+  // header compression table used to decode header blocks, in octets. The
+  // encoder can select any size equal to or less than this value by using
+  // signaling specific to the header compression format inside a header block
+  // (see [COMPRESSION]). The initial value is 4,096 octets.
+  HEADER_TABLE_SIZE = 0x1,
+
+  // This setting can be used to disable server push (Section 8.2). An endpoint
+  // MUST NOT send a PUSH_PROMISE frame if it receives this parameter set to a
+  // value of 0. An endpoint that has both set this parameter to 0 and had it
+  // acknowledged MUST treat the receipt of a PUSH_PROMISE frame as a connection
+  // error (Section 5.4.1) of type PROTOCOL_ERROR.
+  //
+  // The initial value is 1, which indicates that server push is permitted. Any
+  // value other than 0 or 1 MUST be treated as a connection error (Section
+  // 5.4.1) of type PROTOCOL_ERROR.
+  ENABLE_PUSH = 0x2,
+
+  // Indicates the maximum number of concurrent streams that the sender will
+  // allow. This limit is directional: it applies to the number of streams that
+  // the sender permits the receiver to create. Initially, there is no limit to
+  // this value. It is recommended that this value be no smaller than 100, so as
+  // to not unnecessarily limit parallelism.
+  //
+  // A value of 0 for MAX_CONCURRENT_STREAMS SHOULD NOT be treated as
+  // special by endpoints. A zero value does prevent the creation of new
+  // streams; however, this can also happen for any limit that is exhausted with
+  // active streams. Servers SHOULD only set a zero value for short durations;
+  // if a server does not wish to accept requests, closing the connection is
+  // more appropriate.
+  MAX_CONCURRENT_STREAMS = 0x3,
+
+  // Indicates the sender's initial window size (in octets) for stream-level
+  // flow control. The initial value is 2^16-1 (65,535) octets.
+  //
+  // This setting affects the window size of all streams (see Section 6.9.2).
+  //
+  // Values above the maximum flow-control window size of 2^31-1 MUST be treated
+  // as a connection error (Section 5.4.1) of type FLOW_CONTROL_ERROR.
+  INITIAL_WINDOW_SIZE = 0x4,
+
+  // Indicates the size of the largest frame payload that the sender is willing
+  // to receive, in octets.
+  //
+  // The initial value is 2^14 (16,384) octets. The value advertised by an
+  // endpoint MUST be between this initial value and the maximum allowed frame
+  // size (2^24-1 or 16,777,215 octets), inclusive. Values outside this range
+  // MUST be treated as a connection error (Section 5.4.1) of type
+  // PROTOCOL_ERROR.
+  MAX_FRAME_SIZE = 0x5,
+
+  // This advisory setting informs a peer of the maximum size of header list
+  // that the sender is prepared to accept, in octets. The value is based on the
+  // uncompressed size of header fields, including the length of the name and
+  // value in octets plus an overhead of 32 octets for each header field.
+  //
+  // For any given request, a lower limit than what is advertised MAY be
+  // enforced. The initial value of this setting is unlimited.
+  MAX_HEADER_LIST_SIZE = 0x6,
+};
+
+// Is the settings parameter supported (so far that means it is in RFC 7540)?
+inline bool IsSupportedHttp2SettingsParameter(uint32_t v) {
+  return 0 < v &&
+         v <= static_cast<uint32_t>(
+                  Http2SettingsParameter::MAX_HEADER_LIST_SIZE);
+}
+inline bool IsSupportedHttp2SettingsParameter(Http2SettingsParameter v) {
+  return IsSupportedHttp2SettingsParameter(static_cast<uint32_t>(v));
+}
+
+// Format the specified settings parameter.
+NET_EXPORT_PRIVATE std::string Http2SettingsParameterToString(uint32_t v);
+NET_EXPORT_PRIVATE std::string Http2SettingsParameterToString(
+    Http2SettingsParameter v);
+inline std::ostream& operator<<(std::ostream& out, Http2SettingsParameter v) {
+  return out << Http2SettingsParameterToString(v);
+}
+
+// Information about the initial, minimum and maximum value of settings (not
+// applicable to all settings parameters).
+class Http2SettingsInfo {
+ public:
+  // Default value for HEADER_TABLE_SIZE.
+  static constexpr uint32_t DefaultHeaderTableSize() { return 4096; }
+
+  // Default value for ENABLE_PUSH.
+  static constexpr bool DefaultEnablePush() { return true; }
+
+  // Default value for INITIAL_WINDOW_SIZE.
+  static constexpr uint32_t DefaultInitialWindowSize() { return 65535; }
+
+  // Maximum value for INITIAL_WINDOW_SIZE, and for the connection flow control
+  // window, and for each stream flow control window.
+  static constexpr uint32_t MaximumWindowSize() { return UInt31Mask(); }
+
+  // Default value for MAX_FRAME_SIZE.
+  static constexpr uint32_t DefaultMaxFrameSize() { return 16384; }
+
+  // Minimum value for MAX_FRAME_SIZE.
+  static constexpr uint32_t MinimumMaxFrameSize() { return 16384; }
+
+  // Maximum value for MAX_FRAME_SIZE.
+  static constexpr uint32_t MaximumMaxFrameSize() { return (1 << 24) - 1; }
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP2_HTTP2_CONSTANTS_H_
diff --git a/net/http2/http2_constants_test.cc b/net/http2/http2_constants_test.cc
new file mode 100644
index 0000000..f7f24f7
--- /dev/null
+++ b/net/http2/http2_constants_test.cc
@@ -0,0 +1,272 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/http2_constants.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+namespace {
+
+class Http2ConstantsTest : public testing::Test {};
+
+TEST(Http2ConstantsTest, Http2FrameType) {
+  EXPECT_EQ(Http2FrameType::DATA, static_cast<Http2FrameType>(0));
+  EXPECT_EQ(Http2FrameType::HEADERS, static_cast<Http2FrameType>(1));
+  EXPECT_EQ(Http2FrameType::PRIORITY, static_cast<Http2FrameType>(2));
+  EXPECT_EQ(Http2FrameType::RST_STREAM, static_cast<Http2FrameType>(3));
+  EXPECT_EQ(Http2FrameType::SETTINGS, static_cast<Http2FrameType>(4));
+  EXPECT_EQ(Http2FrameType::PUSH_PROMISE, static_cast<Http2FrameType>(5));
+  EXPECT_EQ(Http2FrameType::PING, static_cast<Http2FrameType>(6));
+  EXPECT_EQ(Http2FrameType::GOAWAY, static_cast<Http2FrameType>(7));
+  EXPECT_EQ(Http2FrameType::WINDOW_UPDATE, static_cast<Http2FrameType>(8));
+  EXPECT_EQ(Http2FrameType::CONTINUATION, static_cast<Http2FrameType>(9));
+  EXPECT_EQ(Http2FrameType::ALTSVC, static_cast<Http2FrameType>(10));
+}
+
+TEST(Http2ConstantsTest, Http2FrameTypeToString) {
+  EXPECT_EQ("DATA", Http2FrameTypeToString(Http2FrameType::DATA));
+  EXPECT_EQ("HEADERS", Http2FrameTypeToString(Http2FrameType::HEADERS));
+  EXPECT_EQ("PRIORITY", Http2FrameTypeToString(Http2FrameType::PRIORITY));
+  EXPECT_EQ("RST_STREAM", Http2FrameTypeToString(Http2FrameType::RST_STREAM));
+  EXPECT_EQ("SETTINGS", Http2FrameTypeToString(Http2FrameType::SETTINGS));
+  EXPECT_EQ("PUSH_PROMISE",
+            Http2FrameTypeToString(Http2FrameType::PUSH_PROMISE));
+  EXPECT_EQ("PING", Http2FrameTypeToString(Http2FrameType::PING));
+  EXPECT_EQ("GOAWAY", Http2FrameTypeToString(Http2FrameType::GOAWAY));
+  EXPECT_EQ("WINDOW_UPDATE",
+            Http2FrameTypeToString(Http2FrameType::WINDOW_UPDATE));
+  EXPECT_EQ("CONTINUATION",
+            Http2FrameTypeToString(Http2FrameType::CONTINUATION));
+  EXPECT_EQ("ALTSVC", Http2FrameTypeToString(Http2FrameType::ALTSVC));
+
+  EXPECT_EQ("DATA", Http2FrameTypeToString(0));
+  EXPECT_EQ("HEADERS", Http2FrameTypeToString(1));
+  EXPECT_EQ("PRIORITY", Http2FrameTypeToString(2));
+  EXPECT_EQ("RST_STREAM", Http2FrameTypeToString(3));
+  EXPECT_EQ("SETTINGS", Http2FrameTypeToString(4));
+  EXPECT_EQ("PUSH_PROMISE", Http2FrameTypeToString(5));
+  EXPECT_EQ("PING", Http2FrameTypeToString(6));
+  EXPECT_EQ("GOAWAY", Http2FrameTypeToString(7));
+  EXPECT_EQ("WINDOW_UPDATE", Http2FrameTypeToString(8));
+  EXPECT_EQ("CONTINUATION", Http2FrameTypeToString(9));
+  EXPECT_EQ("ALTSVC", Http2FrameTypeToString(10));
+
+  EXPECT_EQ("UnknownFrameType(99)", Http2FrameTypeToString(99));
+}
+
+TEST(Http2ConstantsTest, Http2FrameFlag) {
+  EXPECT_EQ(Http2FrameFlag::FLAG_END_STREAM, static_cast<Http2FrameFlag>(0x01));
+  EXPECT_EQ(Http2FrameFlag::FLAG_ACK, static_cast<Http2FrameFlag>(0x01));
+  EXPECT_EQ(Http2FrameFlag::FLAG_END_HEADERS,
+            static_cast<Http2FrameFlag>(0x04));
+  EXPECT_EQ(Http2FrameFlag::FLAG_PADDED, static_cast<Http2FrameFlag>(0x08));
+  EXPECT_EQ(Http2FrameFlag::FLAG_PRIORITY, static_cast<Http2FrameFlag>(0x20));
+
+  EXPECT_EQ(Http2FrameFlag::FLAG_END_STREAM, 0x01);
+  EXPECT_EQ(Http2FrameFlag::FLAG_ACK, 0x01);
+  EXPECT_EQ(Http2FrameFlag::FLAG_END_HEADERS, 0x04);
+  EXPECT_EQ(Http2FrameFlag::FLAG_PADDED, 0x08);
+  EXPECT_EQ(Http2FrameFlag::FLAG_PRIORITY, 0x20);
+}
+
+TEST(Http2ConstantsTest, Http2FrameFlagsToString) {
+  // Single flags...
+
+  // 0b00000001
+  EXPECT_EQ("END_STREAM",
+            Http2FrameFlagsToString(Http2FrameType::DATA,
+                                    Http2FrameFlag::FLAG_END_STREAM));
+  EXPECT_EQ("END_STREAM",
+            Http2FrameFlagsToString(Http2FrameType::HEADERS, 0x01));
+  EXPECT_EQ("ACK", Http2FrameFlagsToString(Http2FrameType::SETTINGS,
+                                           Http2FrameFlag::FLAG_ACK));
+  EXPECT_EQ("ACK", Http2FrameFlagsToString(Http2FrameType::PING, 0x01));
+
+  // 0b00000010
+  EXPECT_EQ("0x02", Http2FrameFlagsToString(0xff, 0x02));
+
+  // 0b00000100
+  EXPECT_EQ("END_HEADERS",
+            Http2FrameFlagsToString(Http2FrameType::HEADERS,
+                                    Http2FrameFlag::FLAG_END_HEADERS));
+  EXPECT_EQ("END_HEADERS",
+            Http2FrameFlagsToString(Http2FrameType::PUSH_PROMISE, 0x04));
+  EXPECT_EQ("END_HEADERS", Http2FrameFlagsToString(0x09, 0x04));
+  EXPECT_EQ("0x04", Http2FrameFlagsToString(0xff, 0x04));
+
+  // 0b00001000
+  EXPECT_EQ("PADDED", Http2FrameFlagsToString(Http2FrameType::DATA,
+                                              Http2FrameFlag::FLAG_PADDED));
+  EXPECT_EQ("PADDED", Http2FrameFlagsToString(Http2FrameType::HEADERS, 0x08));
+  EXPECT_EQ("PADDED", Http2FrameFlagsToString(0x05, 0x08));
+  EXPECT_EQ("0x08", Http2FrameFlagsToString(0xff, Http2FrameFlag::FLAG_PADDED));
+
+  // 0b00010000
+  EXPECT_EQ("0x10", Http2FrameFlagsToString(Http2FrameType::SETTINGS, 0x10));
+
+  // 0b00100000
+  EXPECT_EQ("PRIORITY", Http2FrameFlagsToString(Http2FrameType::HEADERS, 0x20));
+  EXPECT_EQ("0x20",
+            Http2FrameFlagsToString(Http2FrameType::PUSH_PROMISE, 0x20));
+
+  // 0b01000000
+  EXPECT_EQ("0x40", Http2FrameFlagsToString(0xff, 0x40));
+
+  // 0b10000000
+  EXPECT_EQ("0x80", Http2FrameFlagsToString(0xff, 0x80));
+
+  // Combined flags...
+
+  EXPECT_EQ("END_STREAM|PADDED|0xf6",
+            Http2FrameFlagsToString(Http2FrameType::DATA, 0xff));
+  EXPECT_EQ("END_STREAM|END_HEADERS|PADDED|PRIORITY|0xd2",
+            Http2FrameFlagsToString(Http2FrameType::HEADERS, 0xff));
+  EXPECT_EQ("0xff", Http2FrameFlagsToString(Http2FrameType::PRIORITY, 0xff));
+  EXPECT_EQ("0xff", Http2FrameFlagsToString(Http2FrameType::RST_STREAM, 0xff));
+  EXPECT_EQ("ACK|0xfe",
+            Http2FrameFlagsToString(Http2FrameType::SETTINGS, 0xff));
+  EXPECT_EQ("END_HEADERS|PADDED|0xf3",
+            Http2FrameFlagsToString(Http2FrameType::PUSH_PROMISE, 0xff));
+  EXPECT_EQ("ACK|0xfe", Http2FrameFlagsToString(Http2FrameType::PING, 0xff));
+  EXPECT_EQ("0xff", Http2FrameFlagsToString(Http2FrameType::GOAWAY, 0xff));
+  EXPECT_EQ("0xff",
+            Http2FrameFlagsToString(Http2FrameType::WINDOW_UPDATE, 0xff));
+  EXPECT_EQ("END_HEADERS|0xfb",
+            Http2FrameFlagsToString(Http2FrameType::CONTINUATION, 0xff));
+  EXPECT_EQ("0xff", Http2FrameFlagsToString(Http2FrameType::ALTSVC, 0xff));
+  EXPECT_EQ("0xff", Http2FrameFlagsToString(0xff, 0xff));
+}
+
+TEST(Http2ConstantsTest, Http2ErrorCode) {
+  EXPECT_EQ(Http2ErrorCode::HTTP2_NO_ERROR, static_cast<Http2ErrorCode>(0x0));
+  EXPECT_EQ(Http2ErrorCode::PROTOCOL_ERROR, static_cast<Http2ErrorCode>(0x1));
+  EXPECT_EQ(Http2ErrorCode::INTERNAL_ERROR, static_cast<Http2ErrorCode>(0x2));
+  EXPECT_EQ(Http2ErrorCode::FLOW_CONTROL_ERROR,
+            static_cast<Http2ErrorCode>(0x3));
+  EXPECT_EQ(Http2ErrorCode::SETTINGS_TIMEOUT, static_cast<Http2ErrorCode>(0x4));
+  EXPECT_EQ(Http2ErrorCode::STREAM_CLOSED, static_cast<Http2ErrorCode>(0x5));
+  EXPECT_EQ(Http2ErrorCode::FRAME_SIZE_ERROR, static_cast<Http2ErrorCode>(0x6));
+  EXPECT_EQ(Http2ErrorCode::REFUSED_STREAM, static_cast<Http2ErrorCode>(0x7));
+  EXPECT_EQ(Http2ErrorCode::CANCEL, static_cast<Http2ErrorCode>(0x8));
+  EXPECT_EQ(Http2ErrorCode::COMPRESSION_ERROR,
+            static_cast<Http2ErrorCode>(0x9));
+  EXPECT_EQ(Http2ErrorCode::CONNECT_ERROR, static_cast<Http2ErrorCode>(0xa));
+  EXPECT_EQ(Http2ErrorCode::ENHANCE_YOUR_CALM,
+            static_cast<Http2ErrorCode>(0xb));
+  EXPECT_EQ(Http2ErrorCode::INADEQUATE_SECURITY,
+            static_cast<Http2ErrorCode>(0xc));
+  EXPECT_EQ(Http2ErrorCode::HTTP_1_1_REQUIRED,
+            static_cast<Http2ErrorCode>(0xd));
+}
+
+TEST(Http2ConstantsTest, Http2ErrorCodeToString) {
+  EXPECT_EQ("NO_ERROR", Http2ErrorCodeToString(Http2ErrorCode::HTTP2_NO_ERROR));
+  EXPECT_EQ("NO_ERROR", Http2ErrorCodeToString(0x0));
+  EXPECT_EQ("PROTOCOL_ERROR",
+            Http2ErrorCodeToString(Http2ErrorCode::PROTOCOL_ERROR));
+  EXPECT_EQ("PROTOCOL_ERROR", Http2ErrorCodeToString(0x1));
+  EXPECT_EQ("INTERNAL_ERROR",
+            Http2ErrorCodeToString(Http2ErrorCode::INTERNAL_ERROR));
+  EXPECT_EQ("INTERNAL_ERROR", Http2ErrorCodeToString(0x2));
+  EXPECT_EQ("FLOW_CONTROL_ERROR",
+            Http2ErrorCodeToString(Http2ErrorCode::FLOW_CONTROL_ERROR));
+  EXPECT_EQ("FLOW_CONTROL_ERROR", Http2ErrorCodeToString(0x3));
+  EXPECT_EQ("SETTINGS_TIMEOUT",
+            Http2ErrorCodeToString(Http2ErrorCode::SETTINGS_TIMEOUT));
+  EXPECT_EQ("SETTINGS_TIMEOUT", Http2ErrorCodeToString(0x4));
+  EXPECT_EQ("STREAM_CLOSED",
+            Http2ErrorCodeToString(Http2ErrorCode::STREAM_CLOSED));
+  EXPECT_EQ("STREAM_CLOSED", Http2ErrorCodeToString(0x5));
+  EXPECT_EQ("FRAME_SIZE_ERROR",
+            Http2ErrorCodeToString(Http2ErrorCode::FRAME_SIZE_ERROR));
+  EXPECT_EQ("FRAME_SIZE_ERROR", Http2ErrorCodeToString(0x6));
+  EXPECT_EQ("REFUSED_STREAM",
+            Http2ErrorCodeToString(Http2ErrorCode::REFUSED_STREAM));
+  EXPECT_EQ("REFUSED_STREAM", Http2ErrorCodeToString(0x7));
+  EXPECT_EQ("CANCEL", Http2ErrorCodeToString(Http2ErrorCode::CANCEL));
+  EXPECT_EQ("CANCEL", Http2ErrorCodeToString(0x8));
+  EXPECT_EQ("COMPRESSION_ERROR",
+            Http2ErrorCodeToString(Http2ErrorCode::COMPRESSION_ERROR));
+  EXPECT_EQ("COMPRESSION_ERROR", Http2ErrorCodeToString(0x9));
+  EXPECT_EQ("CONNECT_ERROR",
+            Http2ErrorCodeToString(Http2ErrorCode::CONNECT_ERROR));
+  EXPECT_EQ("CONNECT_ERROR", Http2ErrorCodeToString(0xa));
+  EXPECT_EQ("ENHANCE_YOUR_CALM",
+            Http2ErrorCodeToString(Http2ErrorCode::ENHANCE_YOUR_CALM));
+  EXPECT_EQ("ENHANCE_YOUR_CALM", Http2ErrorCodeToString(0xb));
+  EXPECT_EQ("INADEQUATE_SECURITY",
+            Http2ErrorCodeToString(Http2ErrorCode::INADEQUATE_SECURITY));
+  EXPECT_EQ("INADEQUATE_SECURITY", Http2ErrorCodeToString(0xc));
+  EXPECT_EQ("HTTP_1_1_REQUIRED",
+            Http2ErrorCodeToString(Http2ErrorCode::HTTP_1_1_REQUIRED));
+  EXPECT_EQ("HTTP_1_1_REQUIRED", Http2ErrorCodeToString(0xd));
+
+  EXPECT_EQ("UnknownErrorCode(0x123)", Http2ErrorCodeToString(0x123));
+}
+
+TEST(Http2ConstantsTest, Http2SettingsParameter) {
+  EXPECT_EQ(Http2SettingsParameter::HEADER_TABLE_SIZE,
+            static_cast<Http2SettingsParameter>(0x1));
+  EXPECT_EQ(Http2SettingsParameter::ENABLE_PUSH,
+            static_cast<Http2SettingsParameter>(0x2));
+  EXPECT_EQ(Http2SettingsParameter::MAX_CONCURRENT_STREAMS,
+            static_cast<Http2SettingsParameter>(0x3));
+  EXPECT_EQ(Http2SettingsParameter::INITIAL_WINDOW_SIZE,
+            static_cast<Http2SettingsParameter>(0x4));
+  EXPECT_EQ(Http2SettingsParameter::MAX_FRAME_SIZE,
+            static_cast<Http2SettingsParameter>(0x5));
+  EXPECT_EQ(Http2SettingsParameter::MAX_HEADER_LIST_SIZE,
+            static_cast<Http2SettingsParameter>(0x6));
+
+  EXPECT_TRUE(IsSupportedHttp2SettingsParameter(
+      Http2SettingsParameter::HEADER_TABLE_SIZE));
+  EXPECT_TRUE(
+      IsSupportedHttp2SettingsParameter(Http2SettingsParameter::ENABLE_PUSH));
+  EXPECT_TRUE(IsSupportedHttp2SettingsParameter(
+      Http2SettingsParameter::MAX_CONCURRENT_STREAMS));
+  EXPECT_TRUE(IsSupportedHttp2SettingsParameter(
+      Http2SettingsParameter::INITIAL_WINDOW_SIZE));
+  EXPECT_TRUE(IsSupportedHttp2SettingsParameter(
+      Http2SettingsParameter::MAX_FRAME_SIZE));
+  EXPECT_TRUE(IsSupportedHttp2SettingsParameter(
+      Http2SettingsParameter::MAX_HEADER_LIST_SIZE));
+
+  EXPECT_FALSE(IsSupportedHttp2SettingsParameter(
+      static_cast<Http2SettingsParameter>(0)));
+  EXPECT_FALSE(IsSupportedHttp2SettingsParameter(
+      static_cast<Http2SettingsParameter>(7)));
+}
+
+TEST(Http2ConstantsTest, Http2SettingsParameterToString) {
+  EXPECT_EQ("HEADER_TABLE_SIZE",
+            Http2SettingsParameterToString(
+                Http2SettingsParameter::HEADER_TABLE_SIZE));
+  EXPECT_EQ("HEADER_TABLE_SIZE", Http2SettingsParameterToString(0x1));
+  EXPECT_EQ("ENABLE_PUSH", Http2SettingsParameterToString(
+                               Http2SettingsParameter::ENABLE_PUSH));
+  EXPECT_EQ("ENABLE_PUSH", Http2SettingsParameterToString(0x2));
+  EXPECT_EQ("MAX_CONCURRENT_STREAMS",
+            Http2SettingsParameterToString(
+                Http2SettingsParameter::MAX_CONCURRENT_STREAMS));
+  EXPECT_EQ("MAX_CONCURRENT_STREAMS", Http2SettingsParameterToString(0x3));
+  EXPECT_EQ("INITIAL_WINDOW_SIZE",
+            Http2SettingsParameterToString(
+                Http2SettingsParameter::INITIAL_WINDOW_SIZE));
+  EXPECT_EQ("INITIAL_WINDOW_SIZE", Http2SettingsParameterToString(0x4));
+  EXPECT_EQ("MAX_FRAME_SIZE", Http2SettingsParameterToString(
+                                  Http2SettingsParameter::MAX_FRAME_SIZE));
+  EXPECT_EQ("MAX_FRAME_SIZE", Http2SettingsParameterToString(0x5));
+  EXPECT_EQ("MAX_HEADER_LIST_SIZE",
+            Http2SettingsParameterToString(
+                Http2SettingsParameter::MAX_HEADER_LIST_SIZE));
+  EXPECT_EQ("MAX_HEADER_LIST_SIZE", Http2SettingsParameterToString(0x6));
+
+  EXPECT_EQ("UnknownSettingsParameter(0x123)",
+            Http2SettingsParameterToString(0x123));
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/http2_constants_test_util.cc b/net/http2/http2_constants_test_util.cc
new file mode 100644
index 0000000..2f4be40
--- /dev/null
+++ b/net/http2/http2_constants_test_util.cc
@@ -0,0 +1,142 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/http2_constants_test_util.h"
+
+namespace net {
+namespace test {
+
+std::vector<Http2FrameType> AllHttp2FrameTypes() {
+  // clang-format off
+  return {
+      Http2FrameType::DATA,
+      Http2FrameType::HEADERS,
+      Http2FrameType::PRIORITY,
+      Http2FrameType::RST_STREAM,
+      Http2FrameType::SETTINGS,
+      Http2FrameType::PUSH_PROMISE,
+      Http2FrameType::PING,
+      Http2FrameType::GOAWAY,
+      Http2FrameType::WINDOW_UPDATE,
+      Http2FrameType::CONTINUATION,
+      Http2FrameType::ALTSVC,
+  };
+  // clang-format on
+}
+
+std::vector<Http2FrameFlag> AllHttp2FrameFlagsForFrameType(
+    Http2FrameType type) {
+  // clang-format off
+  switch (type) {
+    case Http2FrameType::DATA:
+      return {
+          Http2FrameFlag::FLAG_END_STREAM,
+          Http2FrameFlag::FLAG_PADDED,
+      };
+    case Http2FrameType::HEADERS:
+      return {
+          Http2FrameFlag::FLAG_END_STREAM,
+          Http2FrameFlag::FLAG_END_HEADERS,
+          Http2FrameFlag::FLAG_PADDED,
+          Http2FrameFlag::FLAG_PRIORITY,
+      };
+    case Http2FrameType::SETTINGS:
+      return {
+          Http2FrameFlag::FLAG_ACK,
+      };
+    case Http2FrameType::PUSH_PROMISE:
+      return {
+          Http2FrameFlag::FLAG_END_HEADERS,
+          Http2FrameFlag::FLAG_PADDED,
+      };
+    case Http2FrameType::PING:
+      return {
+          Http2FrameFlag::FLAG_ACK,
+      };
+    case Http2FrameType::CONTINUATION:
+      return {
+          Http2FrameFlag::FLAG_END_HEADERS,
+      };
+    default:
+      return std::vector<Http2FrameFlag>{};
+  }
+  // clang-format on
+}
+
+std::vector<Http2ErrorCode> AllHttp2ErrorCodes() {
+  // clang-format off
+  return {
+      Http2ErrorCode::HTTP2_NO_ERROR,
+      Http2ErrorCode::PROTOCOL_ERROR,
+      Http2ErrorCode::INTERNAL_ERROR,
+      Http2ErrorCode::FLOW_CONTROL_ERROR,
+      Http2ErrorCode::SETTINGS_TIMEOUT,
+      Http2ErrorCode::STREAM_CLOSED,
+      Http2ErrorCode::FRAME_SIZE_ERROR,
+      Http2ErrorCode::REFUSED_STREAM,
+      Http2ErrorCode::CANCEL,
+      Http2ErrorCode::COMPRESSION_ERROR,
+      Http2ErrorCode::CONNECT_ERROR,
+      Http2ErrorCode::ENHANCE_YOUR_CALM,
+      Http2ErrorCode::INADEQUATE_SECURITY,
+      Http2ErrorCode::HTTP_1_1_REQUIRED,
+  };
+  // clang-format on
+}
+
+std::vector<Http2SettingsParameter> AllHttp2SettingsParameters() {
+  // clang-format off
+  return {
+      Http2SettingsParameter::HEADER_TABLE_SIZE,
+      Http2SettingsParameter::ENABLE_PUSH,
+      Http2SettingsParameter::MAX_CONCURRENT_STREAMS,
+      Http2SettingsParameter::INITIAL_WINDOW_SIZE,
+      Http2SettingsParameter::MAX_FRAME_SIZE,
+      Http2SettingsParameter::MAX_HEADER_LIST_SIZE,
+  };
+  // clang-format on
+}
+
+// Returns a mask of flags supported for the specified frame type. Returns
+// zero for unknown frame types.
+uint8_t KnownFlagsMaskForFrameType(Http2FrameType type) {
+  switch (type) {
+    case Http2FrameType::DATA:
+      return Http2FrameFlag::FLAG_END_STREAM | Http2FrameFlag::FLAG_PADDED;
+    case Http2FrameType::HEADERS:
+      return Http2FrameFlag::FLAG_END_STREAM |
+             Http2FrameFlag::FLAG_END_HEADERS | Http2FrameFlag::FLAG_PADDED |
+             Http2FrameFlag::FLAG_PRIORITY;
+    case Http2FrameType::PRIORITY:
+      return 0x00;
+    case Http2FrameType::RST_STREAM:
+      return 0x00;
+    case Http2FrameType::SETTINGS:
+      return Http2FrameFlag::FLAG_ACK;
+    case Http2FrameType::PUSH_PROMISE:
+      return Http2FrameFlag::FLAG_END_HEADERS | Http2FrameFlag::FLAG_PADDED;
+    case Http2FrameType::PING:
+      return Http2FrameFlag::FLAG_ACK;
+    case Http2FrameType::GOAWAY:
+      return 0x00;
+    case Http2FrameType::WINDOW_UPDATE:
+      return 0x00;
+    case Http2FrameType::CONTINUATION:
+      return Http2FrameFlag::FLAG_END_HEADERS;
+    case Http2FrameType::ALTSVC:
+      return 0x00;
+    default:
+      return 0x00;
+  }
+}
+
+uint8_t InvalidFlagMaskForFrameType(Http2FrameType type) {
+  if (IsSupportedHttp2FrameType(type)) {
+    return ~KnownFlagsMaskForFrameType(type);
+  }
+  return 0x00;
+}
+
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/http2_constants_test_util.h b/net/http2/http2_constants_test_util.h
new file mode 100644
index 0000000..6d20cec
--- /dev/null
+++ b/net/http2/http2_constants_test_util.h
@@ -0,0 +1,40 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_HTTP2_CONSTANTS_TEST_UTIL_H_
+#define NET_HTTP2_HTTP2_CONSTANTS_TEST_UTIL_H_
+
+#include <vector>
+
+#include "net/http2/http2_constants.h"
+
+namespace net {
+namespace test {
+
+// Returns a vector of all supported frame types.
+std::vector<Http2FrameType> AllHttp2FrameTypes();
+
+// Returns a vector of all supported frame flags for the specified
+// frame type. Empty if the type is unknown.
+std::vector<Http2FrameFlag> AllHttp2FrameFlagsForFrameType(Http2FrameType type);
+
+// Returns a vector of all supported RST_STREAM and GOAWAY error codes.
+std::vector<Http2ErrorCode> AllHttp2ErrorCodes();
+
+// Returns a vector of all supported parameters in SETTINGS frames.
+std::vector<Http2SettingsParameter> AllHttp2SettingsParameters();
+
+// Returns a mask of flags supported for the specified frame type. Returns
+// zero for unknown frame types.
+uint8_t KnownFlagsMaskForFrameType(Http2FrameType type);
+
+// Returns a mask of flag bits known to be invalid for the frame type.
+// For unknown frame types, the mask is zero; i.e., we don't know that any
+// are invalid.
+uint8_t InvalidFlagMaskForFrameType(Http2FrameType type);
+
+}  // namespace test
+}  // namespace net
+
+#endif  // NET_HTTP2_HTTP2_CONSTANTS_TEST_UTIL_H_
diff --git a/net/http2/http2_structures.cc b/net/http2/http2_structures.cc
new file mode 100644
index 0000000..83360430
--- /dev/null
+++ b/net/http2/http2_structures.cc
@@ -0,0 +1,138 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/http2_structures.h"
+
+#include <cstring>  // For std::memcmp
+#include <sstream>
+#include <string>
+
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+
+using std::string;
+using base::StringPiece;
+
+namespace net {
+
+// Http2FrameHeader:
+
+bool Http2FrameHeader::IsProbableHttpResponse() const {
+  return (payload_length == 0x485454 &&      // "HTT"
+          static_cast<char>(type) == 'P' &&  // "P"
+          flags == '/');                     // "/"
+}
+
+string Http2FrameHeader::ToString() const {
+  std::stringstream ss;
+  ss << "length=" << payload_length << ", type=" << Http2FrameTypeToString(type)
+     << ", flags=" << FlagsToString() << ", stream=" << stream_id;
+  return ss.str();
+}
+
+string Http2FrameHeader::FlagsToString() const {
+  return Http2FrameFlagsToString(type, flags);
+}
+
+bool operator==(const Http2FrameHeader& a, const Http2FrameHeader& b) {
+  return a.payload_length == b.payload_length && a.stream_id == b.stream_id &&
+         a.type == b.type && a.flags == b.flags;
+}
+
+std::ostream& operator<<(std::ostream& out, const Http2FrameHeader& v) {
+  return out << v.ToString();
+}
+
+// Http2PriorityFields:
+
+bool operator==(const Http2PriorityFields& a, const Http2PriorityFields& b) {
+  return a.stream_dependency == b.stream_dependency && a.weight == b.weight;
+}
+
+std::string Http2PriorityFields::ToString() const {
+  std::stringstream ss;
+  ss << "E=" << (is_exclusive ? "true" : "false")
+     << ", stream=" << stream_dependency
+     << ", weight=" << static_cast<uint32_t>(weight);
+  return ss.str();
+}
+
+std::ostream& operator<<(std::ostream& out, const Http2PriorityFields& v) {
+  return out << v.ToString();
+}
+
+// Http2RstStreamFields:
+
+bool operator==(const Http2RstStreamFields& a, const Http2RstStreamFields& b) {
+  return a.error_code == b.error_code;
+}
+
+std::ostream& operator<<(std::ostream& out, const Http2RstStreamFields& v) {
+  return out << "error_code=" << v.error_code;
+}
+
+// Http2SettingFields:
+
+bool operator==(const Http2SettingFields& a, const Http2SettingFields& b) {
+  return a.parameter == b.parameter && a.value == b.value;
+}
+std::ostream& operator<<(std::ostream& out, const Http2SettingFields& v) {
+  return out << "parameter=" << v.parameter << ", value=" << v.value;
+}
+
+// Http2PushPromiseFields:
+
+bool operator==(const Http2PushPromiseFields& a,
+                const Http2PushPromiseFields& b) {
+  return a.promised_stream_id == b.promised_stream_id;
+}
+
+std::ostream& operator<<(std::ostream& out, const Http2PushPromiseFields& v) {
+  return out << "promised_stream_id=" << v.promised_stream_id;
+}
+
+// Http2PingFields:
+
+bool operator==(const Http2PingFields& a, const Http2PingFields& b) {
+  static_assert((sizeof a.opaque_data) == Http2PingFields::EncodedSize(),
+                "Why not the same size?");
+  return 0 == std::memcmp(a.opaque_data, b.opaque_data, sizeof a.opaque_data);
+}
+
+std::ostream& operator<<(std::ostream& out, const Http2PingFields& v) {
+  string s = base::HexEncode(v.opaque_data, sizeof v.opaque_data);
+  base::CollapseWhitespaceASCII(s, /*trim_sequences_with_line_breaks=*/false);
+  return out << "opaque_data=[" << s << "]";
+}
+
+// Http2GoAwayFields:
+
+bool operator==(const Http2GoAwayFields& a, const Http2GoAwayFields& b) {
+  return a.last_stream_id == b.last_stream_id && a.error_code == b.error_code;
+}
+std::ostream& operator<<(std::ostream& out, const Http2GoAwayFields& v) {
+  return out << "last_stream_id=" << v.last_stream_id
+             << ", error_code=" << v.error_code;
+}
+
+// Http2WindowUpdateFields:
+
+bool operator==(const Http2WindowUpdateFields& a,
+                const Http2WindowUpdateFields& b) {
+  return a.window_size_increment == b.window_size_increment;
+}
+std::ostream& operator<<(std::ostream& out, const Http2WindowUpdateFields& v) {
+  return out << "window_size_increment=" << v.window_size_increment;
+}
+
+// Http2AltSvcFields:
+
+bool operator==(const Http2AltSvcFields& a, const Http2AltSvcFields& b) {
+  return a.origin_length == b.origin_length;
+}
+std::ostream& operator<<(std::ostream& out, const Http2AltSvcFields& v) {
+  return out << "origin_length=" << v.origin_length;
+}
+
+}  // namespace net
diff --git a/net/http2/http2_structures.h b/net/http2/http2_structures.h
new file mode 100644
index 0000000..95328e1
--- /dev/null
+++ b/net/http2/http2_structures.h
@@ -0,0 +1,326 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_HTTP2_STRUCTURES_H_
+#define NET_HTTP2_HTTP2_STRUCTURES_H_
+
+// Defines structs for various fixed sized structures in HTTP/2.
+//
+// Those structs with multiple fields have constructors that take arguments in
+// the same order as their encoding (which may be different from their order
+// in the struct). For single field structs, use aggregate initialization if
+// desired, e.g.:
+//
+//   Http2RstStreamFields var{Http2ErrorCode::ENHANCE_YOUR_CALM};
+// or:
+//   SomeFunc(Http2RstStreamFields{Http2ErrorCode::ENHANCE_YOUR_CALM});
+//
+// Each struct includes a static method EncodedSize which returns the number
+// of bytes of the encoding.
+//
+// With the exception of Http2FrameHeader, all the types are named
+// Http2<X>Fields, where X is the title-case form of the frame which always
+// includes the fields; the "always" is to cover the case of the PRIORITY frame;
+// its fields optionally appear in the HEADERS frame, but the struct is called
+// Http2PriorityFields.
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <ostream>
+#include <string>
+
+#include "base/logging.h"
+#include "net/base/net_export.h"
+#include "net/http2/http2_constants.h"
+
+namespace net {
+
+struct NET_EXPORT_PRIVATE Http2FrameHeader {
+  Http2FrameHeader() {}
+  Http2FrameHeader(uint32_t payload_length,
+                   Http2FrameType type,
+                   uint8_t flags,
+                   uint32_t stream_id)
+      : payload_length(payload_length),
+        stream_id(stream_id),
+        type(type),
+        flags(static_cast<Http2FrameFlag>(flags)) {
+    DCHECK_LT(payload_length, static_cast<uint32_t>(1 << 24))
+        << "Payload Length is only a 24 bit field\n"
+        << ToString();
+  }
+
+  static constexpr size_t EncodedSize() { return 9; }
+
+  // Keep the current value of those flags that are in
+  // valid_flags, and clear all the others.
+  void RetainFlags(uint8_t valid_flags) {
+    flags = static_cast<Http2FrameFlag>(flags & valid_flags);
+  }
+
+  // Returns true if any of the flags in flag_mask are set,
+  // otherwise false.
+  bool HasAnyFlags(uint8_t flag_mask) const { return 0 != (flags & flag_mask); }
+
+  // Is the END_STREAM flag set?
+  bool IsEndStream() const {
+    DCHECK(type == Http2FrameType::DATA || type == Http2FrameType::HEADERS)
+        << ToString();
+    return (flags & Http2FrameFlag::FLAG_END_STREAM) != 0;
+  }
+
+  // Is the ACK flag set?
+  bool IsAck() const {
+    DCHECK(type == Http2FrameType::SETTINGS || type == Http2FrameType::PING)
+        << ToString();
+    return (flags & Http2FrameFlag::FLAG_ACK) != 0;
+  }
+
+  // Is the END_HEADERS flag set?
+  bool IsEndHeaders() const {
+    DCHECK(type == Http2FrameType::HEADERS ||
+           type == Http2FrameType::PUSH_PROMISE ||
+           type == Http2FrameType::CONTINUATION)
+        << ToString();
+    return (flags & Http2FrameFlag::FLAG_END_HEADERS) != 0;
+  }
+
+  // Is the PADDED flag set?
+  bool IsPadded() const {
+    DCHECK(type == Http2FrameType::DATA || type == Http2FrameType::HEADERS ||
+           type == Http2FrameType::PUSH_PROMISE)
+        << ToString();
+    return (flags & Http2FrameFlag::FLAG_PADDED) != 0;
+  }
+
+  // Is the PRIORITY flag set?
+  bool HasPriority() const {
+    DCHECK_EQ(type, Http2FrameType::HEADERS) << ToString();
+    return (flags & Http2FrameFlag::FLAG_PRIORITY) != 0;
+  }
+
+  // Does the encoding of this header start with "HTTP/", indicating that it
+  // might be from a non-HTTP/2 server.
+  bool IsProbableHttpResponse() const;
+
+  // Produce strings useful for debugging/logging messages.
+  std::string ToString() const;
+  std::string FlagsToString() const;
+
+  // 24 bit length of the payload after the header, including any padding.
+  // First field in encoding.
+  uint32_t payload_length;  // 24 bits
+
+  // 31 bit stream id, with high bit (32nd bit) reserved (must be zero),
+  // and is cleared during decoding.
+  // Fourth field in encoding.
+  uint32_t stream_id;
+
+  // Type of the frame.
+  // Second field in encoding.
+  Http2FrameType type;
+
+  // Flag bits, with interpretations that depend upon the frame type.
+  // Flag bits not used by the frame type are cleared.
+  // Third field in encoding.
+  Http2FrameFlag flags;
+};
+
+NET_EXPORT_PRIVATE bool operator==(const Http2FrameHeader& a,
+                                   const Http2FrameHeader& b);
+NET_EXPORT_PRIVATE inline bool operator!=(const Http2FrameHeader& a,
+                                          const Http2FrameHeader& b) {
+  return !(a == b);
+}
+NET_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& out,
+                                            const Http2FrameHeader& v);
+
+// Http2PriorityFields:
+
+struct NET_EXPORT_PRIVATE Http2PriorityFields {
+  Http2PriorityFields() {}
+  Http2PriorityFields(uint32_t stream_dependency,
+                      uint32_t weight,
+                      bool is_exclusive)
+      : stream_dependency(stream_dependency),
+        weight(weight),
+        is_exclusive(is_exclusive) {
+    // Can't have the high-bit set in the stream id because we need to use
+    // that for the EXCLUSIVE flag bit.
+    DCHECK_EQ(stream_dependency, stream_dependency & StreamIdMask())
+        << "Stream Dependency is only a 31-bit field.\n"
+        << ToString();
+    DCHECK_LE(1u, weight) << "Weight is too small.";
+    DCHECK_LE(weight, 256u) << "Weight is too large.";
+  }
+  static constexpr size_t EncodedSize() { return 5; }
+
+  // Produce strings useful for debugging/logging messages.
+  std::string ToString() const;
+
+  // A 31-bit stream identifier for the stream that this stream depends on.
+  uint32_t stream_dependency;
+
+  // Weight (1 to 256) is encoded as a byte in the range 0 to 255, so we
+  // add one when decoding, and store it in a field larger than a byte.
+  uint32_t weight;
+
+  // A single-bit flag indicating that the stream dependency is exclusive;
+  // extracted from high bit of stream dependency field during decoding.
+  bool is_exclusive;
+};
+
+NET_EXPORT_PRIVATE bool operator==(const Http2PriorityFields& a,
+                                   const Http2PriorityFields& b);
+NET_EXPORT_PRIVATE inline bool operator!=(const Http2PriorityFields& a,
+                                          const Http2PriorityFields& b) {
+  return !(a == b);
+}
+NET_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& out,
+                                            const Http2PriorityFields& v);
+
+// Http2RstStreamFields:
+
+struct Http2RstStreamFields {
+  static constexpr size_t EncodedSize() { return 4; }
+  bool IsSupportedErrorCode() const {
+    return IsSupportedHttp2ErrorCode(error_code);
+  }
+
+  Http2ErrorCode error_code;
+};
+
+NET_EXPORT_PRIVATE bool operator==(const Http2RstStreamFields& a,
+                                   const Http2RstStreamFields& b);
+NET_EXPORT_PRIVATE inline bool operator!=(const Http2RstStreamFields& a,
+                                          const Http2RstStreamFields& b) {
+  return !(a == b);
+}
+NET_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& out,
+                                            const Http2RstStreamFields& v);
+
+// Http2SettingFields:
+
+struct Http2SettingFields {
+  Http2SettingFields() {}
+  Http2SettingFields(Http2SettingsParameter parameter, uint32_t value)
+      : parameter(parameter), value(value) {}
+  static constexpr size_t EncodedSize() { return 6; }
+  bool IsSupportedParameter() const {
+    return IsSupportedHttp2SettingsParameter(parameter);
+  }
+
+  Http2SettingsParameter parameter;
+  uint32_t value;
+};
+
+NET_EXPORT_PRIVATE bool operator==(const Http2SettingFields& a,
+                                   const Http2SettingFields& b);
+NET_EXPORT_PRIVATE inline bool operator!=(const Http2SettingFields& a,
+                                          const Http2SettingFields& b) {
+  return !(a == b);
+}
+NET_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& out,
+                                            const Http2SettingFields& v);
+
+// Http2PushPromiseFields:
+
+struct Http2PushPromiseFields {
+  static constexpr size_t EncodedSize() { return 4; }
+
+  uint32_t promised_stream_id;
+};
+
+NET_EXPORT_PRIVATE bool operator==(const Http2PushPromiseFields& a,
+                                   const Http2PushPromiseFields& b);
+NET_EXPORT_PRIVATE inline bool operator!=(const Http2PushPromiseFields& a,
+                                          const Http2PushPromiseFields& b) {
+  return !(a == b);
+}
+NET_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& out,
+                                            const Http2PushPromiseFields& v);
+
+// Http2PingFields:
+
+struct Http2PingFields {
+  static constexpr size_t EncodedSize() { return 8; }
+
+  // TODO(jamessynge): Rename opaque_data to opaque_bytes.
+  uint8_t opaque_data[8];
+};
+
+NET_EXPORT_PRIVATE bool operator==(const Http2PingFields& a,
+                                   const Http2PingFields& b);
+NET_EXPORT_PRIVATE inline bool operator!=(const Http2PingFields& a,
+                                          const Http2PingFields& b) {
+  return !(a == b);
+}
+NET_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& out,
+                                            const Http2PingFields& v);
+
+// Http2GoAwayFields:
+
+struct Http2GoAwayFields {
+  Http2GoAwayFields() {}
+  Http2GoAwayFields(uint32_t last_stream_id, Http2ErrorCode error_code)
+      : last_stream_id(last_stream_id), error_code(error_code) {}
+  static constexpr size_t EncodedSize() { return 8; }
+  bool IsSupportedErrorCode() const {
+    return IsSupportedHttp2ErrorCode(error_code);
+  }
+
+  uint32_t last_stream_id;
+  Http2ErrorCode error_code;
+};
+
+NET_EXPORT_PRIVATE bool operator==(const Http2GoAwayFields& a,
+                                   const Http2GoAwayFields& b);
+NET_EXPORT_PRIVATE inline bool operator!=(const Http2GoAwayFields& a,
+                                          const Http2GoAwayFields& b) {
+  return !(a == b);
+}
+NET_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& out,
+                                            const Http2GoAwayFields& v);
+
+// Http2WindowUpdateFields:
+
+struct Http2WindowUpdateFields {
+  static constexpr size_t EncodedSize() { return 4; }
+
+  // 31-bit, unsigned increase in the window size (only positive values are
+  // allowed). The high-bit is reserved for the future.
+  uint32_t window_size_increment;
+};
+
+NET_EXPORT_PRIVATE bool operator==(const Http2WindowUpdateFields& a,
+                                   const Http2WindowUpdateFields& b);
+NET_EXPORT_PRIVATE inline bool operator!=(const Http2WindowUpdateFields& a,
+                                          const Http2WindowUpdateFields& b) {
+  return !(a == b);
+}
+NET_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& out,
+                                            const Http2WindowUpdateFields& v);
+
+// Http2AltSvcFields:
+
+struct Http2AltSvcFields {
+  static constexpr size_t EncodedSize() { return 2; }
+
+  // This is the one fixed size portion of the ALTSVC payload.
+  uint16_t origin_length;
+};
+
+NET_EXPORT_PRIVATE bool operator==(const Http2AltSvcFields& a,
+                                   const Http2AltSvcFields& b);
+NET_EXPORT_PRIVATE inline bool operator!=(const Http2AltSvcFields& a,
+                                          const Http2AltSvcFields& b) {
+  return !(a == b);
+}
+NET_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& out,
+                                            const Http2AltSvcFields& v);
+
+}  // namespace net
+
+#endif  // NET_HTTP2_HTTP2_STRUCTURES_H_
diff --git a/net/http2/http2_structures_test.cc b/net/http2/http2_structures_test.cc
new file mode 100644
index 0000000..96035c77
--- /dev/null
+++ b/net/http2/http2_structures_test.cc
@@ -0,0 +1,486 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/http2_structures.h"
+
+// Tests are focused on Http2FrameHeader because it has by far the most
+// methods of any of the structures.
+// Note that EXPECT.*DEATH tests are slow (a fork is probably involved).
+
+// And in case you're wondering, yes, these are ridiculously thorough tests,
+// but believe it or not, I've found stupid bugs this way.
+
+#include <memory>
+#include <ostream>
+#include <sstream>
+#include <tuple>
+#include <type_traits>
+#include <vector>
+
+#include "base/template_util.h"
+#include "net/http2/http2_structures_test_util.h"
+#include "net/http2/tools/failure.h"
+#include "net/http2/tools/http2_random.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::AssertionResult;
+using ::testing::AssertionSuccess;
+using ::testing::Combine;
+using ::testing::EndsWith;
+using ::testing::HasSubstr;
+using ::testing::MatchesRegex;
+using ::testing::Not;
+using ::testing::Values;
+using ::testing::ValuesIn;
+
+namespace net {
+namespace test {
+namespace {
+
+template <typename E>
+E IncrementEnum(E e) {
+  typedef typename base::underlying_type<E>::type I;
+  return static_cast<E>(1 + static_cast<I>(e));
+}
+
+#if GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
+std::vector<Http2FrameType> ValidFrameTypes() {
+  std::vector<Http2FrameType> valid_types{Http2FrameType::DATA};
+  while (valid_types.back() != Http2FrameType::ALTSVC) {
+    valid_types.push_back(IncrementEnum(valid_types.back()));
+  }
+  return valid_types;
+}
+#endif  // GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
+
+TEST(Http2FrameHeaderTest, Constructor) {
+  Http2Random random;
+  uint8_t frame_type = 0;
+  do {
+    // Only the payload length is DCHECK'd in the constructor, so we need to
+    // make sure it is a "uint24".
+    uint32_t payload_length = random.Rand32() & 0xffffff;
+    Http2FrameType type = static_cast<Http2FrameType>(frame_type);
+    uint8_t flags = random.Rand8();
+    uint32_t stream_id = random.Rand32();
+
+    Http2FrameHeader v(payload_length, type, flags, stream_id);
+
+    EXPECT_EQ(payload_length, v.payload_length);
+    EXPECT_EQ(type, v.type);
+    EXPECT_EQ(flags, v.flags);
+    EXPECT_EQ(stream_id, v.stream_id);
+  } while (frame_type++ == 255);
+
+#if GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
+  EXPECT_DEBUG_DEATH(Http2FrameHeader(0x01000000, Http2FrameType::DATA, 0, 1),
+                     "payload_length");
+#endif  // GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
+}
+
+TEST(Http2FrameHeaderTest, Eq) {
+  Http2Random random;
+  uint32_t payload_length = random.Rand32() & 0xffffff;
+  Http2FrameType type = static_cast<Http2FrameType>(random.Rand8());
+
+  uint8_t flags = random.Rand8();
+  uint32_t stream_id = random.Rand32();
+
+  Http2FrameHeader v(payload_length, type, flags, stream_id);
+
+  EXPECT_EQ(payload_length, v.payload_length);
+  EXPECT_EQ(type, v.type);
+  EXPECT_EQ(flags, v.flags);
+  EXPECT_EQ(stream_id, v.stream_id);
+
+  Http2FrameHeader u(0, type, ~flags, stream_id);
+
+  EXPECT_NE(u, v);
+  EXPECT_NE(v, u);
+  EXPECT_FALSE(u == v);
+  EXPECT_FALSE(v == u);
+  EXPECT_TRUE(u != v);
+  EXPECT_TRUE(v != u);
+
+  u = v;
+
+  EXPECT_EQ(u, v);
+  EXPECT_EQ(v, u);
+  EXPECT_TRUE(u == v);
+  EXPECT_TRUE(v == u);
+  EXPECT_FALSE(u != v);
+  EXPECT_FALSE(v != u);
+}
+
+#if GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
+// The tests of the valid frame types include EXPECT_DEBUG_DEATH, which is
+// quite slow, so using value parameterized tests in order to allow sharding.
+class Http2FrameHeaderTypeAndFlagTest
+    : public ::testing::TestWithParam<
+          std::tuple<Http2FrameType, Http2FrameFlag>> {
+ protected:
+  Http2FrameHeaderTypeAndFlagTest()
+      : type_(std::get<0>(GetParam())), flags_(std::get<1>(GetParam())) {
+    LOG(INFO) << "Frame type: " << type_;
+    LOG(INFO) << "Frame flags: " << Http2FrameFlagsToString(type_, flags_);
+  }
+
+  const Http2FrameType type_;
+  const Http2FrameFlag flags_;
+};
+
+class IsEndStreamTest : public Http2FrameHeaderTypeAndFlagTest {};
+INSTANTIATE_TEST_CASE_P(IsEndStream,
+                        IsEndStreamTest,
+                        Combine(ValuesIn(ValidFrameTypes()),
+                                Values(~Http2FrameFlag::FLAG_END_STREAM,
+                                       0xff)));
+TEST_P(IsEndStreamTest, IsEndStream) {
+  const bool is_set = (flags_ & Http2FrameFlag::FLAG_END_STREAM) ==
+                      Http2FrameFlag::FLAG_END_STREAM;
+  LOG(INFO) << "is_set=" << is_set;
+  Http2FrameHeader v(0, type_, flags_, 0);
+  switch (type_) {
+    case Http2FrameType::DATA:
+    case Http2FrameType::HEADERS:
+      EXPECT_EQ(is_set, v.IsEndStream()) << v;
+      if (is_set) {
+        EXPECT_THAT(v.FlagsToString(), MatchesRegex(".*\\|?END_STREAM\\|.*"));
+      } else {
+        EXPECT_THAT(v.FlagsToString(), Not(HasSubstr("END_STREAM")));
+      }
+      v.RetainFlags(Http2FrameFlag::FLAG_END_STREAM);
+      EXPECT_EQ(is_set, v.IsEndStream()) << v;
+      {
+        std::stringstream s;
+        s << v;
+        EXPECT_EQ(v.ToString(), s.str());
+        if (is_set) {
+          EXPECT_THAT(s.str(), HasSubstr("flags=END_STREAM,"));
+        } else {
+          EXPECT_THAT(s.str(), HasSubstr("flags=,"));
+        }
+      }
+      break;
+    default:
+      EXPECT_DEBUG_DEATH(v.IsEndStream(), "DATA.*HEADERS") << v;
+  }
+}
+
+class IsACKTest : public Http2FrameHeaderTypeAndFlagTest {};
+INSTANTIATE_TEST_CASE_P(IsAck,
+                        IsACKTest,
+                        Combine(ValuesIn(ValidFrameTypes()),
+                                Values(~Http2FrameFlag::FLAG_ACK, 0xff)));
+TEST_P(IsACKTest, IsAck) {
+  const bool is_set =
+      (flags_ & Http2FrameFlag::FLAG_ACK) == Http2FrameFlag::FLAG_ACK;
+  LOG(INFO) << "is_set=" << is_set;
+  Http2FrameHeader v(0, type_, flags_, 0);
+  switch (type_) {
+    case Http2FrameType::SETTINGS:
+    case Http2FrameType::PING:
+      EXPECT_EQ(is_set, v.IsAck()) << v;
+      if (is_set) {
+        EXPECT_THAT(v.FlagsToString(), MatchesRegex(".*\\|?ACK\\|.*"));
+      } else {
+        EXPECT_THAT(v.FlagsToString(), Not(HasSubstr("ACK")));
+      }
+      v.RetainFlags(Http2FrameFlag::FLAG_ACK);
+      EXPECT_EQ(is_set, v.IsAck()) << v;
+      {
+        std::stringstream s;
+        s << v;
+        EXPECT_EQ(v.ToString(), s.str());
+        if (is_set) {
+          EXPECT_THAT(s.str(), HasSubstr("flags=ACK,"));
+        } else {
+          EXPECT_THAT(s.str(), HasSubstr("flags=,"));
+        }
+      }
+      break;
+    default:
+      EXPECT_DEBUG_DEATH(v.IsAck(), "SETTINGS.*PING") << v;
+  }
+}
+
+class IsEndHeadersTest : public Http2FrameHeaderTypeAndFlagTest {};
+INSTANTIATE_TEST_CASE_P(IsEndHeaders,
+                        IsEndHeadersTest,
+                        Combine(ValuesIn(ValidFrameTypes()),
+                                Values(~Http2FrameFlag::FLAG_END_HEADERS,
+                                       0xff)));
+TEST_P(IsEndHeadersTest, IsEndHeaders) {
+  const bool is_set = (flags_ & Http2FrameFlag::FLAG_END_HEADERS) ==
+                      Http2FrameFlag::FLAG_END_HEADERS;
+  LOG(INFO) << "is_set=" << is_set;
+  Http2FrameHeader v(0, type_, flags_, 0);
+  switch (type_) {
+    case Http2FrameType::HEADERS:
+    case Http2FrameType::PUSH_PROMISE:
+    case Http2FrameType::CONTINUATION:
+      EXPECT_EQ(is_set, v.IsEndHeaders()) << v;
+      if (is_set) {
+        EXPECT_THAT(v.FlagsToString(), MatchesRegex(".*\\|?END_HEADERS\\|.*"));
+      } else {
+        EXPECT_THAT(v.FlagsToString(), Not(HasSubstr("END_HEADERS")));
+      }
+      v.RetainFlags(Http2FrameFlag::FLAG_END_HEADERS);
+      EXPECT_EQ(is_set, v.IsEndHeaders()) << v;
+      {
+        std::stringstream s;
+        s << v;
+        EXPECT_EQ(v.ToString(), s.str());
+        if (is_set) {
+          EXPECT_THAT(s.str(), HasSubstr("flags=END_HEADERS,"));
+        } else {
+          EXPECT_THAT(s.str(), HasSubstr("flags=,"));
+        }
+      }
+      break;
+    default:
+      EXPECT_DEBUG_DEATH(v.IsEndHeaders(),
+                         "HEADERS.*PUSH_PROMISE.*CONTINUATION")
+          << v;
+  }
+}
+
+class IsPaddedTest : public Http2FrameHeaderTypeAndFlagTest {};
+INSTANTIATE_TEST_CASE_P(IsPadded,
+                        IsPaddedTest,
+                        Combine(ValuesIn(ValidFrameTypes()),
+                                Values(~Http2FrameFlag::FLAG_PADDED, 0xff)));
+TEST_P(IsPaddedTest, IsPadded) {
+  const bool is_set =
+      (flags_ & Http2FrameFlag::FLAG_PADDED) == Http2FrameFlag::FLAG_PADDED;
+  LOG(INFO) << "is_set=" << is_set;
+  Http2FrameHeader v(0, type_, flags_, 0);
+  switch (type_) {
+    case Http2FrameType::DATA:
+    case Http2FrameType::HEADERS:
+    case Http2FrameType::PUSH_PROMISE:
+      EXPECT_EQ(is_set, v.IsPadded()) << v;
+      if (is_set) {
+        EXPECT_THAT(v.FlagsToString(), MatchesRegex(".*\\|?PADDED\\|.*"));
+      } else {
+        EXPECT_THAT(v.FlagsToString(), Not(HasSubstr("PADDED")));
+      }
+      v.RetainFlags(Http2FrameFlag::FLAG_PADDED);
+      EXPECT_EQ(is_set, v.IsPadded()) << v;
+      {
+        std::stringstream s;
+        s << v;
+        EXPECT_EQ(v.ToString(), s.str());
+        if (is_set) {
+          EXPECT_THAT(s.str(), HasSubstr("flags=PADDED,"));
+        } else {
+          EXPECT_THAT(s.str(), HasSubstr("flags=,"));
+        }
+      }
+      break;
+    default:
+      EXPECT_DEBUG_DEATH(v.IsPadded(), "DATA.*HEADERS.*PUSH_PROMISE") << v;
+  }
+}
+
+class HasPriorityTest : public Http2FrameHeaderTypeAndFlagTest {};
+INSTANTIATE_TEST_CASE_P(HasPriority,
+                        HasPriorityTest,
+                        Combine(ValuesIn(ValidFrameTypes()),
+                                Values(~Http2FrameFlag::FLAG_PRIORITY, 0xff)));
+TEST_P(HasPriorityTest, HasPriority) {
+  const bool is_set =
+      (flags_ & Http2FrameFlag::FLAG_PRIORITY) == Http2FrameFlag::FLAG_PRIORITY;
+  LOG(INFO) << "is_set=" << is_set;
+  Http2FrameHeader v(0, type_, flags_, 0);
+  switch (type_) {
+    case Http2FrameType::HEADERS:
+      EXPECT_EQ(is_set, v.HasPriority()) << v;
+      if (is_set) {
+        EXPECT_THAT(v.FlagsToString(), MatchesRegex(".*\\|?PRIORITY\\|.*"));
+      } else {
+        EXPECT_THAT(v.FlagsToString(), Not(HasSubstr("PRIORITY")));
+      }
+      v.RetainFlags(Http2FrameFlag::FLAG_PRIORITY);
+      EXPECT_EQ(is_set, v.HasPriority()) << v;
+      {
+        std::stringstream s;
+        s << v;
+        EXPECT_EQ(v.ToString(), s.str());
+        if (is_set) {
+          EXPECT_THAT(s.str(), HasSubstr("flags=PRIORITY,"));
+        } else {
+          EXPECT_THAT(s.str(), HasSubstr("flags=,"));
+        }
+      }
+      break;
+    default:
+      EXPECT_DEBUG_DEATH(v.HasPriority(), "HEADERS") << v;
+  }
+}
+
+TEST(Http2PriorityFieldsTest, Constructor) {
+  Http2Random random;
+  uint32_t stream_dependency = random.Rand32() & StreamIdMask();
+  uint32_t weight = 1 + random.Rand8();
+  bool is_exclusive = random.OneIn(2);
+
+  Http2PriorityFields v(stream_dependency, weight, is_exclusive);
+
+  EXPECT_EQ(stream_dependency, v.stream_dependency);
+  EXPECT_EQ(weight, v.weight);
+  EXPECT_EQ(is_exclusive, v.is_exclusive);
+
+  // The high-bit must not be set on the stream id.
+  EXPECT_DEBUG_DEATH(
+      Http2PriorityFields(stream_dependency | 0x80000000, weight, is_exclusive),
+      "31-bit");
+
+  // The weight must be in the range 1-256.
+  EXPECT_DEBUG_DEATH(Http2PriorityFields(stream_dependency, 0, is_exclusive),
+                     "too small");
+  EXPECT_DEBUG_DEATH(
+      Http2PriorityFields(stream_dependency, weight + 256, is_exclusive),
+      "too large");
+}
+#endif  // GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
+
+TEST(Http2RstStreamFieldsTest, IsSupported) {
+  Http2RstStreamFields v{Http2ErrorCode::HTTP2_NO_ERROR};
+  EXPECT_TRUE(v.IsSupportedErrorCode()) << v;
+
+  Http2RstStreamFields u{static_cast<Http2ErrorCode>(~0)};
+  EXPECT_FALSE(u.IsSupportedErrorCode()) << v;
+}
+
+TEST(Http2SettingFieldsTest, Misc) {
+  Http2Random random;
+  Http2SettingsParameter parameter =
+      static_cast<Http2SettingsParameter>(random.Rand16());
+  uint32_t value = random.Rand32();
+
+  Http2SettingFields v(parameter, value);
+
+  EXPECT_EQ(v, v);
+  EXPECT_EQ(parameter, v.parameter);
+  EXPECT_EQ(value, v.value);
+
+  if (static_cast<uint16_t>(parameter) < 7) {
+    EXPECT_TRUE(v.IsSupportedParameter()) << v;
+  } else {
+    EXPECT_FALSE(v.IsSupportedParameter()) << v;
+  }
+
+  Http2SettingFields u(parameter, ~value);
+  EXPECT_NE(v, u);
+  EXPECT_EQ(v.parameter, u.parameter);
+  EXPECT_NE(v.value, u.value);
+
+  Http2SettingFields w(IncrementEnum(parameter), value);
+  EXPECT_NE(v, w);
+  EXPECT_NE(v.parameter, w.parameter);
+  EXPECT_EQ(v.value, w.value);
+
+  Http2SettingFields x(Http2SettingsParameter::MAX_FRAME_SIZE, 123);
+  std::stringstream s;
+  s << x;
+  EXPECT_EQ("parameter=MAX_FRAME_SIZE, value=123", s.str());
+}
+
+TEST(Http2PushPromiseTest, Misc) {
+  Http2Random random;
+  uint32_t promised_stream_id = random.Rand32() & StreamIdMask();
+
+  Http2PushPromiseFields v{promised_stream_id};
+  EXPECT_EQ(promised_stream_id, v.promised_stream_id);
+  EXPECT_EQ(v, v);
+
+  std::stringstream s1;
+  s1 << "promised_stream_id=" << promised_stream_id;
+  std::stringstream s2;
+  s2 << v;
+  EXPECT_EQ(s1.str(), s2.str());
+
+  // High-bit is reserved, but not used, so we can set it.
+  promised_stream_id |= 0x80000000;
+  Http2PushPromiseFields w{promised_stream_id};
+  EXPECT_EQ(w, w);
+  EXPECT_NE(v, w);
+
+  v.promised_stream_id = promised_stream_id;
+  EXPECT_EQ(v, w);
+}
+
+TEST(Http2GoAwayFieldsTest, Misc) {
+  Http2Random random;
+  uint32_t last_stream_id = random.Rand32() & StreamIdMask();
+  Http2ErrorCode error_code = static_cast<Http2ErrorCode>(random.Rand32());
+
+  Http2GoAwayFields v(last_stream_id, error_code);
+  EXPECT_EQ(v, v);
+  EXPECT_EQ(last_stream_id, v.last_stream_id);
+  EXPECT_EQ(error_code, v.error_code);
+
+  if (static_cast<uint32_t>(error_code) < 14) {
+    EXPECT_TRUE(v.IsSupportedErrorCode()) << v;
+  } else {
+    EXPECT_FALSE(v.IsSupportedErrorCode()) << v;
+  }
+
+  Http2GoAwayFields u(~last_stream_id, error_code);
+  EXPECT_NE(v, u);
+  EXPECT_NE(v.last_stream_id, u.last_stream_id);
+  EXPECT_EQ(v.error_code, u.error_code);
+}
+
+TEST(Http2WindowUpdateTest, Misc) {
+  Http2Random random;
+  uint32_t window_size_increment = random.Rand32() & UInt31Mask();
+
+  Http2WindowUpdateFields v{window_size_increment};
+  EXPECT_EQ(window_size_increment, v.window_size_increment);
+  EXPECT_EQ(v, v);
+
+  std::stringstream s1;
+  s1 << "window_size_increment=" << window_size_increment;
+  std::stringstream s2;
+  s2 << v;
+  EXPECT_EQ(s1.str(), s2.str());
+
+  // High-bit is reserved, but not used, so we can set it.
+  window_size_increment |= 0x80000000;
+  Http2WindowUpdateFields w{window_size_increment};
+  EXPECT_EQ(w, w);
+  EXPECT_NE(v, w);
+
+  v.window_size_increment = window_size_increment;
+  EXPECT_EQ(v, w);
+}
+
+TEST(Http2AltSvcTest, Misc) {
+  Http2Random random;
+  uint16_t origin_length = random.Rand16();
+
+  Http2AltSvcFields v{origin_length};
+  EXPECT_EQ(origin_length, v.origin_length);
+  EXPECT_EQ(v, v);
+
+  std::stringstream s1;
+  s1 << "origin_length=" << origin_length;
+  std::stringstream s2;
+  s2 << v;
+  EXPECT_EQ(s1.str(), s2.str());
+
+  Http2AltSvcFields w{++origin_length};
+  EXPECT_EQ(w, w);
+  EXPECT_NE(v, w);
+
+  v.origin_length = w.origin_length;
+  EXPECT_EQ(v, w);
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/http2_structures_test_util.cc b/net/http2/http2_structures_test_util.cc
new file mode 100644
index 0000000..1e6f429
--- /dev/null
+++ b/net/http2/http2_structures_test_util.cc
@@ -0,0 +1,107 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/http2_structures_test_util.h"
+
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_constants_test_util.h"
+#include "net/http2/http2_structures.h"
+#include "net/http2/tools/http2_random.h"
+
+namespace net {
+namespace test {
+
+void Randomize(Http2FrameHeader* p, RandomBase* rng) {
+  p->payload_length = rng->Rand32() & 0xffffff;
+  p->type = static_cast<Http2FrameType>(rng->Rand8());
+  p->flags = static_cast<Http2FrameFlag>(rng->Rand8());
+  p->stream_id = rng->Rand32() & StreamIdMask();
+}
+void Randomize(Http2PriorityFields* p, RandomBase* rng) {
+  p->stream_dependency = rng->Rand32() & StreamIdMask();
+  p->weight = rng->Rand8() + 1;
+  p->is_exclusive = rng->OneIn(2);
+}
+void Randomize(Http2RstStreamFields* p, RandomBase* rng) {
+  p->error_code = static_cast<Http2ErrorCode>(rng->Rand32());
+}
+void Randomize(Http2SettingFields* p, RandomBase* rng) {
+  p->parameter = static_cast<Http2SettingsParameter>(rng->Rand16());
+  p->value = rng->Rand32();
+}
+void Randomize(Http2PushPromiseFields* p, RandomBase* rng) {
+  p->promised_stream_id = rng->Rand32() & StreamIdMask();
+}
+void Randomize(Http2PingFields* p, RandomBase* rng) {
+  for (int ndx = 0; ndx < 8; ++ndx) {
+    p->opaque_data[ndx] = rng->Rand8();
+  }
+}
+void Randomize(Http2GoAwayFields* p, RandomBase* rng) {
+  p->last_stream_id = rng->Rand32() & StreamIdMask();
+  p->error_code = static_cast<Http2ErrorCode>(rng->Rand32());
+}
+void Randomize(Http2WindowUpdateFields* p, RandomBase* rng) {
+  p->window_size_increment = rng->Rand32() & 0x7fffffff;
+}
+void Randomize(Http2AltSvcFields* p, RandomBase* rng) {
+  p->origin_length = rng->Rand16();
+}
+
+void ScrubFlagsOfHeader(Http2FrameHeader* header) {
+  uint8_t invalid_mask = InvalidFlagMaskForFrameType(header->type);
+  uint8_t keep_mask = ~invalid_mask;
+  header->RetainFlags(keep_mask);
+}
+
+bool FrameIsPadded(const Http2FrameHeader& header) {
+  switch (header.type) {
+    case Http2FrameType::DATA:
+    case Http2FrameType::HEADERS:
+    case Http2FrameType::PUSH_PROMISE:
+      return header.IsPadded();
+    default:
+      return false;
+  }
+}
+
+bool FrameHasPriority(const Http2FrameHeader& header) {
+  switch (header.type) {
+    case Http2FrameType::HEADERS:
+      return header.HasPriority();
+    case Http2FrameType::PRIORITY:
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool FrameCanHavePayload(const Http2FrameHeader& header) {
+  switch (header.type) {
+    case Http2FrameType::DATA:
+    case Http2FrameType::HEADERS:
+    case Http2FrameType::PUSH_PROMISE:
+    case Http2FrameType::CONTINUATION:
+    case Http2FrameType::PING:
+    case Http2FrameType::GOAWAY:
+    case Http2FrameType::ALTSVC:
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool FrameCanHaveHpackPayload(const Http2FrameHeader& header) {
+  switch (header.type) {
+    case Http2FrameType::HEADERS:
+    case Http2FrameType::PUSH_PROMISE:
+    case Http2FrameType::CONTINUATION:
+      return true;
+    default:
+      return false;
+  }
+}
+
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/http2_structures_test_util.h b/net/http2/http2_structures_test_util.h
new file mode 100644
index 0000000..710a530
--- /dev/null
+++ b/net/http2/http2_structures_test_util.h
@@ -0,0 +1,61 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_HTTP2_STRUCTURES_TEST_UTIL_H_
+#define NET_HTTP2_HTTP2_STRUCTURES_TEST_UTIL_H_
+
+#include <string>
+
+#include "net/http2/http2_structures.h"
+#include "net/http2/tools/http2_frame_builder.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+class RandomBase;
+
+template <class S>
+std::string SerializeStructure(const S& s) {
+  Http2FrameBuilder fb;
+  fb.Append(s);
+  EXPECT_EQ(S::EncodedSize(), fb.size());
+  return fb.buffer();
+}
+
+// Randomize the members of out, in a manner that yields encodeable contents
+// (e.g. a "uint24" field has only the low 24 bits set).
+void Randomize(Http2FrameHeader* out, RandomBase* rng);
+void Randomize(Http2PriorityFields* out, RandomBase* rng);
+void Randomize(Http2RstStreamFields* out, RandomBase* rng);
+void Randomize(Http2SettingFields* out, RandomBase* rng);
+void Randomize(Http2PushPromiseFields* out, RandomBase* rng);
+void Randomize(Http2PingFields* out, RandomBase* rng);
+void Randomize(Http2GoAwayFields* out, RandomBase* rng);
+void Randomize(Http2WindowUpdateFields* out, RandomBase* rng);
+void Randomize(Http2AltSvcFields* out, RandomBase* rng);
+
+// Clear bits of header->flags that are known to be invalid for the
+// type. For unknown frame types, no change is made.
+void ScrubFlagsOfHeader(Http2FrameHeader* header);
+
+// Is the frame with this header padded? Only true for known/supported frame
+// types.
+bool FrameIsPadded(const Http2FrameHeader& header);
+
+// Does the frame with this header have Http2PriorityFields?
+bool FrameHasPriority(const Http2FrameHeader& header);
+
+// Does the frame with this header have a variable length payload (including
+// empty) payload (e.g. DATA or HEADERS)? Really a test of the frame type.
+bool FrameCanHavePayload(const Http2FrameHeader& header);
+
+// Does the frame with this header have a variable length HPACK payload
+// (including empty) payload (e.g. HEADERS)? Really a test of the frame type.
+bool FrameCanHaveHpackPayload(const Http2FrameHeader& header);
+
+}  // namespace test
+}  // namespace net
+
+#endif  // NET_HTTP2_HTTP2_STRUCTURES_TEST_UTIL_H_
diff --git a/net/http2/tools/failure.cc b/net/http2/tools/failure.cc
new file mode 100644
index 0000000..b354be1e
--- /dev/null
+++ b/net/http2/tools/failure.cc
@@ -0,0 +1,29 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/tools/failure.h"
+
+namespace net {
+namespace test {
+
+// This is a copy of the same named method in ::testing::internal.
+// TODO(jamessynge): See about getting something like VERIFY_* adopted by
+// gUnit (probably a very difficult task!).
+std::string GetBoolAssertionFailureMessage(
+    const ::testing::AssertionResult& assertion_result,
+    const char* expression_text,
+    const char* actual_predicate_value,
+    const char* expected_predicate_value) {
+  const char* actual_message = assertion_result.message();
+  ::testing::Message msg;
+  msg << "Value of: " << expression_text
+      << "\n  Actual: " << actual_predicate_value;
+  if (actual_message[0] != '\0')
+    msg << " (" << actual_message << ")";
+  msg << "\nExpected: " << expected_predicate_value;
+  return msg.GetString();
+}
+
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/tools/failure.h b/net/http2/tools/failure.h
new file mode 100644
index 0000000..aa36d79
--- /dev/null
+++ b/net/http2/tools/failure.h
@@ -0,0 +1,154 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_TOOLS_FAILURE_H_
+#define NET_HTTP2_TOOLS_FAILURE_H_
+
+// Defines VERIFY_* macros, analogous to gUnit's EXPECT_* and ASSERT_* macros,
+// but these return an appropriate AssertionResult if the condition is not
+// satisfied. This enables one to create a function for verifying expectations
+// that are needed by multiple callers or that rely on arguments not accessible
+// to the main test method. Using VERIFY_SUCCESS allows one to annotate the
+// a failing AssertionResult with more context.
+
+#include <iosfwd>
+#include <sstream>
+#include <string>
+
+#include "base/macros.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+template <typename T>
+class VerifyThatHelper {
+ public:
+  VerifyThatHelper(const T& value, ::testing::Matcher<T> matcher) {
+    matches_ = matcher.Matches(value);
+    if (!matches_) {
+      printed_value_ = ::testing::PrintToString(value);
+
+      std::ostringstream os;
+      matcher.DescribeTo(&os);
+      matcher_description_ = os.str();
+    }
+  }
+
+  operator bool() const { return matches_; }
+
+  const std::string& printed_value() const { return printed_value_; }
+  const std::string& matcher_description() const {
+    return matcher_description_;
+  }
+
+ private:
+  bool matches_;
+  std::string printed_value_;
+  std::string matcher_description_;
+
+  DISALLOW_COPY_AND_ASSIGN(VerifyThatHelper);
+};
+
+// Constructs a failure message for Boolean assertions such as VERIFY_TRUE.
+std::string GetBoolAssertionFailureMessage(
+    const ::testing::AssertionResult& assertion_result,
+    const char* expression_text,
+    const char* actual_predicate_value,
+    const char* expected_predicate_value);
+
+}  // namespace test
+}  // namespace net
+
+// Macro for adding verification location to output stream or AssertionResult.
+// Starts with a new-line because of the way that gUnit displays failures for
+//    EXPECT_TRUE(CallToFunctionThatFailsToVerify()).
+#define VERIFY_FAILED_LOCATION_                   \
+  "\n"                                            \
+      << "(VERIFY failed in " << __func__ << "\n" \
+      << "               at " __FILE__ " : " << __LINE__ << ")\n"
+
+// Implements Boolean test verifications VERIFY_TRUE and VERIFY_FALSE.
+// text is a textual represenation of expression as it was passed into
+// VERIFY_TRUE or VERIFY_FALSE.
+// clang-format off
+#define VERIFY_TEST_BOOLEAN_(condition, text, actual, expected)        \
+  if (const ::testing::AssertionResult __assertion_result =            \
+          ::testing::AssertionResult((condition) ? expected : actual)) \
+    ;                                                                  \
+  else                                                                 \
+    return ::testing::AssertionFailure()                               \
+         << VERIFY_FAILED_LOCATION_                                    \
+         << ::net::test::GetBoolAssertionFailureMessage(              \
+                __assertion_result, text, #actual, #expected)
+// clang-format on
+
+// Boolean assertions. condition can be either a Boolean expression or an
+// expression convertable to a boolean (such as a ::gtl::labs::optional).
+#define VERIFY_TRUE(condition) \
+  VERIFY_TEST_BOOLEAN_(condition, #condition, false, true)
+
+#define VERIFY_FALSE(condition) \
+  VERIFY_TEST_BOOLEAN_(condition, #condition, true, false)
+
+// Convenient helper macro for writing methods that return an AssertionFailure
+// that includes the tested condition in the message (in the manner of
+// ASSERT_THAT and EXPECT_THAT).
+//
+// This macro avoids the do {} while(false) trick and putting braces around
+// the if so you can do things like:
+// VERIFY_THAT(foo, Lt(4)) << "foo too big in iteration " << i;
+// (This parallels the implementation of CHECK().)
+//
+// We use an if statement with an empty true branch so that this doesn't eat
+// a neighboring else when used in an unbraced if statement like:
+// if (condition)
+//   VERIFY_THAT(foo, Eq(bar));
+// else
+//   FAIL();
+#define VERIFY_THAT(value, matcher)                                       \
+  if (const auto& _verify_that_helper =                                   \
+          ::net::test::VerifyThatHelper<decltype(value)>(value, matcher)) \
+    ;                                                                     \
+  else                                                                    \
+    return ::testing::AssertionFailure()                                  \
+           << "Failed to verify that '" #value "' ("                      \
+           << _verify_that_helper.printed_value() << ") "                 \
+           << _verify_that_helper.matcher_description()                   \
+           << " (on " __FILE__ ":" << __LINE__ << "). "
+
+// Useful variants of VERIFY_THAT, similar to the corresponding EXPECT_X or
+// ASSERT_X defined by gUnit.
+#define VERIFY_EQ(val1, val2) VERIFY_THAT(val1, ::testing::Eq(val2))
+#define VERIFY_NE(val1, val2) VERIFY_THAT(val1, ::testing::Ne(val2))
+#define VERIFY_GT(val1, val2) VERIFY_THAT(val1, ::testing::Gt(val2))
+#define VERIFY_LT(val1, val2) VERIFY_THAT(val1, ::testing::Lt(val2))
+#define VERIFY_GE(val1, val2) VERIFY_THAT(val1, ::testing::Ge(val2))
+#define VERIFY_LE(val1, val2) VERIFY_THAT(val1, ::testing::Le(val2))
+
+// Convenience macro matching EXPECT_OK
+#define VERIFY_OK(statement) VERIFY_EQ(::util::Status::OK, (statement))
+
+// This version verifies that an expression of type AssertionResult is
+// AssertionSuccess. If instead the value is an AssertionFailure, it appends
+// info about the current code location to the failure's message and returns
+// the failure to the caller of the current method. It permits the code site
+// to append further messages to the failure message. For example:
+//    VERIFY_SUCCESS(SomeCall()) << "Some more context about SomeCall";
+// clang-format off
+#define VERIFY_SUCCESS(expr)                                  \
+  if (::testing::AssertionResult __assertion_result = (expr)) \
+    ;                                                         \
+  else                                                        \
+    return __assertion_result << VERIFY_FAILED_LOCATION_
+// clang-format on
+
+#define VERIFY_AND_RETURN_SUCCESS(expression) \
+  {                                           \
+    VERIFY_SUCCESS(expression);               \
+    return ::testing::AssertionSuccess();     \
+  }
+
+#endif  // NET_HTTP2_TOOLS_FAILURE_H_
diff --git a/net/http2/tools/http2_bug_tracker.h b/net/http2/tools/http2_bug_tracker.h
new file mode 100644
index 0000000..0e31bc2
--- /dev/null
+++ b/net/http2/tools/http2_bug_tracker.h
@@ -0,0 +1,14 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_TOOLS_HTTP2_BUG_TRACKER_H_
+#define NET_HTTP2_TOOLS_HTTP2_BUG_TRACKER_H_
+
+#include "base/logging.h"
+
+#define HTTP2_BUG LOG(DFATAL)
+#define HTTP2_BUG_IF LOG_IF(DFATAL, (condition))
+#define FLAGS_http2_always_log_bugs_for_tests (true)
+
+#endif  // NET_HTTP2_TOOLS_HTTP2_BUG_TRACKER_H_
diff --git a/net/http2/tools/http2_frame_builder.cc b/net/http2/tools/http2_frame_builder.cc
new file mode 100644
index 0000000..c072525
--- /dev/null
+++ b/net/http2/tools/http2_frame_builder.cc
@@ -0,0 +1,182 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/tools/http2_frame_builder.h"
+
+#ifdef WIN32
+#include <winsock2.h>  // for htonl() functions
+#else
+#include <arpa/inet.h>
+#include <netinet/in.h>  // for htonl, htons
+#endif
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::StringPiece;
+
+namespace net {
+namespace test {
+
+Http2FrameBuilder::Http2FrameBuilder(Http2FrameType type,
+                                     uint8_t flags,
+                                     uint32_t stream_id) {
+  AppendUInt24(0);  // Frame payload length, unknown so far.
+  Append(type);
+  AppendUInt8(flags);
+  AppendUInt31(stream_id);
+}
+
+Http2FrameBuilder::Http2FrameBuilder(const Http2FrameHeader& v) {
+  Append(v);
+}
+
+void Http2FrameBuilder::Append(StringPiece s) {
+  s.AppendToString(&buffer_);
+}
+
+void Http2FrameBuilder::AppendBytes(const void* data, uint32_t num_bytes) {
+  Append(StringPiece(static_cast<const char*>(data), num_bytes));
+}
+
+void Http2FrameBuilder::AppendZeroes(size_t num_zero_bytes) {
+  char zero = 0;
+  buffer_.append(num_zero_bytes, zero);
+}
+
+void Http2FrameBuilder::AppendUInt8(uint8_t value) {
+  AppendBytes(&value, 1);
+}
+
+void Http2FrameBuilder::AppendUInt16(uint16_t value) {
+  value = htons(value);
+  AppendBytes(&value, 2);
+}
+
+void Http2FrameBuilder::AppendUInt24(uint32_t value) {
+  // Doesn't make sense to try to append a larger value, as that doesn't
+  // simulate something an encoder could do (i.e. the other 8 bits simply aren't
+  // there to be occupied).
+  EXPECT_EQ(value, value & 0xffffff);
+  value = htonl(value);
+  AppendBytes(reinterpret_cast<char*>(&value) + 1, 3);
+}
+
+void Http2FrameBuilder::AppendUInt31(uint32_t value) {
+  // If you want to test the high-bit being set, call AppendUInt32 instead.
+  uint32_t tmp = value & StreamIdMask();
+  EXPECT_EQ(value, value & StreamIdMask())
+      << "High-bit of uint32 should be clear.";
+  value = htonl(tmp);
+  AppendBytes(&value, 4);
+}
+
+void Http2FrameBuilder::AppendUInt32(uint32_t value) {
+  value = htonl(value);
+  AppendBytes(&value, sizeof(value));
+}
+
+void Http2FrameBuilder::Append(Http2ErrorCode error_code) {
+  AppendUInt32(static_cast<uint32_t>(error_code));
+}
+
+void Http2FrameBuilder::Append(Http2FrameType type) {
+  AppendUInt8(static_cast<uint8_t>(type));
+}
+
+void Http2FrameBuilder::Append(Http2SettingsParameter parameter) {
+  AppendUInt16(static_cast<uint16_t>(parameter));
+}
+
+void Http2FrameBuilder::Append(const Http2FrameHeader& v) {
+  AppendUInt24(v.payload_length);
+  Append(v.type);
+  AppendUInt8(v.flags);
+  AppendUInt31(v.stream_id);
+}
+
+void Http2FrameBuilder::Append(const Http2PriorityFields& v) {
+  // The EXCLUSIVE flag is the high-bit of the 32-bit stream dependency field.
+  uint32_t tmp = v.stream_dependency & StreamIdMask();
+  EXPECT_EQ(tmp, v.stream_dependency);
+  if (v.is_exclusive) {
+    tmp |= 0x80000000;
+  }
+  AppendUInt32(tmp);
+
+  // The PRIORITY frame's weight field is logically in the range [1, 256],
+  // but is encoded as a byte in the range [0, 255].
+  ASSERT_LE(1u, v.weight);
+  ASSERT_LE(v.weight, 256u);
+  AppendUInt8(v.weight - 1);
+}
+
+void Http2FrameBuilder::Append(const Http2RstStreamFields& v) {
+  Append(v.error_code);
+}
+
+void Http2FrameBuilder::Append(const Http2SettingFields& v) {
+  Append(v.parameter);
+  AppendUInt32(v.value);
+}
+
+void Http2FrameBuilder::Append(const Http2PushPromiseFields& v) {
+  AppendUInt31(v.promised_stream_id);
+}
+
+void Http2FrameBuilder::Append(const Http2PingFields& v) {
+  AppendBytes(v.opaque_data, sizeof Http2PingFields::opaque_data);
+}
+
+void Http2FrameBuilder::Append(const Http2GoAwayFields& v) {
+  AppendUInt31(v.last_stream_id);
+  Append(v.error_code);
+}
+
+void Http2FrameBuilder::Append(const Http2WindowUpdateFields& v) {
+  EXPECT_NE(0u, v.window_size_increment) << "Increment must be non-zero.";
+  AppendUInt31(v.window_size_increment);
+}
+
+void Http2FrameBuilder::Append(const Http2AltSvcFields& v) {
+  AppendUInt16(v.origin_length);
+}
+
+// Methods for changing existing buffer contents.
+
+void Http2FrameBuilder::WriteAt(StringPiece s, size_t offset) {
+  ASSERT_LE(offset, buffer_.size());
+  size_t len = offset + s.size();
+  if (len > buffer_.size()) {
+    buffer_.resize(len);
+  }
+  for (size_t ndx = 0; ndx < s.size(); ++ndx) {
+    buffer_[offset + ndx] = s[ndx];
+  }
+}
+
+void Http2FrameBuilder::WriteBytesAt(const void* data,
+                                     uint32_t num_bytes,
+                                     size_t offset) {
+  WriteAt(StringPiece(static_cast<const char*>(data), num_bytes), offset);
+}
+
+void Http2FrameBuilder::WriteUInt24At(uint32_t value, size_t offset) {
+  ASSERT_LT(value, static_cast<uint32_t>(1 << 24));
+  value = htonl(value);
+  WriteBytesAt(reinterpret_cast<char*>(&value) + 1, sizeof(value) - 1, offset);
+}
+
+void Http2FrameBuilder::SetPayloadLength(uint32_t payload_length) {
+  WriteUInt24At(payload_length, 0);
+}
+
+size_t Http2FrameBuilder::SetPayloadLength() {
+  EXPECT_GE(size(), Http2FrameHeader::EncodedSize());
+  uint32_t payload_length = size() - Http2FrameHeader::EncodedSize();
+  SetPayloadLength(payload_length);
+  return payload_length;
+}
+
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/tools/http2_frame_builder.h b/net/http2/tools/http2_frame_builder.h
new file mode 100644
index 0000000..c665c759
--- /dev/null
+++ b/net/http2/tools/http2_frame_builder.h
@@ -0,0 +1,100 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_TOOLS_HTTP2_FRAME_BUILDER_H_
+#define NET_HTTP2_TOOLS_HTTP2_FRAME_BUILDER_H_
+
+// Http2FrameBuilder builds wire-format HTTP/2 frames (or fragments thereof)
+// from components.
+//
+// For now, this is only intended for use in tests, and thus has EXPECT* in the
+// code. If desired to use it in an encoder, it will need optimization work,
+// especially w.r.t memory mgmt, and the EXPECT* will need to be removed or
+// replaced with DCHECKs.
+
+#include <stddef.h>  // for size_t
+
+#include <string>
+
+#include "base/strings/string_piece.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+namespace test {
+
+class Http2FrameBuilder {
+ public:
+  Http2FrameBuilder(Http2FrameType type, uint8_t flags, uint32_t stream_id);
+  explicit Http2FrameBuilder(const Http2FrameHeader& v);
+  Http2FrameBuilder() {}
+  ~Http2FrameBuilder() {}
+
+  size_t size() const { return buffer_.size(); }
+  const std::string& buffer() const { return buffer_; }
+
+  //----------------------------------------------------------------------------
+  // Methods for appending to the end of the buffer.
+
+  // Append a sequence of bytes from various sources.
+  void Append(base::StringPiece s);
+  void AppendBytes(const void* data, uint32_t num_bytes);
+
+  // Append an array of type T[N] to the string. Intended for tests with arrays
+  // initialized from literals, such as:
+  //    const char kData[] = {0x12, 0x23, ...};
+  //    builder.AppendBytes(kData);
+  template <typename T, size_t N>
+  void AppendBytes(T (&buf)[N]) {
+    AppendBytes(buf, N * sizeof(buf[0]));
+  }
+
+  // Support for appending padding. Does not read or write the Pad Length field.
+  void AppendZeroes(size_t num_zero_bytes);
+
+  // Append various sizes of unsigned integers.
+  void AppendUInt8(uint8_t value);
+  void AppendUInt16(uint16_t value);
+  void AppendUInt24(uint32_t value);
+  void AppendUInt31(uint32_t value);
+  void AppendUInt32(uint32_t value);
+
+  // Append various enums.
+  void Append(Http2ErrorCode error_code);
+  void Append(Http2FrameType type);
+  void Append(Http2SettingsParameter parameter);
+
+  // Append various structures.
+  void Append(const Http2FrameHeader& v);
+  void Append(const Http2PriorityFields& v);
+  void Append(const Http2RstStreamFields& v);
+  void Append(const Http2SettingFields& v);
+  void Append(const Http2PushPromiseFields& v);
+  void Append(const Http2PingFields& v);
+  void Append(const Http2GoAwayFields& v);
+  void Append(const Http2WindowUpdateFields& v);
+  void Append(const Http2AltSvcFields& v);
+
+  // Methods for changing existing buffer contents (mostly focused on updating
+  // the payload length).
+
+  void WriteAt(base::StringPiece s, size_t offset);
+  void WriteBytesAt(const void* data, uint32_t num_bytes, size_t offset);
+  void WriteUInt24At(uint32_t value, size_t offset);
+
+  // Set the payload length to the specified size.
+  void SetPayloadLength(uint32_t payload_length);
+
+  // Sets the payload length to the size of the buffer minus the size of
+  // the frame header.
+  size_t SetPayloadLength();
+
+ private:
+  std::string buffer_;
+};
+
+}  // namespace test
+}  // namespace net
+
+#endif  // NET_HTTP2_TOOLS_HTTP2_FRAME_BUILDER_H_
diff --git a/net/http2/tools/http2_random.cc b/net/http2/tools/http2_random.cc
new file mode 100644
index 0000000..8732d6a8
--- /dev/null
+++ b/net/http2/tools/http2_random.cc
@@ -0,0 +1,59 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/tools/http2_random.h"
+
+#include <limits>
+#include <memory>
+
+#include "base/rand_util.h"
+
+namespace net {
+namespace test {
+
+bool Http2Random::OneIn(int n) {
+  return base::RandGenerator(n) == 0;
+}
+
+int32_t Http2Random::Uniform(int32_t n) {
+  return base::RandGenerator(n);
+}
+
+uint8_t Http2Random::Rand8() {
+  return base::RandGenerator(
+      static_cast<uint64_t>(std::numeric_limits<uint8_t>::max()) + 1);
+}
+
+uint16_t Http2Random::Rand16() {
+  return base::RandGenerator(
+      static_cast<uint64_t>(std::numeric_limits<uint16_t>::max()) + 1);
+}
+
+uint32_t Http2Random::Rand32() {
+  return base::RandGenerator(
+      static_cast<uint64_t>(std::numeric_limits<uint32_t>::max()) + 1);
+}
+
+uint64_t Http2Random::Rand64() {
+  return base::RandUint64();
+}
+
+int32_t Http2Random::Next() {
+  return Rand32();
+}
+
+int32_t Http2Random::Skewed(int max_log) {
+  const uint32_t base = Rand32() % (max_log + 1);
+  const uint32_t mask = ((base < 32) ? (1u << base) : 0u) - 1u;
+  return Rand32() & mask;
+}
+
+std::string Http2Random::RandString(int length) {
+  std::unique_ptr<char[]> buffer(new char[length]);
+  base::RandBytes(buffer.get(), length);
+  return std::string(buffer.get(), length);
+}
+
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/tools/http2_random.h b/net/http2/tools/http2_random.h
new file mode 100644
index 0000000..f7956cb8
--- /dev/null
+++ b/net/http2/tools/http2_random.h
@@ -0,0 +1,46 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_TOOLS_HTTP2_RANDOM_H_
+#define NET_HTTP2_TOOLS_HTTP2_RANDOM_H_
+
+#include <stdint.h>
+
+#include <string>
+
+namespace net {
+namespace test {
+
+class RandomBase {
+ public:
+  virtual ~RandomBase() {}
+  virtual bool OneIn(int n) = 0;
+  virtual int32_t Uniform(int32_t n) = 0;
+  virtual uint8_t Rand8() = 0;
+  virtual uint16_t Rand16() = 0;
+  virtual uint32_t Rand32() = 0;
+  virtual uint64_t Rand64() = 0;
+  virtual int32_t Next() = 0;
+  virtual int32_t Skewed(int max_log) = 0;
+  virtual std::string RandString(int length) = 0;
+};
+
+class Http2Random : public RandomBase {
+ public:
+  ~Http2Random() override {}
+  bool OneIn(int n) override;
+  int32_t Uniform(int32_t n) override;
+  uint8_t Rand8() override;
+  uint16_t Rand16() override;
+  uint32_t Rand32() override;
+  uint64_t Rand64() override;
+  int32_t Next() override;
+  int32_t Skewed(int max_log) override;
+  std::string RandString(int length) override;
+};
+
+}  // namespace test
+}  // namespace net
+
+#endif  // NET_HTTP2_TOOLS_HTTP2_RANDOM_H_
diff --git a/net/http2/tools/random_decoder_test.cc b/net/http2/tools/random_decoder_test.cc
new file mode 100644
index 0000000..8fd36743
--- /dev/null
+++ b/net/http2/tools/random_decoder_test.cc
@@ -0,0 +1,179 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/tools/random_decoder_test.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <memory>
+
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/tools/failure.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// It's rather time consuming to decode large buffers one at a time,
+// especially with the log level cranked up. So, by default we don't do
+// that unless explicitly requested.
+
+using ::testing::AssertionFailure;
+using ::testing::AssertionResult;
+using ::testing::AssertionSuccess;
+using base::StringPiece;
+
+namespace net {
+namespace test {
+
+std::string HexEncode(StringPiece s) {
+  return base::HexEncode(s.data(), s.size());
+}
+
+RandomDecoderTest::RandomDecoderTest() {}
+
+bool RandomDecoderTest::StopDecodeOnDone() {
+  return stop_decode_on_done_;
+}
+
+DecodeStatus RandomDecoderTest::DecodeSegments(DecodeBuffer* original,
+                                               const SelectSize& select_size) {
+  DecodeStatus status = DecodeStatus::kDecodeInProgress;
+  bool first = true;
+  VLOG(2) << "DecodeSegments: input size=" << original->Remaining();
+  while (first || original->HasData()) {
+    size_t remaining = original->Remaining();
+    size_t size = std::min(
+        remaining, select_size.Run(first, original->Offset(), remaining));
+    DecodeBuffer db(original->cursor(), size);
+    VLOG(2) << "Decoding " << size << " bytes of " << remaining << " remaining";
+    if (first) {
+      first = false;
+      status = StartDecoding(&db);
+    } else {
+      status = ResumeDecoding(&db);
+    }
+    // A decoder MUST consume some input (if any is available), else we could
+    // get stuck in infinite loops.
+    if (db.Offset() == 0 && db.HasData() &&
+        status != DecodeStatus::kDecodeError) {
+      ADD_FAILURE() << "Decoder didn't make any progress; db.FullSize="
+                    << db.FullSize()
+                    << "   original.Offset=" << original->Offset();
+      return DecodeStatus::kDecodeError;
+    }
+    original->AdvanceCursor(db.Offset());
+    switch (status) {
+      case DecodeStatus::kDecodeDone:
+        if (original->Empty() || StopDecodeOnDone()) {
+          return DecodeStatus::kDecodeDone;
+        }
+        continue;
+      case DecodeStatus::kDecodeInProgress:
+        continue;
+      case DecodeStatus::kDecodeError:
+        return DecodeStatus::kDecodeError;
+    }
+  }
+  return status;
+}
+
+// Decode |original| multiple times, with different segmentations, validating
+// after each decode, returning on the first failure.
+AssertionResult RandomDecoderTest::DecodeAndValidateSeveralWays(
+    DecodeBuffer* original,
+    bool return_non_zero_on_first,
+    const Validator& validator) {
+  const uint32_t original_remaining = original->Remaining();
+  VLOG(1) << "DecodeAndValidateSeveralWays - Start, remaining = "
+          << original_remaining;
+  uint32_t first_consumed;
+  {
+    // Fast decode (no stopping unless decoder does so).
+    DecodeBuffer input(original->cursor(), original_remaining);
+    VLOG(2) << "DecodeSegmentsAndValidate with SelectRemaining";
+    VERIFY_SUCCESS(DecodeSegmentsAndValidate(
+        &input, base::Bind(&SelectRemaining), validator))
+        << "\nFailed with SelectRemaining; input.Offset=" << input.Offset()
+        << "; input.Remaining=" << input.Remaining();
+    first_consumed = input.Offset();
+  }
+  if (original_remaining <= 30) {
+    // Decode again, one byte at a time.
+    DecodeBuffer input(original->cursor(), original_remaining);
+    VLOG(2) << "DecodeSegmentsAndValidate with SelectOne";
+    VERIFY_SUCCESS(
+        DecodeSegmentsAndValidate(&input, base::Bind(&SelectOne), validator))
+        << "\nFailed with SelectOne; input.Offset=" << input.Offset()
+        << "; input.Remaining=" << input.Remaining();
+    VERIFY_EQ(first_consumed, input.Offset()) << "\nFailed with SelectOne";
+  }
+  if (original_remaining <= 20) {
+    // Decode again, one or zero bytes at a time.
+    DecodeBuffer input(original->cursor(), original_remaining);
+    VLOG(2) << "DecodeSegmentsAndValidate with SelectZeroAndOne";
+    bool zero_next = !return_non_zero_on_first;
+    VERIFY_SUCCESS(DecodeSegmentsAndValidate(
+        &input, base::Bind(&SelectZeroAndOne, &zero_next), validator))
+        << "\nFailed with SelectZeroAndOne";
+    VERIFY_EQ(first_consumed, input.Offset())
+        << "\nFailed with SelectZeroAndOne; input.Offset=" << input.Offset()
+        << "; input.Remaining=" << input.Remaining();
+  }
+  {
+    // Decode again, with randomly selected segment sizes.
+    DecodeBuffer input(original->cursor(), original_remaining);
+    VLOG(2) << "DecodeSegmentsAndValidate with SelectRandom";
+    VERIFY_SUCCESS(DecodeSegmentsAndValidate(
+        &input, base::Bind(&RandomDecoderTest::SelectRandom,
+                           base::Unretained(this), return_non_zero_on_first),
+        validator))
+        << "\nFailed with SelectRandom; input.Offset=" << input.Offset()
+        << "; input.Remaining=" << input.Remaining();
+    VERIFY_EQ(first_consumed, input.Offset()) << "\nFailed with SelectRandom";
+  }
+  VERIFY_EQ(original_remaining, original->Remaining());
+  original->AdvanceCursor(first_consumed);
+  VLOG(1) << "DecodeAndValidateSeveralWays - SUCCESS";
+  return ::testing::AssertionSuccess();
+}
+
+// static
+size_t RandomDecoderTest::SelectZeroAndOne(bool* zero_next,
+                                           bool first,
+                                           size_t offset,
+                                           size_t remaining) {
+  if (*zero_next) {
+    *zero_next = false;
+    return 0;
+  } else {
+    *zero_next = true;
+    return 1;
+  }
+}
+
+size_t RandomDecoderTest::SelectRandom(bool return_non_zero_on_first,
+                                       bool first,
+                                       size_t offset,
+                                       size_t remaining) {
+  uint32_t r = random_.Rand32();
+  if (first && return_non_zero_on_first) {
+    CHECK_LT(0u, remaining);
+    if (remaining == 1) {
+      return 1;
+    }
+    return 1 + (r % remaining);  // size in range [1, remaining).
+  }
+  return r % (remaining + 1);  // size in range [0, remaining].
+}
+
+uint32_t RandomDecoderTest::RandStreamId() {
+  return random_.Rand32() & StreamIdMask();
+}
+
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/tools/random_decoder_test.h b/net/http2/tools/random_decoder_test.h
new file mode 100644
index 0000000..f3ab829
--- /dev/null
+++ b/net/http2/tools/random_decoder_test.h
@@ -0,0 +1,264 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_TOOLS_RANDOM_DECODER_TEST_H_
+#define NET_HTTP2_TOOLS_RANDOM_DECODER_TEST_H_
+
+// RandomDecoderTest is a base class for tests of decoding various kinds
+// of HTTP/2 and HPACK encodings.
+
+// TODO(jamessynge): Move more methods into .cc file.
+
+#include <stddef.h>
+
+#include <memory>
+#include <string>
+#include <type_traits>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/strings/string_piece.h"
+#include "base/template_util.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/tools/failure.h"
+#include "net/http2/tools/http2_random.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+// Some helpers.
+
+template <typename T, size_t N>
+base::StringPiece ToStringPiece(T (&data)[N]) {
+  return base::StringPiece(reinterpret_cast<const char*>(data), N * sizeof(T));
+}
+
+// strings/hex_ascii_dump.h doesn't support StringPiece args for this case.
+std::string HexEncode(base::StringPiece s);
+
+// Overwrite the enum with some random value, probably not a valid value for
+// the enum type, but which fits into its storage.
+template <typename T,
+          typename E = typename std::enable_if<std::is_enum<T>::value>::type>
+void CorruptEnum(T* out, RandomBase* rng) {
+  // Per cppreference.com, if the destination type of a static_cast is
+  // smaller than the source type (i.e. type of r and uint32 below), the
+  // resulting value is the smallest unsigned value equal to the source value
+  // modulo 2^n, where n is the number of bits used to represent the
+  // destination type unsigned U.
+  typedef typename base::underlying_type<T>::type underlying_type_T;
+  typedef typename std::make_unsigned<underlying_type_T>::type
+      unsigned_underlying_type_T;
+  auto r = static_cast<unsigned_underlying_type_T>(rng->Rand32());
+  *out = static_cast<T>(r);
+}
+
+// Base class for tests of the ability to decode a sequence of bytes with
+// various boundaries between the DecodeBuffers provided to the decoder.
+class RandomDecoderTest : public ::testing::Test {
+ public:
+  // SelectSize returns the size of the next DecodeBuffer to be passed to the
+  // decoder. Note that RandomDecoderTest allows that size to be zero, though
+  // some decoders can't deal with that on the first byte, hence the |first|
+  // parameter.
+  typedef base::Callback<size_t(bool first, size_t offset, size_t remaining)>
+      SelectSize;
+
+  // Validator returns an AssertionResult so test can do:
+  // EXPECT_THAT(DecodeAndValidate(..., validator));
+  typedef ::testing::AssertionResult AssertionResult;
+  typedef base::Callback<AssertionResult(const DecodeBuffer& input,
+                                         DecodeStatus status)>
+      Validator;
+  typedef base::Callback<AssertionResult()> NoArgValidator;
+
+  RandomDecoderTest();
+
+ protected:
+  // TODO(jamessynge): Modify StartDecoding, etc. to (somehow) return
+  // AssertionResult so that the VERIFY_* methods exported from
+  // gunit_helpers.h can be widely used.
+
+  // Start decoding; call allows sub-class to Reset the decoder, or deal with
+  // the first byte if that is done in a unique fashion.  Might be called with
+  // a zero byte buffer.
+  virtual DecodeStatus StartDecoding(DecodeBuffer* db) = 0;
+
+  // Resume decoding of the input after a prior call to StartDecoding, and
+  // possibly many calls to ResumeDecoding.
+  virtual DecodeStatus ResumeDecoding(DecodeBuffer* db) = 0;
+
+  // Return true if a decode status of kDecodeDone indicates that
+  // decoding should stop.
+  virtual bool StopDecodeOnDone();
+
+  // Decode buffer |original| until we run out of input, or kDecodeDone is
+  // returned by the decoder AND StopDecodeOnDone() returns true. Segments
+  // (i.e. cuts up) the original DecodeBuffer into (potentially) smaller buffers
+  // by calling |select_size| to decide how large each buffer should be.
+  // We do this to test the ability to deal with arbitrary boundaries, as might
+  // happen in transport.
+  // Returns the final DecodeStatus.
+  DecodeStatus DecodeSegments(DecodeBuffer* original,
+                              const SelectSize& select_size);
+
+  // Decode buffer |original| until we run out of input, or kDecodeDone is
+  // returned by the decoder AND StopDecodeOnDone() returns true. Segments
+  // (i.e. cuts up) the original DecodeBuffer into (potentially) smaller buffers
+  // by calling |select_size| to decide how large each buffer should be.
+  // We do this to test the ability to deal with arbitrary boundaries, as might
+  // happen in transport.
+  // Invokes |validator| with the final decode status and the original decode
+  // buffer, with the cursor advanced as far as has been consumed by the decoder
+  // and returns validator's result.
+  ::testing::AssertionResult DecodeSegmentsAndValidate(
+      DecodeBuffer* original,
+      const SelectSize& select_size,
+      const Validator& validator) {
+    DecodeStatus status = DecodeSegments(original, select_size);
+    VERIFY_AND_RETURN_SUCCESS(validator.Run(*original, status));
+  }
+
+  // Returns a size for fast decoding, i.e. passing all that
+  // is available to the decoder.
+  static size_t SelectRemaining(bool first, size_t offset, size_t remaining) {
+    return remaining;
+  }
+
+  // Returns a size for decoding a single byte at a time.
+  static size_t SelectOne(bool first, size_t offset, size_t remaining) {
+    return 1;
+  }
+
+  // Returns a size for decoding a single byte at a time, where
+  // zero byte buffers are also allowed. Alternates between zero and one.
+  static size_t SelectZeroAndOne(bool* zero_next,
+                                 bool first,
+                                 size_t offset,
+                                 size_t remaining);
+
+  // Returns a size for decoding random sized segments.
+  size_t SelectRandom(bool return_non_zero_on_first,
+                      bool first,
+                      size_t offset,
+                      size_t remaining);
+
+  // Decode |original| multiple times, with different segmentations of the
+  // decode buffer, validating after each decode, and confirming that they
+  // each decode the same amount. Returns on the first failure, else returns
+  // success.
+  AssertionResult DecodeAndValidateSeveralWays(DecodeBuffer* original,
+                                               bool return_non_zero_on_first,
+                                               const Validator& validator);
+
+  static AssertionResult SucceedingValidator(const DecodeBuffer& input,
+                                             DecodeStatus status) {
+    return ::testing::AssertionSuccess();
+  }
+
+  static Validator ToValidator(const Validator& validator) { return validator; }
+
+  static AssertionResult RunNoArgValidator(const NoArgValidator& validator,
+                                           const DecodeBuffer& input,
+                                           DecodeStatus status) {
+    return validator.Run();
+  }
+
+  static Validator ToValidator(const NoArgValidator& validator) {
+    return base::Bind(&RunNoArgValidator, validator);
+  }
+
+  // Wraps a validator with another validator
+  // that first checks that the DecodeStatus is kDecodeDone and
+  // that the DecodeBuffer is empty.
+  // TODO(jamessynge): Replace this overload with the next, as using this method
+  // usually means that the wrapped function doesn't need to be passed the
+  // DecodeBuffer nor the DecodeStatus.
+  static AssertionResult ValidateDoneAndEmptyImpl(const Validator& wrapped,
+                                                  const DecodeBuffer& input,
+                                                  DecodeStatus status) {
+    VERIFY_EQ(status, DecodeStatus::kDecodeDone);
+    VERIFY_EQ(0u, input.Remaining()) << "\nOffset=" << input.Offset();
+    return wrapped.Run(input, status);
+  }
+  static Validator ValidateDoneAndEmpty(const Validator& wrapped) {
+    return base::Bind(&ValidateDoneAndEmptyImpl, wrapped);
+  }
+  static AssertionResult ValidateDoneAndEmptyNoArgImpl(
+      const NoArgValidator& wrapped,
+      const DecodeBuffer& input,
+      DecodeStatus status) {
+    VERIFY_EQ(status, DecodeStatus::kDecodeDone);
+    VERIFY_EQ(0u, input.Remaining()) << "\nOffset=" << input.Offset();
+    return wrapped.Run();
+  }
+  static Validator ValidateDoneAndEmpty(const NoArgValidator& wrapped) {
+    return base::Bind(&ValidateDoneAndEmptyNoArgImpl, wrapped);
+  }
+  static Validator ValidateDoneAndEmpty() {
+    return ValidateDoneAndEmpty(base::Bind(&SucceedingValidator));
+  }
+
+  // Wraps a validator with another validator
+  // that first checks that the DecodeStatus is kDecodeDone and
+  // that the DecodeBuffer has the expected offset.
+  // TODO(jamessynge): Replace this overload with the next, as using this method
+  // usually means that the wrapped function doesn't need to be passed the
+  // DecodeBuffer nor the DecodeStatus.
+  static AssertionResult ValidateDoneAndOffsetImpl(uint32_t offset,
+                                                   const Validator& wrapped,
+                                                   const DecodeBuffer& input,
+                                                   DecodeStatus status) {
+    VERIFY_EQ(status, DecodeStatus::kDecodeDone);
+    VERIFY_EQ(offset, input.Offset()) << "\nRemaining=" << input.Remaining();
+    return wrapped.Run(input, status);
+  }
+  static Validator ValidateDoneAndOffset(uint32_t offset,
+                                         const Validator& wrapped) {
+    // Make a copy of |wrapped| (by not using base::ConstRef) to avoid lifetime
+    // issues if this method is called with a temporary Validator.
+    return base::Bind(&ValidateDoneAndOffsetImpl, offset, wrapped);
+  }
+  static AssertionResult ValidateDoneAndOffsetNoArgImpl(
+      uint32_t offset,
+      const NoArgValidator& wrapped,
+      const DecodeBuffer& input,
+      DecodeStatus status) {
+    VERIFY_EQ(status, DecodeStatus::kDecodeDone);
+    VERIFY_EQ(offset, input.Offset()) << "\nRemaining=" << input.Remaining();
+    return wrapped.Run();
+  }
+  static Validator ValidateDoneAndOffset(uint32_t offset,
+                                         const NoArgValidator& wrapped) {
+    // Make a copy of |wrapped| (by not using base::ConstRef) to avoid lifetime
+    // issues if this method is called with a temporary Validator.
+    return base::Bind(&ValidateDoneAndOffsetNoArgImpl, offset, wrapped);
+  }
+  static Validator ValidateDoneAndOffset(uint32_t offset) {
+    // Make a copy of |wrapped| (by not using base::ConstRef) to avoid lifetime
+    // issues if this method is called with a temporary Validator.
+    return ValidateDoneAndOffset(offset, base::Bind(&SucceedingValidator));
+  }
+
+  // Expose |random_| as RandomBase so callers do not have to care about which
+  // sub-class of RandomBase is used, nor can they rely on the specific
+  // sub-class that RandomDecoderTest uses.
+  RandomBase& Random() { return random_; }
+  RandomBase* RandomPtr() { return &random_; }
+
+  uint32_t RandStreamId();
+
+  bool stop_decode_on_done_ = true;
+
+ private:
+  Http2Random random_;
+};
+
+}  // namespace test
+}  // namespace net
+
+#endif  // NET_HTTP2_TOOLS_RANDOM_DECODER_TEST_H_
diff --git a/net/net.gypi b/net/net.gypi
index 484e83c..7fe72a64 100644
--- a/net/net.gypi
+++ b/net/net.gypi
@@ -784,6 +784,69 @@
       'http/url_security_manager.h',
       'http/url_security_manager_posix.cc',
       'http/url_security_manager_win.cc',
+      'http2/decoder/decode_buffer.cc',
+      'http2/decoder/decode_buffer.h',
+      'http2/decoder/decode_http2_structures.cc',
+      'http2/decoder/decode_http2_structures.h',
+      'http2/decoder/decode_status.cc',
+      'http2/decoder/decode_status.h',
+      'http2/decoder/frame_decoder_state.cc',
+      'http2/decoder/frame_decoder_state.h',
+      'http2/decoder/http2_frame_decoder.cc',
+      'http2/decoder/http2_frame_decoder.h',
+      'http2/decoder/http2_frame_decoder_listener.cc',
+      'http2/decoder/http2_frame_decoder_listener.h',
+      'http2/decoder/http2_structure_decoder.cc',
+      'http2/decoder/http2_structure_decoder.h',
+      'http2/decoder/payload_decoders/altsvc_payload_decoder.cc',
+      'http2/decoder/payload_decoders/altsvc_payload_decoder.h',
+      'http2/decoder/payload_decoders/continuation_payload_decoder.cc',
+      'http2/decoder/payload_decoders/continuation_payload_decoder.h',
+      'http2/decoder/payload_decoders/data_payload_decoder.cc',
+      'http2/decoder/payload_decoders/data_payload_decoder.h',
+      'http2/decoder/payload_decoders/goaway_payload_decoder.cc',
+      'http2/decoder/payload_decoders/goaway_payload_decoder.h',
+      'http2/decoder/payload_decoders/headers_payload_decoder.cc',
+      'http2/decoder/payload_decoders/headers_payload_decoder.h',
+      'http2/decoder/payload_decoders/ping_payload_decoder.cc',
+      'http2/decoder/payload_decoders/ping_payload_decoder.h',
+      'http2/decoder/payload_decoders/priority_payload_decoder.cc',
+      'http2/decoder/payload_decoders/priority_payload_decoder.h',
+      'http2/decoder/payload_decoders/push_promise_payload_decoder.cc',
+      'http2/decoder/payload_decoders/push_promise_payload_decoder.h',
+      'http2/decoder/payload_decoders/rst_stream_payload_decoder.cc',
+      'http2/decoder/payload_decoders/rst_stream_payload_decoder.h',
+      'http2/decoder/payload_decoders/settings_payload_decoder.cc',
+      'http2/decoder/payload_decoders/settings_payload_decoder.h',
+      'http2/decoder/payload_decoders/unknown_payload_decoder.cc',
+      'http2/decoder/payload_decoders/unknown_payload_decoder.h',
+      'http2/decoder/payload_decoders/window_update_payload_decoder.cc',
+      'http2/decoder/payload_decoders/window_update_payload_decoder.h',
+      'http2/hpack/decoder/hpack_block_decoder.cc',
+      'http2/hpack/decoder/hpack_block_decoder.h',
+      'http2/hpack/decoder/hpack_decoder_string_buffer.cc',
+      'http2/hpack/decoder/hpack_decoder_string_buffer.h',
+      'http2/hpack/decoder/hpack_entry_decoder.cc',
+      'http2/hpack/decoder/hpack_entry_decoder.h',
+      'http2/hpack/decoder/hpack_entry_decoder_listener.cc',
+      'http2/hpack/decoder/hpack_entry_decoder_listener.h',
+      'http2/hpack/decoder/hpack_entry_type_decoder.cc',
+      'http2/hpack/decoder/hpack_entry_type_decoder.h',
+      'http2/hpack/decoder/hpack_string_decoder.cc',
+      'http2/hpack/decoder/hpack_string_decoder.h',
+      'http2/hpack/decoder/hpack_string_decoder_listener.cc',
+      'http2/hpack/decoder/hpack_string_decoder_listener.h',
+      'http2/hpack/decoder/hpack_varint_decoder.cc',
+      'http2/hpack/decoder/hpack_varint_decoder.h',
+      'http2/hpack/http2_hpack_constants.cc',
+      'http2/hpack/http2_hpack_constants.h',
+      'http2/hpack/huffman/http2_hpack_huffman_decoder.cc',
+      'http2/hpack/huffman/http2_hpack_huffman_decoder.h',
+      'http2/http2_constants.cc',
+      'http2/http2_constants.h',
+      'http2/http2_structures.cc',
+      'http2/http2_structures.h',
+      'http2/tools/http2_bug_tracker.h',
       'nqe/cached_network_quality.cc',
       'nqe/cached_network_quality.h',
       'nqe/effective_connection_type.cc',
@@ -1678,6 +1741,68 @@
       'http/transport_security_persister_unittest.cc',
       'http/transport_security_state_unittest.cc',
       'http/url_security_manager_unittest.cc',
+      'http2/decoder/decode_buffer_test.cc',
+      'http2/decoder/decode_http2_structures_test.cc',
+      'http2/decoder/frame_decoder_state_test_util.cc',
+      'http2/decoder/frame_decoder_state_test_util.h',
+      'http2/decoder/frame_parts.cc',
+      'http2/decoder/frame_parts.h',
+      'http2/decoder/frame_parts_collector.cc',
+      'http2/decoder/frame_parts_collector.h',
+      'http2/decoder/frame_parts_collector_listener.cc',
+      'http2/decoder/frame_parts_collector_listener.h',
+      'http2/decoder/http2_frame_decoder_listener_test_util.cc',
+      'http2/decoder/http2_frame_decoder_listener_test_util.h',
+      'http2/decoder/http2_frame_decoder_test.cc',
+      'http2/decoder/http2_structure_decoder_test.cc',
+      'http2/decoder/http2_structure_decoder_test_util.h',
+      'http2/decoder/payload_decoders/altsvc_payload_decoder_test.cc',
+      'http2/decoder/payload_decoders/continuation_payload_decoder_test.cc',
+      'http2/decoder/payload_decoders/data_payload_decoder_test.cc',
+      'http2/decoder/payload_decoders/goaway_payload_decoder_test.cc',
+      'http2/decoder/payload_decoders/headers_payload_decoder_test.cc',
+      'http2/decoder/payload_decoders/payload_decoder_base_test_util.cc',
+      'http2/decoder/payload_decoders/payload_decoder_base_test_util.h',
+      'http2/decoder/payload_decoders/ping_payload_decoder_test.cc',
+      'http2/decoder/payload_decoders/priority_payload_decoder_test.cc',
+      'http2/decoder/payload_decoders/push_promise_payload_decoder_test.cc',
+      'http2/decoder/payload_decoders/rst_stream_payload_decoder_test.cc',
+      'http2/decoder/payload_decoders/settings_payload_decoder_test.cc',
+      'http2/decoder/payload_decoders/unknown_payload_decoder_test.cc',
+      'http2/decoder/payload_decoders/window_update_payload_decoder_test.cc',
+      'http2/hpack/decoder/hpack_block_collector.cc',
+      'http2/hpack/decoder/hpack_block_collector.h',
+      'http2/hpack/decoder/hpack_block_decoder_test.cc',
+      'http2/hpack/decoder/hpack_decoder_string_buffer_test.cc',
+      'http2/hpack/decoder/hpack_entry_collector.cc',
+      'http2/hpack/decoder/hpack_entry_collector.h',
+      'http2/hpack/decoder/hpack_entry_decoder_test.cc',
+      'http2/hpack/decoder/hpack_entry_type_decoder_test.cc',
+      'http2/hpack/decoder/hpack_string_collector.cc',
+      'http2/hpack/decoder/hpack_string_collector.h',
+      'http2/hpack/decoder/hpack_string_decoder_test.cc',
+      'http2/hpack/decoder/hpack_varint_decoder_test.cc',
+      'http2/hpack/http2_hpack_constants_test.cc',
+      'http2/hpack/huffman/http2_hpack_huffman_decoder_test.cc',
+      'http2/hpack/tools/hpack_block_builder.cc',
+      'http2/hpack/tools/hpack_block_builder.h',
+      'http2/hpack/tools/hpack_block_builder_test.cc',
+      'http2/hpack/tools/hpack_example.cc',
+      'http2/hpack/tools/hpack_example.h',
+      'http2/http2_constants_test.cc',
+      'http2/http2_constants_test_util.cc',
+      'http2/http2_constants_test_util.h',
+      'http2/http2_structures_test.cc',
+      'http2/http2_structures_test_util.cc',
+      'http2/http2_structures_test_util.h',
+      'http2/tools/failure.cc',
+      'http2/tools/failure.h',
+      'http2/tools/http2_frame_builder.cc',
+      'http2/tools/http2_frame_builder.h',
+      'http2/tools/http2_random.cc',
+      'http2/tools/http2_random.h',
+      'http2/tools/random_decoder_test.cc',
+      'http2/tools/random_decoder_test.h',
       'log/bounded_file_net_log_observer_unittest.cc',
       'log/net_log_capture_mode_unittest.cc',
       'log/net_log_unittest.cc',
diff --git a/remoting/test/chromoting_test_driver_environment.h b/remoting/test/chromoting_test_driver_environment.h
index 369a4e7..df2cb21e 100644
--- a/remoting/test/chromoting_test_driver_environment.h
+++ b/remoting/test/chromoting_test_driver_environment.h
@@ -40,7 +40,7 @@
     std::string host_jid;
     std::string pin;
     base::FilePath refresh_token_file_path;
-    bool use_test_environment;
+    bool use_test_environment = false;
   };
 
   explicit ChromotingTestDriverEnvironment(const EnvironmentOptions& options);
diff --git a/services/service_manager/service_manager.cc b/services/service_manager/service_manager.cc
index 9a31773..dab2bd7 100644
--- a/services/service_manager/service_manager.cc
+++ b/services/service_manager/service_manager.cc
@@ -10,7 +10,6 @@
 
 #include "base/bind.h"
 #include "base/command_line.h"
-#include "base/debug/alias.h"
 #include "base/guid.h"
 #include "base/logging.h"
 #include "base/macros.h"
@@ -928,19 +927,8 @@
         package_path = result->package_path;
       }
 
-      Identity source_instance_identity;
-      base::debug::Alias(&has_source_instance);
-      base::debug::Alias(&package_path);
-      base::debug::Alias(&source);
-      base::debug::Alias(&target);
-      if (source_instance)
-        source_instance_identity = source_instance->identity();
-      base::debug::Alias(&source_instance_identity);
-#if defined(GOOGLE_CHROME_BUILD)
-      // We do not currently want to hit this code path in production, but it's
-      // happening somehow. https://crbug.com/649673.
-      CHECK(false);
-#endif
+      // TODO(rockot): Find a way to block this code path for content but allow
+      // it for chrome_mash.
       instance->StartWithFilePath(package_path);
     }
   }
diff --git a/services/ui/demo/BUILD.gn b/services/ui/demo/BUILD.gn
index 5a4e2bb..85f03db 100644
--- a/services/ui/demo/BUILD.gn
+++ b/services/ui/demo/BUILD.gn
@@ -8,8 +8,6 @@
 
 source_set("lib") {
   sources = [
-    "bitmap_uploader.cc",
-    "bitmap_uploader.h",
     "mus_demo.cc",
     "mus_demo.h",
   ]
@@ -26,7 +24,10 @@
     "//services/ui/public/cpp",
     "//services/ui/public/cpp:internal",
     "//services/ui/public/interfaces",
+    "//ui/aura",
+    "//ui/aura_extra",
     "//ui/gfx/geometry",
+    "//ui/wm",
   ]
 }
 
diff --git a/services/ui/demo/DEPS b/services/ui/demo/DEPS
index a968818dd..e23d3e58 100644
--- a/services/ui/demo/DEPS
+++ b/services/ui/demo/DEPS
@@ -1,5 +1,8 @@
 include_rules = [
   "+gpu/GLES2",
   "+services/ui/public/cpp",
+  "+ui/aura",
+  "+ui/aura_extra",
+  "+ui/wm",
 ]
 
diff --git a/services/ui/demo/bitmap_uploader.cc b/services/ui/demo/bitmap_uploader.cc
deleted file mode 100644
index b208ed8..0000000
--- a/services/ui/demo/bitmap_uploader.cc
+++ /dev/null
@@ -1,243 +0,0 @@
-// Copyright 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 "services/ui/demo/bitmap_uploader.h"
-
-#include <stddef.h>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "cc/ipc/compositor_frame.mojom.h"
-#include "cc/quads/render_pass.h"
-#include "cc/quads/solid_color_draw_quad.h"
-#include "cc/quads/texture_draw_quad.h"
-#include "services/ui/public/cpp/context_provider.h"
-#include "services/ui/public/cpp/gles2_context.h"
-#include "services/ui/public/cpp/gpu/gpu_service.h"
-#include "services/ui/public/cpp/window.h"
-
-namespace ui {
-namespace {
-
-const uint32_t g_transparent_color = 0x00000000;
-
-}  // namespace
-
-const char kBitmapUploaderForAcceleratedWidget[] =
-    "__BITMAP_UPLOADER_ACCELERATED_WIDGET__";
-
-BitmapUploader::BitmapUploader(Window* window)
-    : window_(window),
-      color_(g_transparent_color),
-      width_(0),
-      height_(0),
-      format_(BGRA),
-      next_resource_id_(1u),
-      weak_factory_(this) {}
-
-void BitmapUploader::Init(ui::GpuService* gpu_service) {
-  gpu_service->EstablishGpuChannel(base::Bind(
-      &BitmapUploader::OnGpuChannelEstablished, weak_factory_.GetWeakPtr(),
-      gpu_service->gpu_memory_buffer_manager()));
-}
-
-BitmapUploader::~BitmapUploader() {
-  compositor_frame_sink_->DetachFromClient();
-}
-
-// Sets the color which is RGBA.
-void BitmapUploader::SetColor(uint32_t color) {
-  if (color_ == color)
-    return;
-  color_ = color;
-  if (compositor_frame_sink_)
-    Upload();
-}
-
-// Sets a bitmap.
-void BitmapUploader::SetBitmap(int width,
-                               int height,
-                               std::unique_ptr<std::vector<unsigned char>> data,
-                               Format format) {
-  width_ = width;
-  height_ = height;
-  bitmap_ = std::move(data);
-  format_ = format;
-  if (compositor_frame_sink_)
-    Upload();
-}
-
-void BitmapUploader::Upload() {
-  if (!compositor_frame_sink_ || !compositor_frame_sink_->context_provider())
-    return;
-
-  const gfx::Rect bounds(window_->bounds().size());
-
-  cc::CompositorFrame frame;
-  // TODO(rjkroege): Support device scale factors other than 1.
-  frame.metadata.device_scale_factor = 1.0f;
-  frame.resource_list.resize(0u);
-
-  const cc::RenderPassId render_pass_id(1, 1);
-  std::unique_ptr<cc::RenderPass> pass = cc::RenderPass::Create();
-  pass->SetAll(render_pass_id, bounds, bounds, gfx::Transform(),
-               true /* has_transparent_background */);
-
-  // The SharedQuadState is owned by the SharedQuadStateList
-  // shared_quad_state_list.
-  cc::SharedQuadState* sqs = pass->CreateAndAppendSharedQuadState();
-  sqs->SetAll(gfx::Transform(), bounds.size(), bounds, bounds,
-              false /* is_clipped */, 1.f /* opacity */, SkBlendMode::kSrc,
-              0 /* sorting_context_id */);
-
-  if (bitmap_.get()) {
-    gpu::gles2::GLES2Interface* gl =
-        compositor_frame_sink_->context_provider()->ContextGL();
-    gfx::Size bitmap_size(width_, height_);
-    GLuint texture_id = BindTextureForSize(bitmap_size);
-    gl->TexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bitmap_size.width(),
-                      bitmap_size.height(), TextureFormat(), GL_UNSIGNED_BYTE,
-                      &((*bitmap_)[0]));
-
-    gpu::Mailbox mailbox;
-    gl->GenMailboxCHROMIUM(mailbox.name);
-    gl->ProduceTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
-
-    const GLuint64 fence_sync = gl->InsertFenceSyncCHROMIUM();
-    gl->ShallowFlushCHROMIUM();
-
-    gpu::SyncToken sync_token;
-    gl->GenSyncTokenCHROMIUM(fence_sync, sync_token.GetData());
-
-    cc::TransferableResource resource;
-    resource.id = next_resource_id_++;
-    resource_to_texture_id_map_[resource.id] = texture_id;
-    resource.format = cc::ResourceFormat::RGBA_8888;
-    resource.filter = GL_LINEAR;
-    resource.size = bitmap_size;
-    resource.mailbox_holder =
-        gpu::MailboxHolder(mailbox, sync_token, GL_TEXTURE_2D);
-    resource.read_lock_fences_enabled = false;
-    resource.is_software = false;
-    resource.is_overlay_candidate = false;
-    frame.resource_list.push_back(std::move(resource));
-
-    cc::TextureDrawQuad* quad =
-        pass->CreateAndAppendDrawQuad<cc::TextureDrawQuad>();
-
-    gfx::Size rect_size;
-    if (width_ <= bounds.width() && height_ <= bounds.height()) {
-      rect_size.SetSize(width_, height_);
-    } else {
-      // The source bitmap is larger than the viewport. Resize it while
-      // maintaining the aspect ratio.
-      float width_ratio = static_cast<float>(width_) / bounds.width();
-      float height_ratio = static_cast<float>(height_) / bounds.height();
-      if (width_ratio > height_ratio) {
-        rect_size.SetSize(bounds.width(), height_ / width_ratio);
-      } else {
-        rect_size.SetSize(width_ / height_ratio, bounds.height());
-      }
-    }
-    gfx::Rect rect(rect_size);
-    const bool needs_blending = true;
-    const bool premultiplied_alpha = true;
-    const gfx::PointF uv_top_left(0.f, 0.f);
-    const gfx::PointF uv_bottom_right(1.f, 1.f);
-    float vertex_opacity[4] = {1.f, 1.f, 1.f, 1.f};
-    const bool y_flipped = false;
-    const bool nearest_neighbor = false;
-    quad->SetAll(sqs, rect, rect, rect, needs_blending, resource.id,
-                 gfx::Size(), premultiplied_alpha, uv_top_left, uv_bottom_right,
-                 g_transparent_color, vertex_opacity, y_flipped,
-                 nearest_neighbor, false);
-  }
-
-  if (color_ != g_transparent_color) {
-    cc::SolidColorDrawQuad* quad =
-        pass->CreateAndAppendDrawQuad<cc::SolidColorDrawQuad>();
-    const bool force_antialiasing_off = false;
-    const gfx::Rect opaque_rect(0, 0, 0, 0);
-    const bool needs_blending = true;
-    quad->SetAll(sqs, bounds, opaque_rect, bounds, needs_blending, color_,
-                 force_antialiasing_off);
-  }
-
-  frame.render_pass_list.push_back(std::move(pass));
-
-  // TODO(rjkroege, fsamuel): We should throttle frames.
-  compositor_frame_sink_->SubmitCompositorFrame(std::move(frame));
-}
-
-void BitmapUploader::OnGpuChannelEstablished(
-    gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
-    scoped_refptr<gpu::GpuChannelHost> gpu_channel) {
-  compositor_frame_sink_ = window_->RequestCompositorFrameSink(
-      mojom::CompositorFrameSinkType::DEFAULT,
-      new ContextProvider(std::move(gpu_channel)), gpu_memory_buffer_manager);
-  compositor_frame_sink_->BindToClient(this);
-}
-
-uint32_t BitmapUploader::BindTextureForSize(const gfx::Size& size) {
-  gpu::gles2::GLES2Interface* gl =
-      compositor_frame_sink_->context_provider()->ContextGL();
-  // TODO(jamesr): Recycle textures.
-  GLuint texture = 0u;
-  gl->GenTextures(1, &texture);
-  gl->BindTexture(GL_TEXTURE_2D, texture);
-  gl->TexImage2D(GL_TEXTURE_2D, 0, TextureFormat(), size.width(), size.height(),
-                 0, TextureFormat(), GL_UNSIGNED_BYTE, 0);
-  gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-  return texture;
-}
-
-void BitmapUploader::SetBeginFrameSource(cc::BeginFrameSource* source) {}
-
-void BitmapUploader::ReclaimResources(
-    const cc::ReturnedResourceArray& resources) {
-  gpu::gles2::GLES2Interface* gl =
-      compositor_frame_sink_->context_provider()->ContextGL();
-  // TODO(jamesr): Recycle.
-  for (size_t i = 0; i < resources.size(); ++i) {
-    cc::ReturnedResource resource = std::move(resources[i]);
-    DCHECK_EQ(1, resource.count);
-    gl->WaitSyncTokenCHROMIUM(resource.sync_token.GetConstData());
-    uint32_t texture_id = resource_to_texture_id_map_[resource.id];
-    DCHECK_NE(0u, texture_id);
-    resource_to_texture_id_map_.erase(resource.id);
-    gl->DeleteTextures(1, &texture_id);
-  }
-}
-
-void BitmapUploader::SetTreeActivationCallback(const base::Closure& callback) {
-  // TODO(fsamuel): Implement this.
-}
-
-void BitmapUploader::DidReceiveCompositorFrameAck() {
-  // TODO(fsamuel): Implement this.
-}
-
-void BitmapUploader::DidLoseCompositorFrameSink() {
-  // TODO(fsamuel): Implement this.
-}
-
-void BitmapUploader::OnDraw(const gfx::Transform& transform,
-                            const gfx::Rect& viewport,
-                            bool resourceless_software_draw) {
-  // TODO(fsamuel): Implement this.
-}
-
-void BitmapUploader::SetMemoryPolicy(const cc::ManagedMemoryPolicy& policy) {
-  // TODO(fsamuel): Implement this.
-}
-
-void BitmapUploader::SetExternalTilePriorityConstraints(
-    const gfx::Rect& viewport_rect,
-    const gfx::Transform& transform) {
-  // TODO(fsamuel): Implement this.
-}
-
-}  // namespace ui
diff --git a/services/ui/demo/bitmap_uploader.h b/services/ui/demo/bitmap_uploader.h
deleted file mode 100644
index 7a22bb0..0000000
--- a/services/ui/demo/bitmap_uploader.h
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright 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 SERVICES_UI_DEMO_BITMAP_UPLOADER_H_
-#define SERVICES_UI_DEMO_BITMAP_UPLOADER_H_
-
-#include <stdint.h>
-
-#include <memory>
-#include <unordered_map>
-
-#include "base/compiler_specific.h"
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "cc/output/compositor_frame_sink_client.h"
-#include "gpu/GLES2/gl2chromium.h"
-#include "gpu/GLES2/gl2extchromium.h"
-#include "services/ui/public/cpp/window_compositor_frame_sink.h"
-
-namespace gpu {
-class GpuChannelHost;
-}
-
-namespace ui {
-
-class GpuService;
-class Window;
-
-extern const char kBitmapUploaderForAcceleratedWidget[];
-
-// BitmapUploader is useful if you want to draw a bitmap or color in a
-// Window.
-class BitmapUploader : public cc::CompositorFrameSinkClient {
- public:
-  explicit BitmapUploader(Window* window);
-  ~BitmapUploader() override;
-
-  void Init(GpuService* gpu_service);
-  // Sets the color which is RGBA.
-  void SetColor(uint32_t color);
-
-  enum Format {
-    RGBA,  // Pixel layout on Android.
-    BGRA,  // Pixel layout everywhere else.
-  };
-
-  // Sets a bitmap.
-  void SetBitmap(int width,
-                 int height,
-                 std::unique_ptr<std::vector<unsigned char>> data,
-                 Format format);
-
- private:
-  void Upload();
-
-  void OnGpuChannelEstablished(
-      gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
-      scoped_refptr<gpu::GpuChannelHost> gpu_channel);
-
-  uint32_t BindTextureForSize(const gfx::Size& size);
-
-  uint32_t TextureFormat() const {
-    return format_ == BGRA ? GL_BGRA_EXT : GL_RGBA;
-  }
-
-  void SetIdNamespace(uint32_t id_namespace);
-
-  // cc::CompositorFrameSinkClient implementation.
-  void SetBeginFrameSource(cc::BeginFrameSource* source) override;
-  void ReclaimResources(const cc::ReturnedResourceArray& resources) override;
-  void SetTreeActivationCallback(const base::Closure& callback) override;
-  void DidReceiveCompositorFrameAck() override;
-  void DidLoseCompositorFrameSink() override;
-  void OnDraw(const gfx::Transform& transform,
-              const gfx::Rect& viewport,
-              bool resourceless_software_draw) override;
-  void SetMemoryPolicy(const cc::ManagedMemoryPolicy& policy) override;
-  void SetExternalTilePriorityConstraints(
-      const gfx::Rect& viewport_rect,
-      const gfx::Transform& transform) override;
-
-  Window* window_;
-  std::unique_ptr<WindowCompositorFrameSink> compositor_frame_sink_;
-
-  uint32_t color_;
-  int width_;
-  int height_;
-  Format format_;
-  std::unique_ptr<std::vector<unsigned char>> bitmap_;
-  uint32_t next_resource_id_;
-  std::unordered_map<uint32_t, uint32_t> resource_to_texture_id_map_;
-
-  base::WeakPtrFactory<BitmapUploader> weak_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(BitmapUploader);
-};
-
-}  // namespace ui
-
-#endif  // SERVICES_UI_DEMO_BITMAP_UPLOADER_H_
diff --git a/services/ui/demo/mus_demo.cc b/services/ui/demo/mus_demo.cc
index d0a90bb..9520f24 100644
--- a/services/ui/demo/mus_demo.cc
+++ b/services/ui/demo/mus_demo.cc
@@ -8,16 +8,23 @@
 #include "base/time/time.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "services/service_manager/public/cpp/service_context.h"
-#include "services/ui/demo/bitmap_uploader.h"
 #include "services/ui/public/cpp/gpu/gpu_service.h"
-#include "services/ui/public/cpp/window.h"
-#include "services/ui/public/cpp/window_tree_client.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "third_party/skia/include/core/SkImageInfo.h"
 #include "third_party/skia/include/core/SkPaint.h"
 #include "third_party/skia/include/core/SkRect.h"
+#include "ui/aura/client/default_capture_client.h"
+#include "ui/aura/env.h"
+#include "ui/aura/mus/mus_context_factory.h"
+#include "ui/aura/mus/property_converter.h"
+#include "ui/aura/mus/window_tree_client.h"
+#include "ui/aura/mus/window_tree_host_mus.h"
+#include "ui/aura/window.h"
+#include "ui/aura_extra/image_window_delegate.h"
 #include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/image/image.h"
+#include "ui/wm/core/wm_state.h"
 
 namespace ui {
 namespace demo {
@@ -69,9 +76,22 @@
 void MusDemo::OnStart() {
   screen_ = base::MakeUnique<display::ScreenBase>();
   display::Screen::SetScreenInstance(screen_.get());
+
+  env_ = aura::Env::CreateInstance(aura::Env::Mode::MUS);
+  capture_client_ = base::MakeUnique<aura::client::DefaultCaptureClient>();
+  property_converter_ = base::MakeUnique<aura::PropertyConverter>();
+  wm_state_ = base::MakeUnique<::wm::WMState>();
+
   gpu_service_ = GpuService::Create(context()->connector());
-  window_tree_client_ = base::MakeUnique<WindowTreeClient>(this, this);
-  window_tree_client_->ConnectAsWindowManager(context()->connector());
+  context_factory_ =
+      base::MakeUnique<aura::MusContextFactory>(gpu_service_.get());
+  env_->set_context_factory(context_factory_.get());
+
+  window_tree_client_ = base::MakeUnique<aura::WindowTreeClient>(
+      context()->connector(), this, this);
+  window_tree_client_->ConnectAsWindowManager();
+
+  env_->SetWindowTreeClient(window_tree_client_.get());
 }
 
 bool MusDemo::OnConnect(const service_manager::ServiceInfo& remote_info,
@@ -79,55 +99,88 @@
   return true;
 }
 
-void MusDemo::OnEmbed(Window* window) {
+void MusDemo::OnEmbed(
+    std::unique_ptr<aura::WindowTreeHostMus> window_tree_host) {
   // Not called for the WindowManager.
   NOTREACHED();
 }
 
-void MusDemo::OnEmbedRootDestroyed(Window* root) {
+void MusDemo::OnUnembed(aura::Window* root) {
+  NOTREACHED();
+}
+
+void MusDemo::OnEmbedRootDestroyed(aura::Window* root) {
   // Not called for the WindowManager.
   NOTREACHED();
 }
 
-void MusDemo::OnLostConnection(WindowTreeClient* client) {
-  window_ = nullptr;
+void MusDemo::OnLostConnection(aura::WindowTreeClient* client) {
+  root_window_ = nullptr;
   window_tree_client_.reset();
   timer_.Stop();
 }
 
 void MusDemo::OnPointerEventObserved(const PointerEvent& event,
-                                     Window* target) {}
+                                     aura::Window* target) {}
 
-void MusDemo::SetWindowManagerClient(WindowManagerClient* client) {}
+aura::client::CaptureClient* MusDemo::GetCaptureClient() {
+  return capture_client_.get();
+}
 
-bool MusDemo::OnWmSetBounds(Window* window, gfx::Rect* bounds) {
+aura::PropertyConverter* MusDemo::GetPropertyConverter() {
+  return property_converter_.get();
+}
+
+void MusDemo::SetWindowManagerClient(aura::WindowManagerClient* client) {}
+
+bool MusDemo::OnWmSetBounds(aura::Window* window, gfx::Rect* bounds) {
   return true;
 }
 
-bool MusDemo::OnWmSetProperty(Window* window,
+bool MusDemo::OnWmSetProperty(aura::Window* window,
                               const std::string& name,
                               std::unique_ptr<std::vector<uint8_t>>* new_data) {
   return true;
 }
 
-Window* MusDemo::OnWmCreateTopLevelWindow(
+aura::Window* MusDemo::OnWmCreateTopLevelWindow(
+    mojom::WindowType window_type,
     std::map<std::string, std::vector<uint8_t>>* properties) {
+  NOTREACHED();
   return nullptr;
 }
 
 void MusDemo::OnWmClientJankinessChanged(
-    const std::set<Window*>& client_windows,
+    const std::set<aura::Window*>& client_windows,
     bool janky) {
   // Don't care
 }
 
-void MusDemo::OnWmNewDisplay(Window* window, const display::Display& display) {
-  DCHECK(!window_);  // Only support one display.
-  window_ = window;
+void MusDemo::OnWmWillCreateDisplay(const display::Display& display) {
+  screen_->display_list().AddDisplay(display,
+                                     display::DisplayList::Type::PRIMARY);
+}
 
-  // Initialize bitmap uploader for sending frames to MUS.
-  uploader_.reset(new BitmapUploader(window_));
-  uploader_->Init(gpu_service_.get());
+void MusDemo::OnWmNewDisplay(
+    std::unique_ptr<aura::WindowTreeHostMus> window_tree_host,
+    const display::Display& display) {
+  DCHECK(!root_window_);  // Only support one display.
+
+  window_tree_host->InitHost();
+  window_tree_host->Show();
+  root_window_ = window_tree_host->window();
+  // Take ownership of the WTH.
+  window_tree_host_ = std::move(window_tree_host);
+
+  // Initialize the window for the bitmap.
+  window_delegate_ = new aura_extra::ImageWindowDelegate();
+  bitmap_window_ = base::MakeUnique<aura::Window>(window_delegate_);
+  bitmap_window_->Init(LAYER_TEXTURED);
+  bitmap_window_->SetBounds(root_window_->bounds());
+  bitmap_window_->Show();
+  bitmap_window_->SetName("Bitmap");
+
+  root_window_->AddChild(bitmap_window_.get());
 
   // Draw initial frame and start the timer to regularly draw frames.
   DrawFrame();
@@ -135,30 +188,31 @@
                base::Bind(&MusDemo::DrawFrame, base::Unretained(this)));
 }
 
-void MusDemo::OnWmDisplayRemoved(ui::Window* window) {
-  window->Destroy();
+void MusDemo::OnWmDisplayRemoved(aura::WindowTreeHostMus* window_tree_host) {
+  timer_.Stop();
+  root_window_->RemoveChild(bitmap_window_.get());
+  bitmap_window_.reset();
 }
 
 void MusDemo::OnWmDisplayModified(const display::Display& display) {}
 
-void MusDemo::OnWmPerformMoveLoop(Window* window,
+mojom::EventResult MusDemo::OnAccelerator(uint32_t id, const Event& event) {
+  return mojom::EventResult::UNHANDLED;
+}
+
+void MusDemo::OnWmPerformMoveLoop(aura::Window* window,
                                   mojom::MoveLoopSource source,
                                   const gfx::Point& cursor_location,
                                   const base::Callback<void(bool)>& on_done) {
   // Don't care
 }
 
-void MusDemo::OnWmCancelMoveLoop(Window* window) {}
+void MusDemo::OnWmCancelMoveLoop(aura::Window* window) {}
 
-void MusDemo::AllocBitmap() {
-  const gfx::Rect bounds = window_->GetBoundsInRoot();
-
-  // Allocate bitmap the same size as the window for drawing.
-  bitmap_.reset();
-  SkImageInfo image_info = SkImageInfo::MakeN32(bounds.width(), bounds.height(),
-                                                kPremul_SkAlphaType);
-  bitmap_.allocPixels(image_info);
-}
+void MusDemo::OnWmSetClientArea(
+    aura::Window* window,
+    const gfx::Insets& insets,
+    const std::vector<gfx::Rect>& additional_client_areas) {}
 
 void MusDemo::DrawFrame() {
   base::TimeTicks now = base::TimeTicks::Now();
@@ -171,13 +225,12 @@
   if (angle_ >= 360.0)
     angle_ = 0.0;
 
-  const gfx::Rect bounds = window_->GetBoundsInRoot();
-
-  // Check that bitmap and window sizes match, otherwise reallocate bitmap.
-  const SkImageInfo info = bitmap_.info();
-  if (info.width() != bounds.width() || info.height() != bounds.height()) {
-    AllocBitmap();
-  }
+  // Re-initialize the bitmap
+  bitmap_.reset();
+  const gfx::Rect bounds = bitmap_window_->bounds();
+  SkImageInfo image_info = SkImageInfo::MakeN32(bounds.width(), bounds.height(),
+                                                kPremul_SkAlphaType);
+  bitmap_.allocPixels(image_info);
 
   // Draw the rotated square on background in bitmap.
   SkCanvas canvas(bitmap_);
@@ -186,28 +239,13 @@
   DrawSquare(bounds, angle_, &canvas);
   canvas.flush();
 
-  // Copy pixels data into vector that will be passed to BitmapUploader.
-  // TODO(rjkroege): Make a 1/0-copy bitmap uploader for the contents of a
-  // SkBitmap.
-  bitmap_.lockPixels();
-  const unsigned char* addr =
-      static_cast<const unsigned char*>(bitmap_.getPixels());
-  const int bytes = bounds.width() * bounds.height() * 4;
-  std::unique_ptr<std::vector<unsigned char>> data(
-      new std::vector<unsigned char>(addr, addr + bytes));
-  bitmap_.unlockPixels();
+  gfx::ImageSkiaRep image_skia_rep(bitmap_, 1);
+  gfx::ImageSkia image_skia(image_skia_rep);
+  gfx::Image image(image_skia);
 
-#if defined(OS_ANDROID)
-  // TODO(jcivelli): find a way to not have an ifdef here.
-  BitmapUploader::Format bitmap_format = BitmapUploader::RGBA;
-#else
-  BitmapUploader::Format bitmap_format = BitmapUploader::BGRA;
-#endif
-
-  // Send frame to MUS via BitmapUploader.
-  uploader_->SetBitmap(bounds.width(), bounds.height(), std::move(data),
-                       bitmap_format);
+  window_delegate_->SetImage(image);
+  bitmap_window_->SchedulePaintInRect(bitmap_window_->bounds());
 }
 
 }  // namespace demo
-}  // namespace ui
+}  // namespace aura
diff --git a/services/ui/demo/mus_demo.h b/services/ui/demo/mus_demo.h
index 941b238..e253509 100644
--- a/services/ui/demo/mus_demo.h
+++ b/services/ui/demo/mus_demo.h
@@ -15,23 +15,41 @@
 #include "base/macros.h"
 #include "base/timer/timer.h"
 #include "services/service_manager/public/cpp/service.h"
-#include "services/ui/public/cpp/window_manager_delegate.h"
-#include "services/ui/public/cpp/window_tree_client_delegate.h"
 #include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/aura/mus/window_manager_delegate.h"
+#include "ui/aura/mus/window_tree_client_delegate.h"
 #include "ui/display/screen_base.h"
 
+namespace aura {
+class Env;
+class PropertyConverter;
+
+namespace client {
+class DefaultCaptureClient;
+}
+}  // namespace aura
+
+namespace aura_extra {
+class ImageWindowDelegate;
+}
+
+namespace wm {
+class WMState;
+}
+
 namespace ui {
-class BitmapUploader;
+class ContextFactory;
 class GpuService;
 
 namespace demo {
 
-// A simple MUS Demo service. This service connects to the service:ui, creates a
-// new window and draws a spinning square in the center of the window. Provides
-// a simple way to demonstrate that the graphic stack works as intended.
+// A simple MUS Demo service. This service connects to the service:ui, adds a
+// new window to the root Window, and draws a spinning square in the center of
+// the window. Provides a simple way to demonstrate that the graphic stack works
+// as intended.
 class MusDemo : public service_manager::Service,
-                public WindowTreeClientDelegate,
-                public WindowManagerDelegate {
+                public aura::WindowTreeClientDelegate,
+                public aura::WindowManagerDelegate {
  public:
   MusDemo();
   ~MusDemo() override;
@@ -42,48 +60,66 @@
   bool OnConnect(const service_manager::ServiceInfo& remote_info,
                  service_manager::InterfaceRegistry* registry) override;
 
-  // WindowTreeClientDelegate:
-  void OnEmbed(Window* root) override;
-  void OnEmbedRootDestroyed(Window* root) override;
-  void OnLostConnection(WindowTreeClient* client) override;
-  void OnPointerEventObserved(const PointerEvent& event,
-                              Window* target) override;
+  // aura::WindowTreeClientDelegate:
+  void OnEmbed(
+      std::unique_ptr<aura::WindowTreeHostMus> window_tree_host) override;
+  void OnUnembed(aura::Window* root) override;
+  void OnEmbedRootDestroyed(aura::Window* root) override;
+  void OnLostConnection(aura::WindowTreeClient* client) override;
+  void OnPointerEventObserved(const ui::PointerEvent& event,
+                              aura::Window* target) override;
+  aura::client::CaptureClient* GetCaptureClient() override;
+  aura::PropertyConverter* GetPropertyConverter() override;
 
-  // WindowManagerDelegate:
-  void SetWindowManagerClient(WindowManagerClient* client) override;
-  bool OnWmSetBounds(Window* window, gfx::Rect* bounds) override;
+  // aura::WindowManagerDelegate:
+  void SetWindowManagerClient(aura::WindowManagerClient* client) override;
+  bool OnWmSetBounds(aura::Window* window, gfx::Rect* bounds) override;
   bool OnWmSetProperty(
-      Window* window,
+      aura::Window* window,
       const std::string& name,
       std::unique_ptr<std::vector<uint8_t>>* new_data) override;
-  Window* OnWmCreateTopLevelWindow(
+  aura::Window* OnWmCreateTopLevelWindow(
+      ui::mojom::WindowType window_type,
       std::map<std::string, std::vector<uint8_t>>* properties) override;
-  void OnWmClientJankinessChanged(const std::set<Window*>& client_windows,
+  void OnWmClientJankinessChanged(const std::set<aura::Window*>& client_windows,
                                   bool janky) override;
-  void OnWmNewDisplay(Window* window, const display::Display& display) override;
-  void OnWmDisplayRemoved(ui::Window* window) override;
+  void OnWmWillCreateDisplay(const display::Display& display) override;
+  void OnWmNewDisplay(std::unique_ptr<aura::WindowTreeHostMus> window_tree_host,
+                      const display::Display& display) override;
+  void OnWmDisplayRemoved(aura::WindowTreeHostMus* window_tree_host) override;
   void OnWmDisplayModified(const display::Display& display) override;
-  void OnWmPerformMoveLoop(Window* window,
-                           mojom::MoveLoopSource source,
+  ui::mojom::EventResult OnAccelerator(uint32_t id,
+                                       const ui::Event& event) override;
+  void OnWmPerformMoveLoop(aura::Window* window,
+                           ui::mojom::MoveLoopSource source,
                            const gfx::Point& cursor_location,
                            const base::Callback<void(bool)>& on_done) override;
-  void OnWmCancelMoveLoop(Window* window) override;
-
-  // Allocate a bitmap the same size as the window to draw into.
-  void AllocBitmap();
+  void OnWmCancelMoveLoop(aura::Window* window) override;
+  void OnWmSetClientArea(
+      aura::Window* window,
+      const gfx::Insets& insets,
+      const std::vector<gfx::Rect>& additional_client_areas) override;
 
   // Draws one frame, incrementing the rotation angle.
   void DrawFrame();
 
-  Window* window_ = nullptr;
-  std::unique_ptr<WindowTreeClient> window_tree_client_;
-  std::unique_ptr<GpuService> gpu_service_;
-
-  // Dummy screen required to be the screen instance.
+  aura::Window* root_window_ = nullptr;
+  std::unique_ptr<aura::WindowTreeClient> window_tree_client_;
+  std::unique_ptr<aura::WindowTreeHostMus> window_tree_host_;
+  std::unique_ptr<ui::GpuService> gpu_service_;
+  std::unique_ptr<ui::ContextFactory> context_factory_;
+  std::unique_ptr<aura::Env> env_;
   std::unique_ptr<display::ScreenBase> screen_;
 
-  // Used to send frames to mus.
-  std::unique_ptr<BitmapUploader> uploader_;
+  std::unique_ptr<aura::client::DefaultCaptureClient> capture_client_;
+  std::unique_ptr<::wm::WMState> wm_state_;
+  std::unique_ptr<aura::PropertyConverter> property_converter_;
+
+  // Window to which we draw the bitmap.
+  std::unique_ptr<aura::Window> bitmap_window_;
+
+  // Destroys itself when the window gets destroyed.
+  aura_extra::ImageWindowDelegate* window_delegate_ = nullptr;
 
   // Bitmap that is the same size as our client window area.
   SkBitmap bitmap_;
@@ -101,6 +137,6 @@
 };
 
 }  // namespace demo
-}  // namespace ui
+}  // namespace aura
 
 #endif  // SERVICES_UI_DEMO_MUS_DEMO_H_
diff --git a/services/ui/ime/test_ime_driver/manifest.json b/services/ui/ime/test_ime_driver/manifest.json
index 3211013..7e980eb 100644
--- a/services/ui/ime/test_ime_driver/manifest.json
+++ b/services/ui/ime/test_ime_driver/manifest.json
@@ -3,7 +3,7 @@
   "display_name": "Test IME Driver",
   "interface_provider_specs": {
     "service_manager:connector": {
-      "provides": { "ime:test_ime_driver": [ "ui::mojom::IMEDriver" ] },
+      "provides": { "ime:test_ime_driver": [] },
       "requires": {
         "ui": [ "ui:ime_registrar" ]
       }
diff --git a/skia/config/SkUserConfig.h b/skia/config/SkUserConfig.h
index c6e914a..b37dad0 100644
--- a/skia/config/SkUserConfig.h
+++ b/skia/config/SkUserConfig.h
@@ -201,10 +201,6 @@
 #   define SK_SUPPORT_LEGACY_GETDEVICE
 #endif
 
-#ifndef SK_SUPPORT_LEGACY_CLIP_REGIONOPS
-#define SK_SUPPORT_LEGACY_CLIP_REGIONOPS
-#endif
-
 // Workaround for poor anisotropic mipmap quality,
 // pending Skia ripmap support.
 // (https://bugs.chromium.org/p/skia/issues/detail?id=4863)
diff --git a/skia/ext/analysis_canvas.cc b/skia/ext/analysis_canvas.cc
index 35e76d0..bed72531 100644
--- a/skia/ext/analysis_canvas.cc
+++ b/skia/ext/analysis_canvas.cc
@@ -391,13 +391,13 @@
 }
 
 void AnalysisCanvas::onClipRect(const SkRect& rect,
-                                SkRegion::Op op,
+                                SkClipOp op,
                                 ClipEdgeStyle edge_style) {
   INHERITED::onClipRect(rect, op, edge_style);
 }
 
 void AnalysisCanvas::onClipPath(const SkPath& path,
-                                SkRegion::Op op,
+                                SkClipOp op,
                                 ClipEdgeStyle edge_style) {
   OnComplexClip();
   INHERITED::onClipRect(path.getBounds(), op, edge_style);
@@ -424,7 +424,7 @@
 }
 
 void AnalysisCanvas::onClipRRect(const SkRRect& rrect,
-                                 SkRegion::Op op,
+                                 SkClipOp op,
                                  ClipEdgeStyle edge_style) {
   SkIRect clip_device_bounds;
   if (getClipDeviceBounds(&clip_device_bounds) &&
@@ -438,7 +438,7 @@
   INHERITED::onClipRect(rrect.getBounds(), op, edge_style);
 }
 
-void AnalysisCanvas::onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op) {
+void AnalysisCanvas::onClipRegion(const SkRegion& deviceRgn, SkClipOp op) {
   const ClipEdgeStyle edge_style = kHard_ClipEdgeStyle;
   if (deviceRgn.isRect()) {
     onClipRect(SkRect::MakeFromIRect(deviceRgn.getBounds()), op, edge_style);
diff --git a/skia/ext/analysis_canvas.h b/skia/ext/analysis_canvas.h
index f13bfad4..a9aeb96 100644
--- a/skia/ext/analysis_canvas.h
+++ b/skia/ext/analysis_canvas.h
@@ -86,15 +86,15 @@
   void willRestore() override;
 
   void onClipRect(const SkRect& rect,
-                  SkRegion::Op op,
+                  SkClipOp op,
                   ClipEdgeStyle edge_style) override;
   void onClipRRect(const SkRRect& rrect,
-                   SkRegion::Op op,
+                   SkClipOp op,
                    ClipEdgeStyle edge_style) override;
   void onClipPath(const SkPath& path,
-                  SkRegion::Op op,
+                  SkClipOp op,
                   ClipEdgeStyle edge_style) override;
-  void onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op) override;
+  void onClipRegion(const SkRegion& deviceRgn, SkClipOp op) override;
 
   void onDrawText(const void* text,
                   size_t byteLength,
diff --git a/skia/ext/benchmarking_canvas.cc b/skia/ext/benchmarking_canvas.cc
index ff9c287a..e5adc0d 100644
--- a/skia/ext/benchmarking_canvas.cc
+++ b/skia/ext/benchmarking_canvas.cc
@@ -264,7 +264,7 @@
   return std::move(val);
 }
 
-std::unique_ptr<base::Value> AsValue(SkRegion::Op op) {
+std::unique_ptr<base::Value> AsValue(SkClipOp op) {
   static const char* gOpStrings[] = { "Difference",
                                       "Intersect",
                                       "Union",
@@ -493,7 +493,7 @@
 }
 
 void BenchmarkingCanvas::onClipRect(const SkRect& rect,
-                                    SkRegion::Op region_op,
+                                    SkClipOp region_op,
                                     SkCanvas::ClipEdgeStyle style) {
   AutoOp op(this, "ClipRect");
   op.addParam("rect", AsValue(rect));
@@ -504,7 +504,7 @@
 }
 
 void BenchmarkingCanvas::onClipRRect(const SkRRect& rrect,
-                                     SkRegion::Op region_op,
+                                     SkClipOp region_op,
                                      SkCanvas::ClipEdgeStyle style) {
   AutoOp op(this, "ClipRRect");
   op.addParam("rrect", AsValue(rrect));
@@ -515,7 +515,7 @@
 }
 
 void BenchmarkingCanvas::onClipPath(const SkPath& path,
-                                    SkRegion::Op region_op,
+                                    SkClipOp region_op,
                                     SkCanvas::ClipEdgeStyle style) {
   AutoOp op(this, "ClipPath");
   op.addParam("path", AsValue(path));
@@ -526,7 +526,7 @@
 }
 
 void BenchmarkingCanvas::onClipRegion(const SkRegion& region,
-                                      SkRegion::Op region_op) {
+                                      SkClipOp region_op) {
   AutoOp op(this, "ClipRegion");
   op.addParam("region", AsValue(region));
   op.addParam("op", AsValue(region_op));
diff --git a/skia/ext/benchmarking_canvas.h b/skia/ext/benchmarking_canvas.h
index 1e6f5f6..b81da79 100644
--- a/skia/ext/benchmarking_canvas.h
+++ b/skia/ext/benchmarking_canvas.h
@@ -35,10 +35,10 @@
   void didConcat(const SkMatrix&) override;
   void didSetMatrix(const SkMatrix&) override;
 
-  void onClipRect(const SkRect&, SkRegion::Op, ClipEdgeStyle) override;
-  void onClipRRect(const SkRRect&, SkRegion::Op, ClipEdgeStyle) override;
-  void onClipPath(const SkPath&, SkRegion::Op, ClipEdgeStyle) override;
-  void onClipRegion(const SkRegion&, SkRegion::Op) override;
+  void onClipRect(const SkRect&, SkClipOp, ClipEdgeStyle) override;
+  void onClipRRect(const SkRRect&, SkClipOp, ClipEdgeStyle) override;
+  void onClipPath(const SkPath&, SkClipOp, ClipEdgeStyle) override;
+  void onClipRegion(const SkRegion&, SkClipOp) override;
 
   void onDrawPaint(const SkPaint&) override;
   void onDrawPoints(PointMode, size_t count, const SkPoint pts[],
diff --git a/storage/browser/blob/blob_entry.cc b/storage/browser/blob/blob_entry.cc
index 8ccc037e..094eb365 100644
--- a/storage/browser/blob/blob_entry.cc
+++ b/storage/browser/blob/blob_entry.cc
@@ -36,19 +36,7 @@
       transport_allowed_callback(transport_allowed_callback),
       num_building_dependent_blobs(num_building_dependent_blobs) {}
 
-BlobEntry::BuildingState::~BuildingState() {
-  DCHECK(!copy_quota_request);
-  DCHECK(!transport_quota_request);
-}
-
-void BlobEntry::BuildingState::CancelRequests() {
-  if (copy_quota_request) {
-    copy_quota_request->Cancel();
-  }
-  if (transport_quota_request) {
-    transport_quota_request->Cancel();
-  }
-}
+BlobEntry::BuildingState::~BuildingState() {}
 
 BlobEntry::BlobEntry(const std::string& content_type,
                      const std::string& content_disposition)
diff --git a/storage/browser/blob/blob_entry.h b/storage/browser/blob/blob_entry.h
index 72d21fcb..3c3869e 100644
--- a/storage/browser/blob/blob_entry.h
+++ b/storage/browser/blob/blob_entry.h
@@ -24,15 +24,16 @@
 class ShareableBlobDataItem;
 class ViewBlobInternalsJob;
 
-// Represents a blob in BlobStorageRegistry. Exported only for unit tests.
+// This class represents a blob in BlobStorageRegistry. We export this only for
+// unit tests.
 class STORAGE_EXPORT BlobEntry {
  public:
   using TransportAllowedCallback =
       base::Callback<void(BlobStatus,
                           std::vector<BlobMemoryController::FileCreationInfo>)>;
 
-  // Records a copy from a referenced blob. Copies happen after referenced blobs
-  // are complete & quota for the copies is granted.
+  // This records a copy from a referenced blob. When we finish building our
+  // blob we perform all of these copies.
   struct STORAGE_EXPORT ItemCopyEntry {
     ItemCopyEntry(scoped_refptr<ShareableBlobDataItem> source_item,
                   size_t source_item_offset,
@@ -49,11 +50,11 @@
     DISALLOW_COPY_AND_ASSIGN(ItemCopyEntry);
   };
 
-  // Building state for pending blobs. State can include:
-  // 1. Waiting for quota to be granted for transport data (PENDING_QUOTA)
+  // This keeps track of our building state for our blob. While building, four
+  // things can be happening mostly simultaneously:
+  // 1. Waiting for quota to be reserved for memory needed (PENDING_QUOTA)
   // 2. Waiting for user population of data after quota (PENDING_TRANSPORT)
-  // 3. Waiting for blobs we reference to complete & quota granted for possible
-  //    copies. (PENDING_INTERNALS)
+  // 3. Waiting for blobs we reference to complete (PENDING_INTERNALS)
   struct STORAGE_EXPORT BuildingState {
     // |transport_allowed_callback| is not null when data needs population. See
     // BlobStorageContext::BuildBlob for when the callback is called.
@@ -62,9 +63,6 @@
                   size_t num_building_dependent_blobs);
     ~BuildingState();
 
-    // Cancels pending memory or file requests.
-    void CancelRequests();
-
     const bool transport_items_present;
     // We can have trasnport data that's either populated or unpopulated. If we
     // need population, this is populated.
@@ -77,10 +75,7 @@
     size_t num_building_dependent_blobs;
 
     base::WeakPtr<BlobMemoryController::QuotaAllocationTask>
-        transport_quota_request;
-
-    // Copy quota is always memory.
-    base::WeakPtr<BlobMemoryController::QuotaAllocationTask> copy_quota_request;
+        memory_quota_request;
 
     // These are copies from a referenced blob item to our blob items. Some of
     // these entries may have changed from bytes to files if they were paged.
@@ -102,9 +97,7 @@
 
   // Returns if we're a pending blob that can finish building.
   bool CanFinishBuilding() const {
-    // PENDING_INTERNALS means transport is finished.
-    return status_ == BlobStatus::PENDING_INTERNALS && building_state_ &&
-           !building_state_->copy_quota_request &&
+    return status_ == BlobStatus::PENDING_INTERNALS &&
            building_state_->num_building_dependent_blobs == 0;
   }
 
diff --git a/storage/browser/blob/blob_memory_controller.cc b/storage/browser/blob/blob_memory_controller.cc
index f4a002fc..6cc6c3f 100644
--- a/storage/browser/blob/blob_memory_controller.cc
+++ b/storage/browser/blob/blob_memory_controller.cc
@@ -5,7 +5,6 @@
 #include "storage/browser/blob/blob_memory_controller.h"
 
 #include <algorithm>
-#include <numeric>
 
 #include "base/callback.h"
 #include "base/callback_helpers.h"
@@ -46,8 +45,6 @@
   base::CreateDirectoryAndGetError(blob_storage_dir, &error);
   UMA_HISTOGRAM_ENUMERATION("Storage.Blob.CreateDirectoryResult", -error,
                             -File::FILE_ERROR_MAX);
-  DLOG_IF(ERROR, error != File::FILE_OK)
-      << "Error creating blob storage directory: " << error;
   return error;
 }
 
@@ -77,6 +74,15 @@
       return std::make_pair(std::vector<FileCreationInfo>(),
                             creation_info.error);
     }
+
+    // Grab the file info to get the "last modified" time and store the file.
+    File::Info file_info;
+    bool success = file.GetInfo(&file_info);
+    creation_info.error = success ? File::FILE_OK : File::FILE_ERROR_FAILED;
+    if (!success) {
+      return std::make_pair(std::vector<FileCreationInfo>(),
+                            creation_info.error);
+    }
     creation_info.file = std::move(file);
 
     result.push_back(std::move(creation_info));
@@ -128,10 +134,6 @@
     if (bytes_written < 0)
       break;
   }
-  if (!file.Flush()) {
-    creation_info.error = File::FILE_ERROR_FAILED;
-    return creation_info;
-  }
 
   File::Info info;
   bool success = file.GetInfo(&info);
@@ -160,10 +162,6 @@
   for (const auto& size_pair : file_id_to_sizes) {
     file_sizes_output->push_back(size_pair.second);
   }
-  DCHECK_EQ(std::accumulate(file_sizes_output->begin(),
-                            file_sizes_output->end(), 0ull),
-            total_size_output)
-      << "Illegal builder configuration, temporary files must be totally used.";
   return total_size_output;
 }
 
@@ -300,7 +298,7 @@
   }
   ~FileQuotaAllocationTask() override {}
 
-  void RunDoneCallback(std::vector<FileCreationInfo> file_info, bool success) {
+  void RunDoneCallback(bool success, std::vector<FileCreationInfo> file_info) {
     // Make sure we clear the weak pointers we gave to the caller beforehand.
     weak_factory_.InvalidateWeakPtrs();
 
@@ -315,7 +313,7 @@
       controller_->pending_file_quota_tasks_.erase(my_list_position_);
     }
 
-    done_callback_.Run(std::move(file_info), success);
+    done_callback_.Run(success, std::move(file_info));
   }
 
   base::WeakPtr<QuotaAllocationTask> GetWeakPtr() {
@@ -343,7 +341,7 @@
     for (size_t i = 0; i < files.size(); i++) {
       files[i].file_reference = std::move(references[i]);
     }
-    RunDoneCallback(std::move(files), true);
+    RunDoneCallback(true, std::move(files));
   }
 
   // The my_list_position_ iterator is stored so that we can remove ourself
@@ -381,7 +379,6 @@
 void BlobMemoryController::DisableFilePaging(base::File::Error reason) {
   UMA_HISTOGRAM_ENUMERATION("Storage.Blob.PagingDisabled", -reason,
                             -File::FILE_ERROR_MAX);
-  DLOG(ERROR) << "Blob storage paging disabled, reason: " << reason;
   file_paging_enabled_ = false;
   in_flight_memory_used_ = 0;
   items_paging_to_file_.clear();
@@ -401,7 +398,7 @@
     memory_request->RunDoneCallback(false);
   }
   for (auto& file_request : old_file_tasks) {
-    file_request->RunDoneCallback(std::vector<FileCreationInfo>(), false);
+    file_request->RunDoneCallback(false, std::vector<FileCreationInfo>());
   }
 }
 
diff --git a/storage/browser/blob/blob_memory_controller.h b/storage/browser/blob/blob_memory_controller.h
index 865d3e0..068e22b 100644
--- a/storage/browser/blob/blob_memory_controller.h
+++ b/storage/browser/blob/blob_memory_controller.h
@@ -103,7 +103,7 @@
   // The bool argument is true if we successfully received file quota, and the
   // vector argument provides the file info.
   using FileQuotaRequestCallback =
-      base::Callback<void(std::vector<FileCreationInfo>, bool)>;
+      base::Callback<void(bool, std::vector<FileCreationInfo>)>;
 
   // We enable file paging if |file_runner| isn't a nullptr.
   BlobMemoryController(const base::FilePath& storage_directory,
diff --git a/storage/browser/blob/blob_reader.cc b/storage/browser/blob/blob_reader.cc
index 5584036..1ec3002 100644
--- a/storage/browser/blob/blob_reader.cc
+++ b/storage/browser/blob/blob_reader.cc
@@ -14,7 +14,6 @@
 
 #include "base/bind.h"
 #include "base/callback_helpers.h"
-#include "base/debug/stack_trace.h"
 #include "base/sequenced_task_runner.h"
 #include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
diff --git a/storage/browser/blob/blob_storage_context.cc b/storage/browser/blob/blob_storage_context.cc
index acc086e..1fecda8 100644
--- a/storage/browser/blob/blob_storage_context.cc
+++ b/storage/browser/blob/blob_storage_context.cc
@@ -92,13 +92,9 @@
   size_t num_files_with_unknown_size = 0;
   size_t num_building_dependent_blobs = 0;
 
-  bool found_memory_transport = false;
-  bool found_file_transport = false;
-
   base::CheckedNumeric<uint64_t> checked_total_size = 0;
   base::CheckedNumeric<uint64_t> checked_total_memory_size = 0;
-  base::CheckedNumeric<uint64_t> checked_transport_quota_needed = 0;
-  base::CheckedNumeric<uint64_t> checked_copy_quota_needed = 0;
+  base::CheckedNumeric<uint64_t> checked_memory_quota_needed = 0;
 
   for (scoped_refptr<BlobDataItem> input_item : input_builder.items_) {
     const DataElement& input_element = input_item->data_element();
@@ -109,19 +105,11 @@
 
     if (IsBytes(type)) {
       DCHECK_NE(0 + DataElement::kUnknownSize, input_element.length());
-      found_memory_transport = true;
-      if (found_file_transport) {
-        // We cannot have both memory and file transport items.
-        status = BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
-        return;
-      }
-      contains_unpopulated_transport_items |=
-          (type == DataElement::TYPE_BYTES_DESCRIPTION);
-      checked_transport_quota_needed += length;
+      checked_memory_quota_needed += length;
       checked_total_size += length;
       scoped_refptr<ShareableBlobDataItem> item = new ShareableBlobDataItem(
           std::move(input_item), ShareableBlobDataItem::QUOTA_NEEDED);
-      pending_transport_items.push_back(item);
+      pending_memory_items.push_back(item);
       transport_items.push_back(item.get());
       output_blob->AppendSharedBlobItem(std::move(item));
       continue;
@@ -186,14 +174,14 @@
         copies.push_back(ItemCopyEntry(slice.first_source_item,
                                        slice.first_item_slice_offset,
                                        slice.dest_items.front()));
-        pending_copy_items.push_back(slice.dest_items.front());
+        pending_memory_items.push_back(slice.dest_items.front());
       }
       if (slice.last_source_item) {
         copies.push_back(
             ItemCopyEntry(slice.last_source_item, 0, slice.dest_items.back()));
-        pending_copy_items.push_back(slice.dest_items.back());
+        pending_memory_items.push_back(slice.dest_items.back());
       }
-      checked_copy_quota_needed += slice.copying_memory_size;
+      checked_memory_quota_needed += slice.copying_memory_size;
 
       for (auto& shareable_item : slice.dest_items) {
         output_blob->AppendSharedBlobItem(std::move(shareable_item));
@@ -201,30 +189,14 @@
       continue;
     }
 
-    // If the source item is a temporary file item, then we need to keep track
-    // of that and mark it as needing quota.
-    scoped_refptr<ShareableBlobDataItem> item;
-    if (BlobDataBuilder::IsFutureFileItem(input_element)) {
-      found_file_transport = true;
-      if (found_memory_transport) {
-        // We cannot have both memory and file transport items.
-        status = BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
-        return;
-      }
-      contains_unpopulated_transport_items = true;
-      item = new ShareableBlobDataItem(std::move(input_item),
-                                       ShareableBlobDataItem::QUOTA_NEEDED);
-      pending_transport_items.push_back(item);
-      transport_items.push_back(item.get());
-      checked_transport_quota_needed += length;
-    } else {
-      item = new ShareableBlobDataItem(
-          std::move(input_item),
-          ShareableBlobDataItem::POPULATED_WITHOUT_QUOTA);
-    }
+    DCHECK(!BlobDataBuilder::IsFutureFileItem(input_element))
+        << "File allocation not implemented.";
     if (length == DataElement::kUnknownSize)
       num_files_with_unknown_size++;
 
+    scoped_refptr<ShareableBlobDataItem> item = new ShareableBlobDataItem(
+        std::move(input_item), ShareableBlobDataItem::POPULATED_WITHOUT_QUOTA);
+
     checked_total_size += length;
     output_blob->AppendSharedBlobItem(std::move(item));
   }
@@ -234,18 +206,14 @@
     return;
   }
   if (!checked_total_size.IsValid() || !checked_total_memory_size.IsValid() ||
-      !checked_transport_quota_needed.IsValid() ||
-      !checked_copy_quota_needed.IsValid()) {
+      !checked_memory_quota_needed.IsValid()) {
     status = BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
     return;
   }
   total_size = checked_total_size.ValueOrDie();
   total_memory_size = checked_total_memory_size.ValueOrDie();
-  transport_quota_needed = checked_transport_quota_needed.ValueOrDie();
-  copy_quota_needed = checked_copy_quota_needed.ValueOrDie();
-  transport_quota_type = found_file_transport ? TransportQuotaType::FILE
-                                              : TransportQuotaType::MEMORY;
-  if (transport_quota_needed) {
+  memory_quota_needed = checked_memory_quota_needed.ValueOrDie();
+  if (memory_quota_needed) {
     status = BlobStatus::PENDING_QUOTA;
   } else {
     status = BlobStatus::PENDING_INTERNALS;
@@ -330,16 +298,8 @@
         data_item =
             new BlobDataItem(std::move(element), source_item->data_handle_);
 
-        if (BlobDataBuilder::IsFutureFileItem(source_item->data_element())) {
-          // The source file isn't a real file yet (path is fake), so store the
-          // items we need to copy from later.
-          if (item_index == first_item_index) {
-            first_item_slice_offset = item_offset;
-            first_source_item = source_items[item_index];
-          } else {
-            last_source_item = source_items[item_index];
-          }
-        }
+        DCHECK(!BlobDataBuilder::IsFutureFileItem(source_item->data_element()))
+            << "File allocation unimplemented.";
         break;
       }
       case DataElement::TYPE_FILE_FILESYSTEM: {
@@ -381,13 +341,8 @@
     : memory_controller_(base::FilePath(), scoped_refptr<base::TaskRunner>()),
       ptr_factory_(this) {}
 
-BlobStorageContext::BlobStorageContext(
-    base::FilePath storage_directory,
-    scoped_refptr<base::TaskRunner> file_runner)
-    : memory_controller_(std::move(storage_directory), std::move(file_runner)),
-      ptr_factory_(this) {}
-
-BlobStorageContext::~BlobStorageContext() {}
+BlobStorageContext::~BlobStorageContext() {
+}
 
 std::unique_ptr<BlobDataHandle> BlobStorageContext::GetBlobDataFromUUID(
     const std::string& uuid) {
@@ -459,9 +414,14 @@
   // stores the complete item representation in the internal data.
   BlobFlattener flattener(content, entry, &registry_);
 
-  DCHECK(!flattener.contains_unpopulated_transport_items ||
+  DCHECK(flattener.status != BlobStatus::PENDING_TRANSPORT ||
+         !transport_allowed_callback)
+      << "There is no pending content for the user to populate, so the "
+         "callback should be null.";
+  DCHECK(flattener.status != BlobStatus::PENDING_TRANSPORT ||
          transport_allowed_callback)
-      << "If we have pending unpopulated content then a callback is required";
+      << "If we have pending content then there needs to be a callback "
+         "present.";
 
   entry->set_size(flattener.total_size);
   entry->set_status(flattener.status);
@@ -470,14 +430,8 @@
   UMA_HISTOGRAM_COUNTS("Storage.Blob.ItemCount", entry->items().size());
   UMA_HISTOGRAM_COUNTS("Storage.Blob.TotalSize",
                        flattener.total_memory_size / 1024);
-
-  uint64_t total_memory_needed =
-      flattener.copy_quota_needed +
-      (flattener.transport_quota_type == TransportQuotaType::MEMORY
-           ? flattener.transport_quota_needed
-           : 0);
   UMA_HISTOGRAM_COUNTS("Storage.Blob.TotalUnsharedSize",
-                       total_memory_needed / 1024);
+                       flattener.memory_quota_needed / 1024);
 
   size_t num_building_dependent_blobs = 0;
   std::vector<std::unique_ptr<BlobDataHandle>> dependent_blobs;
@@ -500,7 +454,7 @@
   }
 
   entry->set_building_state(base::MakeUnique<BlobEntry::BuildingState>(
-      !flattener.pending_transport_items.empty(), transport_allowed_callback,
+      !flattener.transport_items.empty(), transport_allowed_callback,
       num_building_dependent_blobs));
   BlobEntry::BuildingState* building_state = entry->building_state_.get();
   std::swap(building_state->copies, flattener.copies);
@@ -514,52 +468,21 @@
     return handle;
   }
 
-  // Avoid the state where we might grant only one quota.
-  if (!memory_controller_.CanReserveQuota(flattener.copy_quota_needed +
-                                          flattener.transport_quota_needed)) {
+  if (!memory_controller_.CanReserveQuota(flattener.memory_quota_needed)) {
     CancelBuildingBlobInternal(entry, BlobStatus::ERR_OUT_OF_MEMORY);
     return handle;
   }
 
-  if (flattener.copy_quota_needed > 0) {
-    // The blob can complete during the execution of |ReserveMemoryQuota|.
+  if (flattener.memory_quota_needed > 0) {
+    // The blob can complete during the execution of this line.
     base::WeakPtr<QuotaAllocationTask> pending_request =
         memory_controller_.ReserveMemoryQuota(
-            std::move(flattener.pending_copy_items),
-            base::Bind(&BlobStorageContext::OnEnoughSpaceForCopies,
+            std::move(flattener.pending_memory_items),
+            base::Bind(&BlobStorageContext::OnEnoughSizeForMemory,
                        ptr_factory_.GetWeakPtr(), content.uuid_));
     // Building state will be null if the blob is already finished.
     if (entry->building_state_)
-      entry->building_state_->copy_quota_request = std::move(pending_request);
-  }
-
-  if (flattener.transport_quota_needed > 0) {
-    base::WeakPtr<QuotaAllocationTask> pending_request;
-
-    switch (flattener.transport_quota_type) {
-      case TransportQuotaType::MEMORY: {
-        // The blob can complete during the execution of |ReserveMemoryQuota|.
-        std::vector<BlobMemoryController::FileCreationInfo> empty_files;
-        pending_request = memory_controller_.ReserveMemoryQuota(
-            std::move(flattener.pending_transport_items),
-            base::Bind(&BlobStorageContext::OnEnoughSpaceForTransport,
-                       ptr_factory_.GetWeakPtr(), content.uuid_,
-                       base::Passed(&empty_files)));
-        break;
-      }
-      case TransportQuotaType::FILE:
-        pending_request = memory_controller_.ReserveFileQuota(
-            std::move(flattener.pending_transport_items),
-            base::Bind(&BlobStorageContext::OnEnoughSpaceForTransport,
-                       ptr_factory_.GetWeakPtr(), content.uuid_));
-        break;
-    }
-
-    // Building state will be null if the blob is already finished.
-    if (entry->building_state_) {
-      entry->building_state_->transport_quota_request =
-          std::move(pending_request);
-    }
+      entry->building_state_->memory_quota_request = std::move(pending_request);
   }
 
   if (entry->CanFinishBuilding())
@@ -699,26 +622,8 @@
           const char* src_data =
               copy.source_item->item()->bytes() + copy.source_item_offset;
           copy.dest_item->item()->item_->SetToBytes(src_data, dest_size);
-          break;
-        }
-        case DataElement::TYPE_FILE: {
-          // If we expected a memory item (and our source was paged to disk) we
-          // free that memory.
-          if (dest_type == DataElement::TYPE_BYTES_DESCRIPTION)
-            copy.dest_item->set_memory_allocation(nullptr);
-
-          const DataElement& source_element =
-              copy.source_item->item()->data_element();
-          std::unique_ptr<DataElement> new_element(new DataElement());
-          new_element->SetToFilePathRange(
-              source_element.path(),
-              source_element.offset() + copy.source_item_offset, dest_size,
-              source_element.expected_modification_time());
-          scoped_refptr<BlobDataItem> new_item(new BlobDataItem(
-              std::move(new_element), copy.source_item->item()->data_handle_));
-          copy.dest_item->set_item(std::move(new_item));
-          break;
-        }
+        } break;
+        case DataElement::TYPE_FILE:
         case DataElement::TYPE_UNKNOWN:
         case DataElement::TYPE_BLOB:
         case DataElement::TYPE_BYTES_DESCRIPTION:
@@ -765,10 +670,8 @@
   NotifyTransportCompleteInternal(entry);
 }
 
-void BlobStorageContext::OnEnoughSpaceForTransport(
-    const std::string& uuid,
-    std::vector<BlobMemoryController::FileCreationInfo> files,
-    bool success) {
+void BlobStorageContext::OnEnoughSizeForMemory(const std::string& uuid,
+                                               bool success) {
   if (!success) {
     CancelBuildingBlob(uuid, BlobStatus::ERR_OUT_OF_MEMORY);
     return;
@@ -777,25 +680,15 @@
   if (!entry || !entry->building_state_.get())
     return;
   BlobEntry::BuildingState& building_state = *entry->building_state_;
-  DCHECK(!building_state.transport_quota_request);
-  DCHECK(building_state.transport_items_present);
+  DCHECK(!building_state.memory_quota_request);
 
-  entry->set_status(BlobStatus::PENDING_TRANSPORT);
-  RequestTransport(entry, std::move(files));
-
-  if (entry->CanFinishBuilding())
-    FinishBuilding(entry);
-}
-
-void BlobStorageContext::OnEnoughSpaceForCopies(const std::string& uuid,
-                                                bool success) {
-  if (!success) {
-    CancelBuildingBlob(uuid, BlobStatus::ERR_OUT_OF_MEMORY);
-    return;
+  if (building_state.transport_items_present) {
+    entry->set_status(BlobStatus::PENDING_TRANSPORT);
+    RequestTransport(entry,
+                     std::vector<BlobMemoryController::FileCreationInfo>());
+  } else {
+    entry->set_status(BlobStatus::PENDING_INTERNALS);
   }
-  BlobEntry* entry = registry_.GetEntry(uuid);
-  if (!entry)
-    return;
 
   if (entry->CanFinishBuilding())
     FinishBuilding(entry);
@@ -823,8 +716,12 @@
 }
 
 void BlobStorageContext::ClearAndFreeMemory(BlobEntry* entry) {
-  if (entry->building_state_)
-    entry->building_state_->CancelRequests();
+  if (entry->building_state_) {
+    BlobEntry::BuildingState* building_state = entry->building_state_.get();
+    if (building_state->memory_quota_request) {
+      building_state->memory_quota_request->Cancel();
+    }
+  }
   entry->ClearItems();
   entry->ClearOffsets();
   entry->set_size(0);
diff --git a/storage/browser/blob/blob_storage_context.h b/storage/browser/blob/blob_storage_context.h
index 9e068a7..c64581e9 100644
--- a/storage/browser/blob/blob_storage_context.h
+++ b/storage/browser/blob/blob_storage_context.h
@@ -36,7 +36,6 @@
 namespace content {
 class BlobDispatcherHost;
 class BlobDispatcherHostTest;
-class BlobStorageBrowserTest;
 }
 
 namespace storage {
@@ -47,17 +46,14 @@
 class ShareableBlobDataItem;
 
 // This class handles the logistics of blob storage within the browser process.
-// This class is not threadsafe, access on IO thread. In Chromium there is one
-// instance per profile.
+// We are single threaded and should only be used on the IO thread. In Chromium
+// there is one instance per profile.
 class STORAGE_EXPORT BlobStorageContext {
  public:
   using TransportAllowedCallback = BlobEntry::TransportAllowedCallback;
 
   // Initializes the context without disk support.
   BlobStorageContext();
-  // Disk support is enabled if |file_runner| isn't null.
-  BlobStorageContext(base::FilePath storage_directory,
-                     scoped_refptr<base::TaskRunner> file_runner);
   ~BlobStorageContext();
 
   std::unique_ptr<BlobDataHandle> GetBlobDataFromUUID(const std::string& uuid);
@@ -129,7 +125,6 @@
  protected:
   friend class content::BlobDispatcherHost;
   friend class content::BlobDispatcherHostTest;
-  friend class content::BlobStorageBrowserTest;
   friend class BlobTransportHost;
   friend class BlobTransportHostTest;
   friend class BlobDataHandle;
@@ -138,8 +133,6 @@
   friend class BlobSliceTest;
   friend class BlobStorageContextTest;
 
-  enum class TransportQuotaType { MEMORY, FILE };
-
   // Transforms a BlobDataBuilder into a BlobEntry with no blob references.
   // BlobSlice is used to flatten out these references. Records the total size
   // and items for memory and file quota requests.
@@ -159,8 +152,6 @@
     //   reference ourself.
     BlobStatus status = BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
 
-    bool contains_unpopulated_transport_items = false;
-
     // This is the total size of the blob, including all memory, files, etc.
     uint64_t total_size = 0;
     // Total memory size of the blob (not including files, etc).
@@ -168,19 +159,14 @@
 
     std::vector<std::pair<std::string, BlobEntry*>> dependent_blobs;
 
-    TransportQuotaType transport_quota_type = TransportQuotaType::MEMORY;
-    uint64_t transport_quota_needed = 0;
-    std::vector<scoped_refptr<ShareableBlobDataItem>> pending_transport_items;
-    // Hold a separate vector of pointers to declare them as populated.
+    uint64_t memory_quota_needed = 0;
+    std::vector<scoped_refptr<ShareableBlobDataItem>> pending_memory_items;
+
     std::vector<ShareableBlobDataItem*> transport_items;
 
-    // Copy quota is always memory quota.
-    uint64_t copy_quota_needed = 0;
-    std::vector<scoped_refptr<ShareableBlobDataItem>> pending_copy_items;
-
     // These record all future copies we'll need to do from referenced blobs.
-    // This happens when we do a partial slice from a pending data or file
-    // item.
+    // This
+    // happens when we do a partial slice from a pending data or file item.
     std::vector<BlobEntry::ItemCopyEntry> copies;
 
    private:
@@ -249,13 +235,7 @@
       BlobEntry* entry,
       std::vector<BlobMemoryController::FileCreationInfo> files);
 
-  // The files array is empty for memory quota request responses.
-  void OnEnoughSpaceForTransport(
-      const std::string& uuid,
-      std::vector<BlobMemoryController::FileCreationInfo> files,
-      bool can_fit);
-
-  void OnEnoughSpaceForCopies(const std::string& uuid, bool can_fit);
+  void OnEnoughSizeForMemory(const std::string& uuid, bool can_fit);
 
   void OnDependentBlobFinished(const std::string& owning_blob_uuid,
                                BlobStatus reason);
diff --git a/storage/common/blob_storage/blob_storage_constants.h b/storage/common/blob_storage/blob_storage_constants.h
index b94e886..f54d20a 100644
--- a/storage/common/blob_storage/blob_storage_constants.h
+++ b/storage/common/blob_storage/blob_storage_constants.h
@@ -28,8 +28,8 @@
   size_t max_blob_in_memory_space = 500 * 1024 * 1024;
 
   // This is the maximum amount of disk space we can use.
-  // TODO(dmurph): Determine initial heuristic based on disk usage & arch.
-  uint64_t max_blob_disk_space = 0ull;
+  // TODO(dmurph): Consider storage size of the device.
+  uint64_t max_blob_disk_space = 5ull * 1024 * 1024 * 1024;
 
   // This is the minimum file size we can use when paging blob items to disk.
   // We combine items until we reach at least this size.
@@ -73,13 +73,12 @@
   // Blob state section:
   // The blob has finished.
   DONE = 200,
-  // Waiting for memory or file quota for the to-be transported data.
+  // The system is pending on quota being granted, the transport layer
+  // populating pending data, and/or copying data from dependent blobs. See
+  // BlobEntry::BuildingState determine which of these are happening, as they
+  // all can happen concurrently.
   PENDING_QUOTA = 201,
-  // Waiting for data to be transported (quota has been granted).
   PENDING_TRANSPORT = 202,
-  // Waiting for any operations involving dependent blobs after transport data
-  // has been populated. See BlobEntry::BuildingState for more info.
-  // TODO(dmurph): Change to PENDING_REFERENCED_BLOBS (crbug.com/670398).
   PENDING_INTERNALS = 203,
   LAST = PENDING_INTERNALS
 };
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 1dbeb4e..38c79aa2 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -12720,7 +12720,7 @@
         "swarming": {
           "can_use_on_swarming_builders": false
         },
-        "test": "media_mojo_shell_unittests"
+        "test": "media_service_unittests"
       },
       {
         "swarming": {
@@ -12756,7 +12756,7 @@
         "swarming": {
           "can_use_on_swarming_builders": false
         },
-        "test": "media_mojo_shell_unittests"
+        "test": "media_service_unittests"
       },
       {
         "swarming": {
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json
index 797d1d5..fe982fa0 100644
--- a/testing/buildbot/chromium.gpu.fyi.json
+++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -9971,6 +9971,29 @@
           "--show-stdout",
           "--browser=release",
           "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=d3d11 --use-passthrough-cmd-decoder"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "webgl_conformance_d3d11_passthrough_tests",
+        "override_compile_targets": [
+          "telemetry_gpu_integration_test_run"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6613",
+              "os": "Windows-2008ServerR2-SP1"
+            }
+          ]
+        }
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=release",
+          "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=d3d9"
         ],
         "isolate_name": "telemetry_gpu_integration_test",
@@ -10127,6 +10150,29 @@
           "--show-stdout",
           "--browser=release",
           "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=d3d11 --use-passthrough-cmd-decoder"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "webgl_conformance_d3d11_passthrough_tests",
+        "override_compile_targets": [
+          "telemetry_gpu_integration_test_run"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-2008ServerR2-SP1"
+            }
+          ]
+        }
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=release",
+          "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=d3d9"
         ],
         "isolate_name": "telemetry_gpu_integration_test",
@@ -10541,6 +10587,29 @@
           "--show-stdout",
           "--browser=debug",
           "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=d3d11 --use-passthrough-cmd-decoder"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "webgl_conformance_d3d11_passthrough_tests",
+        "override_compile_targets": [
+          "telemetry_gpu_integration_test_run"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0412",
+              "os": "Windows-10-10586"
+            }
+          ]
+        }
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=debug",
+          "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=d3d9"
         ],
         "isolate_name": "telemetry_gpu_integration_test",
@@ -11017,6 +11086,29 @@
           "--show-stdout",
           "--browser=release",
           "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=d3d11 --use-passthrough-cmd-decoder"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "webgl_conformance_d3d11_passthrough_tests",
+        "override_compile_targets": [
+          "telemetry_gpu_integration_test_run"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0412",
+              "os": "Windows-10-10586"
+            }
+          ]
+        }
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=release",
+          "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=d3d9"
         ],
         "isolate_name": "telemetry_gpu_integration_test",
@@ -11454,6 +11546,29 @@
           "--show-stdout",
           "--browser=debug",
           "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=d3d11 --use-passthrough-cmd-decoder"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "webgl_conformance_d3d11_passthrough_tests",
+        "override_compile_targets": [
+          "telemetry_gpu_integration_test_run"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6613",
+              "os": "Windows-2008ServerR2-SP1"
+            }
+          ]
+        }
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=debug",
+          "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=d3d9"
         ],
         "isolate_name": "telemetry_gpu_integration_test",
@@ -11868,6 +11983,29 @@
           "--show-stdout",
           "--browser=debug",
           "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=d3d11 --use-passthrough-cmd-decoder"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "webgl_conformance_d3d11_passthrough_tests",
+        "override_compile_targets": [
+          "telemetry_gpu_integration_test_run"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-2008ServerR2-SP1"
+            }
+          ]
+        }
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=debug",
+          "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=d3d9"
         ],
         "isolate_name": "telemetry_gpu_integration_test",
@@ -12305,6 +12443,29 @@
           "--show-stdout",
           "--browser=debug",
           "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=d3d11 --use-passthrough-cmd-decoder"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "webgl_conformance_d3d11_passthrough_tests",
+        "override_compile_targets": [
+          "telemetry_gpu_integration_test_run"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0412",
+              "os": "Windows-2008ServerR2-SP1"
+            }
+          ]
+        }
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=debug",
+          "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=d3d9"
         ],
         "isolate_name": "telemetry_gpu_integration_test",
@@ -12758,6 +12919,29 @@
           "--show-stdout",
           "--browser=release",
           "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=d3d11 --use-passthrough-cmd-decoder"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "webgl_conformance_d3d11_passthrough_tests",
+        "override_compile_targets": [
+          "telemetry_gpu_integration_test_run"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6779",
+              "os": "Windows-2008ServerR2-SP1"
+            }
+          ]
+        }
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=release",
+          "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=d3d9"
         ],
         "isolate_name": "telemetry_gpu_integration_test",
@@ -13232,6 +13416,29 @@
           "--show-stdout",
           "--browser=release",
           "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=d3d11 --use-passthrough-cmd-decoder"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "webgl_conformance_d3d11_passthrough_tests",
+        "override_compile_targets": [
+          "telemetry_gpu_integration_test_run"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6613",
+              "os": "Windows-2008ServerR2-SP1"
+            }
+          ]
+        }
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=release",
+          "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=d3d9"
         ],
         "isolate_name": "telemetry_gpu_integration_test",
@@ -13685,6 +13892,29 @@
           "--show-stdout",
           "--browser=release",
           "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=d3d11 --use-passthrough-cmd-decoder"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "webgl_conformance_d3d11_passthrough_tests",
+        "override_compile_targets": [
+          "telemetry_gpu_integration_test_run"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:041a",
+              "os": "Windows-2008ServerR2-SP1"
+            }
+          ]
+        }
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=release",
+          "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=d3d9"
         ],
         "isolate_name": "telemetry_gpu_integration_test",
@@ -14138,6 +14368,29 @@
           "--show-stdout",
           "--browser=release",
           "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=d3d11 --use-passthrough-cmd-decoder"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "webgl_conformance_d3d11_passthrough_tests",
+        "override_compile_targets": [
+          "telemetry_gpu_integration_test_run"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:0f02",
+              "os": "Windows-2008ServerR2-SP1"
+            }
+          ]
+        }
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=release",
+          "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=d3d9"
         ],
         "isolate_name": "telemetry_gpu_integration_test",
@@ -14684,6 +14937,29 @@
           "--show-stdout",
           "--browser=release",
           "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=d3d11 --use-passthrough-cmd-decoder"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "webgl_conformance_d3d11_passthrough_tests",
+        "override_compile_targets": [
+          "telemetry_gpu_integration_test_run"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-2008ServerR2-SP1"
+            }
+          ]
+        }
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=release",
+          "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=d3d9"
         ],
         "isolate_name": "telemetry_gpu_integration_test",
@@ -15160,6 +15436,29 @@
           "--show-stdout",
           "--browser=release",
           "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=d3d11 --use-passthrough-cmd-decoder"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "webgl_conformance_d3d11_passthrough_tests",
+        "override_compile_targets": [
+          "telemetry_gpu_integration_test_run"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0412",
+              "os": "Windows-2008ServerR2-SP1"
+            }
+          ]
+        }
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=release",
+          "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=d3d9"
         ],
         "isolate_name": "telemetry_gpu_integration_test",
@@ -15574,6 +15873,29 @@
           "--show-stdout",
           "--browser=debug_x64",
           "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=d3d11 --use-passthrough-cmd-decoder"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "webgl_conformance_d3d11_passthrough_tests",
+        "override_compile_targets": [
+          "telemetry_gpu_integration_test_run"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-2008ServerR2-SP1"
+            }
+          ]
+        }
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=debug_x64",
+          "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=d3d9"
         ],
         "isolate_name": "telemetry_gpu_integration_test",
@@ -16084,6 +16406,29 @@
           "--show-stdout",
           "--browser=release_x64",
           "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=d3d11 --use-passthrough-cmd-decoder"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "webgl_conformance_d3d11_passthrough_tests",
+        "override_compile_targets": [
+          "telemetry_gpu_integration_test_run"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-2008ServerR2-SP1"
+            }
+          ]
+        }
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=release_x64",
+          "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=d3d9"
         ],
         "isolate_name": "telemetry_gpu_integration_test",
@@ -16521,6 +16866,29 @@
           "--show-stdout",
           "--browser=debug",
           "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=d3d11 --use-passthrough-cmd-decoder"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "webgl_conformance_d3d11_passthrough_tests",
+        "override_compile_targets": [
+          "telemetry_gpu_integration_test_run"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-2012ServerR2-SP0"
+            }
+          ]
+        }
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=debug",
+          "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=d3d9"
         ],
         "isolate_name": "telemetry_gpu_integration_test",
@@ -17000,6 +17368,29 @@
           "--show-stdout",
           "--browser=release",
           "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=d3d11 --use-passthrough-cmd-decoder"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "webgl_conformance_d3d11_passthrough_tests",
+        "override_compile_targets": [
+          "telemetry_gpu_integration_test_run"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-2012ServerR2-SP0"
+            }
+          ]
+        }
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=release",
+          "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=d3d9"
         ],
         "isolate_name": "telemetry_gpu_integration_test",
diff --git a/testing/buildbot/chromium.linux.json b/testing/buildbot/chromium.linux.json
index be6f07ee..6ab065d 100644
--- a/testing/buildbot/chromium.linux.json
+++ b/testing/buildbot/chromium.linux.json
@@ -3176,7 +3176,7 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "media_mojo_shell_unittests"
+        "test": "media_service_unittests"
       },
       {
         "swarming": {
diff --git a/testing/buildbot/filters/browser-side-navigation.linux.browser_tests.filter b/testing/buildbot/filters/browser-side-navigation.linux.browser_tests.filter
index 7bf3459..998192dc 100644
--- a/testing/buildbot/filters/browser-side-navigation.linux.browser_tests.filter
+++ b/testing/buildbot/filters/browser-side-navigation.linux.browser_tests.filter
@@ -8,8 +8,6 @@
 # TabManagerTest.TabManagerBasics crashes sometimes
 -TabManagerTest.TabManagerBasics
 -ThreatDOMDetailsTest.Everything
--WebNavigationApiTest.RequestOpenTab
--WebNavigationApiTest.UserAction
 
 # https://crbug.com/652767: NavigationHandle::GetResponseHeaders sometimes
 # returns null for browser-side navigations
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl
index c6d44074..1d57587 100644
--- a/testing/buildbot/gn_isolate_map.pyl
+++ b/testing/buildbot/gn_isolate_map.pyl
@@ -571,14 +571,27 @@
     "label": "//media:media_unittests",
     "type": "windowed_test_launcher",
   },
-  "media_mojo_shell_unittests": {
-    "label": "//media/mojo/services:media_mojo_shell_unittests",
+  "media_service_unittests": {
+    "label": "//media/mojo/services:media_service_unittests",
     "type": "console_test_launcher",
   },
   "media_blink_unittests": {
     "label": "//media/blink:media_blink_unittests",
     "type": "windowed_test_launcher",
   },
+  "media_router_perf_tests": {
+    "label": "//chrome/test/media_router:media_router_perf_tests",
+    "type": "script",
+    "script": "//chrome/test/media_router/telemetry/run_benchmark",
+    "args": [
+      "--browser=release",
+      "--also-run-disabled-tests",
+      "-v",
+      "--use-live-sites",
+      "--output-format=chartjson",
+      "--output-dir=${ISOLATED_OUTDIR}",
+    ],
+  },
   "media_router_tests": {
     "label": "//chrome/test/media_router:media_router_tests",
     "type": "script",
diff --git a/testing/buildbot/manage.py b/testing/buildbot/manage.py
index 3715285..c3e2a95 100755
--- a/testing/buildbot/manage.py
+++ b/testing/buildbot/manage.py
@@ -167,6 +167,7 @@
   'content_junit_tests',
   'content_junit_tests',
   'junit_unit_tests',
+  'media_router_perf_tests',
   'media_router_tests',
   'net_junit_tests',
   'net_junit_tests',
diff --git a/third_party/WebKit/LayoutTests/LeakExpectations b/third_party/WebKit/LayoutTests/LeakExpectations
index 365b239..c667847 100644
--- a/third_party/WebKit/LayoutTests/LeakExpectations
+++ b/third_party/WebKit/LayoutTests/LeakExpectations
@@ -54,6 +54,7 @@
 # -----------------------------------------------------------------
 
 crbug.com/364417 paint/invalidation/japanese-rl-selection-clear.html [ Leak ]
+crbug.com/364417 virtual/spinvalidation/paint/invalidation/japanese-rl-selection-clear.html [ Leak ]
 
 crbug.com/455369 fast/html/marquee-destroyed-without-removed-from-crash.html [ Leak Pass ]
 
diff --git a/third_party/WebKit/LayoutTests/fast/text/selection-with-inline-padding-expected.html b/third_party/WebKit/LayoutTests/fast/text/selection-with-inline-padding-expected.html
deleted file mode 100644
index b4c4bd2..0000000
--- a/third_party/WebKit/LayoutTests/fast/text/selection-with-inline-padding-expected.html
+++ /dev/null
@@ -1,27 +0,0 @@
-<!DOCTYPE html>
-<html>
-    <head>
-        <title>Test paddings and selection for inline elements</title>
-        <style>
-            p > b, pre > b { padding: 0 10px; }
-        </style>
-    </head>
-    <body>
-        <p>
-            Lorem ipsum dolor sit amet, consectetur adipiscing elit.<br>
-            <b id="test">Sed dictum erat sit amet pharetra pretium.</b><br>
-            Nullam a est vitae orci tempus tincidunt nec at dolor.
-        </p>
-        <p>
-            Tests that selections do not include padding in the Y direction for
-            inline elements where it should be ignored.
-        </p>
-        <script>
-            var node = document.getElementById('test').firstChild;
-            var range = document.createRange();
-            range.setStart(node, 5);
-            range.setEnd(node, node.length - 5);
-            window.getSelection().addRange(range);
-        </script>
-    </body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/fast/text/selection-with-inline-padding.html b/third_party/WebKit/LayoutTests/fast/text/selection-with-inline-padding.html
index 56c2a6c..7992c4b 100644
--- a/third_party/WebKit/LayoutTests/fast/text/selection-with-inline-padding.html
+++ b/third_party/WebKit/LayoutTests/fast/text/selection-with-inline-padding.html
@@ -1,27 +1,25 @@
 <!DOCTYPE html>
-<html>
-    <head>
-        <title>Test paddings and selection for inline elements</title>
-        <style>
-            p > b, pre > b { padding: 10px; }
-        </style>
-    </head>
-    <body>
-        <p>
-            Lorem ipsum dolor sit amet, consectetur adipiscing elit.<br>
-            <b id="test">Sed dictum erat sit amet pharetra pretium.</b><br>
-            Nullam a est vitae orci tempus tincidunt nec at dolor.
-        </p>
-        <p>
-            Tests that selections do not include padding in the Y direction for
-            inline elements where it should be ignored.
-        </p>
-        <script>
-            var node = document.getElementById('test').firstChild;
-            var range = document.createRange();
-            range.setStart(node, 5);
-            range.setEnd(node, node.length - 5);
-            window.getSelection().addRange(range);
-        </script>
-    </body>
-</html>
+<style>
+p > b {
+  padding: 10px;
+}
+</style>
+<p>
+  Lorem ipsum dolor sit amet, consectetur adipiscing elit.<br>
+  <b id="test">Sed dictum erat sit amet pharetra pretium.</b><br>
+  Nullam a est vitae orci tempus tincidunt nec at dolor.
+</p>
+<p>
+  Tests whether selection includes padding in the Y direction for inline
+  elements. This padding would ideally not be included. http://crbug.com/657325#c13
+</p>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
+<script>
+runAfterLayoutAndPaint(function() {
+  var node = document.getElementById('test').firstChild;
+  var range = document.createRange();
+  range.setStart(node, 5);
+  range.setEnd(node, node.length - 5);
+  window.getSelection().addRange(range);
+}, true);
+</script>
diff --git a/third_party/WebKit/LayoutTests/images/resources/png-simple.png b/third_party/WebKit/LayoutTests/images/resources/png-simple.png
new file mode 100644
index 0000000..0ddfdad9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/resources/png-simple.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/resize-scrollable-iframe-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/resize-scrollable-iframe-expected.txt
index b55dc80..aa5b644 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/resize-scrollable-iframe-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/resize-scrollable-iframe-expected.txt
@@ -18,11 +18,21 @@
         },
         {
           "object": "LayoutView #document",
+          "rect": [8, 193, 285, 200],
+          "reason": "incremental"
+        },
+        {
+          "object": "LayoutView #document",
           "rect": [8, 393, 285, 15],
           "reason": "scroll"
         },
         {
           "object": "LayoutView #document",
+          "rect": [93, 108, 200, 285],
+          "reason": "incremental"
+        },
+        {
+          "object": "LayoutView #document",
           "rect": [8, 193, 85, 15],
           "reason": "scroll"
         },
@@ -77,6 +87,10 @@
     {
       "object": "VerticalScrollbar",
       "reason": "scroll"
+    },
+    {
+      "object": "LayoutView #document",
+      "reason": "incremental"
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table-cell-collapsed-border-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/table-cell-collapsed-border-expected.txt
index cd078c63..c8b10b4 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/table-cell-collapsed-border-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table-cell-collapsed-border-expected.txt
@@ -7,6 +7,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutTable TABLE",
+          "rect": [3, 60, 441, 405],
+          "reason": "invalidate paint rectangle"
+        },
+        {
           "object": "LayoutTableCell TD id='t3'",
           "rect": [220, 260, 122, 110],
           "reason": "style change"
@@ -26,48 +31,8 @@
   ],
   "objectPaintInvalidations": [
     {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD id='t1'",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD id='t2'",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD id='t3'",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
+      "object": "LayoutTable TABLE",
+      "reason": "invalidate paint rectangle"
     },
     {
       "object": "LayoutTableCell TD id='t1'",
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table-outer-border-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/table-outer-border-expected.txt
index b06d1cb..dd21182 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/table-outer-border-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table-outer-border-expected.txt
@@ -95,14 +95,6 @@
       "reason": "layoutObject removal"
     },
     {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
       "object": "LayoutTable TABLE id='table' class='green'",
       "reason": "layoutObject insertion"
     },
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table-section-repaint-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/table-section-repaint-expected.txt
index 34142075..ca5147f 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/table-section-repaint-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table-section-repaint-expected.txt
@@ -7,6 +7,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutTable TABLE",
+          "rect": [8, 308, 60, 83],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutTableCell TD class='half'",
           "rect": [8, 353, 60, 38],
           "reason": "bounds change"
@@ -100,11 +105,6 @@
           "object": "LayoutTableCell TD class='red half'",
           "rect": [8, 8, 60, 30],
           "reason": "bounds change"
-        },
-        {
-          "object": "LayoutTable TABLE",
-          "rect": [8, 372, 60, 19],
-          "reason": "incremental"
         }
       ]
     }
@@ -172,7 +172,7 @@
     },
     {
       "object": "LayoutTable TABLE",
-      "reason": "incremental"
+      "reason": "forced by layout"
     },
     {
       "object": "LayoutTableRow TR",
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table/border-collapse-change-separate-to-collapse-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/table/border-collapse-change-separate-to-collapse-expected.txt
index 18cc801..ba69534 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/table/border-collapse-change-separate-to-collapse-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table/border-collapse-change-separate-to-collapse-expected.txt
@@ -51,14 +51,6 @@
   ],
   "objectPaintInvalidations": [
     {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
       "object": "LayoutTable TABLE id='table'",
       "reason": "style change"
     },
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-cell-append-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-cell-append-expected.txt
index 59ef756..e9f5d7b 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-cell-append-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-cell-append-expected.txt
@@ -8,8 +8,8 @@
       "paintInvalidations": [
         {
           "object": "LayoutTable TABLE",
-          "rect": [8, 59, 120, 5],
-          "reason": "incremental"
+          "rect": [8, 8, 120, 56],
+          "reason": "forced by layout"
         },
         {
           "object": "LayoutTableCell TD",
@@ -17,11 +17,6 @@
           "reason": "layoutObject insertion"
         },
         {
-          "object": "LayoutTable TABLE",
-          "rect": [65, 8, 63, 56],
-          "reason": "incremental"
-        },
-        {
           "object": "LayoutTableCell TD",
           "rect": [8, 8, 62, 56],
           "reason": "location change"
@@ -31,16 +26,8 @@
   ],
   "objectPaintInvalidations": [
     {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
       "object": "LayoutTable TABLE",
-      "reason": "incremental"
+      "reason": "forced by layout"
     },
     {
       "object": "LayoutTableRow TR id='row'",
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-cell-border-color-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-cell-border-color-expected.txt
index 76964c5..89451a0 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-cell-border-color-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-cell-border-color-expected.txt
@@ -7,6 +7,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutTable TABLE",
+          "rect": [8, 8, 114, 54],
+          "reason": "invalidate paint rectangle"
+        },
+        {
           "object": "LayoutTableCell TD id='foo'",
           "rect": [8, 8, 60, 54],
           "reason": "style change"
@@ -16,12 +21,8 @@
   ],
   "objectPaintInvalidations": [
     {
-      "object": "LayoutTableCell TD id='foo'",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
+      "object": "LayoutTable TABLE",
+      "reason": "invalidate paint rectangle"
     },
     {
       "object": "LayoutTableCell TD id='foo'",
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-cell-border-width-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-cell-border-width-expected.txt
index 6a5f6dc0..7b946e8 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-cell-border-width-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-cell-border-width-expected.txt
@@ -8,8 +8,8 @@
       "paintInvalidations": [
         {
           "object": "LayoutTable TABLE",
-          "rect": [8, 58, 114, 4],
-          "reason": "incremental"
+          "rect": [8, 8, 114, 54],
+          "reason": "forced by layout"
         },
         {
           "object": "LayoutTableCell TD id='foo'",
@@ -25,27 +25,14 @@
           "object": "LayoutTableCell TD",
           "rect": [62, 8, 55, 52],
           "reason": "bounds change"
-        },
-        {
-          "object": "LayoutTable TABLE",
-          "rect": [116, 8, 6, 54],
-          "reason": "incremental"
         }
       ]
     }
   ],
   "objectPaintInvalidations": [
     {
-      "object": "LayoutTableCell TD id='foo'",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
       "object": "LayoutTable TABLE",
-      "reason": "incremental"
+      "reason": "forced by layout"
     },
     {
       "object": "LayoutTableRow TR",
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-col-border-color-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-col-border-color-expected.txt
index 27c93b4..09774d8 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-col-border-color-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-col-border-color-expected.txt
@@ -7,6 +7,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutTable TABLE",
+          "rect": [8, 8, 113, 104],
+          "reason": "invalidate paint rectangle"
+        },
+        {
           "object": "LayoutTableCol COL id='col'",
           "rect": [8, 8, 113, 104],
           "reason": "style change"
@@ -16,20 +21,8 @@
   ],
   "objectPaintInvalidations": [
     {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
+      "object": "LayoutTable TABLE",
+      "reason": "invalidate paint rectangle"
     },
     {
       "object": "LayoutTableCol COL id='col'",
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-col-border-width-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-col-border-width-expected.txt
index 5ba2070..e54cf9ba 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-col-border-width-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-col-border-width-expected.txt
@@ -7,16 +7,16 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutTable TABLE",
+          "rect": [8, 8, 113, 104],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutTableCol COL id='col'",
           "rect": [8, 8, 113, 104],
           "reason": "style change"
         },
         {
-          "object": "LayoutTable TABLE",
-          "rect": [8, 108, 113, 4],
-          "reason": "incremental"
-        },
-        {
           "object": "LayoutTableCell TD",
           "rect": [8, 59, 60, 53],
           "reason": "bounds change"
@@ -50,35 +50,14 @@
           "object": "LayoutTableCell TD",
           "rect": [63, 59, 54, 51],
           "reason": "bounds change"
-        },
-        {
-          "object": "LayoutTable TABLE",
-          "rect": [116, 8, 5, 104],
-          "reason": "incremental"
         }
       ]
     }
   ],
   "objectPaintInvalidations": [
     {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
       "object": "LayoutTable TABLE",
-      "reason": "incremental"
+      "reason": "forced by layout"
     },
     {
       "object": "LayoutTableCol COL id='col'",
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-colgroup-border-color-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-colgroup-border-color-expected.txt
index 71449ce..5629d33 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-colgroup-border-color-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-colgroup-border-color-expected.txt
@@ -7,6 +7,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutTable TABLE",
+          "rect": [7, 7, 167, 104],
+          "reason": "invalidate paint rectangle"
+        },
+        {
           "object": "LayoutTableCol COLGROUP id='colgroup'",
           "rect": [8, 8, 166, 102],
           "reason": "style change"
@@ -16,28 +21,8 @@
   ],
   "objectPaintInvalidations": [
     {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
+      "object": "LayoutTable TABLE",
+      "reason": "invalidate paint rectangle"
     },
     {
       "object": "LayoutTableCol COLGROUP id='colgroup'",
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-colgroup-border-width-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-colgroup-border-width-expected.txt
index 7f1d98f..8f3e28c 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-colgroup-border-width-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-colgroup-border-width-expected.txt
@@ -7,6 +7,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutTable TABLE",
+          "rect": [8, 8, 166, 102],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutTableCol COLGROUP id='colgroup'",
           "rect": [8, 8, 166, 102],
           "reason": "style change"
@@ -60,43 +65,14 @@
           "object": "LayoutTableCell TD",
           "rect": [116, 9, 54, 50],
           "reason": "bounds change"
-        },
-        {
-          "object": "LayoutTable TABLE",
-          "rect": [169, 8, 5, 102],
-          "reason": "incremental"
         }
       ]
     }
   ],
   "objectPaintInvalidations": [
     {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
       "object": "LayoutTable TABLE",
-      "reason": "incremental"
+      "reason": "forced by layout"
     },
     {
       "object": "LayoutTableCol COLGROUP id='colgroup'",
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-row-border-width-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-row-border-width-expected.txt
index 4013197..42b0a3b9 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-row-border-width-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-row-border-width-expected.txt
@@ -7,6 +7,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutTable TABLE",
+          "rect": [8, 8, 60, 103],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutTableCell TD",
           "rect": [8, 8, 60, 54],
           "reason": "forced by layout"
@@ -17,11 +22,6 @@
           "reason": "location change"
         },
         {
-          "object": "LayoutTable TABLE",
-          "rect": [8, 109, 60, 2],
-          "reason": "incremental"
-        },
-        {
           "object": "LayoutTableRow TR id='row'",
           "rect": [10, 10, 56, 50],
           "reason": "style change"
@@ -30,27 +30,14 @@
           "object": "LayoutTableRow TR id='row'",
           "rect": [9, 9, 54, 50],
           "reason": "style change"
-        },
-        {
-          "object": "LayoutTable TABLE",
-          "rect": [62, 8, 6, 103],
-          "reason": "incremental"
         }
       ]
     }
   ],
   "objectPaintInvalidations": [
     {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
       "object": "LayoutTable TABLE",
-      "reason": "incremental"
+      "reason": "forced by layout"
     },
     {
       "object": "LayoutTableRow TR id='row'",
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-table-border-color-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-table-border-color-expected.txt
index 5a9447d..e9ea09b 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-table-border-color-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-table-border-color-expected.txt
@@ -16,10 +16,6 @@
   ],
   "objectPaintInvalidations": [
     {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
       "object": "LayoutTable TABLE id='tbl'",
       "reason": "style change"
     }
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-table-border-width-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-table-border-width-expected.txt
index 7ebee422..c2f06f30 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-table-border-width-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-table-border-width-expected.txt
@@ -21,10 +21,6 @@
   ],
   "objectPaintInvalidations": [
     {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
       "object": "LayoutTable TABLE id='tbl'",
       "reason": "style change"
     },
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-tbody-border-width-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-tbody-border-width-expected.txt
index 2b0dc2a..87ac20939 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-tbody-border-width-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table/cached-change-tbody-border-width-expected.txt
@@ -8,8 +8,8 @@
       "paintInvalidations": [
         {
           "object": "LayoutTable TABLE",
-          "rect": [8, 159, 114, 2],
-          "reason": "incremental"
+          "rect": [8, 8, 114, 153],
+          "reason": "forced by layout"
         },
         {
           "object": "LayoutTableSection TBODY id='tbody'",
@@ -70,43 +70,14 @@
           "object": "LayoutTableCell TD",
           "rect": [63, 59, 54, 51],
           "reason": "bounds change"
-        },
-        {
-          "object": "LayoutTable TABLE",
-          "rect": [115, 8, 7, 153],
-          "reason": "incremental"
         }
       ]
     }
   ],
   "objectPaintInvalidations": [
     {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
       "object": "LayoutTable TABLE",
-      "reason": "incremental"
+      "reason": "forced by layout"
     },
     {
       "object": "LayoutTableSection TBODY id='tbody'",
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table/collapsed-border-cell-resize-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/table/collapsed-border-cell-resize-expected.txt
index b34767d..1ef83120 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/table/collapsed-border-cell-resize-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table/collapsed-border-cell-resize-expected.txt
@@ -7,6 +7,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutTable TABLE",
+          "rect": [8, 8, 104, 204],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutTableCell TD id='target'",
           "rect": [8, 8, 104, 204],
           "reason": "border box change"
@@ -16,6 +21,10 @@
   ],
   "objectPaintInvalidations": [
     {
+      "object": "LayoutTable TABLE",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutTableCell TD id='target'",
       "reason": "border box change"
     }
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-cell-change-collapsed-border-color-expected.html b/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-cell-change-collapsed-border-color-expected.html
new file mode 100644
index 0000000..cf6608bf
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-cell-change-collapsed-border-color-expected.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<p style="height: 50px">Passes if green borders are visible surrounding A's cell and no red.
+<table style="border-collapse: collapse;">
+  <tr>
+    <td id="a" style="border: 5px solid green; width: 100px; height: 100px">A</td>
+  </tr>
+</table>
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-cell-change-collapsed-border-color-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-cell-change-collapsed-border-color-expected.txt
new file mode 100644
index 0000000..bf4df5b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-cell-change-collapsed-border-color-expected.txt
@@ -0,0 +1,49 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutTable TABLE",
+          "rect": [8, 82, 112, 112],
+          "reason": "invalidate paint rectangle"
+        }
+      ]
+    },
+    {
+      "name": "LayoutTableCell TD id='a'",
+      "position": [10, 84],
+      "bounds": [107, 107],
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutTableCell TD id='a'",
+          "rect": [-2, -2, 112, 112],
+          "reason": "style change"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutTable TABLE",
+      "reason": "invalidate paint rectangle"
+    },
+    {
+      "object": "LayoutTableCell TD id='a'",
+      "reason": "style change"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "style change"
+    },
+    {
+      "object": "RowBackground",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-cell-change-collapsed-border-color.html b/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-cell-change-collapsed-border-color.html
new file mode 100644
index 0000000..96981e21
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-cell-change-collapsed-border-color.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<p style="height: 50px">Passes if green borders are visible surrounding A's cell and no red.
+<table style="border-collapse: collapse;">
+  <tr>
+    <td id="a" style="border: 5px solid red; width: 100px; height: 100px; will-change: transform">A</td>
+  </tr>
+</table>
+<script src="../resources/text-based-repaint.js"></script>
+<script>
+function repaintTest() {
+  a.style.borderColor = 'green';
+}
+onload = runRepaintAndPixelTest;
+</script>
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-cell-collapsed-border-add-anonymous-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-cell-collapsed-border-add-anonymous-expected.txt
new file mode 100644
index 0000000..c2f1a9a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-cell-collapsed-border-add-anonymous-expected.txt
@@ -0,0 +1,2 @@
+Passes if no crash.
+A
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-cell-collapsed-border-add-anonymous.html b/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-cell-collapsed-border-add-anonymous.html
new file mode 100644
index 0000000..680d574
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-cell-collapsed-border-add-anonymous.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<style>
+td { will-change: transform; }
+</style>
+Passes if no crash.
+<table id="table" style="border-collapse: collapse">
+  <tr>
+    <td style="border: 4px solid pink"></td>
+    <td></td>
+  </tr>
+</table>
+<script src="../../../resources/run-after-layout-and-paint.js"></script>
+<script>
+if (window.testRunner)
+  testRunner.dumpAsText();
+runAfterLayoutAndPaint(function() {
+  table.appendChild(document.createTextNode("A"));
+}, true);
+</script>
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-row-change-collapsed-border-color-expected.html b/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-row-change-collapsed-border-color-expected.html
new file mode 100644
index 0000000..dc1f099
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-row-change-collapsed-border-color-expected.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<p style="height: 50px">Passes if green borders are visible surrounding A's cell and no red.
+<table style="border-collapse: collapse;">
+  <tr id="a" style="border: 5px solid green">
+    <td style="width: 100px; height: 100px">A</td>
+  </tr>
+</table>
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-row-change-collapsed-border-color-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-row-change-collapsed-border-color-expected.txt
new file mode 100644
index 0000000..c26169aa2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-row-change-collapsed-border-color-expected.txt
@@ -0,0 +1,41 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutTable TABLE",
+          "rect": [8, 82, 112, 112],
+          "reason": "invalidate paint rectangle"
+        }
+      ]
+    },
+    {
+      "name": "LayoutTableRow TR id='a'",
+      "position": [10, 84],
+      "bounds": [107, 107],
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutTableRow TR id='a'",
+          "rect": [0, 0, 107, 107],
+          "reason": "style change"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutTable TABLE",
+      "reason": "invalidate paint rectangle"
+    },
+    {
+      "object": "LayoutTableRow TR id='a'",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-row-change-collapsed-border-color.html b/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-row-change-collapsed-border-color.html
new file mode 100644
index 0000000..b3f51b3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-row-change-collapsed-border-color.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<p style="height: 50px">Passes if green borders are visible surrounding A's cell and no red.
+<table style="border-collapse: collapse;">
+  <tr id="a" style="border: 5px solid red; will-change: transform">
+    <td style="width: 100px; height: 100px">A</td>
+  </tr>
+</table>
+<script src="../resources/text-based-repaint.js"></script>
+<script>
+function repaintTest() {
+  a.style.borderColor = 'green';
+}
+onload = runRepaintAndPixelTest;
+</script>
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-section-change-collapsed-border-color-expected.html b/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-section-change-collapsed-border-color-expected.html
new file mode 100644
index 0000000..b76f9bf
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-section-change-collapsed-border-color-expected.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<p style="height: 50px">Passes if green borders are visible surrounding A's cell and no red.
+<table style="border-collapse: collapse;">
+  <thead id="a" style="border: 5px solid green">
+    <tr>
+      <td style="width: 100px; height: 100px">A</td>
+    </tr>
+  </head>
+</table>
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-section-change-collapsed-border-color-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-section-change-collapsed-border-color-expected.txt
new file mode 100644
index 0000000..8505bd02
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-section-change-collapsed-border-color-expected.txt
@@ -0,0 +1,41 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutTable TABLE",
+          "rect": [8, 82, 112, 112],
+          "reason": "invalidate paint rectangle"
+        }
+      ]
+    },
+    {
+      "name": "LayoutTableSection THEAD id='a'",
+      "position": [10, 84],
+      "bounds": [107, 107],
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutTableSection THEAD id='a'",
+          "rect": [0, 0, 107, 107],
+          "reason": "style change"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutTable TABLE",
+      "reason": "invalidate paint rectangle"
+    },
+    {
+      "object": "LayoutTableSection THEAD id='a'",
+      "reason": "style change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-section-change-collapsed-border-color.html b/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-section-change-collapsed-border-color.html
new file mode 100644
index 0000000..c7b9932
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/table/composited-section-change-collapsed-border-color.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<p style="height: 50px">Passes if green borders are visible surrounding A's cell and no red.
+<table style="border-collapse: collapse;">
+  <thead id="a" style="border: 5px solid red; will-change: transform">
+    <tr>
+      <td style="width: 100px; height: 100px">A</td>
+    </tr>
+  </head>
+</table>
+<script src="../resources/text-based-repaint.js"></script>
+<script>
+function repaintTest() {
+  a.style.borderColor = 'green';
+}
+onload = runRepaintAndPixelTest;
+</script>
diff --git a/third_party/WebKit/LayoutTests/paint/text/selection-no-clip-text.html b/third_party/WebKit/LayoutTests/paint/text/selection-no-clip-text.html
new file mode 100644
index 0000000..477fbd3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/text/selection-no-clip-text.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+Passes if top of all text in second row is visible when fully selected.
+<div style="line-height: 0.7">X<br>YYYY</div>
+<script>
+var range = document.createRange();
+range.selectNode(document.body);
+document.getSelection().addRange(range);
+</script>
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/text/selection-with-inline-padding-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/text/selection-with-inline-padding-expected.png
new file mode 100644
index 0000000..ab249ea
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/text/selection-with-inline-padding-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/text/selection-with-inline-padding-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/text/selection-with-inline-padding-expected.txt
new file mode 100644
index 0000000..ecf50154
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/text/selection-with-inline-padding-expected.txt
@@ -0,0 +1,22 @@
+layer at (0,0) size 800x600
+  LayoutView at (0,0) size 800x600
+layer at (0,0) size 800x148
+  LayoutBlockFlow {HTML} at (0,0) size 800x148
+    LayoutBlockFlow {BODY} at (8,16) size 784x116
+      LayoutBlockFlow {P} at (0,0) size 784x60
+        LayoutText {#text} at (0,0) size 330x19
+          text run at (0,0) width 330: "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
+        LayoutBR {BR} at (330,0) size 0x19
+        LayoutInline {B} at (0,0) size 301x39
+          LayoutText {#text} at (10,20) size 281x19
+            text run at (10,20) width 281: "Sed dictum erat sit amet pharetra pretium."
+        LayoutBR {BR} at (300,20) size 1x19
+        LayoutText {#text} at (0,40) size 315x19
+          text run at (0,40) width 315: "Nullam a est vitae orci tempus tincidunt nec at dolor."
+      LayoutBlockFlow {P} at (0,76) size 784x40
+        LayoutText {#text} at (0,0) size 729x39
+          text run at (0,0) width 409: "Tests whether selection includes padding in the Y direction for inline "
+          text run at (408,0) width 321: "elements. This padding would ideally not be included."
+          text run at (0,20) width 181: "http://crbug.com/657325#c13"
+selection start: position 5 of child 0 {#text} of child 3 {B} of child 0 {P} of body
+selection end:   position 37 of child 0 {#text} of child 3 {B} of child 0 {P} of body
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/resize-scrollable-iframe-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/resize-scrollable-iframe-expected.txt
index 61b869b..bab242a 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/resize-scrollable-iframe-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/resize-scrollable-iframe-expected.txt
@@ -18,11 +18,21 @@
         },
         {
           "object": "LayoutView #document",
+          "rect": [8, 193, 285, 200],
+          "reason": "incremental"
+        },
+        {
+          "object": "LayoutView #document",
           "rect": [8, 393, 285, 15],
           "reason": "scroll"
         },
         {
           "object": "LayoutView #document",
+          "rect": [93, 108, 200, 285],
+          "reason": "incremental"
+        },
+        {
+          "object": "LayoutView #document",
           "rect": [8, 193, 85, 15],
           "reason": "scroll"
         },
@@ -77,6 +87,10 @@
     {
       "object": "VerticalScrollbar",
       "reason": "scroll"
+    },
+    {
+      "object": "LayoutView #document",
+      "reason": "incremental"
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/table-cell-collapsed-border-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/table-cell-collapsed-border-expected.txt
index e452e1f..b1b1bfb 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/table-cell-collapsed-border-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/table-cell-collapsed-border-expected.txt
@@ -7,6 +7,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutTable TABLE",
+          "rect": [3, 64, 441, 405],
+          "reason": "invalidate paint rectangle"
+        },
+        {
           "object": "LayoutTableCell TD id='t3'",
           "rect": [220, 264, 122, 110],
           "reason": "style change"
@@ -26,48 +31,8 @@
   ],
   "objectPaintInvalidations": [
     {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD id='t1'",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD id='t2'",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD id='t3'",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
+      "object": "LayoutTable TABLE",
+      "reason": "invalidate paint rectangle"
     },
     {
       "object": "LayoutTableCell TD id='t1'",
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/table-collapsed-border-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/table-collapsed-border-expected.txt
index 71fa8da..bf2ed89b 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/table-collapsed-border-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/table-collapsed-border-expected.txt
@@ -13,6 +13,11 @@
         },
         {
           "object": "LayoutTable TABLE",
+          "rect": [8, 286, 95, 82],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutTable TABLE",
           "rect": [8, 102, 95, 82],
           "reason": "full"
         },
@@ -102,11 +107,6 @@
           "reason": "layoutObject removal"
         },
         {
-          "object": "LayoutTable TABLE",
-          "rect": [74, 286, 29, 82],
-          "reason": "incremental"
-        },
-        {
           "object": "LayoutTableCell TD",
           "rect": [8, 224, 14, 22],
           "reason": "forced by layout"
@@ -147,30 +147,6 @@
       "reason": "layoutObject removal"
     },
     {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
       "object": "LayoutTable TABLE id='t'",
       "reason": "style change"
     },
@@ -256,7 +232,7 @@
     },
     {
       "object": "LayoutTable TABLE",
-      "reason": "incremental"
+      "reason": "forced by layout"
     },
     {
       "object": "LayoutTableCell TD",
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/table/cached-change-cell-sl-border-color-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/table/cached-change-cell-sl-border-color-expected.txt
index 6bb9255..2882c0c 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/table/cached-change-cell-sl-border-color-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/table/cached-change-cell-sl-border-color-expected.txt
@@ -7,6 +7,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutTable (relative positioned) TABLE",
+          "rect": [8, 8, 114, 54],
+          "reason": "invalidate paint rectangle"
+        },
+        {
           "object": "LayoutTableCell TD id='foo'",
           "rect": [8, 8, 60, 54],
           "reason": "style change"
@@ -30,12 +35,8 @@
       "reason": "layoutObject removal"
     },
     {
-      "object": "LayoutTableCell TD id='foo'",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
+      "object": "LayoutTable (relative positioned) TABLE",
+      "reason": "invalidate paint rectangle"
     },
     {
       "object": "LayoutTableCell TD id='foo'",
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/text/selection-no-clip-text-expected.png b/third_party/WebKit/LayoutTests/platform/linux/paint/text/selection-no-clip-text-expected.png
new file mode 100644
index 0000000..52d9f71b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/linux/paint/text/selection-no-clip-text-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/text/selection-no-clip-text-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/paint/text/selection-no-clip-text-expected.txt
new file mode 100644
index 0000000..9efa6f0b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/linux/paint/text/selection-no-clip-text-expected.txt
@@ -0,0 +1,16 @@
+layer at (0,0) size 800x600
+  LayoutView at (0,0) size 800x600
+layer at (0,0) size 800x58
+  LayoutBlockFlow {HTML} at (0,0) size 800x58
+    LayoutBlockFlow {BODY} at (8,8) size 784x42
+      LayoutBlockFlow (anonymous) at (0,0) size 784x20
+        LayoutText {#text} at (0,0) size 399x19
+          text run at (0,0) width 399: "Passes if top of all text in second row is visible when fully selected."
+      LayoutBlockFlow {DIV} at (0,20) size 784x22
+        LayoutText {#text} at (0,-4) size 11x19
+          text run at (0,-4) width 11: "X"
+        LayoutBR {BR} at (11,-4) size 0x19
+        LayoutText {#text} at (0,7) size 44x19
+          text run at (0,7) width 44: "YYYY"
+selection start: position 0 of child 0 {#text} of body
+selection end:   position 4 of child 2 {#text} of child 1 {DIV} of body
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/text/selection-with-inline-padding-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/text/selection-with-inline-padding-expected.png
new file mode 100644
index 0000000..2040ce8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/text/selection-with-inline-padding-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/text/selection-with-inline-padding-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/text/selection-with-inline-padding-expected.png
new file mode 100644
index 0000000..85fbe4e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/text/selection-with-inline-padding-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/text/selection-with-inline-padding-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/text/selection-with-inline-padding-expected.png
new file mode 100644
index 0000000..c83872da
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/text/selection-with-inline-padding-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/text/selection-with-inline-padding-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/text/selection-with-inline-padding-expected.txt
new file mode 100644
index 0000000..408ed81f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/text/selection-with-inline-padding-expected.txt
@@ -0,0 +1,22 @@
+layer at (0,0) size 800x600
+  LayoutView at (0,0) size 800x600
+layer at (0,0) size 800x138
+  LayoutBlockFlow {HTML} at (0,0) size 800x138
+    LayoutBlockFlow {BODY} at (8,16) size 784x106
+      LayoutBlockFlow {P} at (0,0) size 784x54
+        LayoutText {#text} at (0,0) size 357x18
+          text run at (0,0) width 357: "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
+        LayoutBR {BR} at (356,0) size 1x18
+        LayoutInline {B} at (0,0) size 314x38
+          LayoutText {#text} at (10,18) size 294x18
+            text run at (10,18) width 294: "Sed dictum erat sit amet pharetra pretium."
+        LayoutBR {BR} at (313,18) size 1x18
+        LayoutText {#text} at (0,36) size 339x18
+          text run at (0,36) width 339: "Nullam a est vitae orci tempus tincidunt nec at dolor."
+      LayoutBlockFlow {P} at (0,70) size 784x36
+        LayoutText {#text} at (0,0) size 730x36
+          text run at (0,0) width 446: "Tests whether selection includes padding in the Y direction for inline "
+          text run at (445,0) width 285: "elements. This padding would ideally not be"
+          text run at (0,18) width 253: "included. http://crbug.com/657325#c13"
+selection start: position 5 of child 0 {#text} of child 3 {B} of child 0 {P} of body
+selection end:   position 37 of child 0 {#text} of child 3 {B} of child 0 {P} of body
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/line-flow-with-floats-9-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/line-flow-with-floats-9-expected.txt
index e4a9ce9..f5e66e64 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/line-flow-with-floats-9-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/line-flow-with-floats-9-expected.txt
@@ -94,6 +94,11 @@
         {
           "object": "LayoutView #document",
           "rect": [485, 0, 15, 600],
+          "reason": "incremental"
+        },
+        {
+          "object": "LayoutView #document",
+          "rect": [485, 0, 15, 600],
           "reason": "scroll"
         }
       ]
@@ -109,6 +114,10 @@
       "reason": "scroll"
     },
     {
+      "object": "LayoutView #document",
+      "reason": "incremental"
+    },
+    {
       "object": "LayoutBlockFlow P",
       "reason": "incremental"
     },
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/table-collapsed-border-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/table-collapsed-border-expected.txt
index d64d6549..4aabc19 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/table-collapsed-border-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/table-collapsed-border-expected.txt
@@ -18,6 +18,11 @@
         },
         {
           "object": "LayoutTable TABLE",
+          "rect": [8, 268, 101, 76],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutTable TABLE",
           "rect": [8, 96, 101, 76],
           "reason": "full"
         },
@@ -102,11 +107,6 @@
           "reason": "layoutObject removal"
         },
         {
-          "object": "LayoutTable TABLE",
-          "rect": [79, 268, 30, 76],
-          "reason": "incremental"
-        },
-        {
           "object": "LayoutTableCell TD",
           "rect": [8, 210, 14, 20],
           "reason": "forced by layout"
@@ -147,30 +147,6 @@
       "reason": "layoutObject removal"
     },
     {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
       "object": "LayoutTable TABLE id='t'",
       "reason": "style change"
     },
@@ -256,7 +232,7 @@
     },
     {
       "object": "LayoutTable TABLE",
-      "reason": "incremental"
+      "reason": "forced by layout"
     },
     {
       "object": "LayoutTableCell TD",
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/table/cached-change-cell-sl-border-color-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/table/cached-change-cell-sl-border-color-expected.txt
index 1804202..d682150 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/table/cached-change-cell-sl-border-color-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/table/cached-change-cell-sl-border-color-expected.txt
@@ -7,6 +7,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutTable (relative positioned) TABLE",
+          "rect": [8, 8, 114, 54],
+          "reason": "invalidate paint rectangle"
+        },
+        {
           "object": "LayoutTableCell TD id='foo'",
           "rect": [8, 8, 60, 54],
           "reason": "style change"
@@ -30,12 +35,8 @@
       "reason": "layoutObject removal"
     },
     {
-      "object": "LayoutTableCell TD id='foo'",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
+      "object": "LayoutTable (relative positioned) TABLE",
+      "reason": "invalidate paint rectangle"
     },
     {
       "object": "LayoutTableCell TD id='foo'",
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/text/selection-no-clip-text-expected.png b/third_party/WebKit/LayoutTests/platform/mac/paint/text/selection-no-clip-text-expected.png
new file mode 100644
index 0000000..50ebdd3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/paint/text/selection-no-clip-text-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/text/selection-no-clip-text-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/paint/text/selection-no-clip-text-expected.txt
new file mode 100644
index 0000000..885d2026
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/paint/text/selection-no-clip-text-expected.txt
@@ -0,0 +1,16 @@
+layer at (0,0) size 800x600
+  LayoutView at (0,0) size 800x600
+layer at (0,0) size 800x56
+  LayoutBlockFlow {HTML} at (0,0) size 800x56
+    LayoutBlockFlow {BODY} at (8,8) size 784x40
+      LayoutBlockFlow (anonymous) at (0,0) size 784x18
+        LayoutText {#text} at (0,0) size 433x18
+          text run at (0,0) width 433: "Passes if top of all text in second row is visible when fully selected."
+      LayoutBlockFlow {DIV} at (0,18) size 784x22
+        LayoutText {#text} at (0,-4) size 12x18
+          text run at (0,-4) width 12: "X"
+        LayoutBR {BR} at (11,-4) size 1x18
+        LayoutText {#text} at (0,7) size 47x18
+          text run at (0,7) width 47: "YYYY"
+selection start: position 0 of child 0 {#text} of body
+selection end:   position 4 of child 2 {#text} of child 1 {DIV} of body
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/text/selection-with-inline-padding-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/text/selection-with-inline-padding-expected.png
new file mode 100644
index 0000000..1a452c58
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/text/selection-with-inline-padding-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/text/selection-with-inline-padding-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/text/selection-with-inline-padding-expected.txt
new file mode 100644
index 0000000..8da17d12
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/text/selection-with-inline-padding-expected.txt
@@ -0,0 +1,22 @@
+layer at (0,0) size 800x600
+  LayoutView at (0,0) size 800x600
+layer at (0,0) size 800x138
+  LayoutBlockFlow {HTML} at (0,0) size 800x138
+    LayoutBlockFlow {BODY} at (8,16) size 784x106
+      LayoutBlockFlow {P} at (0,0) size 784x54
+        LayoutText {#text} at (0,0) size 357x17
+          text run at (0,0) width 357: "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
+        LayoutBR {BR} at (356,0) size 1x17
+        LayoutInline {B} at (0,0) size 314x37
+          LayoutText {#text} at (10,18) size 294x17
+            text run at (10,18) width 294: "Sed dictum erat sit amet pharetra pretium."
+        LayoutBR {BR} at (313,18) size 1x17
+        LayoutText {#text} at (0,36) size 339x17
+          text run at (0,36) width 339: "Nullam a est vitae orci tempus tincidunt nec at dolor."
+      LayoutBlockFlow {P} at (0,70) size 784x36
+        LayoutText {#text} at (0,0) size 730x35
+          text run at (0,0) width 446: "Tests whether selection includes padding in the Y direction for inline "
+          text run at (445,0) width 285: "elements. This padding would ideally not be"
+          text run at (0,18) width 253: "included. http://crbug.com/657325#c13"
+selection start: position 5 of child 0 {#text} of child 3 {B} of child 0 {P} of body
+selection end:   position 37 of child 0 {#text} of child 3 {B} of child 0 {P} of body
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/line-flow-with-floats-9-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/line-flow-with-floats-9-expected.txt
index bc46f17..8a565500 100644
--- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/line-flow-with-floats-9-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/line-flow-with-floats-9-expected.txt
@@ -94,6 +94,11 @@
         {
           "object": "LayoutView #document",
           "rect": [485, 0, 15, 600],
+          "reason": "incremental"
+        },
+        {
+          "object": "LayoutView #document",
+          "rect": [485, 0, 15, 600],
           "reason": "scroll"
         }
       ]
@@ -109,6 +114,10 @@
       "reason": "scroll"
     },
     {
+      "object": "LayoutView #document",
+      "reason": "incremental"
+    },
+    {
       "object": "LayoutBlockFlow P",
       "reason": "incremental"
     },
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/table-collapsed-border-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/table-collapsed-border-expected.txt
index 5eba99d..ce4853e 100644
--- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/table-collapsed-border-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/table-collapsed-border-expected.txt
@@ -18,6 +18,11 @@
         },
         {
           "object": "LayoutTable TABLE",
+          "rect": [8, 268, 101, 76],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutTable TABLE",
           "rect": [8, 96, 101, 76],
           "reason": "full"
         },
@@ -102,11 +107,6 @@
           "reason": "layoutObject removal"
         },
         {
-          "object": "LayoutTable TABLE",
-          "rect": [79, 268, 30, 76],
-          "reason": "incremental"
-        },
-        {
           "object": "LayoutTableCell TD",
           "rect": [8, 210, 14, 20],
           "reason": "forced by layout"
@@ -147,30 +147,6 @@
       "reason": "layoutObject removal"
     },
     {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
-    },
-    {
       "object": "LayoutTable TABLE id='t'",
       "reason": "style change"
     },
@@ -256,7 +232,7 @@
     },
     {
       "object": "LayoutTable TABLE",
-      "reason": "incremental"
+      "reason": "forced by layout"
     },
     {
       "object": "LayoutTableCell TD",
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/table/cached-change-cell-sl-border-color-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/table/cached-change-cell-sl-border-color-expected.txt
index 560cab9..1904492 100644
--- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/table/cached-change-cell-sl-border-color-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/table/cached-change-cell-sl-border-color-expected.txt
@@ -7,6 +7,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutTable (relative positioned) TABLE",
+          "rect": [8, 8, 114, 54],
+          "reason": "invalidate paint rectangle"
+        },
+        {
           "object": "LayoutTableCell TD id='foo'",
           "rect": [8, 8, 60, 54],
           "reason": "style change"
@@ -30,12 +35,8 @@
       "reason": "layoutObject removal"
     },
     {
-      "object": "LayoutTableCell TD id='foo'",
-      "reason": "style change"
-    },
-    {
-      "object": "LayoutTableCell TD",
-      "reason": "style change"
+      "object": "LayoutTable (relative positioned) TABLE",
+      "reason": "invalidate paint rectangle"
     },
     {
       "object": "LayoutTableCell TD id='foo'",
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/text/selection-no-clip-text-expected.png b/third_party/WebKit/LayoutTests/platform/win/paint/text/selection-no-clip-text-expected.png
new file mode 100644
index 0000000..47783e8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win/paint/text/selection-no-clip-text-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/text/selection-no-clip-text-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/text/selection-no-clip-text-expected.txt
new file mode 100644
index 0000000..d32756f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win/paint/text/selection-no-clip-text-expected.txt
@@ -0,0 +1,16 @@
+layer at (0,0) size 800x600
+  LayoutView at (0,0) size 800x600
+layer at (0,0) size 800x56
+  LayoutBlockFlow {HTML} at (0,0) size 800x56
+    LayoutBlockFlow {BODY} at (8,8) size 784x40
+      LayoutBlockFlow (anonymous) at (0,0) size 784x18
+        LayoutText {#text} at (0,0) size 433x17
+          text run at (0,0) width 433: "Passes if top of all text in second row is visible when fully selected."
+      LayoutBlockFlow {DIV} at (0,18) size 784x22
+        LayoutText {#text} at (0,-3) size 12x17
+          text run at (0,-3) width 12: "X"
+        LayoutBR {BR} at (11,-3) size 1x17
+        LayoutText {#text} at (0,8) size 47x17
+          text run at (0,8) width 47: "YYYY"
+selection start: position 0 of child 0 {#text} of body
+selection end:   position 4 of child 2 {#text} of child 1 {DIV} of body
diff --git a/third_party/WebKit/LayoutTests/platform/win7/fast/text/selection-with-inline-padding-expected.png b/third_party/WebKit/LayoutTests/platform/win7/fast/text/selection-with-inline-padding-expected.png
new file mode 100644
index 0000000..8e5829f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win7/fast/text/selection-with-inline-padding-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/webaudio/codec-tests/flac/flac-decode-expected.wav b/third_party/WebKit/LayoutTests/webaudio/codec-tests/flac/flac-decode-expected.wav
new file mode 100644
index 0000000..4dde09129
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/webaudio/codec-tests/flac/flac-decode-expected.wav
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/webaudio/codec-tests/flac/flac-decode.html b/third_party/WebKit/LayoutTests/webaudio/codec-tests/flac/flac-decode.html
new file mode 100644
index 0000000..5d2eaa7e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/webaudio/codec-tests/flac/flac-decode.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<html>
+  <head>
+    <title>Test FLAC Decoding</title>
+    <script src="../../resources/audio-codec-test.js"></script>
+    <script src="../../resources/audio-testing.js"></script>
+    <script src="../../resources/buffer-loader.js"></script>
+  </head>
+
+  <body>
+    <script>
+      var url = "flac-test.flac";
+
+      window.onload = function () { runDecodingTest(url) };
+    </script>
+  </body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/webaudio/codec-tests/flac/flac-test.flac b/third_party/WebKit/LayoutTests/webaudio/codec-tests/flac/flac-test.flac
new file mode 100644
index 0000000..471157a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/webaudio/codec-tests/flac/flac-test.flac
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/webaudio/codec-tests/opus/opus-decode-expected.wav b/third_party/WebKit/LayoutTests/webaudio/codec-tests/opus/opus-decode-expected.wav
new file mode 100644
index 0000000..1d36b67
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/webaudio/codec-tests/opus/opus-decode-expected.wav
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/webaudio/codec-tests/opus/opus-decode.html b/third_party/WebKit/LayoutTests/webaudio/codec-tests/opus/opus-decode.html
new file mode 100644
index 0000000..03596deb1
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/webaudio/codec-tests/opus/opus-decode.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<html>
+  <head>
+    <title>Test OPUS Decoding</title>
+    <script src="../../resources/audio-codec-test.js"></script>
+    <script src="../../resources/audio-testing.js"></script>
+    <script src="../../resources/buffer-loader.js"></script>
+  </head>
+
+  <body>
+    <script>
+      var url = "opus-test.opus";
+
+      window.onload = function () { runDecodingTest(url, 48000) };
+    </script>
+  </body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/webaudio/codec-tests/opus/opus-test.opus b/third_party/WebKit/LayoutTests/webaudio/codec-tests/opus/opus-test.opus
new file mode 100644
index 0000000..a1f787f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/webaudio/codec-tests/opus/opus-test.opus
Binary files differ
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScriptController.cpp b/third_party/WebKit/Source/bindings/core/v8/ScriptController.cpp
index c32e0ed..949601e 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ScriptController.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/ScriptController.cpp
@@ -32,7 +32,6 @@
 
 #include "bindings/core/v8/ScriptController.h"
 
-#include "bindings/core/v8/BindingSecurity.h"
 #include "bindings/core/v8/ScriptSourceCode.h"
 #include "bindings/core/v8/ScriptValue.h"
 #include "bindings/core/v8/V8Binding.h"
@@ -78,16 +77,6 @@
 
 namespace blink {
 
-bool ScriptController::canAccessFromCurrentOrigin(v8::Isolate* isolate,
-                                                  Frame* frame) {
-  if (!frame)
-    return false;
-  return !isolate->InContext() ||
-         BindingSecurity::shouldAllowAccessToFrame(
-             currentDOMWindow(isolate), frame,
-             BindingSecurity::ErrorReportOption::Report);
-}
-
 ScriptController::ScriptController(LocalFrame* frame)
     : m_windowProxyManager(WindowProxyManager::create(*frame)) {}
 
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScriptController.h b/third_party/WebKit/Source/bindings/core/v8/ScriptController.h
index 301ace9b..eb78263f 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ScriptController.h
+++ b/third_party/WebKit/Source/bindings/core/v8/ScriptController.h
@@ -124,8 +124,6 @@
   void enableEval();
   void disableEval(const String& errorMessage);
 
-  static bool canAccessFromCurrentOrigin(v8::Isolate*, Frame*);
-
   void collectIsolatedContexts(
       Vector<std::pair<ScriptState*, SecurityOrigin*>>&);
 
diff --git a/third_party/WebKit/Source/core/BUILD.gn b/third_party/WebKit/Source/core/BUILD.gn
index 969ac81..b20f845a 100644
--- a/third_party/WebKit/Source/core/BUILD.gn
+++ b/third_party/WebKit/Source/core/BUILD.gn
@@ -52,8 +52,7 @@
 
 config("blink_core_pch") {
   if (enable_precompiled_headers) {
-    # TODO(thakis): Enable for clang once it works, https://crbug.com/667891
-    if (is_win && !is_clang) {
+    if (is_win) {
       # This is a string rather than a file GN knows about. It has to match
       # exactly what's in the /FI flag below, and what might appear in the
       # source code in quotes for an #include directive.
@@ -1296,6 +1295,7 @@
     "paint/SVGInlineTextBoxPainterTest.cpp",
     "paint/StubChromeClientForSPv2.h",
     "paint/TableCellPainterTest.cpp",
+    "paint/TablePainterTest.cpp",
     "paint/TextPainterTest.cpp",
     "paint/VideoPainterTest.cpp",
     "streams/ReadableStreamOperationsTest.cpp",
diff --git a/third_party/WebKit/Source/core/css/StyleSheetContents.cpp b/third_party/WebKit/Source/core/css/StyleSheetContents.cpp
index be7d917d..1403ca2 100644
--- a/third_party/WebKit/Source/core/css/StyleSheetContents.cpp
+++ b/third_party/WebKit/Source/core/css/StyleSheetContents.cpp
@@ -491,6 +491,10 @@
   return root->clientSingleOwnerDocument();
 }
 
+Document* StyleSheetContents::anyOwnerDocument() const {
+  return rootStyleSheet()->clientAnyOwnerDocument();
+}
+
 static bool childRulesHaveFailedOrCanceledSubresources(
     const HeapVector<Member<StyleRuleBase>>& rules) {
   for (unsigned i = 0; i < rules.size(); ++i) {
@@ -531,15 +535,18 @@
   return childRulesHaveFailedOrCanceledSubresources(m_childRules);
 }
 
-Document* StyleSheetContents::clientSingleOwnerDocument() const {
-  if (!m_hasSingleOwnerDocument || clientSize() <= 0)
+Document* StyleSheetContents::clientAnyOwnerDocument() const {
+  if (clientSize() <= 0)
     return nullptr;
-
   if (m_loadingClients.size())
     return (*m_loadingClients.begin())->ownerDocument();
   return (*m_completedClients.begin())->ownerDocument();
 }
 
+Document* StyleSheetContents::clientSingleOwnerDocument() const {
+  return m_hasSingleOwnerDocument ? clientAnyOwnerDocument() : nullptr;
+}
+
 StyleSheetContents* StyleSheetContents::parentStyleSheet() const {
   return m_ownerRule ? m_ownerRule->parentStyleSheet() : nullptr;
 }
diff --git a/third_party/WebKit/Source/core/css/StyleSheetContents.h b/third_party/WebKit/Source/core/css/StyleSheetContents.h
index 954dd098..5fbb04c 100644
--- a/third_party/WebKit/Source/core/css/StyleSheetContents.h
+++ b/third_party/WebKit/Source/core/css/StyleSheetContents.h
@@ -86,6 +86,10 @@
   Document* singleOwnerDocument() const;
   bool hasSingleOwnerDocument() const { return m_hasSingleOwnerDocument; }
 
+  // Gets the first owner document in the list of registered clients, or nullptr
+  // if there are none.
+  Document* anyOwnerDocument() const;
+
   const String& charset() const { return m_parserContext.charset(); }
 
   bool loadCompleted() const;
@@ -187,6 +191,7 @@
   void notifyRemoveFontFaceRule(const StyleRuleFontFace*);
 
   Document* clientSingleOwnerDocument() const;
+  Document* clientAnyOwnerDocument() const;
 
   Member<StyleRuleImport> m_ownerRule;
 
diff --git a/third_party/WebKit/Source/core/css/parser/CSSLazyParsingState.cpp b/third_party/WebKit/Source/core/css/parser/CSSLazyParsingState.cpp
index 1a231c6..b3d2ec5 100644
--- a/third_party/WebKit/Source/core/css/parser/CSSLazyParsingState.cpp
+++ b/third_party/WebKit/Source/core/css/parser/CSSLazyParsingState.cpp
@@ -5,6 +5,7 @@
 #include "core/css/parser/CSSLazyParsingState.h"
 #include "core/css/parser/CSSLazyPropertyParserImpl.h"
 #include "core/css/parser/CSSParserTokenRange.h"
+#include "core/dom/Document.h"
 #include "core/frame/UseCounter.h"
 #include "platform/Histogram.h"
 
@@ -21,7 +22,8 @@
       m_parsedStyleRules(0),
       m_totalStyleRules(0),
       m_styleRulesNeededForNextMilestone(0),
-      m_usage(UsageGe0) {
+      m_usage(UsageGe0),
+      m_shouldUseCount(!!m_context.useCounter()) {
   recordUsageMetrics();
 }
 
@@ -33,9 +35,21 @@
 
 const CSSParserContext& CSSLazyParsingState::context() {
   DCHECK(m_owningContents);
-  UseCounter* sheetCounter = UseCounter::getFrom(m_owningContents);
-  if (sheetCounter != m_context.useCounter())
-    m_context = CSSParserContext(m_context, sheetCounter);
+  if (!m_shouldUseCount) {
+    DCHECK(!m_context.useCounter());
+    return m_context;
+  }
+
+  // Try as best as possible to grab a valid UseCounter if the underlying
+  // document has gone away.
+  if (!m_document)
+    m_document = m_owningContents->anyOwnerDocument();
+
+  // Always refresh the UseCounter, as the Document can outlive its
+  // underlying frame host causing a use-after-free of m_context's counter.
+  UseCounter* useCounter = UseCounter::getFrom(m_document);
+  if (useCounter != m_context.useCounter())
+    m_context = CSSParserContext(m_context, useCounter);
   return m_context;
 }
 
@@ -106,4 +120,9 @@
   usageHistogram.count(m_usage);
 }
 
+DEFINE_TRACE(CSSLazyParsingState) {
+  visitor->trace(m_owningContents);
+  visitor->trace(m_document);
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/css/parser/CSSLazyParsingState.h b/third_party/WebKit/Source/core/css/parser/CSSLazyParsingState.h
index 8c77cb7..80c846f 100644
--- a/third_party/WebKit/Source/core/css/parser/CSSLazyParsingState.h
+++ b/third_party/WebKit/Source/core/css/parser/CSSLazyParsingState.h
@@ -37,7 +37,7 @@
   bool shouldLazilyParseProperties(const CSSSelectorList&,
                                    const CSSParserTokenRange& block) const;
 
-  DEFINE_INLINE_TRACE() { visitor->trace(m_owningContents); }
+  DECLARE_TRACE();
 
   // Exposed for tests. This enum is used to back a histogram, so new values
   // must be appended to the end, before UsageLastValue.
@@ -66,6 +66,10 @@
   // it should (we DCHECK this fact).
   WeakMember<StyleSheetContents> m_owningContents;
 
+  // Cache the document as a proxy for caching the UseCounter. Grabbing the
+  // UseCounter per every property parse is a bit more expensive.
+  WeakMember<Document> m_document;
+
   // Used for calculating the % of rules that ended up being parsed.
   int m_parsedStyleRules;
   int m_totalStyleRules;
@@ -73,6 +77,11 @@
   int m_styleRulesNeededForNextMilestone;
 
   int m_usage;
+
+  // Whether or not use counting is enabled for parsing. This will usually be
+  // true, except for when stylesheets with @imports are removed from the page.
+  // See StyleRuleImport::setCSSStyleSheet.
+  const bool m_shouldUseCount;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/css/parser/CSSLazyParsingTest.cpp b/third_party/WebKit/Source/core/css/parser/CSSLazyParsingTest.cpp
index 8adfe18..0efd224 100644
--- a/third_party/WebKit/Source/core/css/parser/CSSLazyParsingTest.cpp
+++ b/third_party/WebKit/Source/core/css/parser/CSSLazyParsingTest.cpp
@@ -29,6 +29,7 @@
 
  protected:
   HistogramTester m_histogramTester;
+  Persistent<StyleSheetContents> m_cachedContents;
 };
 
 TEST_F(CSSLazyParsingTest, Simple) {
@@ -119,42 +120,52 @@
       DummyPageHolder::create(IntSize(500, 500));
   CSSParserContext context(HTMLStandardMode,
                            UseCounter::getFrom(&dummyHolder->document()));
-  StyleSheetContents* styleSheet = StyleSheetContents::create(context);
-  CSSStyleSheet* sheet =
-      CSSStyleSheet::create(styleSheet, dummyHolder->document());
-  DCHECK(sheet);
+  m_cachedContents = StyleSheetContents::create(context);
+  {
+    CSSStyleSheet* sheet =
+        CSSStyleSheet::create(m_cachedContents, dummyHolder->document());
+    DCHECK(sheet);
 
-  String sheetText = "body { background-color: red; } p { color: orange;  }";
-  CSSParser::parseSheet(context, styleSheet, sheetText, true /* lazy parse */);
+    String sheetText = "body { background-color: red; } p { color: orange;  }";
+    CSSParser::parseSheet(context, m_cachedContents, sheetText,
+                          true /* lazy parse */);
 
-  // Parse the first property set with the first document as owner.
-  StyleRule* rule = ruleAt(styleSheet, 0);
-  EXPECT_FALSE(hasParsedProperties(rule));
-  rule->properties();
-  EXPECT_TRUE(hasParsedProperties(rule));
+    // Parse the first property set with the first document as owner.
+    StyleRule* rule = ruleAt(m_cachedContents, 0);
+    EXPECT_FALSE(hasParsedProperties(rule));
+    rule->properties();
+    EXPECT_TRUE(hasParsedProperties(rule));
 
-  EXPECT_EQ(&dummyHolder->document(), styleSheet->singleOwnerDocument());
+    EXPECT_EQ(&dummyHolder->document(),
+              m_cachedContents->singleOwnerDocument());
+    UseCounter& useCounter1 = dummyHolder->document().frameHost()->useCounter();
+    EXPECT_TRUE(useCounter1.isCounted(CSSPropertyBackgroundColor));
+    EXPECT_FALSE(useCounter1.isCounted(CSSPropertyColor));
 
-  // Change owner document.
-  styleSheet->unregisterClient(sheet);
+    // Change owner document.
+    m_cachedContents->unregisterClient(sheet);
+    dummyHolder.reset();
+  }
+  // Ensure no stack references to oilpan objects.
+  ThreadState::current()->collectAllGarbage();
+
   std::unique_ptr<DummyPageHolder> dummyHolder2 =
       DummyPageHolder::create(IntSize(500, 500));
-  CSSStyleSheet::create(styleSheet, dummyHolder2->document());
+  CSSStyleSheet* sheet2 =
+      CSSStyleSheet::create(m_cachedContents, dummyHolder2->document());
 
-  EXPECT_EQ(&dummyHolder2->document(), styleSheet->singleOwnerDocument());
+  EXPECT_EQ(&dummyHolder2->document(), m_cachedContents->singleOwnerDocument());
 
   // Parse the second property set with the second document as owner.
-  StyleRule* rule2 = ruleAt(styleSheet, 1);
+  StyleRule* rule2 = ruleAt(m_cachedContents, 1);
   EXPECT_FALSE(hasParsedProperties(rule2));
   rule2->properties();
   EXPECT_TRUE(hasParsedProperties(rule2));
 
-  UseCounter& useCounter1 = dummyHolder->document().frameHost()->useCounter();
   UseCounter& useCounter2 = dummyHolder2->document().frameHost()->useCounter();
-  EXPECT_TRUE(useCounter1.isCounted(CSSPropertyBackgroundColor));
+  EXPECT_TRUE(sheet2);
   EXPECT_TRUE(useCounter2.isCounted(CSSPropertyColor));
   EXPECT_FALSE(useCounter2.isCounted(CSSPropertyBackgroundColor));
-  EXPECT_FALSE(useCounter1.isCounted(CSSPropertyColor));
 }
 
 TEST_F(CSSLazyParsingTest, SimpleRuleUsagePercent) {
diff --git a/third_party/WebKit/Source/core/dom/SecurityContext.cpp b/third_party/WebKit/Source/core/dom/SecurityContext.cpp
index 4a6c560..dfce655 100644
--- a/third_party/WebKit/Source/core/dom/SecurityContext.cpp
+++ b/third_party/WebKit/Source/core/dom/SecurityContext.cpp
@@ -100,13 +100,12 @@
 }
 
 void SecurityContext::setFeaturePolicyFromHeader(
-    const String& headerValue,
-    FeaturePolicy* parentFeaturePolicy,
-    Vector<String>* messages) {
+    const WebParsedFeaturePolicy& parsedHeader,
+    FeaturePolicy* parentFeaturePolicy) {
   DCHECK(!m_featurePolicy);
   m_featurePolicy = FeaturePolicy::createFromParentPolicy(parentFeaturePolicy,
                                                           m_securityOrigin);
-  m_featurePolicy->setHeaderPolicy(headerValue, messages);
+  m_featurePolicy->setHeaderPolicy(parsedHeader);
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/dom/SecurityContext.h b/third_party/WebKit/Source/core/dom/SecurityContext.h
index f625d9d..b58abaa 100644
--- a/third_party/WebKit/Source/core/dom/SecurityContext.h
+++ b/third_party/WebKit/Source/core/dom/SecurityContext.h
@@ -33,6 +33,7 @@
 #include "platform/heap/Handle.h"
 #include "platform/weborigin/Suborigin.h"
 #include "public/platform/WebAddressSpace.h"
+#include "public/platform/WebFeaturePolicy.h"
 #include "public/platform/WebInsecureRequestPolicy.h"
 #include "public/platform/WebURLRequest.h"
 #include "wtf/HashSet.h"
@@ -96,9 +97,8 @@
   void setFeaturePolicyForTesting(std::unique_ptr<FeaturePolicy> newPolicy) {
     m_featurePolicy = std::move(newPolicy);
   }
-  void setFeaturePolicyFromHeader(const String& headerValue,
-                                  FeaturePolicy* parentFeaturePolicy,
-                                  Vector<String>* messages = nullptr);
+  void setFeaturePolicyFromHeader(const WebParsedFeaturePolicy& parsedHeader,
+                                  FeaturePolicy* parentFeaturePolicy);
 
  protected:
   SecurityContext();
diff --git a/third_party/WebKit/Source/core/frame/FeaturePolicyInFrameTest.cpp b/third_party/WebKit/Source/core/frame/FeaturePolicyInFrameTest.cpp
index f937554..b2b45c12 100644
--- a/third_party/WebKit/Source/core/frame/FeaturePolicyInFrameTest.cpp
+++ b/third_party/WebKit/Source/core/frame/FeaturePolicyInFrameTest.cpp
@@ -98,8 +98,9 @@
   Vector<String> messages;
   std::unique_ptr<FeaturePolicy> policy1 =
       createFromParentPolicy(nullptr, m_originA);
-  policy1->setHeaderPolicy("{\"default-off\": [\"self\"], \"default-on\": []}",
-                           &messages);
+  policy1->setHeaderPolicy(FeaturePolicy::parseFeaturePolicy(
+      "{\"default-off\": [\"self\"], \"default-on\": []}", m_originA.get(),
+      &messages));
   EXPECT_EQ(0UL, messages.size());
   document().setFeaturePolicyForTesting(std::move(policy1));
   EXPECT_TRUE(isFeatureEnabledInFrame(kDefaultOffFeature, frame()));
@@ -122,7 +123,8 @@
   Vector<String> messages;
   std::unique_ptr<FeaturePolicy> policy1 =
       createFromParentPolicy(nullptr, m_originA);
-  policy1->setHeaderPolicy("{\"default-on\": []}", &messages);
+  policy1->setHeaderPolicy(FeaturePolicy::parseFeaturePolicy(
+      "{\"default-on\": []}", m_originA.get(), &messages));
   EXPECT_EQ(0UL, messages.size());
   document().setFeaturePolicyForTesting(std::move(policy1));
   EXPECT_TRUE(isFeatureEnabledInFrame(kDefaultOnFeature, frame()));
diff --git a/third_party/WebKit/Source/core/frame/PerformanceMonitor.cpp b/third_party/WebKit/Source/core/frame/PerformanceMonitor.cpp
index 24a1ed10..7b29652a 100644
--- a/third_party/WebKit/Source/core/frame/PerformanceMonitor.cpp
+++ b/third_party/WebKit/Source/core/frame/PerformanceMonitor.cpp
@@ -175,7 +175,6 @@
     : m_localRoot(localRoot) {
   std::fill(std::begin(m_thresholds), std::end(m_thresholds), 0);
   Platform::current()->currentThread()->addTaskTimeObserver(this);
-  Platform::current()->currentThread()->addTaskObserver(this);
 }
 
 PerformanceMonitor::~PerformanceMonitor() {
@@ -205,7 +204,6 @@
   m_subscriptions.clear();
   updateInstrumentation();
   Platform::current()->currentThread()->removeTaskTimeObserver(this);
-  Platform::current()->currentThread()->removeTaskObserver(this);
 }
 
 void PerformanceMonitor::updateInstrumentation() {
@@ -301,7 +299,8 @@
   }
 }
 
-void PerformanceMonitor::willProcessTask() {
+void PerformanceMonitor::willProcessTask(scheduler::TaskQueue*,
+                                         double startTime) {
   // Reset m_taskExecutionContext. We don't clear this in didProcessTask
   // as it is needed in ReportTaskTime which occurs after didProcessTask.
   m_taskExecutionContext = nullptr;
@@ -319,17 +318,32 @@
   m_handlerDepth = 0;
 }
 
-void PerformanceMonitor::didProcessTask() {
+void PerformanceMonitor::didProcessTask(scheduler::TaskQueue*,
+                                        double startTime,
+                                        double endTime) {
   if (!m_enabled)
     return;
   double layoutThreshold = m_thresholds[kLongLayout];
-  if (!layoutThreshold || m_perTaskStyleAndLayoutTime < layoutThreshold)
-    return;
-  ClientThresholds* clientThresholds = m_subscriptions.get(kLongLayout);
-  DCHECK(clientThresholds);
-  for (const auto& it : *clientThresholds) {
-    if (it.value < m_perTaskStyleAndLayoutTime)
-      it.key->reportLongLayout(m_perTaskStyleAndLayoutTime);
+  if (layoutThreshold && m_perTaskStyleAndLayoutTime > layoutThreshold) {
+    ClientThresholds* clientThresholds = m_subscriptions.get(kLongLayout);
+    DCHECK(clientThresholds);
+    for (const auto& it : *clientThresholds) {
+      if (it.value < m_perTaskStyleAndLayoutTime)
+        it.key->reportLongLayout(m_perTaskStyleAndLayoutTime);
+    }
+  }
+
+  double taskTime = endTime - startTime;
+  if (m_thresholds[kLongTask] && taskTime > m_thresholds[kLongTask]) {
+    ClientThresholds* clientThresholds = m_subscriptions.get(kLongTask);
+    for (const auto& it : *clientThresholds) {
+      if (it.value < taskTime) {
+        it.key->reportLongTask(
+            startTime, endTime,
+            m_taskHasMultipleContexts ? nullptr : m_taskExecutionContext,
+            m_taskHasMultipleContexts);
+      }
+    }
   }
 }
 
@@ -346,26 +360,6 @@
   }
 }
 
-void PerformanceMonitor::ReportTaskTime(scheduler::TaskQueue*,
-                                        double startTime,
-                                        double endTime) {
-  if (!m_enabled)
-    return;
-  double taskTime = endTime - startTime;
-  if (!m_thresholds[kLongTask] || taskTime < m_thresholds[kLongTask])
-    return;
-
-  ClientThresholds* clientThresholds = m_subscriptions.get(kLongTask);
-  for (const auto& it : *clientThresholds) {
-    if (it.value < taskTime) {
-      it.key->reportLongTask(
-          startTime, endTime,
-          m_taskHasMultipleContexts ? nullptr : m_taskExecutionContext,
-          m_taskHasMultipleContexts);
-    }
-  }
-}
-
 DEFINE_TRACE(PerformanceMonitor) {
   visitor->trace(m_localRoot);
   visitor->trace(m_taskExecutionContext);
diff --git a/third_party/WebKit/Source/core/frame/PerformanceMonitor.h b/third_party/WebKit/Source/core/frame/PerformanceMonitor.h
index 1c705d4d..b6801278 100644
--- a/third_party/WebKit/Source/core/frame/PerformanceMonitor.h
+++ b/third_party/WebKit/Source/core/frame/PerformanceMonitor.h
@@ -28,7 +28,6 @@
 // (in the local frame tree) in m_webPerformanceObservers.
 class CORE_EXPORT PerformanceMonitor final
     : public GarbageCollectedFinalized<PerformanceMonitor>,
-      public WebThread::TaskObserver,
       public scheduler::TaskTimeObserver {
   WTF_MAKE_NONCOPYABLE(PerformanceMonitor);
 
@@ -118,12 +117,9 @@
                               double time,
                               SourceLocation*);
 
-  // WebThread::TaskObserver implementation.
-  void willProcessTask() override;
-  void didProcessTask() override;
-
   // scheduler::TaskTimeObserver implementation
-  void ReportTaskTime(scheduler::TaskQueue*,
+  void willProcessTask(scheduler::TaskQueue*, double startTime) override;
+  void didProcessTask(scheduler::TaskQueue*,
                       double startTime,
                       double endTime) override;
 
diff --git a/third_party/WebKit/Source/core/frame/PerformanceMonitorTest.cpp b/third_party/WebKit/Source/core/frame/PerformanceMonitorTest.cpp
index 17c4f828..a784878 100644
--- a/third_party/WebKit/Source/core/frame/PerformanceMonitorTest.cpp
+++ b/third_party/WebKit/Source/core/frame/PerformanceMonitorTest.cpp
@@ -32,15 +32,15 @@
     m_monitor->alwaysWillExecuteScript(executionContext);
   }
 
-  void willProcessTask() { m_monitor->willProcessTask(); }
-
-  void didProcessTask() { m_monitor->didProcessTask(); }
-
   // scheduler::TaskTimeObserver implementation
-  void ReportTaskTime(scheduler::TaskQueue* queue,
+  void willProcessTask(scheduler::TaskQueue* queue, double startTime) {
+    m_monitor->willProcessTask(queue, startTime);
+  }
+
+  void didProcessTask(scheduler::TaskQueue* queue,
                       double startTime,
                       double endTime) {
-    m_monitor->ReportTaskTime(queue, startTime, endTime);
+    m_monitor->didProcessTask(queue, startTime, endTime);
   }
 
   String frameContextURL();
@@ -79,18 +79,17 @@
 }
 
 TEST_F(PerformanceMonitorTest, SingleScriptInTask) {
-  willProcessTask();
+  willProcessTask(nullptr, 3719349.445172);
   EXPECT_EQ(0, numUniqueFrameContextsSeen());
   willExecuteScript(executionContext());
   EXPECT_EQ(1, numUniqueFrameContextsSeen());
-  didProcessTask();
-  ReportTaskTime(nullptr, 3719349.445172, 3719349.5561923);  // Long task
+  didProcessTask(nullptr, 3719349.445172, 3719349.5561923);  // Long task
   EXPECT_EQ(1, numUniqueFrameContextsSeen());
   EXPECT_EQ("https://example.com/foo", frameContextURL());
 }
 
 TEST_F(PerformanceMonitorTest, MultipleScriptsInTask_SingleContext) {
-  willProcessTask();
+  willProcessTask(nullptr, 3719349.445172);
   EXPECT_EQ(0, numUniqueFrameContextsSeen());
   willExecuteScript(executionContext());
   EXPECT_EQ(1, numUniqueFrameContextsSeen());
@@ -98,14 +97,13 @@
 
   willExecuteScript(executionContext());
   EXPECT_EQ(1, numUniqueFrameContextsSeen());
-  didProcessTask();
-  ReportTaskTime(nullptr, 3719349.445172, 3719349.5561923);  // Long task
+  didProcessTask(nullptr, 3719349.445172, 3719349.5561923);  // Long task
   EXPECT_EQ(1, numUniqueFrameContextsSeen());
   EXPECT_EQ("https://example.com/foo", frameContextURL());
 }
 
 TEST_F(PerformanceMonitorTest, MultipleScriptsInTask_MultipleContexts) {
-  willProcessTask();
+  willProcessTask(nullptr, 3719349.445172);
   EXPECT_EQ(0, numUniqueFrameContextsSeen());
   willExecuteScript(executionContext());
   EXPECT_EQ(1, numUniqueFrameContextsSeen());
@@ -113,20 +111,18 @@
 
   willExecuteScript(anotherExecutionContext());
   EXPECT_EQ(2, numUniqueFrameContextsSeen());
-  didProcessTask();
-  ReportTaskTime(nullptr, 3719349.445172, 3719349.5561923);  // Long task
+  didProcessTask(nullptr, 3719349.445172, 3719349.5561923);  // Long task
   EXPECT_EQ(2, numUniqueFrameContextsSeen());
   EXPECT_EQ("", frameContextURL());
 }
 
 TEST_F(PerformanceMonitorTest, NoScriptInLongTask) {
-  willProcessTask();
+  willProcessTask(nullptr, 3719349.445172);
   willExecuteScript(executionContext());
-  didProcessTask();
-  ReportTaskTime(nullptr, 3719349.445172, 3719349.445182);
-  willProcessTask();
-  didProcessTask();
-  ReportTaskTime(nullptr, 3719349.445172, 3719349.5561923);  // Long task
+  didProcessTask(nullptr, 3719349.445172, 3719349.445182);
+
+  willProcessTask(nullptr, 3719349.445172);
+  didProcessTask(nullptr, 3719349.445172, 3719349.5561923);  // Long task
   // Without presence of Script, FrameContext URL is not available
   EXPECT_EQ(0, numUniqueFrameContextsSeen());
 }
diff --git a/third_party/WebKit/Source/core/html/HTMLFrameElementBase.cpp b/third_party/WebKit/Source/core/html/HTMLFrameElementBase.cpp
index 2eb31fc9..05e28a7f 100644
--- a/third_party/WebKit/Source/core/html/HTMLFrameElementBase.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLFrameElementBase.cpp
@@ -23,6 +23,7 @@
 
 #include "core/html/HTMLFrameElementBase.h"
 
+#include "bindings/core/v8/BindingSecurity.h"
 #include "bindings/core/v8/ScriptController.h"
 #include "bindings/core/v8/ScriptEventListener.h"
 #include "core/HTMLNames.h"
@@ -56,10 +57,18 @@
 
   const KURL& completeURL = document().completeURL(m_URL);
 
-  if (protocolIsJavaScript(completeURL)) {
-    if (contentFrame() &&
-        !ScriptController::canAccessFromCurrentOrigin(toIsolate(&document()),
-                                                      contentFrame()))
+  if (contentFrame() && protocolIsJavaScript(completeURL)) {
+    // Check if the caller can execute script in the context of the content
+    // frame. NB: This check can be invoked without any JS on the stack for some
+    // parser operations. In such case, we use the origin of the frame element's
+    // containing document as the caller context.
+    v8::Isolate* isolate = toIsolate(&document());
+    LocalDOMWindow* accessingWindow = isolate->InContext()
+                                          ? currentDOMWindow(isolate)
+                                          : document().domWindow();
+    if (!BindingSecurity::shouldAllowAccessToFrame(
+            accessingWindow, contentFrame(),
+            BindingSecurity::ErrorReportOption::Report))
       return false;
   }
 
diff --git a/third_party/WebKit/Source/core/layout/LayoutDetailsMarker.h b/third_party/WebKit/Source/core/layout/LayoutDetailsMarker.h
index ad9e858..a118552 100644
--- a/third_party/WebKit/Source/core/layout/LayoutDetailsMarker.h
+++ b/third_party/WebKit/Source/core/layout/LayoutDetailsMarker.h
@@ -40,6 +40,9 @@
     return type == LayoutObjectDetailsMarker || LayoutBlockFlow::isOfType(type);
   }
   void paint(const PaintInfo&, const LayoutPoint&) const override;
+  bool paintedOutputOfObjectHasNoEffectRegardlessOfSize() const override {
+    return false;
+  }
 
   bool isOpen() const;
 };
diff --git a/third_party/WebKit/Source/core/layout/LayoutObject.h b/third_party/WebKit/Source/core/layout/LayoutObject.h
index a52f630..0528c06b 100644
--- a/third_party/WebKit/Source/core/layout/LayoutObject.h
+++ b/third_party/WebKit/Source/core/layout/LayoutObject.h
@@ -1309,18 +1309,6 @@
 
   void setShouldDoFullPaintInvalidationIncludingNonCompositingDescendants();
 
-  // Returns true if the object itself will not generate any effective painted
-  // output no matter what size the object is. For example, this function can
-  // return false for an object whose size is currently 0x0 but would have
-  // effective painted output if it was set a non-empty size.
-  // It's used to skip unforced paint invalidation (which is when
-  // shouldDoFullPaintInvalidation is false, but mayNeedPaintInvalidation or
-  // childShouldCheckForPaintInvalidation is true) to avoid unnecessary paint
-  // invalidations of empty areas covered by such objects.
-  virtual bool paintedOutputOfObjectHasNoEffectRegardlessOfSize() const {
-    return false;
-  }
-
   // Returns the rect that should have paint invalidated whenever this object
   // changes. The rect is in the view's coordinate space. This method deals with
   // outlines and overflow.
diff --git a/third_party/WebKit/Source/core/layout/LayoutTable.cpp b/third_party/WebKit/Source/core/layout/LayoutTable.cpp
index cb63f2c..5542392 100644
--- a/third_party/WebKit/Source/core/layout/LayoutTable.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutTable.cpp
@@ -99,7 +99,8 @@
 
   // If border was changed, invalidate collapsed borders cache.
   if (!needsLayout() && oldStyle && oldStyle->border() != style()->border())
-    invalidateCollapsedBorders();
+    invalidateCollapsedBorders(PaintInvalidationStyleChange);
+
   if (LayoutTableBoxComponent::doCellsHaveDirtyWidth(*this, *this, diff,
                                                      *oldStyle))
     markAllCellsWidthsDirtyAndOrNeedsLayout(MarkDirtyAndNeedsLayout);
@@ -732,7 +733,7 @@
     updateLayerTransformAfterLayout();
 
     // Layout was changed, so probably borders too.
-    invalidateCollapsedBorders();
+    invalidateCollapsedBorders(PaintInvalidationForcedByLayout);
 
     computeOverflow(clientLogicalBottom());
     updateAfterLayout();
@@ -751,13 +752,19 @@
   clearNeedsLayout();
 }
 
-void LayoutTable::invalidateCollapsedBorders() {
-  m_collapsedBorders.clear();
+void LayoutTable::invalidateCollapsedBorders(PaintInvalidationReason reason) {
+  DCHECK(reason == PaintInvalidationStyleChange ||
+         reason == PaintInvalidationForcedByLayout);
+
+  m_collapsedBordersInfo = nullptr;
   if (!collapseBorders())
     return;
 
   m_collapsedBordersValid = false;
-  setMayNeedPaintInvalidation();
+  if (reason == PaintInvalidationForcedByLayout)
+    setShouldDoFullPaintInvalidation(reason);
+  else
+    setMayNeedPaintInvalidation();
 }
 
 // Collect all the unique border values that we want to paint in a sorted list.
@@ -765,10 +772,15 @@
 // cache of its containing section, and invalidates itself if any border
 // changes. This method doesn't affect layout.
 void LayoutTable::recalcCollapsedBordersIfNeeded() {
-  if (m_collapsedBordersValid || !collapseBorders())
+  if (m_collapsedBordersValid)
     return;
   m_collapsedBordersValid = true;
-  m_collapsedBorders.clear();
+  m_collapsedBordersInfo = nullptr;
+  if (!collapseBorders())
+    return;
+
+  LayoutRect boundsOfChangedCells;
+  Vector<CollapsedBorderValue> values;
   for (LayoutObject* section = firstChild(); section;
        section = section->nextSibling()) {
     if (!section->isTableSection())
@@ -777,12 +789,23 @@
          row = row->nextRow()) {
       for (LayoutTableCell* cell = row->firstCell(); cell;
            cell = cell->nextCell()) {
-        ASSERT(cell->table() == this);
-        cell->collectBorderValues(m_collapsedBorders);
+        DCHECK(cell->table() == this);
+        bool cellChanged = cell->collectBorderValues(values);
+        if (cellChanged && !shouldDoFullPaintInvalidation()) {
+          LayoutRect cellRect = cell->localVisualRect();
+          cell->mapToVisualRectInAncestorSpace(this, cellRect);
+          boundsOfChangedCells.unite(cellRect);
+        }
       }
     }
   }
-  LayoutTableCell::sortBorderValues(m_collapsedBorders);
+  if (!values.isEmpty()) {
+    LayoutTableCell::sortBorderValues(values);
+    m_collapsedBordersInfo =
+        wrapUnique(new CollapsedBordersInfo(std::move(values)));
+  }
+
+  invalidatePaintRectangle(boundsOfChangedCells);
 }
 
 void LayoutTable::addOverflowFromChildren() {
@@ -1662,10 +1685,10 @@
 
 PaintInvalidationReason LayoutTable::invalidatePaintIfNeeded(
     const PaintInvalidationState& paintInvalidationState) {
-  if (collapseBorders() && !m_collapsedBorders.isEmpty())
+  if (hasCollapsedBorders()) {
     paintInvalidationState.paintingLayer()
         .setNeedsPaintPhaseDescendantBlockBackgrounds();
-
+  }
   return LayoutBlock::invalidatePaintIfNeeded(paintInvalidationState);
 }
 
diff --git a/third_party/WebKit/Source/core/layout/LayoutTable.h b/third_party/WebKit/Source/core/layout/LayoutTable.h
index 09ac5911..2148c78 100644
--- a/third_party/WebKit/Source/core/layout/LayoutTable.h
+++ b/third_party/WebKit/Source/core/layout/LayoutTable.h
@@ -29,7 +29,9 @@
 #include "core/CSSPropertyNames.h"
 #include "core/CoreExport.h"
 #include "core/layout/LayoutBlock.h"
+#include "core/paint/PaintResult.h"
 #include "core/style/CollapsedBorderValue.h"
+#include "platform/graphics/paint/CullRect.h"
 #include "wtf/Vector.h"
 #include <memory>
 
@@ -377,8 +379,7 @@
   LayoutTableCell* cellBefore(const LayoutTableCell*) const;
   LayoutTableCell* cellAfter(const LayoutTableCell*) const;
 
-  typedef Vector<CollapsedBorderValue> CollapsedBorderValues;
-  void invalidateCollapsedBorders();
+  void invalidateCollapsedBorders(PaintInvalidationReason);
 
   bool hasSections() const { return m_head || m_foot || m_firstBody; }
 
@@ -407,9 +408,23 @@
 
   void paintMask(const PaintInfo&, const LayoutPoint&) const final;
 
-  const CollapsedBorderValues& collapsedBorders() const {
-    ASSERT(m_collapsedBordersValid);
-    return m_collapsedBorders;
+  struct CollapsedBordersInfo {
+    explicit CollapsedBordersInfo(const Vector<CollapsedBorderValue>& values)
+        : values(std::move(values)) {}
+
+    PaintResult lastPaintResult = FullyPainted;
+    CullRect lastPaintRect;
+    const Vector<CollapsedBorderValue> values;
+  };
+
+  bool hasCollapsedBorders() const {
+    DCHECK(m_collapsedBordersValid);
+    DCHECK(!m_collapsedBordersInfo || collapseBorders());
+    return !!m_collapsedBordersInfo;
+  }
+  CollapsedBordersInfo& getCollapsedBordersInfo() const {
+    DCHECK(hasCollapsedBorders());
+    return *m_collapsedBordersInfo;
   }
 
   void subtractCaptionRect(LayoutRect&) const;
@@ -552,7 +567,7 @@
   // need to compare a cells border against all the adjoining cells, rows,
   // row groups, column, column groups and table. Thus we cache them in this
   // field.
-  CollapsedBorderValues m_collapsedBorders;
+  std::unique_ptr<CollapsedBordersInfo> m_collapsedBordersInfo;
   bool m_collapsedBordersValid : 1;
 
   mutable bool m_hasColElements : 1;
diff --git a/third_party/WebKit/Source/core/layout/LayoutTableCell.cpp b/third_party/WebKit/Source/core/layout/LayoutTableCell.cpp
index e8195e9..24d98a15 100644
--- a/third_party/WebKit/Source/core/layout/LayoutTableCell.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutTableCell.cpp
@@ -67,34 +67,6 @@
   updateColAndRowSpanFlags();
 }
 
-LayoutTableCell::CollapsedBorderValues::CollapsedBorderValues(
-    const LayoutTable& layoutTable,
-    const CollapsedBorderValue& startBorder,
-    const CollapsedBorderValue& endBorder,
-    const CollapsedBorderValue& beforeBorder,
-    const CollapsedBorderValue& afterBorder)
-    : m_layoutTable(layoutTable),
-      m_startBorder(startBorder),
-      m_endBorder(endBorder),
-      m_beforeBorder(beforeBorder),
-      m_afterBorder(afterBorder) {}
-
-void LayoutTableCell::CollapsedBorderValues::setCollapsedBorderValues(
-    const CollapsedBorderValues& other) {
-  m_startBorder = other.startBorder();
-  m_endBorder = other.endBorder();
-  m_beforeBorder = other.beforeBorder();
-  m_afterBorder = other.afterBorder();
-}
-
-String LayoutTableCell::CollapsedBorderValues::debugName() const {
-  return "CollapsedBorderValues";
-}
-
-LayoutRect LayoutTableCell::CollapsedBorderValues::visualRect() const {
-  return m_layoutTable.visualRect();
-}
-
 void LayoutTableCell::willBeRemovedFromTree() {
   LayoutBlockFlow::willBeRemovedFromTree();
 
@@ -500,7 +472,7 @@
     return;
   if (!table->selfNeedsLayout() && !table->normalChildNeedsLayout() &&
       oldStyle && oldStyle->border() != style()->border())
-    table->invalidateCollapsedBorders();
+    table->invalidateCollapsedBorders(PaintInvalidationStyleChange);
 
   if (LayoutTableBoxComponent::doCellsHaveDirtyWidth(*this, *table, diff,
                                                      *oldStyle)) {
@@ -1303,7 +1275,7 @@
   TableCellPainter(*this).paint(paintInfo, paintOffset);
 }
 
-static void addBorderStyle(LayoutTable::CollapsedBorderValues& borderValues,
+static void addBorderStyle(Vector<CollapsedBorderValue>& borderValues,
                            CollapsedBorderValue borderValue) {
   if (!borderValue.isVisible())
     return;
@@ -1315,56 +1287,34 @@
   borderValues.append(borderValue);
 }
 
-void LayoutTableCell::collectBorderValues(
-    LayoutTable::CollapsedBorderValues& borderValues) {
-  CollapsedBorderValues newValues(
-      *table(), computeCollapsedStartBorder(), computeCollapsedEndBorder(),
-      computeCollapsedBeforeBorder(), computeCollapsedAfterBorder());
+bool LayoutTableCell::collectBorderValues(
+    Vector<CollapsedBorderValue>& borderValues) {
+  CollapsedBorderValues newValues = {
+      computeCollapsedStartBorder(), computeCollapsedEndBorder(),
+      computeCollapsedBeforeBorder(), computeCollapsedAfterBorder()};
 
   bool changed = false;
-  if (!newValues.startBorder().isVisible() &&
-      !newValues.endBorder().isVisible() &&
-      !newValues.beforeBorder().isVisible() &&
-      !newValues.afterBorder().isVisible()) {
+  if (newValues.allBordersAreInvisible()) {
     changed = !!m_collapsedBorderValues;
     m_collapsedBorderValues = nullptr;
   } else if (!m_collapsedBorderValues) {
     changed = true;
-    m_collapsedBorderValues = wrapUnique(new CollapsedBorderValues(
-        *table(), newValues.startBorder(), newValues.endBorder(),
-        newValues.beforeBorder(), newValues.afterBorder()));
+    m_collapsedBorderValues = wrapUnique(new CollapsedBorderValues(newValues));
   } else {
-    // We check visuallyEquals so that the table cell is invalidated only if a
-    // changed collapsed border is visible in the first place.
-    changed = !m_collapsedBorderValues->startBorder().visuallyEquals(
-                  newValues.startBorder()) ||
-              !m_collapsedBorderValues->endBorder().visuallyEquals(
-                  newValues.endBorder()) ||
-              !m_collapsedBorderValues->beforeBorder().visuallyEquals(
-                  newValues.beforeBorder()) ||
-              !m_collapsedBorderValues->afterBorder().visuallyEquals(
-                  newValues.afterBorder());
+    changed = !m_collapsedBorderValues->bordersVisuallyEqual(newValues);
     if (changed)
-      m_collapsedBorderValues->setCollapsedBorderValues(newValues);
+      *m_collapsedBorderValues = newValues;
   }
 
-  // If collapsed borders changed, invalidate the cell's display item client on
-  // the table's backing.
-  // TODO(crbug.com/451090#c5): Need a way to invalidate/repaint the borders
-  // only.
-  if (changed)
-    ObjectPaintInvalidator(*table())
-        .slowSetPaintingLayerNeedsRepaintAndInvalidateDisplayItemClient(
-            *this, PaintInvalidationStyleChange);
-
-  addBorderStyle(borderValues, newValues.startBorder());
-  addBorderStyle(borderValues, newValues.endBorder());
-  addBorderStyle(borderValues, newValues.beforeBorder());
-  addBorderStyle(borderValues, newValues.afterBorder());
+  addBorderStyle(borderValues, newValues.startBorder);
+  addBorderStyle(borderValues, newValues.endBorder);
+  addBorderStyle(borderValues, newValues.beforeBorder);
+  addBorderStyle(borderValues, newValues.afterBorder);
+  return changed;
 }
 
 void LayoutTableCell::sortBorderValues(
-    LayoutTable::CollapsedBorderValues& borderValues) {
+    Vector<CollapsedBorderValue>& borderValues) {
   std::sort(borderValues.begin(), borderValues.end(), compareBorders);
 }
 
@@ -1459,8 +1409,6 @@
     return;
 
   ObjectPaintInvalidator invalidator(*this);
-  if (m_collapsedBorderValues)
-    invalidator.invalidateDisplayItemClient(*m_collapsedBorderValues, reason);
   if (m_rowBackgroundDisplayItemClient) {
     invalidator.invalidateDisplayItemClient(*m_rowBackgroundDisplayItemClient,
                                             reason);
diff --git a/third_party/WebKit/Source/core/layout/LayoutTableCell.h b/third_party/WebKit/Source/core/layout/LayoutTableCell.h
index 12f5210..e9a9716 100644
--- a/third_party/WebKit/Source/core/layout/LayoutTableCell.h
+++ b/third_party/WebKit/Source/core/layout/LayoutTableCell.h
@@ -185,8 +185,9 @@
   int borderBefore() const override;
   int borderAfter() const override;
 
-  void collectBorderValues(LayoutTable::CollapsedBorderValues&);
-  static void sortBorderValues(LayoutTable::CollapsedBorderValues&);
+  // Returns true if any collapsed borders related to this cell changed.
+  bool collectBorderValues(Vector<CollapsedBorderValue>&);
+  static void sortBorderValues(Vector<CollapsedBorderValue>&);
 
   void layout() override;
 
@@ -289,33 +290,22 @@
   bool backgroundIsKnownToBeOpaqueInRect(const LayoutRect&) const override;
   void invalidateDisplayItemClients(PaintInvalidationReason) const override;
 
-  // TODO(wkorman): Consider renaming to more clearly differentiate from
-  // CollapsedBorderValue.
-  class CollapsedBorderValues : public DisplayItemClient {
-   public:
-    CollapsedBorderValues(const LayoutTable&,
-                          const CollapsedBorderValue& startBorder,
-                          const CollapsedBorderValue& endBorder,
-                          const CollapsedBorderValue& beforeBorder,
-                          const CollapsedBorderValue& afterBorder);
+  struct CollapsedBorderValues {
+    CollapsedBorderValue startBorder;
+    CollapsedBorderValue endBorder;
+    CollapsedBorderValue beforeBorder;
+    CollapsedBorderValue afterBorder;
 
-    const CollapsedBorderValue& startBorder() const { return m_startBorder; }
-    const CollapsedBorderValue& endBorder() const { return m_endBorder; }
-    const CollapsedBorderValue& beforeBorder() const { return m_beforeBorder; }
-    const CollapsedBorderValue& afterBorder() const { return m_afterBorder; }
-
-    void setCollapsedBorderValues(const CollapsedBorderValues& other);
-
-    // DisplayItemClient methods.
-    String debugName() const;
-    LayoutRect visualRect() const;
-
-   private:
-    const LayoutTable& m_layoutTable;
-    CollapsedBorderValue m_startBorder;
-    CollapsedBorderValue m_endBorder;
-    CollapsedBorderValue m_beforeBorder;
-    CollapsedBorderValue m_afterBorder;
+    bool allBordersAreInvisible() const {
+      return !startBorder.isVisible() && !endBorder.isVisible() &&
+             !beforeBorder.isVisible() && !afterBorder.isVisible();
+    }
+    bool bordersVisuallyEqual(const CollapsedBorderValues& other) const {
+      return startBorder.visuallyEquals(other.startBorder) &&
+             endBorder.visuallyEquals(other.endBorder) &&
+             beforeBorder.visuallyEquals(other.beforeBorder) &&
+             afterBorder.visuallyEquals(other.afterBorder);
+    }
   };
 
   class RowBackgroundDisplayItemClient : public DisplayItemClient {
@@ -331,6 +321,7 @@
   };
 
   bool usesCompositedCellDisplayItemClients() const;
+
   const CollapsedBorderValues* collapsedBorderValues() const {
     return m_collapsedBorderValues.get();
   }
@@ -350,6 +341,8 @@
 
   void ensureIsReadyForPaintInvalidation() override;
 
+  LayoutRect localVisualRect() const override;
+
  protected:
   void styleDidChange(StyleDifference, const ComputedStyle* oldStyle) override;
   void computePreferredLogicalWidths() override;
@@ -373,7 +366,6 @@
   void paintMask(const PaintInfo&, const LayoutPoint&) const override;
 
   LayoutSize offsetFromContainer(const LayoutObject*) const override;
-  LayoutRect localVisualRect() const override;
 
   int borderHalfLeft(bool outer) const;
   int borderHalfRight(bool outer) const;
@@ -411,8 +403,8 @@
   // See also https://code.google.com/p/chromium/issues/detail?id=128227 for
   // some history.
   //
-  // Those functions are called when the cache (m_collapsedBorders) is
-  // invalidated on LayoutTable.
+  // Those functions are called before paint invalidation if the collapsed
+  // borders cache is invalidated on LayoutTable.
   CollapsedBorderValue computeCollapsedStartBorder(
       IncludeBorderColorOrNot = IncludeBorderColor) const;
   CollapsedBorderValue computeCollapsedEndBorder(
diff --git a/third_party/WebKit/Source/core/layout/LayoutTableCol.cpp b/third_party/WebKit/Source/core/layout/LayoutTableCol.cpp
index ba923455..b136ac8 100644
--- a/third_party/WebKit/Source/core/layout/LayoutTableCol.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutTableCol.cpp
@@ -60,7 +60,7 @@
   // the next one can't be, so don't even check its condition.
   if (!table->selfNeedsLayout() && !table->normalChildNeedsLayout() &&
       oldStyle->border() != style()->border()) {
-    table->invalidateCollapsedBorders();
+    table->invalidateCollapsedBorders(PaintInvalidationStyleChange);
   } else if ((oldStyle->logicalWidth() != style()->logicalWidth()) ||
              LayoutTableBoxComponent::doCellsHaveDirtyWidth(*this, *table, diff,
                                                             *oldStyle)) {
diff --git a/third_party/WebKit/Source/core/layout/LayoutTableRow.cpp b/third_party/WebKit/Source/core/layout/LayoutTableRow.cpp
index 03d700d..41d43d6 100644
--- a/third_party/WebKit/Source/core/layout/LayoutTableRow.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutTableRow.cpp
@@ -74,7 +74,7 @@
 
   if (!table->selfNeedsLayout() && !table->normalChildNeedsLayout() &&
       oldStyle->border() != style()->border())
-    table->invalidateCollapsedBorders();
+    table->invalidateCollapsedBorders(PaintInvalidationStyleChange);
 
   if (LayoutTableBoxComponent::doCellsHaveDirtyWidth(*this, *table, diff,
                                                      *oldStyle)) {
diff --git a/third_party/WebKit/Source/core/layout/LayoutTableSection.cpp b/third_party/WebKit/Source/core/layout/LayoutTableSection.cpp
index 572ded7..79f4c10 100644
--- a/third_party/WebKit/Source/core/layout/LayoutTableSection.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutTableSection.cpp
@@ -132,7 +132,7 @@
 
   if (!table->selfNeedsLayout() && !table->normalChildNeedsLayout() &&
       oldStyle->border() != style()->border())
-    table->invalidateCollapsedBorders();
+    table->invalidateCollapsedBorders(PaintInvalidationStyleChange);
 
   if (LayoutTableBoxComponent::doCellsHaveDirtyWidth(*this, *table, diff,
                                                      *oldStyle))
diff --git a/third_party/WebKit/Source/core/layout/LayoutTableSection.h b/third_party/WebKit/Source/core/layout/LayoutTableSection.h
index 5c0b9f70..6c82c3ad 100644
--- a/third_party/WebKit/Source/core/layout/LayoutTableSection.h
+++ b/third_party/WebKit/Source/core/layout/LayoutTableSection.h
@@ -57,6 +57,13 @@
   unsigned m_end;
 };
 
+inline bool operator==(const CellSpan& a, const CellSpan& b) {
+  return a.start() == b.start() && a.end() == b.end();
+}
+inline bool operator!=(const CellSpan& a, const CellSpan& b) {
+  return !(a == b);
+}
+
 class LayoutTableCell;
 class LayoutTableRow;
 
@@ -296,8 +303,13 @@
   // columnPos vectors.
   LayoutRect logicalRectForWritingModeAndDirection(const LayoutRect&) const;
 
+  CellSpan fullTableRowSpan() const { return CellSpan(0, m_grid.size()); }
+  CellSpan fullTableEffectiveColumnSpan() const {
+    return CellSpan(0, table()->numEffectiveColumns());
+  }
   CellSpan dirtiedRows(const LayoutRect& visualRect) const;
   CellSpan dirtiedEffectiveColumns(const LayoutRect& visualRect) const;
+
   const HashSet<LayoutTableCell*>& overflowingCells() const {
     return m_overflowingCells;
   }
@@ -401,11 +413,6 @@
 
   void computeOverflowFromCells(unsigned totalRows, unsigned nEffCols);
 
-  CellSpan fullTableRowSpan() const { return CellSpan(0, m_grid.size()); }
-  CellSpan fullTableEffectiveColumnSpan() const {
-    return CellSpan(0, table()->numEffectiveColumns());
-  }
-
   // These two functions take a rectangle as input that has been flipped by
   // logicalRectForWritingModeAndDirection.
   // The returned span of rows or columns is end-exclusive, and empty if
diff --git a/third_party/WebKit/Source/core/layout/LayoutView.cpp b/third_party/WebKit/Source/core/layout/LayoutView.cpp
index ef2a285..00192c6 100644
--- a/third_party/WebKit/Source/core/layout/LayoutView.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutView.cpp
@@ -1026,4 +1026,13 @@
   return rect;
 }
 
+bool LayoutView::paintedOutputOfObjectHasNoEffectRegardlessOfSize() const {
+  // Frame scroll corner is painted using LayoutView as the display item client.
+  if (!RuntimeEnabledFeatures::rootLayerScrollingEnabled() &&
+      (frameView()->horizontalScrollbar() || frameView()->verticalScrollbar()))
+    return false;
+
+  return LayoutBlockFlow::paintedOutputOfObjectHasNoEffectRegardlessOfSize();
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/layout/LayoutView.h b/third_party/WebKit/Source/core/layout/LayoutView.h
index c2adf61..d2036212 100644
--- a/third_party/WebKit/Source/core/layout/LayoutView.h
+++ b/third_party/WebKit/Source/core/layout/LayoutView.h
@@ -265,6 +265,8 @@
   int viewLogicalWidthForBoxSizing() const;
   int viewLogicalHeightForBoxSizing() const;
 
+  bool paintedOutputOfObjectHasNoEffectRegardlessOfSize() const override;
+
   UntracedMember<FrameView> m_frameView;
 
   // The current selection represented as 2 boundaries.
diff --git a/third_party/WebKit/Source/core/layout/line/RootInlineBox.cpp b/third_party/WebKit/Source/core/layout/line/RootInlineBox.cpp
index cf52c32..811f819 100644
--- a/third_party/WebKit/Source/core/layout/line/RootInlineBox.cpp
+++ b/third_party/WebKit/Source/core/layout/line/RootInlineBox.cpp
@@ -389,7 +389,6 @@
 
 LayoutUnit RootInlineBox::selectionTop() const {
   LayoutUnit selectionTop = m_lineTop;
-
   if (m_hasAnnotationsBefore)
     selectionTop -= !getLineLayoutItem().style()->isFlippedLinesWritingMode()
                         ? computeOverAnnotationAdjustment(m_lineTop)
@@ -399,25 +398,7 @@
       !prevRootBox())
     return selectionTop;
 
-  LayoutUnit prevBottom = prevRootBox()->selectionBottom();
-  if (prevBottom < selectionTop && block().containsFloats()) {
-    // This line has actually been moved further down, probably from a large
-    // line-height, but possibly because the line was forced to clear floats.
-    // If so, let's check the offsets, and only be willing to use the previous
-    // line's bottom if the offsets are greater on both sides.
-    LayoutUnit prevLeft =
-        block().logicalLeftOffsetForLine(prevBottom, DoNotIndentText);
-    LayoutUnit prevRight =
-        block().logicalRightOffsetForLine(prevBottom, DoNotIndentText);
-    LayoutUnit newLeft =
-        block().logicalLeftOffsetForLine(selectionTop, DoNotIndentText);
-    LayoutUnit newRight =
-        block().logicalRightOffsetForLine(selectionTop, DoNotIndentText);
-    if (prevLeft > newLeft || prevRight < newRight)
-      return selectionTop;
-  }
-
-  return prevBottom;
+  return std::min(selectionTop, prevRootBox()->selectionBottom());
 }
 
 LayoutUnit RootInlineBox::selectionBottom() const {
@@ -434,25 +415,7 @@
       !nextRootBox())
     return selectionBottom;
 
-  LayoutUnit nextTop = nextRootBox()->selectionTop();
-  if (nextTop > selectionBottom && block().containsFloats()) {
-    // The next line has actually been moved further over, probably from a large
-    // line-height, but possibly because the line was forced to clear floats.
-    // If so, let's check the offsets, and only be willing to use the next
-    // line's top if the offsets are greater on both sides.
-    LayoutUnit nextLeft =
-        block().logicalLeftOffsetForLine(nextTop, DoNotIndentText);
-    LayoutUnit nextRight =
-        block().logicalRightOffsetForLine(nextTop, DoNotIndentText);
-    LayoutUnit newLeft =
-        block().logicalLeftOffsetForLine(selectionBottom, DoNotIndentText);
-    LayoutUnit newRight =
-        block().logicalRightOffsetForLine(selectionBottom, DoNotIndentText);
-    if (nextLeft > newLeft || nextRight < newRight)
-      return selectionBottom;
-  }
-
-  return nextTop;
+  return std::max(selectionBottom, nextRootBox()->selectionTop());
 }
 
 LayoutUnit RootInlineBox::blockDirectionPointInLine() const {
diff --git a/third_party/WebKit/Source/core/layout/svg/LayoutSVGBlock.h b/third_party/WebKit/Source/core/layout/svg/LayoutSVGBlock.h
index cfc5c091..56077911 100644
--- a/third_party/WebKit/Source/core/layout/svg/LayoutSVGBlock.h
+++ b/third_party/WebKit/Source/core/layout/svg/LayoutSVGBlock.h
@@ -80,6 +80,11 @@
                    const HitTestLocation& locationInContainer,
                    const LayoutPoint& accumulatedOffset,
                    HitTestAction) override;
+
+  // The inherited version doesn't check for SVG effects.
+  bool paintedOutputOfObjectHasNoEffectRegardlessOfSize() const override {
+    return false;
+  }
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/loader/FrameLoader.cpp b/third_party/WebKit/Source/core/loader/FrameLoader.cpp
index a577dbe..213982d 100644
--- a/third_party/WebKit/Source/core/loader/FrameLoader.cpp
+++ b/third_party/WebKit/Source/core/loader/FrameLoader.cpp
@@ -97,6 +97,7 @@
 #include "platform/weborigin/SecurityPolicy.h"
 #include "platform/weborigin/Suborigin.h"
 #include "public/platform/WebCachePolicy.h"
+#include "public/platform/WebFeaturePolicy.h"
 #include "public/platform/WebURLRequest.h"
 #include "wtf/AutoReset.h"
 #include "wtf/text/CString.h"
@@ -584,15 +585,19 @@
           m_documentLoader->response().httpHeaderField(
               HTTPNames::Feature_Policy);
       Vector<String> messages;
+      const WebParsedFeaturePolicy& parsedHeader =
+          FeaturePolicy::parseFeaturePolicy(
+              featurePolicyHeader,
+              m_frame->securityContext()->getSecurityOrigin(), &messages);
       m_frame->securityContext()->setFeaturePolicyFromHeader(
-          featurePolicyHeader, parentFeaturePolicy, &messages);
+          parsedHeader, parentFeaturePolicy);
       for (auto& message : messages) {
         m_frame->document()->addConsoleMessage(ConsoleMessage::create(
             OtherMessageSource, ErrorMessageLevel,
             "Error with Feature-Policy header: " + message));
       }
-      if (!featurePolicyHeader.isEmpty())
-        client()->didSetFeaturePolicyHeader(featurePolicyHeader);
+      if (!parsedHeader.isEmpty())
+        client()->didSetFeaturePolicyHeader(parsedHeader);
     }
   }
 
diff --git a/third_party/WebKit/Source/core/loader/FrameLoaderClient.h b/third_party/WebKit/Source/core/loader/FrameLoaderClient.h
index 56ad147d..79694244 100644
--- a/third_party/WebKit/Source/core/loader/FrameLoaderClient.h
+++ b/third_party/WebKit/Source/core/loader/FrameLoaderClient.h
@@ -46,6 +46,7 @@
 #include "platform/network/ResourceLoadPriority.h"
 #include "platform/weborigin/Referrer.h"
 #include "public/platform/WebEffectiveConnectionType.h"
+#include "public/platform/WebFeaturePolicy.h"
 #include "public/platform/WebInsecureRequestPolicy.h"
 #include "public/platform/WebLoadingBehaviorFlag.h"
 #include "wtf/Forward.h"
@@ -275,7 +276,8 @@
 
   virtual void didChangeSandboxFlags(Frame* childFrame, SandboxFlags) {}
 
-  virtual void didSetFeaturePolicyHeader(const String& headerValue) {}
+  virtual void didSetFeaturePolicyHeader(
+      const WebParsedFeaturePolicy& parsedHeader) {}
 
   // Called when a new Content Security Policy is added to the frame's document.
   // This can be triggered by handling of HTTP headers, handling of <meta>
diff --git a/third_party/WebKit/Source/core/paint/PaintInvalidator.cpp b/third_party/WebKit/Source/core/paint/PaintInvalidator.cpp
index 9982e8a0..8756306e 100644
--- a/third_party/WebKit/Source/core/paint/PaintInvalidator.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintInvalidator.cpp
@@ -214,11 +214,8 @@
     context.paintingLayer->setNeedsPaintPhaseDescendantBlockBackgrounds();
   }
 
-  if (object.isTable()) {
-    const LayoutTable& table = toLayoutTable(object);
-    if (table.collapseBorders() && !table.collapsedBorders().isEmpty())
-      context.paintingLayer->setNeedsPaintPhaseDescendantBlockBackgrounds();
-  }
+  if (object.isTable() && toLayoutTable(object).hasCollapsedBorders())
+    context.paintingLayer->setNeedsPaintPhaseDescendantBlockBackgrounds();
 }
 
 namespace {
diff --git a/third_party/WebKit/Source/core/paint/TableCellPainter.cpp b/third_party/WebKit/Source/core/paint/TableCellPainter.cpp
index 856d8ecc..f3102f6 100644
--- a/third_party/WebKit/Source/core/paint/TableCellPainter.cpp
+++ b/third_party/WebKit/Source/core/paint/TableCellPainter.cpp
@@ -19,40 +19,40 @@
     const ComputedStyle& styleForCellFlow,
     const LayoutTableCell::CollapsedBorderValues& values) {
   if (styleForCellFlow.isHorizontalWritingMode()) {
-    return styleForCellFlow.isLeftToRightDirection() ? values.startBorder()
-                                                     : values.endBorder();
+    return styleForCellFlow.isLeftToRightDirection() ? values.startBorder
+                                                     : values.endBorder;
   }
-  return styleForCellFlow.isFlippedBlocksWritingMode() ? values.afterBorder()
-                                                       : values.beforeBorder();
+  return styleForCellFlow.isFlippedBlocksWritingMode() ? values.afterBorder
+                                                       : values.beforeBorder;
 }
 
 static const CollapsedBorderValue& collapsedRightBorder(
     const ComputedStyle& styleForCellFlow,
     const LayoutTableCell::CollapsedBorderValues& values) {
   if (styleForCellFlow.isHorizontalWritingMode()) {
-    return styleForCellFlow.isLeftToRightDirection() ? values.endBorder()
-                                                     : values.startBorder();
+    return styleForCellFlow.isLeftToRightDirection() ? values.endBorder
+                                                     : values.startBorder;
   }
-  return styleForCellFlow.isFlippedBlocksWritingMode() ? values.beforeBorder()
-                                                       : values.afterBorder();
+  return styleForCellFlow.isFlippedBlocksWritingMode() ? values.beforeBorder
+                                                       : values.afterBorder;
 }
 
 static const CollapsedBorderValue& collapsedTopBorder(
     const ComputedStyle& styleForCellFlow,
     const LayoutTableCell::CollapsedBorderValues& values) {
   if (styleForCellFlow.isHorizontalWritingMode())
-    return values.beforeBorder();
-  return styleForCellFlow.isLeftToRightDirection() ? values.startBorder()
-                                                   : values.endBorder();
+    return values.beforeBorder;
+  return styleForCellFlow.isLeftToRightDirection() ? values.startBorder
+                                                   : values.endBorder;
 }
 
 static const CollapsedBorderValue& collapsedBottomBorder(
     const ComputedStyle& styleForCellFlow,
     const LayoutTableCell::CollapsedBorderValues& values) {
   if (styleForCellFlow.isHorizontalWritingMode())
-    return values.afterBorder();
-  return styleForCellFlow.isLeftToRightDirection() ? values.endBorder()
-                                                   : values.startBorder();
+    return values.afterBorder;
+  return styleForCellFlow.isLeftToRightDirection() ? values.endBorder
+                                                   : values.startBorder;
 }
 
 void TableCellPainter::paint(const PaintInfo& paintInfo,
@@ -68,15 +68,6 @@
   return style;
 }
 
-const DisplayItemClient& TableCellPainter::displayItemClientForBorders() const {
-  // TODO(wkorman): We may need to handle PaintInvalidationDelayedFull.
-  // http://crbug.com/657186
-  return m_layoutTableCell.usesCompositedCellDisplayItemClients()
-             ? static_cast<const DisplayItemClient&>(
-                   *m_layoutTableCell.collapsedBorderValues())
-             : m_layoutTableCell;
-}
-
 void TableCellPainter::paintCollapsedBorders(
     const PaintInfo& paintInfo,
     const LayoutPoint& paintOffset,
@@ -104,18 +95,6 @@
   const CollapsedBorderValue& bottomBorderValue =
       collapsedBottomBorder(styleForCellFlow, *values);
 
-  int displayItemType = DisplayItem::kTableCollapsedBorderBase;
-  if (topBorderValue.shouldPaint(currentBorderValue))
-    displayItemType |= DisplayItem::TableCollapsedBorderTop;
-  if (bottomBorderValue.shouldPaint(currentBorderValue))
-    displayItemType |= DisplayItem::TableCollapsedBorderBottom;
-  if (leftBorderValue.shouldPaint(currentBorderValue))
-    displayItemType |= DisplayItem::TableCollapsedBorderLeft;
-  if (rightBorderValue.shouldPaint(currentBorderValue))
-    displayItemType |= DisplayItem::TableCollapsedBorderRight;
-  if (displayItemType == DisplayItem::kTableCollapsedBorderBase)
-    return;
-
   int topWidth = topBorderValue.width();
   int bottomWidth = bottomBorderValue.width();
   int leftWidth = leftBorderValue.width();
@@ -131,41 +110,32 @@
       paintRect.height() + topWidth / 2 + (bottomWidth + 1) / 2);
 
   GraphicsContext& graphicsContext = paintInfo.context;
-  const DisplayItemClient& client = displayItemClientForBorders();
-  if (DrawingRecorder::useCachedDrawingIfPossible(
-          graphicsContext, client,
-          static_cast<DisplayItem::Type>(displayItemType)))
-    return;
-
-  DrawingRecorder recorder(graphicsContext, client,
-                           static_cast<DisplayItem::Type>(displayItemType),
-                           borderRect);
   Color cellColor = m_layoutTableCell.resolveColor(CSSPropertyColor);
 
   // We never paint diagonals at the joins.  We simply let the border with the
   // highest precedence paint on top of borders with lower precedence.
-  if (displayItemType & DisplayItem::TableCollapsedBorderTop) {
+  if (topBorderValue.shouldPaint(currentBorderValue)) {
     ObjectPainter::drawLineForBoxSide(
         graphicsContext, borderRect.x(), borderRect.y(), borderRect.maxX(),
         borderRect.y() + topWidth, BSTop,
         topBorderValue.color().resolve(cellColor),
         collapsedBorderStyle(topBorderValue.style()), 0, 0, true);
   }
-  if (displayItemType & DisplayItem::TableCollapsedBorderBottom) {
+  if (bottomBorderValue.shouldPaint(currentBorderValue)) {
     ObjectPainter::drawLineForBoxSide(
         graphicsContext, borderRect.x(), borderRect.maxY() - bottomWidth,
         borderRect.maxX(), borderRect.maxY(), BSBottom,
         bottomBorderValue.color().resolve(cellColor),
         collapsedBorderStyle(bottomBorderValue.style()), 0, 0, true);
   }
-  if (displayItemType & DisplayItem::TableCollapsedBorderLeft) {
+  if (leftBorderValue.shouldPaint(currentBorderValue)) {
     ObjectPainter::drawLineForBoxSide(
         graphicsContext, borderRect.x(), borderRect.y(),
         borderRect.x() + leftWidth, borderRect.maxY(), BSLeft,
         leftBorderValue.color().resolve(cellColor),
         collapsedBorderStyle(leftBorderValue.style()), 0, 0, true);
   }
-  if (displayItemType & DisplayItem::TableCollapsedBorderRight) {
+  if (rightBorderValue.shouldPaint(currentBorderValue)) {
     ObjectPainter::drawLineForBoxSide(
         graphicsContext, borderRect.maxX() - rightWidth, borderRect.y(),
         borderRect.maxX(), borderRect.maxY(), BSRight,
diff --git a/third_party/WebKit/Source/core/paint/TableCellPainter.h b/third_party/WebKit/Source/core/paint/TableCellPainter.h
index a87c4b59..749915c 100644
--- a/third_party/WebKit/Source/core/paint/TableCellPainter.h
+++ b/third_party/WebKit/Source/core/paint/TableCellPainter.h
@@ -39,7 +39,6 @@
   void paintMask(const PaintInfo&, const LayoutPoint& paintOffset);
 
  private:
-  const DisplayItemClient& displayItemClientForBorders() const;
   LayoutRect paintRectNotIncludingVisualOverflow(
       const LayoutPoint& paintOffset);
   void paintBackground(const PaintInfo&,
diff --git a/third_party/WebKit/Source/core/paint/TableCellPainterTest.cpp b/third_party/WebKit/Source/core/paint/TableCellPainterTest.cpp
index 1cd5830..adce65d 100644
--- a/third_party/WebKit/Source/core/paint/TableCellPainterTest.cpp
+++ b/third_party/WebKit/Source/core/paint/TableCellPainterTest.cpp
@@ -170,11 +170,12 @@
       "100px solid yellow; background: green; }"
       "  table { margin: 100px; border-collapse: collapse; }"
       "</style>"
-      "<table>"
+      "<table id='table'>"
       "  <tr><td id='cell'></td></tr>"
       "</table>");
 
   LayoutView& layoutView = *document().layoutView();
+  LayoutObject& table = *getLayoutObjectByElementId("table");
   LayoutObject& cell = *getLayoutObjectByElementId("cell");
 
   rootPaintController().invalidateAll();
@@ -188,7 +189,7 @@
       rootPaintController().getDisplayItemList(), 4,
       TestDisplayItem(layoutView, DisplayItem::kDocumentBackground),
       TestDisplayItem(cell, DisplayItem::kBoxDecorationBackground),
-      TestDisplayItem(cell, DisplayItem::kTableCollapsedBorderLast),
+      TestDisplayItem(table, DisplayItem::kTableCollapsedBorders),
       TestDisplayItem(cell, DisplayItem::paintPhaseToDrawingType(
                                 PaintPhaseSelfOutlineOnly)));
 }
diff --git a/third_party/WebKit/Source/core/paint/TablePainter.cpp b/third_party/WebKit/Source/core/paint/TablePainter.cpp
index 16f910484..65a069c 100644
--- a/third_party/WebKit/Source/core/paint/TablePainter.cpp
+++ b/third_party/WebKit/Source/core/paint/TablePainter.cpp
@@ -44,31 +44,61 @@
       }
     }
 
-    if (m_layoutTable.collapseBorders() &&
-        shouldPaintDescendantBlockBackgrounds(paintPhase) &&
-        m_layoutTable.style()->visibility() == EVisibility::Visible) {
-      // Using our cached sorted styles, we then do individual passes,
-      // painting each style of border from lowest precedence to highest
-      // precedence.
-      LayoutTable::CollapsedBorderValues collapsedBorders =
-          m_layoutTable.collapsedBorders();
-      size_t count = collapsedBorders.size();
-      for (size_t i = 0; i < count; ++i) {
-        for (LayoutTableSection* section = m_layoutTable.bottomSection();
-             section; section = m_layoutTable.sectionAbove(section)) {
-          LayoutPoint childPoint =
-              m_layoutTable.flipForWritingModeForChild(section, paintOffset);
-          TableSectionPainter(*section).paintCollapsedBorders(
-              paintInfoForDescendants, childPoint, collapsedBorders[i]);
-        }
-      }
-    }
+    if (shouldPaintDescendantBlockBackgrounds(paintPhase))
+      paintCollapsedBorders(paintInfoForDescendants, paintOffset);
   }
 
   if (shouldPaintSelfOutline(paintPhase))
     ObjectPainter(m_layoutTable).paintOutline(paintInfo, paintOffset);
 }
 
+void TablePainter::paintCollapsedBorders(const PaintInfo& paintInfo,
+                                         const LayoutPoint& paintOffset) {
+  if (!m_layoutTable.hasCollapsedBorders() ||
+      m_layoutTable.style()->visibility() != EVisibility::Visible)
+    return;
+
+  LayoutTable::CollapsedBordersInfo& collapsedBorders =
+      m_layoutTable.getCollapsedBordersInfo();
+
+  // Normally we don't clip individual display items by paint dirty rect
+  // (aka interest rect), to keep display items independent with paint dirty
+  // rect so we can just depend on paint invalidation status to repaint them.
+  // However, the collapsed border display item may be too big to contain all
+  // collapsed borders in a huge table, so we clip it to paint dirty rect.
+  // We need to invalidate the display item if the previous paint is clipped
+  // and the paint dirty rect changed.
+  if (collapsedBorders.lastPaintResult != FullyPainted &&
+      collapsedBorders.lastPaintRect != paintInfo.cullRect())
+    m_layoutTable.setDisplayItemsUncached();
+
+  if (DrawingRecorder::useCachedDrawingIfPossible(
+          paintInfo.context, m_layoutTable,
+          DisplayItem::kTableCollapsedBorders))
+    return;
+
+  DrawingRecorder recorder(
+      paintInfo.context, m_layoutTable, DisplayItem::kTableCollapsedBorders,
+      FloatRect(LayoutRect(paintOffset, m_layoutTable.size())));
+
+  // Using our cached sorted styles, we then do individual passes, painting
+  // each style of border from lowest precedence to highest precedence.
+  PaintResult paintResult = FullyPainted;
+  for (const auto& borderValue : collapsedBorders.values) {
+    for (LayoutTableSection* section = m_layoutTable.bottomSection(); section;
+         section = m_layoutTable.sectionAbove(section)) {
+      LayoutPoint childPoint =
+          m_layoutTable.flipForWritingModeForChild(section, paintOffset);
+      if (TableSectionPainter(*section).paintCollapsedBorders(
+              paintInfo, childPoint, borderValue) ==
+          MayBeClippedByPaintDirtyRect)
+        paintResult = MayBeClippedByPaintDirtyRect;
+    }
+  }
+  collapsedBorders.lastPaintResult = paintResult;
+  collapsedBorders.lastPaintRect = paintInfo.cullRect();
+}
+
 void TablePainter::paintBoxDecorationBackground(
     const PaintInfo& paintInfo,
     const LayoutPoint& paintOffset) {
diff --git a/third_party/WebKit/Source/core/paint/TablePainter.h b/third_party/WebKit/Source/core/paint/TablePainter.h
index d3de8e0e..ac57899 100644
--- a/third_party/WebKit/Source/core/paint/TablePainter.h
+++ b/third_party/WebKit/Source/core/paint/TablePainter.h
@@ -24,6 +24,8 @@
   void paintMask(const PaintInfo&, const LayoutPoint&);
 
  private:
+  void paintCollapsedBorders(const PaintInfo&, const LayoutPoint&);
+
   const LayoutTable& m_layoutTable;
 };
 
diff --git a/third_party/WebKit/Source/core/paint/TablePainterTest.cpp b/third_party/WebKit/Source/core/paint/TablePainterTest.cpp
new file mode 100644
index 0000000..7ebcdff1
--- /dev/null
+++ b/third_party/WebKit/Source/core/paint/TablePainterTest.cpp
@@ -0,0 +1,75 @@
+// Copyright 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 "core/paint/PaintControllerPaintTest.h"
+#include "platform/graphics/paint/DrawingDisplayItem.h"
+
+namespace blink {
+
+using TablePainterTest = PaintControllerPaintTest;
+
+TEST_F(TablePainterTest, CollapsedBorderInterestRectChange) {
+  setBodyInnerHTML(
+      "<style>"
+      "  table { border-collapse: collapse; position: absolute; }"
+      "  td { width: 100px; height: 100px; border: 2px solid blue; }"
+      "</style>"
+      "<table id='table'>"
+      "  <tr><td></td><td></td><td></td><td></td></tr>"
+      "  <tr><td></td><td></td><td></td><td></td></tr>"
+      "  <tr><td></td><td></td><td></td><td></td></tr>"
+      "  <tr><td></td><td></td><td></td><td></td></tr>"
+      "</table>");
+
+  PaintLayer& htmlLayer =
+      *toLayoutBoxModelObject(document().documentElement()->layoutObject())
+           ->layer();
+  LayoutObject& table = *getLayoutObjectByElementId("table");
+
+  rootPaintController().invalidateAll();
+  document().view()->updateAllLifecyclePhasesExceptPaint();
+  IntRect interestRect(300, 300, 300, 300);
+  paint(&interestRect);
+
+  EXPECT_DISPLAY_LIST(
+      rootPaintController().getDisplayItemList(), 4,
+      TestDisplayItem(layoutView(), documentBackgroundType),
+      TestDisplayItem(htmlLayer, DisplayItem::kSubsequence),
+      TestDisplayItem(table, DisplayItem::kTableCollapsedBorders),
+      TestDisplayItem(htmlLayer, DisplayItem::kEndSubsequence));
+  // Painted collapsed borders of the central 4 cells, each 4 operations.
+  EXPECT_EQ(16, static_cast<const DrawingDisplayItem&>(
+                    rootPaintController().getDisplayItemList()[2])
+                    .picture()
+                    ->approximateOpCount());
+
+  // Should repaint collapsed borders if the previous paint didn't fully paint
+  // and interest rect changes.
+  document().view()->updateAllLifecyclePhasesExceptPaint();
+  interestRect = IntRect(0, 0, 1000, 1000);
+  EXPECT_TRUE(paintWithoutCommit(&interestRect));
+  EXPECT_EQ(1, numCachedNewItems());
+  commit();
+  EXPECT_DISPLAY_LIST(
+      rootPaintController().getDisplayItemList(), 4,
+      TestDisplayItem(layoutView(), documentBackgroundType),
+      TestDisplayItem(htmlLayer, DisplayItem::kSubsequence),
+      TestDisplayItem(table, DisplayItem::kTableCollapsedBorders),
+      TestDisplayItem(htmlLayer, DisplayItem::kEndSubsequence));
+  // Painted collapsed borders of all 16 cells, each 4 operations.
+  EXPECT_EQ(64, static_cast<const DrawingDisplayItem&>(
+                    rootPaintController().getDisplayItemList()[2])
+                    .picture()
+                    ->approximateOpCount());
+
+  // Should not repaint collapsed borders if the previous paint fully painted
+  // and interest rect changes.
+  document().view()->updateAllLifecyclePhasesExceptPaint();
+  interestRect = IntRect(0, 0, 400, 400);
+  EXPECT_TRUE(paintWithoutCommit(&interestRect));
+  EXPECT_EQ(4, numCachedNewItems());
+  commit();
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/core/paint/TableSectionPainter.cpp b/third_party/WebKit/Source/core/paint/TableSectionPainter.cpp
index 685ba682..44e1d61 100644
--- a/third_party/WebKit/Source/core/paint/TableSectionPainter.cpp
+++ b/third_party/WebKit/Source/core/paint/TableSectionPainter.cpp
@@ -149,24 +149,27 @@
   return elem1->absoluteColumnIndex() < elem2->absoluteColumnIndex();
 }
 
-void TableSectionPainter::paintCollapsedBorders(
+PaintResult TableSectionPainter::paintCollapsedBorders(
     const PaintInfo& paintInfo,
     const LayoutPoint& paintOffset,
     const CollapsedBorderValue& currentBorderValue) {
-  paintCollapsedSectionBorders(paintInfo, paintOffset, currentBorderValue);
+  PaintResult result =
+      paintCollapsedSectionBorders(paintInfo, paintOffset, currentBorderValue);
   LayoutTable* table = m_layoutTableSection.table();
-  if (table->header() == m_layoutTableSection)
+  if (table->header() == m_layoutTableSection) {
     paintRepeatingHeaderGroup(paintInfo, paintOffset, currentBorderValue,
                               PaintCollapsedBorders);
+  }
+  return result;
 }
 
-void TableSectionPainter::paintCollapsedSectionBorders(
+PaintResult TableSectionPainter::paintCollapsedSectionBorders(
     const PaintInfo& paintInfo,
     const LayoutPoint& paintOffset,
     const CollapsedBorderValue& currentBorderValue) {
   if (!m_layoutTableSection.numRows() ||
       !m_layoutTableSection.table()->effectiveColumns().size())
-    return;
+    return FullyPainted;
 
   LayoutPoint adjustedPaintOffset =
       paintOffset + m_layoutTableSection.location();
@@ -185,7 +188,7 @@
       m_layoutTableSection.dirtiedEffectiveColumns(tableAlignedRect);
 
   if (dirtiedColumns.start() >= dirtiedColumns.end())
-    return;
+    return MayBeClippedByPaintDirtyRect;
 
   // Collapsed borders are painted from the bottom right to the top left so that
   // precedence due to cell position is respected.
@@ -207,6 +210,11 @@
                                                     currentBorderValue);
     }
   }
+
+  if (dirtiedRows == m_layoutTableSection.fullTableRowSpan() &&
+      dirtiedColumns == m_layoutTableSection.fullTableEffectiveColumnSpan())
+    return FullyPainted;
+  return MayBeClippedByPaintDirtyRect;
 }
 
 void TableSectionPainter::paintObject(const PaintInfo& paintInfo,
diff --git a/third_party/WebKit/Source/core/paint/TableSectionPainter.h b/third_party/WebKit/Source/core/paint/TableSectionPainter.h
index d6ff986f..9e33c022 100644
--- a/third_party/WebKit/Source/core/paint/TableSectionPainter.h
+++ b/third_party/WebKit/Source/core/paint/TableSectionPainter.h
@@ -6,6 +6,7 @@
 #define TableSectionPainter_h
 
 #include "core/paint/PaintPhase.h"
+#include "core/paint/PaintResult.h"
 #include "core/style/ShadowData.h"
 #include "wtf/Allocator.h"
 
@@ -26,9 +27,10 @@
       : m_layoutTableSection(layoutTableSection) {}
 
   void paint(const PaintInfo&, const LayoutPoint&);
-  void paintCollapsedBorders(const PaintInfo&,
-                             const LayoutPoint&,
-                             const CollapsedBorderValue&);
+
+  PaintResult paintCollapsedBorders(const PaintInfo&,
+                                    const LayoutPoint&,
+                                    const CollapsedBorderValue&);
 
  private:
   void paintObject(const PaintInfo&, const LayoutPoint&);
@@ -56,9 +58,9 @@
                                  const CollapsedBorderValue& currentBorderValue,
                                  ItemToPaint);
   void paintSection(const PaintInfo&, const LayoutPoint&);
-  void paintCollapsedSectionBorders(const PaintInfo&,
-                                    const LayoutPoint&,
-                                    const CollapsedBorderValue&);
+  PaintResult paintCollapsedSectionBorders(const PaintInfo&,
+                                           const LayoutPoint&,
+                                           const CollapsedBorderValue&);
 
   const LayoutTableSection& m_layoutTableSection;
 };
diff --git a/third_party/WebKit/Source/devtools/front_end/elements/StylesSidebarPane.js b/third_party/WebKit/Source/devtools/front_end/elements/StylesSidebarPane.js
index badb3a6..77571ef 100644
--- a/third_party/WebKit/Source/devtools/front_end/elements/StylesSidebarPane.js
+++ b/third_party/WebKit/Source/devtools/front_end/elements/StylesSidebarPane.js
@@ -1115,6 +1115,9 @@
     for (var matchingIndex of matchingSelectorIndexes)
       matchingSelectors[matchingIndex] = true;
 
+    if (this._parentPane._isEditingStyle)
+      return;
+
     var fragment = this._hoverableSelectorsMode ? this._renderHoverableSelectors(selectorTexts, matchingSelectors) :
                                                   this._renderSimplifiedSelectors(selectorTexts, matchingSelectors);
     this._selectorElement.removeChildren();
diff --git a/third_party/WebKit/Source/devtools/front_end/sources/sourcesView.css b/third_party/WebKit/Source/devtools/front_end/sources/sourcesView.css
index f347560..62bef36 100644
--- a/third_party/WebKit/Source/devtools/front_end/sources/sourcesView.css
+++ b/third_party/WebKit/Source/devtools/front_end/sources/sourcesView.css
@@ -39,6 +39,7 @@
     background-color: #f3f3f3;
     border-top: 1px solid #dadada;
     overflow: hidden;
+    z-index: 0;
 }
 
 .sources-toolbar .toolbar {
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/searchableView.css b/third_party/WebKit/Source/devtools/front_end/ui/searchableView.css
index 726b9aa..cf02aaf 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui/searchableView.css
+++ b/third_party/WebKit/Source/devtools/front_end/ui/searchableView.css
@@ -10,6 +10,7 @@
     border-top: 1px solid #ccc;
     display: flex;
     overflow: hidden;
+    z-index: 0;
 }
 
 .search-bar.replaceable {
diff --git a/third_party/WebKit/Source/devtools/scripts/build/rjsmin.py b/third_party/WebKit/Source/devtools/scripts/build/rjsmin.py
index 2d15e03..54e20ec1 100755
--- a/third_party/WebKit/Source/devtools/scripts/build/rjsmin.py
+++ b/third_party/WebKit/Source/devtools/scripts/build/rjsmin.py
@@ -1,19 +1,5 @@
 #!/usr/bin/env python
-#
-# Copyright 2011 - 2013
-# Andr\xe9 Malo or his licensors, as applicable
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
+# -*- coding: ascii -*-
 r"""
 =====================
  Javascript Minifier
@@ -21,7 +7,26 @@
 
 rJSmin is a javascript minifier written in python.
 
-The minifier is based on the semantics of `jsmin.c by Douglas Crockford`_\.
+The minifier is based on the semantics of `jsmin.c by Douglas Crockford`_\\.
+
+:Copyright:
+
+ Copyright 2011 - 2015
+ Andr\xe9 Malo or his licensors, as applicable
+
+:License:
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
 
 The module is a re-implementation aiming for speed, so it can be used at
 runtime (rather than during a preprocessing step). Usually it produces the
@@ -30,22 +35,24 @@
 - there is no error detection: unterminated string, regex and comment
   literals are treated as regular javascript code and minified as such.
 - Control characters inside string and regex literals are left untouched; they
-  are not converted to spaces (nor to \n)
+  are not converted to spaces (nor to \\n)
 - Newline characters are not allowed inside string and regex literals, except
   for line continuations in string literals (ECMA-5).
 - "return /regex/" is recognized correctly.
+- Line terminators after regex literals are handled more sensibly
 - "+ +" and "- -" sequences are not collapsed to '++' or '--'
 - Newlines before ! operators are removed more sensibly
+- Comments starting with an exclamation mark (``!``) can be kept optionally
 - rJSmin does not handle streams, but only complete strings. (However, the
   module provides a "streamy" interface).
 
-Since most parts of the logic are handled by the regex engine it's way
-faster than the original python port of ``jsmin.c`` by Baruch Even. The speed
-factor varies between about 6 and 55 depending on input and python version
-(it gets faster the more compressed the input already is). Compared to the
+Since most parts of the logic are handled by the regex engine it's way faster
+than the original python port of ``jsmin.c`` by Baruch Even. The speed factor
+varies between about 6 and 55 depending on input and python version (it gets
+faster the more compressed the input already is). Compared to the
 speed-refactored python port by Dave St.Germain the performance gain is less
-dramatic but still between 1.2 and 7. See the docs/BENCHMARKS file for
-details.
+dramatic but still between 3 and 50 (for huge inputs). See the docs/BENCHMARKS
+file for details.
 
 rjsmin.c is a reimplementation of rjsmin.py in C and speeds it up even more.
 
@@ -54,11 +61,13 @@
 .. _jsmin.c by Douglas Crockford:
    http://www.crockford.com/javascript/jsmin.c
 """
-__author__ = "Andr\xe9 Malo"
-__author__ = getattr(__author__, 'decode', lambda x: __author__)('latin-1')
+if __doc__:
+    # pylint: disable = redefined-builtin
+    __doc__ = __doc__.encode('ascii').decode('unicode_escape')
+__author__ = r"Andr\xe9 Malo".encode('ascii').decode('unicode_escape')
 __docformat__ = "restructuredtext en"
 __license__ = "Apache License, Version 2.0"
-__version__ = '1.0.7'
+__version__ = '1.0.12'
 __all__ = ['jsmin']
 
 import re as _re
@@ -79,7 +88,9 @@
     :Return: Minifier
     :Rtype: ``callable``
     """
-    # pylint: disable = R0912, R0914, W0612
+    # pylint: disable = unused-variable
+    # pylint: disable = too-many-locals
+
     if not python_only:
         try:
             import _rjsmin
@@ -90,12 +101,15 @@
     try:
         xrange
     except NameError:
-        xrange = range  # pylint: disable = W0622
+        xrange = range  # pylint: disable = redefined-builtin
 
     space_chars = r'[\000-\011\013\014\016-\040]'
 
     line_comment = r'(?://[^\r\n]*)'
     space_comment = r'(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)'
+    space_comment_nobang = r'(?:/\*(?!!)[^*]*\*+(?:[^/*][^*]*\*+)*/)'
+    bang_comment = r'(?:/\*![^*]*\*+(?:[^/*][^*]*\*+)*/)'
+
     string1 = \
         r'(?:\047[^\047\\\r\n]*(?:\\(?:[^\r\n]|\r?\n|\r)[^\047\\\r\n]*)*\047)'
     string2 = r'(?:"[^"\\\r\n]*(?:\\(?:[^\r\n]|\r?\n|\r)[^"\\\r\n]*)*")'
@@ -105,7 +119,8 @@
     charclass = r'(?:\[[^\\\]\r\n]*(?:\\[^\r\n][^\\\]\r\n]*)*\])'
     nospecial = r'[^/\\\[\r\n]'
     regex = r'(?:/(?![\r\n/*])%s*(?:(?:\\[^\r\n]|%s)%s*)*/)' % (
-        nospecial, charclass, nospecial)
+        nospecial, charclass, nospecial
+    )
     space = r'(?:%s|%s)' % (space_chars, space_comment)
     newline = r'(?:%s?[\r\n])' % line_comment
 
@@ -135,24 +150,33 @@
             return ''.join(['%s%s%s' % (
                 chr(first),
                 last > first + 1 and '-' or '',
-                last != first and chr(last) or '') for first, last in result])
+                last != first and chr(last) or ''
+            ) for first, last in result])  # noqa
 
-        return _re.sub(r'([\000-\040\047])',  # for better portability
-            lambda m: '\\%03o' % ord(m.group(1)), (sequentize(result)
+        return _re.sub(
+            r'([\000-\040\047])',  # \047 for better portability
+            lambda m: '\\%03o' % ord(m.group(1)), (
+                sequentize(result)
                 .replace('\\', '\\\\')
                 .replace('[', '\\[')
-                .replace(']', '\\]')))
+                .replace(']', '\\]')
+            )
+        )
 
     def id_literal_(what):
         """ Make id_literal like char class """
         match = _re.compile(what).match
-        result = ''.join([chr(c) for c in xrange(127) if not match(chr(c))])
+        result = ''.join([
+            chr(c) for c in xrange(127) if not match(chr(c))
+        ])
         return '[^%s]' % fix_charclass(result)
 
     def not_id_literal_(keep):
         """ Make negated id_literal like char class """
         match = _re.compile(id_literal_(keep)).match
-        result = ''.join([chr(c) for c in xrange(127) if not match(chr(c))])
+        result = ''.join([
+            chr(c) for c in xrange(127) if not match(chr(c))
+        ])
         return r'[%s]' % fix_charclass(result)
 
     not_id_literal = not_id_literal_(r'[a-zA-Z0-9_$]')
@@ -162,48 +186,130 @@
     id_literal = id_literal_(r'[a-zA-Z0-9_$]')
     id_literal_open = id_literal_(r'[a-zA-Z0-9_${\[(!+-]')
     id_literal_close = id_literal_(r'[a-zA-Z0-9_$}\])"\047+-]')
+    post_regex_off = id_literal_(r'[^\000-\040}\])?:|,;.&=+-]')
 
     dull = r'[^\047"`/\000-\040]'
 
-    space_sub = _re.compile((
-        r'(%(dull)s+)'
-        r'|(%(strings)s%(dull)s*)'
+    space_sub_simple = _re.compile((
+        # noqa pylint: disable = bad-continuation
+
+        r'(%(dull)s+)'                                         # 0
+        r'|(%(strings)s%(dull)s*)'                             # 1
         r'|(?<=%(preregex1)s)'
             r'%(space)s*(?:%(newline)s%(space)s*)*'
-            r'(%(regex)s%(dull)s*)'
+            r'(%(regex)s)'                                     # 2
+            r'(%(space)s*(?:%(newline)s%(space)s*)+'           # 3
+                r'(?=%(post_regex_off)s))?'
         r'|(?<=%(preregex2)s)'
-            r'%(space)s*(?:%(newline)s%(space)s)*'
-            r'(%(regex)s%(dull)s*)'
+            r'%(space)s*(?:(%(newline)s)%(space)s*)*'          # 4
+            r'(%(regex)s)'                                     # 5
+            r'(%(space)s*(?:%(newline)s%(space)s*)+'           # 6
+                r'(?=%(post_regex_off)s))?'
         r'|(?<=%(id_literal_close)s)'
-            r'%(space)s*(?:(%(newline)s)%(space)s*)+'
+            r'%(space)s*(?:(%(newline)s)%(space)s*)+'          # 7
             r'(?=%(id_literal_open)s)'
-        r'|(?<=%(id_literal)s)(%(space)s)+(?=%(id_literal)s)'
-        r'|(?<=\+)(%(space)s)+(?=\+)'
-        r'|(?<=-)(%(space)s)+(?=-)'
+        r'|(?<=%(id_literal)s)(%(space)s)+(?=%(id_literal)s)'  # 8
+        r'|(?<=\+)(%(space)s)+(?=\+)'                          # 9
+        r'|(?<=-)(%(space)s)+(?=-)'                            # 10
         r'|%(space)s+'
-        r'|(?:%(newline)s%(space)s*)+') % locals()).sub
-    #print space_sub.__self__.pattern
+        r'|(?:%(newline)s%(space)s*)+'
+    ) % locals()).sub
 
-    def space_subber(match):
+    # print space_sub_simple.__self__.pattern
+
+    def space_subber_simple(match):
         """ Substitution callback """
-        # pylint: disable = C0321, R0911
+        # pylint: disable = too-many-return-statements
+
         groups = match.groups()
         if groups[0]:
             return groups[0]
         elif groups[1]:
             return groups[1]
         elif groups[2]:
+            if groups[3]:
+                return groups[2] + '\n'
             return groups[2]
-        elif groups[3]:
-            return groups[3]
-        elif groups[4]:
+        elif groups[5]:
+            return "%s%s%s" % (
+                groups[4] and '\n' or '',
+                groups[5],
+                groups[6] and '\n' or '',
+            )
+        elif groups[7]:
             return '\n'
-        elif groups[5] or groups[6] or groups[7]:
+        elif groups[8] or groups[9] or groups[10]:
             return ' '
         else:
             return ''
 
-    def jsmin(script):  # pylint: disable = W0621
+    space_sub_banged = _re.compile((
+        # noqa pylint: disable = bad-continuation
+
+        r'(%(dull)s+)'                                         # 0
+        r'|(%(strings)s%(dull)s*)'                             # 1
+        r'|(?<=%(preregex1)s)'
+            r'(%(space)s*(?:%(newline)s%(space)s*)*)'          # 2
+            r'(%(regex)s)'                                     # 3
+            r'(%(space)s*(?:%(newline)s%(space)s*)+'           # 4
+                r'(?=%(post_regex_off)s))?'
+        r'|(?<=%(preregex2)s)'
+            r'(%(space)s*(?:(%(newline)s)%(space)s*)*)'        # 5, 6
+            r'(%(regex)s)'                                     # 7
+            r'(%(space)s*(?:%(newline)s%(space)s*)+'           # 8
+                r'(?=%(post_regex_off)s))?'
+        r'|(?<=%(id_literal_close)s)'
+            r'(%(space)s*(?:%(newline)s%(space)s*)+)'          # 9
+            r'(?=%(id_literal_open)s)'
+        r'|(?<=%(id_literal)s)(%(space)s+)(?=%(id_literal)s)'  # 10
+        r'|(?<=\+)(%(space)s+)(?=\+)'                          # 11
+        r'|(?<=-)(%(space)s+)(?=-)'                            # 12
+        r'|(%(space)s+)'                                       # 13
+        r'|((?:%(newline)s%(space)s*)+)'                       # 14
+    ) % locals()).sub
+
+    # print space_sub_banged.__self__.pattern
+
+    keep = _re.compile((
+        r'%(space_chars)s+|%(space_comment_nobang)s+|%(newline)s+'
+        r'|(%(bang_comment)s+)'
+    ) % locals()).sub
+    keeper = lambda m: m.groups()[0] or ''
+
+    # print keep.__self__.pattern
+
+    def space_subber_banged(match):
+        """ Substitution callback """
+        # pylint: disable = too-many-return-statements
+
+        groups = match.groups()
+        if groups[0]:
+            return groups[0]
+        elif groups[1]:
+            return groups[1]
+        elif groups[3]:
+            return "%s%s%s%s" % (
+                keep(keeper, groups[2]),
+                groups[3],
+                keep(keeper, groups[4] or ''),
+                groups[4] and '\n' or '',
+            )
+        elif groups[7]:
+            return "%s%s%s%s%s" % (
+                keep(keeper, groups[5]),
+                groups[6] and '\n' or '',
+                groups[7],
+                keep(keeper, groups[8] or ''),
+                groups[8] and '\n' or '',
+            )
+        elif groups[9]:
+            return keep(keeper, groups[9]) + '\n'
+        elif groups[10] or groups[11] or groups[12]:
+            return keep(keeper, groups[10] or groups[11] or groups[12]) or ' '
+        else:
+            return keep(keeper, groups[13] or groups[14])
+
+    def jsmin(script, keep_bang_comments=False):
         r"""
         Minify javascript based on `jsmin.c by Douglas Crockford`_\.
 
@@ -218,17 +324,29 @@
           `script` : ``str``
             Script to minify
 
+          `keep_bang_comments` : ``bool``
+            Keep comments starting with an exclamation mark? (``/*!...*/``)
+
         :Return: Minified script
         :Rtype: ``str``
         """
-        return space_sub(space_subber, '\n%s\n' % script).strip()
+        # pylint: disable = redefined-outer-name
+
+        if keep_bang_comments:
+            return space_sub_banged(
+                space_subber_banged, '\n%s\n' % script
+            ).strip()
+        else:
+            return space_sub_simple(
+                space_subber_simple, '\n%s\n' % script
+            ).strip()
 
     return jsmin
 
 jsmin = _make_jsmin()
 
 
-def jsmin_for_posers(script):
+def jsmin_for_posers(script, keep_bang_comments=False):
     r"""
     Minify javascript based on `jsmin.c by Douglas Crockford`_\.
 
@@ -240,57 +358,158 @@
        http://www.crockford.com/javascript/jsmin.c
 
     :Warning: This function is the digest of a _make_jsmin() call. It just
-              utilizes the resulting regex. It's just for fun here and may
+              utilizes the resulting regexes. It's here for fun and may
               vanish any time. Use the `jsmin` function instead.
 
     :Parameters:
       `script` : ``str``
         Script to minify
 
+      `keep_bang_comments` : ``bool``
+        Keep comments starting with an exclamation mark? (``/*!...*/``)
+
     :Return: Minified script
     :Rtype: ``str``
     """
-    def subber(match):
-        """ Substitution callback """
-        groups = match.groups()
-        return (
-            groups[0] or
-            groups[1] or
-            groups[2] or
-            groups[3] or
-            (groups[4] and '\n') or
-            (groups[5] and ' ') or
-            (groups[6] and ' ') or
-            (groups[7] and ' ') or
-            '')
+    if not keep_bang_comments:
+        rex = (
+            r'([^\047"/\000-\040]+)|((?:(?:\047[^\047\\\r\n]*(?:\\(?:[^\r\n]'
+            r'|\r?\n|\r)[^\047\\\r\n]*)*\047)|(?:"[^"\\\r\n]*(?:\\(?:[^\r\n]'
+            r'|\r?\n|\r)[^"\\\r\n]*)*"))[^\047"/\000-\040]*)|(?<=[(,=:\[!&|?'
+            r'{};\r\n])(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*'
+            r'][^*]*\*+)*/))*(?:(?:(?://[^\r\n]*)?[\r\n])(?:[\000-\011\013\0'
+            r'14\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*)*((?:/(?![\r'
+            r'\n/*])[^/\\\[\r\n]*(?:(?:\\[^\r\n]|(?:\[[^\\\]\r\n]*(?:\\[^\r'
+            r'\n][^\\\]\r\n]*)*\]))[^/\\\[\r\n]*)*/))((?:[\000-\011\013\014'
+            r'\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*(?:(?:(?://[^\r'
+            r'\n]*)?[\r\n])(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:'
+            r'[^/*][^*]*\*+)*/))*)+(?=[^\000-\040&)+,.:;=?\]|}-]))?|(?<=[\00'
+            r'0-#%-,./:-@\[-^`{-~-]return)(?:[\000-\011\013\014\016-\040]|(?'
+            r':/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*(?:((?:(?://[^\r\n]*)?[\r\n]'
+            r'))(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*'
+            r'\*+)*/))*)*((?:/(?![\r\n/*])[^/\\\[\r\n]*(?:(?:\\[^\r\n]|(?:\['
+            r'[^\\\]\r\n]*(?:\\[^\r\n][^\\\]\r\n]*)*\]))[^/\\\[\r\n]*)*/))(('
+            r'?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)'
+            r'*/))*(?:(?:(?://[^\r\n]*)?[\r\n])(?:[\000-\011\013\014\016-\04'
+            r'0]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*)+(?=[^\000-\040&)+,.:;'
+            r'=?\]|}-]))?|(?<=[^\000-!#%&(*,./:-@\[\\^`{|~])(?:[\000-\011\01'
+            r'3\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*(?:((?:(?:'
+            r'//[^\r\n]*)?[\r\n]))(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]'
+            r'*\*+(?:[^/*][^*]*\*+)*/))*)+(?=[^\000-\040"#%-\047)*,./:-@\\-^'
+            r'`|-~])|(?<=[^\000-#%-,./:-@\[-^`{-~-])((?:[\000-\011\013\014\0'
+            r'16-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)))+(?=[^\000-#%-,./'
+            r':-@\[-^`{-~-])|(?<=\+)((?:[\000-\011\013\014\016-\040]|(?:/\*['
+            r'^*]*\*+(?:[^/*][^*]*\*+)*/)))+(?=\+)|(?<=-)((?:[\000-\011\013'
+            r'\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)))+(?=-)|(?:['
+            r'\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)'
+            r')+|(?:(?:(?://[^\r\n]*)?[\r\n])(?:[\000-\011\013\014\016-\040]'
+            r'|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*)+'
+        )
 
-    return _re.sub(
-        r'([^\047"/\000-\040]+)|((?:(?:\047[^\047\\\r\n]*(?:\\(?:[^\r\n]|\r?'
-        r'\n|\r)[^\047\\\r\n]*)*\047)|(?:"[^"\\\r\n]*(?:\\(?:[^\r\n]|\r?\n|'
-        r'\r)[^"\\\r\n]*)*"))[^\047"/\000-\040]*)|(?<=[(,=:\[!&|?{};\r\n])(?'
-        r':[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*'
-        r'(?:(?:(?://[^\r\n]*)?[\r\n])(?:[\000-\011\013\014\016-\040]|(?:/\*'
-        r'[^*]*\*+(?:[^/*][^*]*\*+)*/))*)*((?:/(?![\r\n/*])[^/\\\[\r\n]*(?:('
-        r'?:\\[^\r\n]|(?:\[[^\\\]\r\n]*(?:\\[^\r\n][^\\\]\r\n]*)*\]))[^/\\\['
-        r'\r\n]*)*/)[^\047"/\000-\040]*)|(?<=[\000-#%-,./:-@\[-^`{-~-]return'
-        r')(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/'
-        r'))*(?:(?:(?://[^\r\n]*)?[\r\n])(?:[\000-\011\013\014\016-\040]|(?:'
-        r'/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)))*((?:/(?![\r\n/*])[^/\\\[\r\n]*(?'
-        r':(?:\\[^\r\n]|(?:\[[^\\\]\r\n]*(?:\\[^\r\n][^\\\]\r\n]*)*\]))[^/'
-        r'\\\[\r\n]*)*/)[^\047"/\000-\040]*)|(?<=[^\000-!#%&(*,./:-@\[\\^`{|'
-        r'~])(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)'
-        r'*/))*(?:((?:(?://[^\r\n]*)?[\r\n]))(?:[\000-\011\013\014\016-\040]'
-        r'|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*)+(?=[^\000-\040"#%-\047)*,./'
-        r':-@\\-^`|-~])|(?<=[^\000-#%-,./:-@\[-^`{-~-])((?:[\000-\011\013\01'
-        r'4\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)))+(?=[^\000-#%-,./:'
-        r'-@\[-^`{-~-])|(?<=\+)((?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*'
-        r'\*+(?:[^/*][^*]*\*+)*/)))+(?=\+)|(?<=-)((?:[\000-\011\013\014\016-'
-        r'\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)))+(?=-)|(?:[\000-\011\013'
-        r'\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))+|(?:(?:(?://[^'
-        r'\r\n]*)?[\r\n])(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^'
-        r'/*][^*]*\*+)*/))*)+', subber, '\n%s\n' % script).strip()
+        def subber(match):
+            """ Substitution callback """
+            groups = match.groups()
+            return (
+                groups[0] or
+                groups[1] or
+                (groups[3] and (groups[2] + '\n')) or
+                groups[2] or
+                (groups[5] and "%s%s%s" % (
+                    groups[4] and '\n' or '',
+                    groups[5],
+                    groups[6] and '\n' or '',
+                )) or
+                (groups[7] and '\n') or
+                (groups[8] and ' ') or
+                (groups[9] and ' ') or
+                (groups[10] and ' ') or
+                ''
+            )
+    else:
+        rex = (
+            r'([^\047"/\000-\040]+)|((?:(?:\047[^\047\\\r\n]*(?:\\(?:[^\r\n]'
+            r'|\r?\n|\r)[^\047\\\r\n]*)*\047)|(?:"[^"\\\r\n]*(?:\\(?:[^\r\n]'
+            r'|\r?\n|\r)[^"\\\r\n]*)*"))[^\047"/\000-\040]*)|(?<=[(,=:\[!&|?'
+            r'{};\r\n])((?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/'
+            r'*][^*]*\*+)*/))*(?:(?:(?://[^\r\n]*)?[\r\n])(?:[\000-\011\013'
+            r'\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*)*)((?:/(?!'
+            r'[\r\n/*])[^/\\\[\r\n]*(?:(?:\\[^\r\n]|(?:\[[^\\\]\r\n]*(?:\\[^'
+            r'\r\n][^\\\]\r\n]*)*\]))[^/\\\[\r\n]*)*/))((?:[\000-\011\013\01'
+            r'4\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*(?:(?:(?://[^'
+            r'\r\n]*)?[\r\n])(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+('
+            r'?:[^/*][^*]*\*+)*/))*)+(?=[^\000-\040&)+,.:;=?\]|}-]))?|(?<=['
+            r'\000-#%-,./:-@\[-^`{-~-]return)((?:[\000-\011\013\014\016-\040'
+            r']|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*(?:((?:(?://[^\r\n]*)?['
+            r'\r\n]))(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*]['
+            r'^*]*\*+)*/))*)*)((?:/(?![\r\n/*])[^/\\\[\r\n]*(?:(?:\\[^\r\n]|'
+            r'(?:\[[^\\\]\r\n]*(?:\\[^\r\n][^\\\]\r\n]*)*\]))[^/\\\[\r\n]*)*'
+            r'/))((?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]'
+            r'*\*+)*/))*(?:(?:(?://[^\r\n]*)?[\r\n])(?:[\000-\011\013\014\01'
+            r'6-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*)+(?=[^\000-\040&)'
+            r'+,.:;=?\]|}-]))?|(?<=[^\000-!#%&(*,./:-@\[\\^`{|~])((?:[\000-'
+            r'\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*(?:'
+            r'(?:(?://[^\r\n]*)?[\r\n])(?:[\000-\011\013\014\016-\040]|(?:/'
+            r'\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*)+)(?=[^\000-\040"#%-\047)*,./'
+            r':-@\\-^`|-~])|(?<=[^\000-#%-,./:-@\[-^`{-~-])((?:[\000-\011\01'
+            r'3\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))+)(?=[^\000'
+            r'-#%-,./:-@\[-^`{-~-])|(?<=\+)((?:[\000-\011\013\014\016-\040]|'
+            r'(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))+)(?=\+)|(?<=-)((?:[\000-\0'
+            r'11\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))+)(?=-'
+            r')|((?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*'
+            r'\*+)*/))+)|((?:(?:(?://[^\r\n]*)?[\r\n])(?:[\000-\011\013\014'
+            r'\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*)+)'
+        )
+
+        keep = _re.compile((
+            r'[\000-\011\013\014\016-\040]+|(?:/\*(?!!)[^*]*\*+(?:[^/*][^*]*'
+            r'\*+)*/)+|(?:(?://[^\r\n]*)?[\r\n])+|((?:/\*![^*]*\*+(?:[^/*][^'
+            r'*]*\*+)*/)+)'
+        ) % locals()).sub
+        keeper = lambda m: m.groups()[0] or ''
+
+        def subber(match):
+            """ Substitution callback """
+            groups = match.groups()
+            return (
+                groups[0] or
+                groups[1] or
+                (groups[3] and "%s%s%s%s" % (
+                    keep(keeper, groups[2]),
+                    groups[3],
+                    keep(keeper, groups[4] or ''),
+                    groups[4] and '\n' or '',
+                )) or
+                (groups[7] and "%s%s%s%s%s" % (
+                    keep(keeper, groups[5]),
+                    groups[6] and '\n' or '',
+                    groups[7],
+                    keep(keeper, groups[8] or ''),
+                    groups[8] and '\n' or '',
+                )) or
+                (groups[9] and keep(keeper, groups[9] + '\n')) or
+                (groups[10] and keep(keeper, groups[10]) or ' ') or
+                (groups[11] and keep(keeper, groups[11]) or ' ') or
+                (groups[12] and keep(keeper, groups[12]) or ' ') or
+                keep(keeper, groups[13] or groups[14])
+            )
+
+    return _re.sub(rex, subber, '\n%s\n' % script).strip()
 
 
 if __name__ == '__main__':
-    import sys as _sys
-    _sys.stdout.write(jsmin(_sys.stdin.read()))
+    def main():
+        """ Main """
+        import sys as _sys
+
+        argv = _sys.argv[1:]
+        keep_bang_comments = '-b' in argv or '-bp' in argv or '-pb' in argv
+        if '-p' in argv or '-bp' in argv or '-pb' in argv:
+            xjsmin = _make_jsmin(python_only=True)
+        else:
+            xjsmin = jsmin
+
+        _sys.stdout.write(xjsmin(
+            _sys.stdin.read(), keep_bang_comments=keep_bang_comments
+        ))
+
+    main()
diff --git a/third_party/WebKit/Source/modules/canvas2d/BaseRenderingContext2D.cpp b/third_party/WebKit/Source/modules/canvas2d/BaseRenderingContext2D.cpp
index 7efea9d..d3fb0e2 100644
--- a/third_party/WebKit/Source/modules/canvas2d/BaseRenderingContext2D.cpp
+++ b/third_party/WebKit/Source/modules/canvas2d/BaseRenderingContext2D.cpp
@@ -739,8 +739,7 @@
   SkPath skPath = path.getSkPath();
   skPath.setFillType(parseWinding(windingRuleString));
   modifiableState().clipPath(skPath, m_clipAntialiasing);
-  c->clipPath(skPath, SkRegion::kIntersect_Op,
-              m_clipAntialiasing == AntiAliased);
+  c->clipPath(skPath, kIntersect_SkClipOp, m_clipAntialiasing == AntiAliased);
   if (ExpensiveCanvasHeuristicParameters::ComplexClipsAreExpensive &&
       !skPath.isRect(0) && hasImageBuffer()) {
     imageBuffer()->setHasExpensiveOp();
diff --git a/third_party/WebKit/Source/modules/canvas2d/ClipList.cpp b/third_party/WebKit/Source/modules/canvas2d/ClipList.cpp
index 49f26839..506fd5da 100644
--- a/third_party/WebKit/Source/modules/canvas2d/ClipList.cpp
+++ b/third_party/WebKit/Source/modules/canvas2d/ClipList.cpp
@@ -29,7 +29,7 @@
 
 void ClipList::playback(SkCanvas* canvas) const {
   for (const ClipOp* it = m_clipList.begin(); it < m_clipList.end(); it++) {
-    canvas->clipPath(it->m_path, SkRegion::kIntersect_Op,
+    canvas->clipPath(it->m_path, kIntersect_SkClipOp,
                      it->m_antiAliasingMode == AntiAliased);
   }
 }
diff --git a/third_party/WebKit/Source/modules/filesystem/FileWriterSync.cpp b/third_party/WebKit/Source/modules/filesystem/FileWriterSync.cpp
index 0bb0fa4..b8529b6 100644
--- a/third_party/WebKit/Source/modules/filesystem/FileWriterSync.cpp
+++ b/third_party/WebKit/Source/modules/filesystem/FileWriterSync.cpp
@@ -41,11 +41,11 @@
 void FileWriterSync::write(Blob* data, ExceptionState& exceptionState) {
   ASSERT(data);
   ASSERT(writer());
-  ASSERT(m_complete);
+  DCHECK(m_complete);
 
   prepareForWrite();
   writer()->write(position(), data->uuid());
-  ASSERT(m_complete);
+  DCHECK(m_complete);
   if (m_error) {
     FileError::throwDOMException(exceptionState, m_error);
     return;
@@ -57,14 +57,14 @@
 
 void FileWriterSync::seek(long long position, ExceptionState& exceptionState) {
   ASSERT(writer());
-  ASSERT(m_complete);
+  DCHECK(m_complete);
   seekInternal(position);
 }
 
 void FileWriterSync::truncate(long long offset,
                               ExceptionState& exceptionState) {
   ASSERT(writer());
-  ASSERT(m_complete);
+  DCHECK(m_complete);
   if (offset < 0) {
     exceptionState.throwDOMException(InvalidStateError,
                                      FileError::invalidStateErrorMessage);
@@ -72,7 +72,7 @@
   }
   prepareForWrite();
   writer()->truncate(offset);
-  ASSERT(m_complete);
+  DCHECK(m_complete);
   if (m_error) {
     FileError::throwDOMException(exceptionState, m_error);
     return;
@@ -84,46 +84,29 @@
 
 void FileWriterSync::didWrite(long long bytes, bool complete) {
   DCHECK_EQ(FileError::kOK, m_error);
-#if DCHECK_IS_ON()
   DCHECK(!m_complete);
   m_complete = complete;
-#endif
 }
 
 void FileWriterSync::didTruncate() {
   DCHECK_EQ(FileError::kOK, m_error);
-#if DCHECK_IS_ON()
   DCHECK(!m_complete);
   m_complete = true;
-#endif
 }
 
 void FileWriterSync::didFail(WebFileError error) {
   DCHECK_EQ(FileError::kOK, m_error);
   m_error = static_cast<FileError::ErrorCode>(error);
-#if DCHECK_IS_ON()
   DCHECK(!m_complete);
   m_complete = true;
-#endif
 }
 
-FileWriterSync::FileWriterSync()
-    : m_error(FileError::kOK)
-#if DCHECK_IS_ON()
-      ,
-      m_complete(true)
-#endif
-{
-}
+FileWriterSync::FileWriterSync() : m_error(FileError::kOK), m_complete(true) {}
 
 void FileWriterSync::prepareForWrite() {
-#if DCHECK_IS_ON()
   DCHECK(m_complete);
-#endif
   m_error = FileError::kOK;
-#if DCHECK_IS_ON()
   m_complete = false;
-#endif
 }
 
 FileWriterSync::~FileWriterSync() {}
diff --git a/third_party/WebKit/Source/modules/filesystem/FileWriterSync.h b/third_party/WebKit/Source/modules/filesystem/FileWriterSync.h
index 5156897..91d49e0 100644
--- a/third_party/WebKit/Source/modules/filesystem/FileWriterSync.h
+++ b/third_party/WebKit/Source/modules/filesystem/FileWriterSync.h
@@ -69,9 +69,7 @@
   void prepareForWrite();
 
   FileError::ErrorCode m_error;
-#if DCHECK_IS_ON()
   bool m_complete;
-#endif
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/BUILD.gn b/third_party/WebKit/Source/platform/BUILD.gn
index b562a1cf..ec4be49 100644
--- a/third_party/WebKit/Source/platform/BUILD.gn
+++ b/third_party/WebKit/Source/platform/BUILD.gn
@@ -1746,6 +1746,7 @@
     "image-decoders/gif/GIFImageDecoderTest.cpp",
     "image-decoders/ico/ICOImageDecoderTest.cpp",
     "image-decoders/jpeg/JPEGImageDecoderTest.cpp",
+    "image-decoders/png/PNGImageDecoderTest.cpp",
     "image-decoders/webp/WEBPImageDecoderTest.cpp",
     "json/JSONParserTest.cpp",
     "json/JSONValuesTest.cpp",
diff --git a/third_party/WebKit/Source/platform/feature_policy/FeaturePolicy.cpp b/third_party/WebKit/Source/platform/feature_policy/FeaturePolicy.cpp
index 51cabe3..be49368 100644
--- a/third_party/WebKit/Source/platform/feature_policy/FeaturePolicy.cpp
+++ b/third_party/WebKit/Source/platform/feature_policy/FeaturePolicy.cpp
@@ -27,69 +27,6 @@
   return nullptr;
 }
 
-// Converts a list of JSON feature policy items into a mapping of features to
-// whitelists. For future compatibility, unrecognized features are simply
-// ignored, as are unparseable origins. If |messages| is not null, then any
-// errors in the input will cause an error message to be appended to it.
-HashMap<const FeaturePolicy::Feature*,
-        std::unique_ptr<FeaturePolicy::Whitelist>>
-parseFeaturePolicyFromJson(std::unique_ptr<JSONArray> policyItems,
-                           RefPtr<SecurityOrigin> origin,
-                           FeaturePolicy::FeatureList& features,
-                           Vector<String>* messages) {
-  HashMap<const FeaturePolicy::Feature*,
-          std::unique_ptr<FeaturePolicy::Whitelist>>
-      whitelists;
-
-  for (size_t i = 0; i < policyItems->size(); ++i) {
-    JSONObject* item = JSONObject::cast(policyItems->at(i));
-    if (!item) {
-      if (messages)
-        messages->append("Policy is not an object");
-      continue;  // Array element is not an object; skip
-    }
-
-    for (size_t j = 0; j < item->size(); ++j) {
-      JSONObject::Entry entry = item->at(j);
-      String featureName = entry.first;
-      JSONArray* targets = JSONArray::cast(entry.second);
-      if (!targets) {
-        if (messages)
-          messages->append("Whitelist is not an array of strings.");
-        continue;
-      }
-
-      const FeaturePolicy::Feature* feature =
-          featureForName(featureName, features);
-      if (!feature)
-        continue;  // Feature is not recognized; skip
-
-      std::unique_ptr<FeaturePolicy::Whitelist> whitelist(
-          new FeaturePolicy::Whitelist);
-      String targetString;
-      for (size_t j = 0; j < targets->size(); ++j) {
-        if (targets->at(j)->asString(&targetString)) {
-          if (equalIgnoringCase(targetString, "self")) {
-            whitelist->add(origin);
-          } else if (targetString == "*") {
-            whitelist->addAll();
-          } else {
-            KURL originUrl = KURL(KURL(), targetString);
-            if (originUrl.isValid()) {
-              whitelist->add(SecurityOrigin::create(originUrl));
-            }
-          }
-        } else {
-          if (messages)
-            messages->append("Whitelist is not an array of strings.");
-        }
-      }
-      whitelists.set(feature, std::move(whitelist));
-    }
-  }
-  return whitelists;
-}
-
 }  // namespace
 
 // Definitions of all features controlled by Feature Policy should appear here.
@@ -120,6 +57,19 @@
 const FeaturePolicy::Feature kWebRTC{
     "webrtc", FeaturePolicy::FeatureDefault::EnableForAll};
 
+// static
+std::unique_ptr<FeaturePolicy::Whitelist> FeaturePolicy::Whitelist::from(
+    const WebFeaturePolicy::ParsedWhitelist& parsedWhitelist) {
+  std::unique_ptr<Whitelist> whitelist(new FeaturePolicy::Whitelist);
+  if (parsedWhitelist.matchesAllOrigins) {
+    whitelist->addAll();
+  } else {
+    for (const WebSecurityOrigin& origin : parsedWhitelist.origins)
+      whitelist->add(static_cast<WTF::PassRefPtr<SecurityOrigin>>(origin));
+  }
+  return whitelist;
+}
+
 FeaturePolicy::Whitelist::Whitelist() : m_matchesAllOrigins(false) {}
 
 void FeaturePolicy::Whitelist::addAll() {
@@ -195,20 +145,79 @@
                                 getDefaultFeatureList());
 }
 
-void FeaturePolicy::setHeaderPolicy(const String& policy,
-                                    Vector<String>* messages) {
-  DCHECK(m_headerWhitelists.isEmpty());
+// static
+WebParsedFeaturePolicy FeaturePolicy::parseFeaturePolicy(
+    const String& policy,
+    RefPtr<SecurityOrigin> origin,
+    Vector<String>* messages) {
+  Vector<WebFeaturePolicy::ParsedWhitelist> whitelists;
+
   // Use a reasonable parse depth limit; the actual maximum depth is only going
   // to be 4 for a valid policy, but we'll give the featurePolicyParser a chance
   // to report more specific errors, unless the string is really invalid.
-  std::unique_ptr<JSONArray> policyJSON = parseJSONHeader(policy, 50);
-  if (!policyJSON) {
+  std::unique_ptr<JSONArray> policyItems = parseJSONHeader(policy, 50);
+  if (!policyItems) {
     if (messages)
       messages->append("Unable to parse header");
-    return;
+    return whitelists;
   }
-  m_headerWhitelists = parseFeaturePolicyFromJson(
-      std::move(policyJSON), m_origin, m_features, messages);
+
+  for (size_t i = 0; i < policyItems->size(); ++i) {
+    JSONObject* item = JSONObject::cast(policyItems->at(i));
+    if (!item) {
+      if (messages)
+        messages->append("Policy is not an object");
+      continue;  // Array element is not an object; skip
+    }
+
+    for (size_t j = 0; j < item->size(); ++j) {
+      JSONObject::Entry entry = item->at(j);
+      String featureName = entry.first;
+      JSONArray* targets = JSONArray::cast(entry.second);
+      if (!targets) {
+        if (messages)
+          messages->append("Whitelist is not an array of strings.");
+        continue;
+      }
+
+      WebFeaturePolicy::ParsedWhitelist whitelist;
+      whitelist.featureName = featureName;
+      Vector<WebSecurityOrigin> origins;
+      String targetString;
+      for (size_t j = 0; j < targets->size(); ++j) {
+        if (targets->at(j)->asString(&targetString)) {
+          if (equalIgnoringCase(targetString, "self")) {
+            if (!origin->isUnique())
+              origins.append(origin);
+          } else if (targetString == "*") {
+            whitelist.matchesAllOrigins = true;
+          } else {
+            WebSecurityOrigin targetOrigin =
+                WebSecurityOrigin::createFromString(targetString);
+            if (!targetOrigin.isNull() && !targetOrigin.isUnique())
+              origins.append(targetOrigin);
+          }
+        } else {
+          if (messages)
+            messages->append("Whitelist is not an array of strings.");
+        }
+      }
+      whitelist.origins = origins;
+      whitelists.append(whitelist);
+    }
+  }
+  return whitelists;
+}
+
+void FeaturePolicy::setHeaderPolicy(const WebParsedFeaturePolicy& policy) {
+  DCHECK(m_headerWhitelists.isEmpty());
+  for (const WebFeaturePolicy::ParsedWhitelist& parsedWhitelist : policy) {
+    const FeaturePolicy::Feature* feature =
+        featureForName(parsedWhitelist.featureName, m_features);
+    if (!feature)
+      continue;
+    m_headerWhitelists.set(feature, Whitelist::from(parsedWhitelist));
+  }
 }
 
 bool FeaturePolicy::isFeatureEnabledForOrigin(
diff --git a/third_party/WebKit/Source/platform/feature_policy/FeaturePolicy.h b/third_party/WebKit/Source/platform/feature_policy/FeaturePolicy.h
index b73ee05..062d6e5 100644
--- a/third_party/WebKit/Source/platform/feature_policy/FeaturePolicy.h
+++ b/third_party/WebKit/Source/platform/feature_policy/FeaturePolicy.h
@@ -7,6 +7,7 @@
 
 #include "platform/PlatformExport.h"
 #include "platform/weborigin/SecurityOrigin.h"
+#include "public/platform/WebFeaturePolicy.h"
 #include "wtf/RefPtr.h"
 #include "wtf/Vector.h"
 #include "wtf/text/WTFString.h"
@@ -73,6 +74,9 @@
   // will always return true.
   class Whitelist final {
    public:
+    static std::unique_ptr<Whitelist> from(
+        const WebFeaturePolicy::ParsedWhitelist&);
+
     Whitelist();
 
     // Adds a single origin to the whitelist.
@@ -123,14 +127,22 @@
 
   using FeatureList = const Vector<const FeaturePolicy::Feature*>;
 
+  // Converts a JSON feature policy string into a vector of whitelists, one for
+  // each feature specified. Unrecognized features are parsed and included
+  // but will be filtered out when the policy is constructed. If |messages| is
+  // not null, then any errors in the input will cause an error message to be
+  // appended to it.
+  static WebParsedFeaturePolicy parseFeaturePolicy(const String& policy,
+                                                   RefPtr<SecurityOrigin>,
+                                                   Vector<String>* messages);
+
   static std::unique_ptr<FeaturePolicy> createFromParentPolicy(
       const FeaturePolicy* parent,
       RefPtr<SecurityOrigin>);
 
-  // Sets the declared policy from the Feature-Policy HTTP header. If the header
-  // cannot be parsed, errors will be appended to the |messages| vector, if not
-  // null.
-  void setHeaderPolicy(const String&, Vector<String>* messages);
+  // Sets the declared policy from the parsed Feature-Policy HTTP header.
+  // Unrecognized features will be ignored.
+  void setHeaderPolicy(const WebParsedFeaturePolicy&);
 
   // Returns whether or not the given feature is enabled by this policy.
   bool isFeatureEnabledForOrigin(const Feature&, const SecurityOrigin&) const;
diff --git a/third_party/WebKit/Source/platform/feature_policy/FeaturePolicyFuzzer.cpp b/third_party/WebKit/Source/platform/feature_policy/FeaturePolicyFuzzer.cpp
index e641895..041a414 100644
--- a/third_party/WebKit/Source/platform/feature_policy/FeaturePolicyFuzzer.cpp
+++ b/third_party/WebKit/Source/platform/feature_policy/FeaturePolicyFuzzer.cpp
@@ -17,9 +17,8 @@
   WTF::Vector<WTF::String> messages;
   RefPtr<blink::SecurityOrigin> origin =
       blink::SecurityOrigin::createFromString("https://example.com/");
-  std::unique_ptr<blink::FeaturePolicy> policy =
-      blink::FeaturePolicy::createFromParentPolicy(nullptr, origin);
-  policy->setHeaderPolicy(WTF::String(data, size), &messages);
+  blink::FeaturePolicy::parseFeaturePolicy(WTF::String(data, size),
+                                           origin.get(), &messages);
   return 0;
 }
 
diff --git a/third_party/WebKit/Source/platform/feature_policy/FeaturePolicyTest.cpp b/third_party/WebKit/Source/platform/feature_policy/FeaturePolicyTest.cpp
index 54ba7bb..b5ec7f2 100644
--- a/third_party/WebKit/Source/platform/feature_policy/FeaturePolicyTest.cpp
+++ b/third_party/WebKit/Source/platform/feature_policy/FeaturePolicyTest.cpp
@@ -87,9 +87,7 @@
   Vector<String> messages;
   for (const char* policyString : kValidPolicies) {
     messages.clear();
-    std::unique_ptr<FeaturePolicy> policy =
-        createFromParentPolicy(nullptr, m_originA);
-    policy->setHeaderPolicy(policyString, &messages);
+    FeaturePolicy::parseFeaturePolicy(policyString, m_originA.get(), &messages);
     EXPECT_EQ(0UL, messages.size());
   }
 }
@@ -98,13 +96,61 @@
   Vector<String> messages;
   for (const char* policyString : kInvalidPolicies) {
     messages.clear();
-    std::unique_ptr<FeaturePolicy> policy =
-        createFromParentPolicy(nullptr, m_originA);
-    policy->setHeaderPolicy(policyString, &messages);
+    FeaturePolicy::parseFeaturePolicy(policyString, m_originA.get(), &messages);
     EXPECT_NE(0UL, messages.size());
   }
 }
 
+TEST_F(FeaturePolicyTest, PolicyParsedCorrectly) {
+  Vector<String> messages;
+
+  // Empty policy.
+  WebParsedFeaturePolicy parsedPolicy =
+      FeaturePolicy::parseFeaturePolicy("{}", m_originA.get(), &messages);
+  EXPECT_EQ(0UL, parsedPolicy.size());
+
+  // Simple policy with "self".
+  parsedPolicy = FeaturePolicy::parseFeaturePolicy(
+      "{\"default-self\": [\"self\"]}", m_originA.get(), &messages);
+  EXPECT_EQ(1UL, parsedPolicy.size());
+  EXPECT_EQ("default-self", parsedPolicy[0].featureName);
+  EXPECT_FALSE(parsedPolicy[0].matchesAllOrigins);
+  EXPECT_EQ(1UL, parsedPolicy[0].origins.size());
+  EXPECT_TRUE(m_originA->isSameSchemeHostPortAndSuborigin(
+      parsedPolicy[0].origins[0].get()));
+
+  // Simple policy with *.
+  parsedPolicy = FeaturePolicy::parseFeaturePolicy(
+      "{\"default-self\": [\"*\"]}", m_originA.get(), &messages);
+  EXPECT_EQ(1UL, parsedPolicy.size());
+  EXPECT_EQ("default-self", parsedPolicy[0].featureName);
+  EXPECT_TRUE(parsedPolicy[0].matchesAllOrigins);
+  EXPECT_EQ(0UL, parsedPolicy[0].origins.size());
+
+  // Complicated policy.
+  parsedPolicy = FeaturePolicy::parseFeaturePolicy(
+      "{\"default-self\": [\"*\"], "
+      "\"default-on\": [\"https://example.net\", \"https://example.org\"], "
+      "\"default-off\": [\"self\"]}",
+      m_originA.get(), &messages);
+  EXPECT_EQ(3UL, parsedPolicy.size());
+  EXPECT_EQ("default-self", parsedPolicy[0].featureName);
+  EXPECT_TRUE(parsedPolicy[0].matchesAllOrigins);
+  EXPECT_EQ(0UL, parsedPolicy[0].origins.size());
+  EXPECT_EQ("default-on", parsedPolicy[1].featureName);
+  EXPECT_FALSE(parsedPolicy[1].matchesAllOrigins);
+  EXPECT_EQ(2UL, parsedPolicy[1].origins.size());
+  EXPECT_TRUE(m_originB->isSameSchemeHostPortAndSuborigin(
+      parsedPolicy[1].origins[0].get()));
+  EXPECT_TRUE(m_originC->isSameSchemeHostPortAndSuborigin(
+      parsedPolicy[1].origins[1].get()));
+  EXPECT_EQ("default-off", parsedPolicy[2].featureName);
+  EXPECT_FALSE(parsedPolicy[2].matchesAllOrigins);
+  EXPECT_EQ(1UL, parsedPolicy[2].origins.size());
+  EXPECT_TRUE(m_originA->isSameSchemeHostPortAndSuborigin(
+      parsedPolicy[2].origins[0].get()));
+}
+
 TEST_F(FeaturePolicyTest, TestInitialPolicy) {
   // +-------------+
   // |(1)Origin A  |
@@ -175,7 +221,8 @@
       createFromParentPolicy(nullptr, m_originA);
   std::unique_ptr<FeaturePolicy> policy2 =
       createFromParentPolicy(policy1.get(), m_originB);
-  policy2->setHeaderPolicy("{\"default-self\": [\"self\"]}", &messages);
+  policy2->setHeaderPolicy(FeaturePolicy::parseFeaturePolicy(
+      "{\"default-self\": [\"self\"]}", m_originB.get(), &messages));
   EXPECT_EQ(0UL, messages.size());
   EXPECT_FALSE(policy2->isFeatureEnabled(kDefaultSelfFeature));
 }
@@ -199,7 +246,8 @@
   Vector<String> messages;
   std::unique_ptr<FeaturePolicy> policy1 =
       createFromParentPolicy(nullptr, m_originA);
-  policy1->setHeaderPolicy("{\"default-self\": [\"self\"]}", &messages);
+  policy1->setHeaderPolicy(FeaturePolicy::parseFeaturePolicy(
+      "{\"default-self\": [\"self\"]}", m_originA.get(), &messages));
   EXPECT_EQ(0UL, messages.size());
   std::unique_ptr<FeaturePolicy> policy2 =
       createFromParentPolicy(policy1.get(), m_originA);
@@ -233,7 +281,8 @@
   Vector<String> messages;
   std::unique_ptr<FeaturePolicy> policy1 =
       createFromParentPolicy(nullptr, m_originA);
-  policy1->setHeaderPolicy("{\"default-self\": [\"self\"]}", &messages);
+  policy1->setHeaderPolicy(FeaturePolicy::parseFeaturePolicy(
+      "{\"default-self\": [\"self\"]}", m_originA.get(), &messages));
   EXPECT_EQ(0UL, messages.size());
   std::unique_ptr<FeaturePolicy> policy2 =
       createFromParentPolicy(policy1.get(), m_originB);
@@ -262,7 +311,8 @@
   Vector<String> messages;
   std::unique_ptr<FeaturePolicy> policy1 =
       createFromParentPolicy(nullptr, m_originA);
-  policy1->setHeaderPolicy("{\"default-self\": [\"" ORIGIN_B "\"]}", &messages);
+  policy1->setHeaderPolicy(FeaturePolicy::parseFeaturePolicy(
+      "{\"default-self\": [\"" ORIGIN_B "\"]}", m_originA.get(), &messages));
   EXPECT_EQ(0UL, messages.size());
   std::unique_ptr<FeaturePolicy> policy2 =
       createFromParentPolicy(policy1.get(), m_originB);
@@ -284,7 +334,8 @@
   Vector<String> messages;
   std::unique_ptr<FeaturePolicy> policy1 =
       createFromParentPolicy(nullptr, m_originA);
-  policy1->setHeaderPolicy("{\"default-on\": []}", &messages);
+  policy1->setHeaderPolicy(FeaturePolicy::parseFeaturePolicy(
+      "{\"default-on\": []}", m_originA.get(), &messages));
   EXPECT_EQ(0UL, messages.size());
   EXPECT_FALSE(policy1->isFeatureEnabled(kDefaultOnFeature));
 }
@@ -302,7 +353,8 @@
   Vector<String> messages;
   std::unique_ptr<FeaturePolicy> policy1 =
       createFromParentPolicy(nullptr, m_originA);
-  policy1->setHeaderPolicy("{\"default-on\": []}", &messages);
+  policy1->setHeaderPolicy(FeaturePolicy::parseFeaturePolicy(
+      "{\"default-on\": []}", m_originA.get(), &messages));
   EXPECT_EQ(0UL, messages.size());
   std::unique_ptr<FeaturePolicy> policy2 =
       createFromParentPolicy(policy1.get(), m_originA);
@@ -324,7 +376,8 @@
       createFromParentPolicy(nullptr, m_originA);
   std::unique_ptr<FeaturePolicy> policy2 =
       createFromParentPolicy(policy1.get(), m_originB);
-  policy2->setHeaderPolicy("{\"default-on\": []}", &messages);
+  policy2->setHeaderPolicy(FeaturePolicy::parseFeaturePolicy(
+      "{\"default-on\": []}", m_originB.get(), &messages));
   EXPECT_EQ(0UL, messages.size());
   EXPECT_FALSE(policy2->isFeatureEnabled(kDefaultOnFeature));
 }
@@ -349,7 +402,8 @@
       createFromParentPolicy(nullptr, m_originA);
   std::unique_ptr<FeaturePolicy> policy2 =
       createFromParentPolicy(policy1.get(), m_originB);
-  policy2->setHeaderPolicy("{\"default-on\": [\"self\"]}", &messages);
+  policy2->setHeaderPolicy(FeaturePolicy::parseFeaturePolicy(
+      "{\"default-on\": [\"self\"]}", m_originB.get(), &messages));
   EXPECT_EQ(0UL, messages.size());
   std::unique_ptr<FeaturePolicy> policy3 =
       createFromParentPolicy(policy2.get(), m_originC);
@@ -370,7 +424,8 @@
   Vector<String> messages;
   std::unique_ptr<FeaturePolicy> policy1 =
       createFromParentPolicy(nullptr, m_originA);
-  policy1->setHeaderPolicy("{\"default-on\": []}", &messages);
+  policy1->setHeaderPolicy(FeaturePolicy::parseFeaturePolicy(
+      "{\"default-on\": []}", m_originA.get(), &messages));
   EXPECT_EQ(0UL, messages.size());
   std::unique_ptr<FeaturePolicy> policy2 =
       createFromParentPolicy(policy1.get(), m_originB);
@@ -394,7 +449,8 @@
   Vector<String> messages;
   std::unique_ptr<FeaturePolicy> policy1 =
       createFromParentPolicy(nullptr, m_originA);
-  policy1->setHeaderPolicy("{\"default-self\": [\"*\"]}", &messages);
+  policy1->setHeaderPolicy(FeaturePolicy::parseFeaturePolicy(
+      "{\"default-self\": [\"*\"]}", m_originA.get(), &messages));
   EXPECT_EQ(0UL, messages.size());
   std::unique_ptr<FeaturePolicy> policy2 =
       createFromParentPolicy(policy1.get(), m_originB);
@@ -422,7 +478,8 @@
   Vector<String> messages;
   std::unique_ptr<FeaturePolicy> policy1 =
       createFromParentPolicy(nullptr, m_originA);
-  policy1->setHeaderPolicy("{\"default-on\": [\"" ORIGIN_B "\"]}", &messages);
+  policy1->setHeaderPolicy(FeaturePolicy::parseFeaturePolicy(
+      "{\"default-on\": [\"" ORIGIN_B "\"]}", m_originA.get(), &messages));
   EXPECT_EQ(0UL, messages.size());
   std::unique_ptr<FeaturePolicy> policy2 =
       createFromParentPolicy(policy1.get(), m_originB);
@@ -453,7 +510,8 @@
   Vector<String> messages;
   std::unique_ptr<FeaturePolicy> policy1 =
       createFromParentPolicy(nullptr, m_originA);
-  policy1->setHeaderPolicy("{\"default-self\": [\"" ORIGIN_B "\"]}", &messages);
+  policy1->setHeaderPolicy(FeaturePolicy::parseFeaturePolicy(
+      "{\"default-self\": [\"" ORIGIN_B "\"]}", m_originA.get(), &messages));
   EXPECT_EQ(0UL, messages.size());
   std::unique_ptr<FeaturePolicy> policy2 =
       createFromParentPolicy(policy1.get(), m_originB);
@@ -484,16 +542,19 @@
   Vector<String> messages;
   std::unique_ptr<FeaturePolicy> policy1 =
       createFromParentPolicy(nullptr, m_originA);
-  policy1->setHeaderPolicy("{\"default-off\": [\"" ORIGIN_B "\"]}", &messages);
+  policy1->setHeaderPolicy(FeaturePolicy::parseFeaturePolicy(
+      "{\"default-off\": [\"" ORIGIN_B "\"]}", m_originA.get(), &messages));
   EXPECT_EQ(0UL, messages.size());
   std::unique_ptr<FeaturePolicy> policy2 =
       createFromParentPolicy(policy1.get(), m_originB);
-  policy2->setHeaderPolicy("{\"default-off\": [\"self\"]}", &messages);
+  policy2->setHeaderPolicy(FeaturePolicy::parseFeaturePolicy(
+      "{\"default-off\": [\"self\"]}", m_originB.get(), &messages));
   std::unique_ptr<FeaturePolicy> policy3 =
       createFromParentPolicy(policy2.get(), m_originB);
   std::unique_ptr<FeaturePolicy> policy4 =
       createFromParentPolicy(policy2.get(), m_originC);
-  policy4->setHeaderPolicy("{\"default-off\": [\"self\"]}", &messages);
+  policy4->setHeaderPolicy(FeaturePolicy::parseFeaturePolicy(
+      "{\"default-off\": [\"self\"]}", m_originC.get(), &messages));
   EXPECT_FALSE(policy1->isFeatureEnabled(kDefaultOffFeature));
   EXPECT_TRUE(policy2->isFeatureEnabled(kDefaultOffFeature));
   EXPECT_FALSE(policy3->isFeatureEnabled(kDefaultOffFeature));
@@ -517,11 +578,13 @@
   Vector<String> messages;
   std::unique_ptr<FeaturePolicy> policy1 =
       createFromParentPolicy(nullptr, m_originA);
-  policy1->setHeaderPolicy("{\"default-self\": [\"*\"]}", &messages);
+  policy1->setHeaderPolicy(FeaturePolicy::parseFeaturePolicy(
+      "{\"default-self\": [\"*\"]}", m_originA.get(), &messages));
   EXPECT_EQ(0UL, messages.size());
   std::unique_ptr<FeaturePolicy> policy2 =
       createFromParentPolicy(policy1.get(), m_originB);
-  policy2->setHeaderPolicy("{\"default-self\": [\"*\"]}", &messages);
+  policy2->setHeaderPolicy(FeaturePolicy::parseFeaturePolicy(
+      "{\"default-self\": [\"*\"]}", m_originB.get(), &messages));
   EXPECT_EQ(0UL, messages.size());
   std::unique_ptr<FeaturePolicy> policy3 =
       createFromParentPolicy(policy2.get(), m_originA);
@@ -547,11 +610,13 @@
   Vector<String> messages;
   std::unique_ptr<FeaturePolicy> policy1 =
       createFromParentPolicy(nullptr, m_originA);
-  policy1->setHeaderPolicy("{\"default-self\": [\"self\"]}", &messages);
+  policy1->setHeaderPolicy(FeaturePolicy::parseFeaturePolicy(
+      "{\"default-self\": [\"self\"]}", m_originA.get(), &messages));
   EXPECT_EQ(0UL, messages.size());
   std::unique_ptr<FeaturePolicy> policy2 =
       createFromParentPolicy(policy1.get(), m_originB);
-  policy2->setHeaderPolicy("{\"default-self\": [\"*\"]}", &messages);
+  policy2->setHeaderPolicy(FeaturePolicy::parseFeaturePolicy(
+      "{\"default-self\": [\"*\"]}", m_originB.get(), &messages));
   EXPECT_EQ(0UL, messages.size());
   std::unique_ptr<FeaturePolicy> policy3 =
       createFromParentPolicy(policy2.get(), m_originA);
@@ -580,13 +645,15 @@
   Vector<String> messages;
   std::unique_ptr<FeaturePolicy> policy1 =
       createFromParentPolicy(nullptr, m_originA);
-  policy1->setHeaderPolicy("{\"default-self\": [\"self\", \"" ORIGIN_B "\"]}",
-                           &messages);
+  policy1->setHeaderPolicy(FeaturePolicy::parseFeaturePolicy(
+      "{\"default-self\": [\"self\", \"" ORIGIN_B "\"]}", m_originA.get(),
+      &messages));
   EXPECT_EQ(0UL, messages.size());
   std::unique_ptr<FeaturePolicy> policy2 =
       createFromParentPolicy(policy1.get(), m_originB);
-  policy2->setHeaderPolicy("{\"default-self\": [\"self\", \"" ORIGIN_C "\"]}",
-                           &messages);
+  policy2->setHeaderPolicy(FeaturePolicy::parseFeaturePolicy(
+      "{\"default-self\": [\"self\", \"" ORIGIN_C "\"]}", m_originB.get(),
+      &messages));
   EXPECT_EQ(0UL, messages.size());
   std::unique_ptr<FeaturePolicy> policy3 =
       createFromParentPolicy(policy2.get(), m_originC);
@@ -612,8 +679,9 @@
   Vector<String> messages;
   std::unique_ptr<FeaturePolicy> policy1 =
       createFromParentPolicy(nullptr, m_originA);
-  policy1->setHeaderPolicy("{\"default-on\": [\"self\", \"" ORIGIN_B "\"]}",
-                           &messages);
+  policy1->setHeaderPolicy(FeaturePolicy::parseFeaturePolicy(
+      "{\"default-on\": [\"self\", \"" ORIGIN_B "\"]}", m_originA.get(),
+      &messages));
   EXPECT_EQ(0UL, messages.size());
   std::unique_ptr<FeaturePolicy> policy2 =
       createFromParentPolicy(policy1.get(), m_originB);
@@ -645,8 +713,9 @@
   Vector<String> messages;
   std::unique_ptr<FeaturePolicy> policy1 =
       createFromParentPolicy(nullptr, m_originA);
-  policy1->setHeaderPolicy("{\"default-self\": [\"self\", \"" ORIGIN_B "\"]}",
-                           &messages);
+  policy1->setHeaderPolicy(FeaturePolicy::parseFeaturePolicy(
+      "{\"default-self\": [\"self\", \"" ORIGIN_B "\"]}", m_originA.get(),
+      &messages));
   EXPECT_EQ(0UL, messages.size());
   std::unique_ptr<FeaturePolicy> policy2 =
       createFromParentPolicy(policy1.get(), m_originB);
@@ -680,14 +749,16 @@
   Vector<String> messages;
   std::unique_ptr<FeaturePolicy> policy1 =
       createFromParentPolicy(nullptr, m_originA);
-  policy1->setHeaderPolicy("{\"default-self\": [\"self\", \"" ORIGIN_B
-                           "\"], \"default-on\": [\"self\"]}",
-                           &messages);
+  policy1->setHeaderPolicy(FeaturePolicy::parseFeaturePolicy(
+      "{\"default-self\": [\"self\", \"" ORIGIN_B
+      "\"], \"default-on\": [\"self\"]}",
+      m_originA.get(), &messages));
   EXPECT_EQ(0UL, messages.size());
   std::unique_ptr<FeaturePolicy> policy2 =
       createFromParentPolicy(policy1.get(), m_originB);
-  policy2->setHeaderPolicy(
-      "{\"default-self\": [\"*\"], \"default-on\": [\"*\"]}", &messages);
+  policy2->setHeaderPolicy(FeaturePolicy::parseFeaturePolicy(
+      "{\"default-self\": [\"*\"], \"default-on\": [\"*\"]}", m_originB.get(),
+      &messages));
   EXPECT_EQ(0UL, messages.size());
   std::unique_ptr<FeaturePolicy> policy3 =
       createFromParentPolicy(policy2.get(), m_originC);
@@ -709,8 +780,9 @@
   Vector<String> messages;
   std::unique_ptr<FeaturePolicy> policy1 =
       createFromParentPolicy(nullptr, m_originA);
-  policy1->setHeaderPolicy("{\"default-off\": [\"self\", \"" ORIGIN_B "\"]}",
-                           &messages);
+  policy1->setHeaderPolicy(FeaturePolicy::parseFeaturePolicy(
+      "{\"default-off\": [\"self\", \"" ORIGIN_B "\"]}", m_originA.get(),
+      &messages));
   EXPECT_EQ(0UL, messages.size());
   EXPECT_TRUE(
       policy1->isFeatureEnabledForOrigin(kDefaultOffFeature, *m_originA));
diff --git a/third_party/WebKit/Source/platform/graphics/GraphicsContext.cpp b/third_party/WebKit/Source/platform/graphics/GraphicsContext.cpp
index cb5f3ad..3e2ef2d 100644
--- a/third_party/WebKit/Source/platform/graphics/GraphicsContext.cpp
+++ b/third_party/WebKit/Source/platform/graphics/GraphicsContext.cpp
@@ -854,8 +854,7 @@
   } else {
     // Clip-based fallback.
     SkAutoCanvasRestore autoRestore(m_canvas, true);
-    m_canvas->clipRRect(dest, SkRegion::kIntersect_Op,
-                        imagePaint.isAntiAlias());
+    m_canvas->clipRRect(dest, imagePaint.isAntiAlias());
     image->draw(m_canvas, imagePaint, dest.rect(), srcRect, respectOrientation,
                 Image::ClampImageToSourceRect);
   }
@@ -1138,17 +1137,17 @@
 }
 
 void GraphicsContext::clipRoundedRect(const FloatRoundedRect& rrect,
-                                      SkRegion::Op regionOp,
+                                      SkClipOp clipOp,
                                       AntiAliasingMode shouldAntialias) {
   if (contextDisabled())
     return;
 
   if (!rrect.isRounded()) {
-    clipRect(rrect.rect(), shouldAntialias, regionOp);
+    clipRect(rrect.rect(), shouldAntialias, clipOp);
     return;
   }
 
-  clipRRect(rrect, shouldAntialias, regionOp);
+  clipRRect(rrect, shouldAntialias, clipOp);
 }
 
 void GraphicsContext::clipOut(const Path& pathToClip) {
@@ -1167,12 +1166,12 @@
   if (contextDisabled())
     return;
 
-  clipRoundedRect(rect, SkRegion::kDifference_Op);
+  clipRoundedRect(rect, kDifference_SkClipOp);
 }
 
 void GraphicsContext::clipRect(const SkRect& rect,
                                AntiAliasingMode aa,
-                               SkRegion::Op op) {
+                               SkClipOp op) {
   if (contextDisabled())
     return;
   ASSERT(m_canvas);
@@ -1182,7 +1181,7 @@
 
 void GraphicsContext::clipPath(const SkPath& path,
                                AntiAliasingMode aa,
-                               SkRegion::Op op) {
+                               SkClipOp op) {
   if (contextDisabled())
     return;
   ASSERT(m_canvas);
@@ -1192,7 +1191,7 @@
 
 void GraphicsContext::clipRRect(const SkRRect& rect,
                                 AntiAliasingMode aa,
-                                SkRegion::Op op) {
+                                SkClipOp op) {
   if (contextDisabled())
     return;
   ASSERT(m_canvas);
diff --git a/third_party/WebKit/Source/platform/graphics/GraphicsContext.h b/third_party/WebKit/Source/platform/graphics/GraphicsContext.h
index 7aa52e1b..f4a56f7 100644
--- a/third_party/WebKit/Source/platform/graphics/GraphicsContext.h
+++ b/third_party/WebKit/Source/platform/graphics/GraphicsContext.h
@@ -35,10 +35,10 @@
 #include "platform/graphics/GraphicsContextState.h"
 #include "platform/graphics/ImageOrientation.h"
 #include "platform/graphics/skia/SkiaUtils.h"
+#include "third_party/skia/include/core/SkClipOp.h"
 #include "third_party/skia/include/core/SkMetaData.h"
 #include "third_party/skia/include/core/SkPictureRecorder.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
-#include "third_party/skia/include/core/SkRegion.h"
 #include "wtf/Allocator.h"
 #include "wtf/Forward.h"
 #include "wtf/Noncopyable.h"
@@ -220,22 +220,22 @@
   void clip(const IntRect& rect) { clipRect(rect); }
   void clip(const FloatRect& rect) { clipRect(rect); }
   void clipRoundedRect(const FloatRoundedRect&,
-                       SkRegion::Op = SkRegion::kIntersect_Op,
+                       SkClipOp = kIntersect_SkClipOp,
                        AntiAliasingMode = AntiAliased);
   void clipOut(const IntRect& rect) {
-    clipRect(rect, NotAntiAliased, SkRegion::kDifference_Op);
+    clipRect(rect, NotAntiAliased, kDifference_SkClipOp);
   }
   void clipOut(const FloatRect& rect) {
-    clipRect(rect, NotAntiAliased, SkRegion::kDifference_Op);
+    clipRect(rect, NotAntiAliased, kDifference_SkClipOp);
   }
   void clipOut(const Path&);
   void clipOutRoundedRect(const FloatRoundedRect&);
   void clipPath(const SkPath&,
                 AntiAliasingMode = NotAntiAliased,
-                SkRegion::Op = SkRegion::kIntersect_Op);
+                SkClipOp = kIntersect_SkClipOp);
   void clipRect(const SkRect&,
                 AntiAliasingMode = NotAntiAliased,
-                SkRegion::Op = SkRegion::kIntersect_Op);
+                SkClipOp = kIntersect_SkClipOp);
 
   void drawText(const Font&, const TextRunPaintInfo&, const FloatPoint&);
   void drawText(const Font&,
@@ -397,7 +397,7 @@
   // SkCanvas wrappers.
   void clipRRect(const SkRRect&,
                  AntiAliasingMode = NotAntiAliased,
-                 SkRegion::Op = SkRegion::kIntersect_Op);
+                 SkClipOp = kIntersect_SkClipOp);
   void concat(const SkMatrix&);
 
   // Apply deferred paint state saves
diff --git a/third_party/WebKit/Source/platform/graphics/InterceptingCanvas.h b/third_party/WebKit/Source/platform/graphics/InterceptingCanvas.h
index fe143b4..316561d6 100644
--- a/third_party/WebKit/Source/platform/graphics/InterceptingCanvas.h
+++ b/third_party/WebKit/Source/platform/graphics/InterceptingCanvas.h
@@ -144,10 +144,10 @@
                       SkScalar x,
                       SkScalar y,
                       const SkPaint&) override = 0;
-  void onClipRect(const SkRect&, SkRegion::Op, ClipEdgeStyle) override = 0;
-  void onClipRRect(const SkRRect&, SkRegion::Op, ClipEdgeStyle) override = 0;
-  void onClipPath(const SkPath&, SkRegion::Op, ClipEdgeStyle) override = 0;
-  void onClipRegion(const SkRegion&, SkRegion::Op) override = 0;
+  void onClipRect(const SkRect&, SkClipOp, ClipEdgeStyle) override = 0;
+  void onClipRRect(const SkRRect&, SkClipOp, ClipEdgeStyle) override = 0;
+  void onClipPath(const SkPath&, SkClipOp, ClipEdgeStyle) override = 0;
+  void onClipRegion(const SkRegion&, SkClipOp) override = 0;
   void onDrawPicture(const SkPicture*,
                      const SkMatrix*,
                      const SkPaint*) override = 0;
@@ -317,27 +317,27 @@
   }
 
   void onClipRect(const SkRect& rect,
-                  SkRegion::Op op,
+                  SkClipOp op,
                   ClipEdgeStyle edgeStyle) override {
     Interceptor interceptor(this);
     this->SkCanvas::onClipRect(rect, op, edgeStyle);
   }
 
   void onClipRRect(const SkRRect& rrect,
-                   SkRegion::Op op,
+                   SkClipOp op,
                    ClipEdgeStyle edgeStyle) override {
     Interceptor interceptor(this);
     this->SkCanvas::onClipRRect(rrect, op, edgeStyle);
   }
 
   void onClipPath(const SkPath& path,
-                  SkRegion::Op op,
+                  SkClipOp op,
                   ClipEdgeStyle edgeStyle) override {
     Interceptor interceptor(this);
     this->SkCanvas::onClipPath(path, op, edgeStyle);
   }
 
-  void onClipRegion(const SkRegion& region, SkRegion::Op op) override {
+  void onClipRegion(const SkRegion& region, SkClipOp op) override {
     Interceptor interceptor(this);
     this->SkCanvas::onClipRegion(region, op);
   }
diff --git a/third_party/WebKit/Source/platform/graphics/LoggingCanvas.cpp b/third_party/WebKit/Source/platform/graphics/LoggingCanvas.cpp
index 51618e1..d9f3b3e 100644
--- a/third_party/WebKit/Source/platform/graphics/LoggingCanvas.cpp
+++ b/third_party/WebKit/Source/platform/graphics/LoggingCanvas.cpp
@@ -493,19 +493,19 @@
   return scalarsArray;
 }
 
-String regionOpName(SkRegion::Op op) {
+String clipOpName(SkClipOp op) {
   switch (op) {
-    case SkRegion::kDifference_Op:
+    case kDifference_SkClipOp:
       return "kDifference_Op";
-    case SkRegion::kIntersect_Op:
+    case kIntersect_SkClipOp:
       return "kIntersect_Op";
-    case SkRegion::kUnion_Op:
+    case kUnion_SkClipOp:
       return "kUnion_Op";
-    case SkRegion::kXOR_Op:
+    case kXOR_SkClipOp:
       return "kXOR_Op";
-    case SkRegion::kReverseDifference_Op:
+    case kReverseDifference_SkClipOp:
       return "kReverseDifference_Op";
-    case SkRegion::kReplace_Op:
+    case kReplace_SkClipOp:
       return "kReplace_Op";
     default:
       return "Unknown type";
@@ -818,42 +818,42 @@
 }
 
 void LoggingCanvas::onClipRect(const SkRect& rect,
-                               SkRegion::Op op,
+                               SkClipOp op,
                                ClipEdgeStyle style) {
   AutoLogger logger(this);
   JSONObject* params = logger.logItemWithParams("clipRect");
   params->setObject("rect", objectForSkRect(rect));
-  params->setString("SkRegion::Op", regionOpName(op));
+  params->setString("SkRegion::Op", clipOpName(op));
   params->setBoolean("softClipEdgeStyle", kSoft_ClipEdgeStyle == style);
   this->SkCanvas::onClipRect(rect, op, style);
 }
 
 void LoggingCanvas::onClipRRect(const SkRRect& rrect,
-                                SkRegion::Op op,
+                                SkClipOp op,
                                 ClipEdgeStyle style) {
   AutoLogger logger(this);
   JSONObject* params = logger.logItemWithParams("clipRRect");
   params->setObject("rrect", objectForSkRRect(rrect));
-  params->setString("SkRegion::Op", regionOpName(op));
+  params->setString("SkRegion::Op", clipOpName(op));
   params->setBoolean("softClipEdgeStyle", kSoft_ClipEdgeStyle == style);
   this->SkCanvas::onClipRRect(rrect, op, style);
 }
 
 void LoggingCanvas::onClipPath(const SkPath& path,
-                               SkRegion::Op op,
+                               SkClipOp op,
                                ClipEdgeStyle style) {
   AutoLogger logger(this);
   JSONObject* params = logger.logItemWithParams("clipPath");
   params->setObject("path", objectForSkPath(path));
-  params->setString("SkRegion::Op", regionOpName(op));
+  params->setString("SkRegion::Op", clipOpName(op));
   params->setBoolean("softClipEdgeStyle", kSoft_ClipEdgeStyle == style);
   this->SkCanvas::onClipPath(path, op, style);
 }
 
-void LoggingCanvas::onClipRegion(const SkRegion& region, SkRegion::Op op) {
+void LoggingCanvas::onClipRegion(const SkRegion& region, SkClipOp op) {
   AutoLogger logger(this);
   JSONObject* params = logger.logItemWithParams("clipRegion");
-  params->setString("op", regionOpName(op));
+  params->setString("op", clipOpName(op));
   this->SkCanvas::onClipRegion(region, op);
 }
 
diff --git a/third_party/WebKit/Source/platform/graphics/LoggingCanvas.h b/third_party/WebKit/Source/platform/graphics/LoggingCanvas.h
index 54e52a9..5a44103 100644
--- a/third_party/WebKit/Source/platform/graphics/LoggingCanvas.h
+++ b/third_party/WebKit/Source/platform/graphics/LoggingCanvas.h
@@ -108,10 +108,10 @@
                       SkScalar x,
                       SkScalar y,
                       const SkPaint&) override;
-  void onClipRect(const SkRect&, SkRegion::Op, ClipEdgeStyle) override;
-  void onClipRRect(const SkRRect&, SkRegion::Op, ClipEdgeStyle) override;
-  void onClipPath(const SkPath&, SkRegion::Op, ClipEdgeStyle) override;
-  void onClipRegion(const SkRegion&, SkRegion::Op) override;
+  void onClipRect(const SkRect&, SkClipOp, ClipEdgeStyle) override;
+  void onClipRRect(const SkRRect&, SkClipOp, ClipEdgeStyle) override;
+  void onClipPath(const SkPath&, SkClipOp, ClipEdgeStyle) override;
+  void onClipRegion(const SkRegion&, SkClipOp) override;
   virtual void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*);
   void didSetMatrix(const SkMatrix&) override;
   void didConcat(const SkMatrix&) override;
diff --git a/third_party/WebKit/Source/platform/graphics/paint/ClipPathDisplayItem.cpp b/third_party/WebKit/Source/platform/graphics/paint/ClipPathDisplayItem.cpp
index 4b2a3c4b..d208879 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/ClipPathDisplayItem.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/ClipPathDisplayItem.cpp
@@ -20,7 +20,7 @@
 void BeginClipPathDisplayItem::appendToWebDisplayItemList(
     const IntRect& visualRect,
     WebDisplayItemList* list) const {
-  list->appendClipPathItem(m_clipPath, SkRegion::kIntersect_Op, true);
+  list->appendClipPathItem(m_clipPath, kIntersect_SkClipOp, true);
 }
 
 void BeginClipPathDisplayItem::analyzeForGpuRasterization(
diff --git a/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.cpp b/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.cpp
index ecaa5b61..f37ca39 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.cpp
@@ -68,23 +68,6 @@
     return "Unknown"
 
 static WTF::String specialDrawingTypeAsDebugString(DisplayItem::Type type) {
-  if (type >= DisplayItem::kTableCollapsedBorderUnalignedBase) {
-    if (type <= DisplayItem::kTableCollapsedBorderBase)
-      return "TableCollapsedBorderAlignment";
-    if (type <= DisplayItem::kTableCollapsedBorderLast) {
-      StringBuilder sb;
-      sb.append("TableCollapsedBorder");
-      if (type & DisplayItem::TableCollapsedBorderTop)
-        sb.append("Top");
-      if (type & DisplayItem::TableCollapsedBorderRight)
-        sb.append("Right");
-      if (type & DisplayItem::TableCollapsedBorderBottom)
-        sb.append("Bottom");
-      if (type & DisplayItem::TableCollapsedBorderLeft)
-        sb.append("Left");
-      return sb.toString();
-    }
-  }
   switch (type) {
     DEBUG_STRING_CASE(BoxDecorationBackground);
     DEBUG_STRING_CASE(Caret);
@@ -126,6 +109,7 @@
     DEBUG_STRING_CASE(TableCellBackgroundFromColumn);
     DEBUG_STRING_CASE(TableCellBackgroundFromSection);
     DEBUG_STRING_CASE(TableCellBackgroundFromRow);
+    DEBUG_STRING_CASE(TableCollapsedBorders);
     DEBUG_STRING_CASE(TableSectionBoxShadowInset);
     DEBUG_STRING_CASE(TableSectionBoxShadowNormal);
     DEBUG_STRING_CASE(TableRowBoxShadowInset);
diff --git a/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.h b/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.h
index 90d9449..efdf25d 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.h
@@ -110,15 +110,7 @@
     kTableCellBackgroundFromColumn,
     kTableCellBackgroundFromSection,
     kTableCellBackgroundFromRow,
-    // Table collapsed borders can be painted together (e.g., left & top) but
-    // there are at most 4 phases of collapsed border painting for a single
-    // cell. To disambiguate these phases of collapsed border painting, a mask
-    // is used. TableCollapsedBorderBase can be larger than
-    // TableCollapsedBorderUnalignedBase to ensure the base lower bits are 0's.
-    kTableCollapsedBorderUnalignedBase,
-    kTableCollapsedBorderBase =
-        (((kTableCollapsedBorderUnalignedBase - 1) >> 4) + 1) << 4,
-    kTableCollapsedBorderLast = kTableCollapsedBorderBase + 0x0f,
+    kTableCollapsedBorders,
     kTableSectionBoxShadowInset,
     kTableSectionBoxShadowNormal,
     kTableRowBoxShadowInset,
@@ -202,19 +194,6 @@
     kTypeLast = kUninitializedType
   };
 
-  static_assert(kTableCollapsedBorderBase >= kTableCollapsedBorderUnalignedBase,
-                "TableCollapsedBorder types overlap with other types");
-  static_assert((kTableCollapsedBorderBase & 0xf) == 0,
-                "The lowest 4 bits of TableCollapsedBorderBase should be zero");
-  // Bits or'ed onto TableCollapsedBorderBase to generate a real table collapsed
-  // border type.
-  enum TableCollapsedBorderSides {
-    TableCollapsedBorderTop = 1 << 0,
-    TableCollapsedBorderRight = 1 << 1,
-    TableCollapsedBorderBottom = 1 << 2,
-    TableCollapsedBorderLeft = 1 << 3,
-  };
-
   DisplayItem(const DisplayItemClient& client, Type type, size_t derivedSize)
       : m_client(&client),
         m_type(type),
diff --git a/third_party/WebKit/Source/platform/graphics/paint/DisplayItemClient.h b/third_party/WebKit/Source/platform/graphics/paint/DisplayItemClient.h
index 25a5bcd9f..fb47d824 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/DisplayItemClient.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/DisplayItemClient.h
@@ -55,6 +55,20 @@
   // offsetFromLayoutObjectWithSubpixelAccumulation().
   virtual LayoutRect visualRect() const = 0;
 
+  // This is declared here instead of in LayoutObject for verifying the
+  // condition in DrawingRecorder.
+  // Returns true if the object itself will not generate any effective painted
+  // output no matter what size the object is. For example, this function can
+  // return false for an object whose size is currently 0x0 but would have
+  // effective painted output if it was set a non-empty size. It's used to skip
+  // unforced paint invalidation of LayoutObjects (which is when
+  // shouldDoFullPaintInvalidation is false, but mayNeedPaintInvalidation or
+  // childShouldCheckForPaintInvalidation is true) to avoid unnecessary paint
+  // invalidations of empty areas covered by such objects.
+  virtual bool paintedOutputOfObjectHasNoEffectRegardlessOfSize() const {
+    return false;
+  }
+
   void setDisplayItemsUncached(
       PaintInvalidationReason reason = PaintInvalidationFull) const {
     m_cacheGenerationOrInvalidationReason.invalidate(reason);
diff --git a/third_party/WebKit/Source/platform/graphics/paint/DrawingRecorder.cpp b/third_party/WebKit/Source/platform/graphics/paint/DrawingRecorder.cpp
index 3516816..a6011c9 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/DrawingRecorder.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/DrawingRecorder.cpp
@@ -61,7 +61,7 @@
     // expand by one pixel in device (pixel) space, but to do that we would need
     // to add the verification mode to Skia.
     cullRect.inflate(1);
-    context.clipRect(cullRect, NotAntiAliased, SkRegion::kIntersect_Op);
+    context.clipRect(cullRect, NotAntiAliased, kIntersect_SkClipOp);
   }
 #endif
 }
@@ -79,9 +79,19 @@
          m_context.getPaintController().newDisplayItemList().size());
 #endif
 
+  sk_sp<const SkPicture> picture = m_context.endRecording();
+
+#if DCHECK_IS_ON()
+  if (!RuntimeEnabledFeatures::slimmingPaintStrictCullRectClippingEnabled() &&
+      !m_context.getPaintController().isForSkPictureBuilder() &&
+      m_displayItemClient.paintedOutputOfObjectHasNoEffectRegardlessOfSize()) {
+    DCHECK_EQ(0, picture->approximateOpCount())
+        << m_displayItemClient.debugName();
+  }
+#endif
+
   m_context.getPaintController().createAndAppend<DrawingDisplayItem>(
-      m_displayItemClient, m_displayItemType, m_context.endRecording(),
-      m_knownToBeOpaque);
+      m_displayItemClient, m_displayItemType, picture, m_knownToBeOpaque);
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/graphics/paint/PaintController.h b/third_party/WebKit/Source/platform/graphics/paint/PaintController.h
index 8aaebc3..cde4c2d 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/PaintController.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/PaintController.h
@@ -187,6 +187,10 @@
 
 #if DCHECK_IS_ON()
   void assertDisplayItemClientsAreLive();
+
+  enum Usage { ForNormalUsage, ForSkPictureBuilder };
+  void setUsage(Usage usage) { m_usage = usage; }
+  bool isForSkPictureBuilder() const { return m_usage == ForSkPictureBuilder; }
 #endif
 
   void setTracksRasterInvalidations(bool value);
@@ -351,6 +355,8 @@
 #if DCHECK_IS_ON()
   // This is used to check duplicated ids during createAndAppend().
   IndicesByClientMap m_newDisplayItemIndicesByClient;
+
+  Usage m_usage;
 #endif
 
   // These are set in useCachedDrawingIfPossible() and
diff --git a/third_party/WebKit/Source/platform/graphics/paint/SkPictureBuilder.cpp b/third_party/WebKit/Source/platform/graphics/paint/SkPictureBuilder.cpp
index cdc23e9b..3905add 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/SkPictureBuilder.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/SkPictureBuilder.cpp
@@ -26,6 +26,10 @@
     m_paintControllerPtr = PaintController::create();
     m_paintController = m_paintControllerPtr.get();
   }
+#if DCHECK_IS_ON()
+  m_paintController->setUsage(PaintController::ForSkPictureBuilder);
+#endif
+
   m_context = wrapUnique(
       new GraphicsContext(*m_paintController, disabledMode, metaData));
 
@@ -35,7 +39,11 @@
   }
 }
 
-SkPictureBuilder::~SkPictureBuilder() {}
+SkPictureBuilder::~SkPictureBuilder() {
+#if DCHECK_IS_ON()
+  m_paintController->setUsage(PaintController::ForNormalUsage);
+#endif
+}
 
 sk_sp<SkPicture> SkPictureBuilder::endRecording() {
   m_context->beginRecording(m_bounds);
diff --git a/third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoderTest.cpp b/third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoderTest.cpp
new file mode 100644
index 0000000..f03406d7
--- /dev/null
+++ b/third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoderTest.cpp
@@ -0,0 +1,179 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "platform/image-decoders/png/PNGImageDecoder.h"
+
+#include "platform/image-decoders/ImageDecoderTestHelpers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include <memory>
+
+namespace blink {
+
+namespace {
+
+std::unique_ptr<ImageDecoder> createDecoder(
+    ImageDecoder::AlphaOption alphaOption) {
+  return wrapUnique(
+      new PNGImageDecoder(alphaOption, ImageDecoder::ColorSpaceTransformed,
+                          ImageDecoder::targetColorSpaceForTesting(),
+                          ImageDecoder::noDecodedImageByteLimit));
+}
+
+std::unique_ptr<ImageDecoder> createDecoder() {
+  return createDecoder(ImageDecoder::AlphaNotPremultiplied);
+}
+
+std::unique_ptr<ImageDecoder> createDecoderWithPngData(const char* pngFile) {
+  auto decoder = createDecoder();
+  auto data = readFile(pngFile);
+  EXPECT_FALSE(data->isEmpty());
+  decoder->setData(data.get(), true);
+  return decoder;
+}
+
+void testSize(const char* pngFile, IntSize expectedSize) {
+  auto decoder = createDecoderWithPngData(pngFile);
+  EXPECT_TRUE(decoder->isSizeAvailable());
+  EXPECT_EQ(expectedSize, decoder->size());
+}
+
+void testRepetitionCount(const char* pngFile, int expectedRepetitionCount) {
+  auto decoder = createDecoderWithPngData(pngFile);
+  // Decoding the frame count sets the repetition count as well.
+  decoder->frameCount();
+  EXPECT_FALSE(decoder->failed());
+  EXPECT_EQ(expectedRepetitionCount, decoder->repetitionCount());
+}
+
+// Verify that the decoder can successfully decode the first frame when
+// initially only half of the frame data is received, resulting in a partially
+// decoded image, and then the rest of the image data is received. Verify that
+// the bitmap hashes of the two stages are different. Also verify that the final
+// bitmap hash is equivalent to the hash when all data is provided at once.
+//
+// This verifies that decoder correctly keeps track of where it stopped
+// decoding when the image was not yet fully received.
+void testProgressiveDecodingContinuesAfterFullData(const char* pngFile,
+                                                   size_t offsetMidFirstFrame) {
+  auto fullData = readFile(pngFile);
+  ASSERT_FALSE(fullData->isEmpty());
+
+  auto decoderUpfront = createDecoder();
+  decoderUpfront->setData(fullData.get(), true);
+  EXPECT_GE(1u, decoderUpfront->frameCount());
+  const ImageFrame* const frameUpfront = decoderUpfront->frameBufferAtIndex(0);
+  ASSERT_EQ(ImageFrame::FrameComplete, frameUpfront->getStatus());
+  const unsigned hashUpfront = hashBitmap(frameUpfront->bitmap());
+
+  auto decoder = createDecoder();
+  RefPtr<SharedBuffer> partialData =
+      SharedBuffer::create(fullData->data(), offsetMidFirstFrame);
+  decoder->setData(partialData, false);
+
+  EXPECT_EQ(1u, decoder->frameCount());
+  const ImageFrame* frame = decoder->frameBufferAtIndex(0);
+  EXPECT_EQ(frame->getStatus(), ImageFrame::FramePartial);
+  const unsigned hashPartial = hashBitmap(frame->bitmap());
+
+  decoder->setData(fullData.get(), true);
+  frame = decoder->frameBufferAtIndex(0);
+  EXPECT_EQ(frame->getStatus(), ImageFrame::FrameComplete);
+  const unsigned hashFull = hashBitmap(frame->bitmap());
+
+  EXPECT_FALSE(decoder->failed());
+  EXPECT_NE(hashFull, hashPartial);
+  EXPECT_EQ(hashFull, hashUpfront);
+}
+
+// Modify the frame data bytes for frame |frameIndex| so that decoding fails.
+// Parsing should work fine, and is checked with |expectedFrameCountBefore|. If
+// the failure should invalidate the decoder, |expectFailure| should be set to
+// true. If not, |expectedFrameCountAfter| should indicate the new frame count
+// after the failure.
+void testFailureDuringDecode(const char* file,
+                             size_t idatOffset,
+                             size_t frameIndex,
+                             bool expectFailure,
+                             size_t expectedFrameCountBefore,
+                             size_t expectedFrameCountAfter = 0u) {
+  RefPtr<SharedBuffer> fullData = readFile(file);
+  ASSERT_FALSE(fullData->isEmpty());
+
+  // This is the offset where the frame data chunk frame |frameIndex| starts.
+  RefPtr<SharedBuffer> data =
+      SharedBuffer::create(fullData->data(), idatOffset + 8u);
+  // Repeat the first 8 bytes of the frame data. This should result in a
+  // successful parse, since frame data is not analyzed in that step, but
+  // should give an error during decoding.
+  data->append(fullData->data() + idatOffset, 8u);
+  data->append(fullData->data() + idatOffset + 16u,
+               fullData->size() - idatOffset - 16u);
+
+  auto decoder = createDecoder();
+  decoder->setData(data.get(), true);
+
+  EXPECT_EQ(expectedFrameCountBefore, decoder->frameCount());
+
+  const ImageFrame* const frame = decoder->frameBufferAtIndex(frameIndex);
+  EXPECT_EQ(expectFailure, decoder->failed());
+  if (!expectFailure) {
+    EXPECT_EQ(expectedFrameCountAfter, decoder->frameCount());
+    EXPECT_EQ(ImageFrame::FrameEmpty, frame->getStatus());
+  }
+}
+
+}  // Anonymous namespace
+
+// Static PNG tests
+
+TEST(StaticPNGTests, repetitionCountTest) {
+  testRepetitionCount("/LayoutTests/images/resources/png-simple.png",
+                      cAnimationNone);
+}
+
+TEST(StaticPNGTests, sizeTest) {
+  testSize("/LayoutTests/images/resources/png-simple.png", IntSize(111, 29));
+}
+
+TEST(StaticPNGTests, MetaDataTest) {
+  const size_t expectedFrameCount = 1;
+  const size_t expectedDuration = 0;
+  auto decoder =
+      createDecoderWithPngData("/LayoutTests/images/resources/png-simple.png");
+  EXPECT_EQ(expectedFrameCount, decoder->frameCount());
+  EXPECT_EQ(expectedDuration, decoder->frameDurationAtIndex(0));
+}
+
+TEST(StaticPNGTests, ProgressiveDecoding) {
+  testProgressiveDecoding(&createDecoder,
+                          "/LayoutTests/images/resources/png-simple.png", 11u);
+}
+
+TEST(StaticPNGTests, ProgressiveDecodingContinuesAfterFullData) {
+  testProgressiveDecodingContinuesAfterFullData(
+      "/LayoutTests/images/resources/png-simple.png", 1000u);
+}
+
+TEST(StaticPNGTests, FailureDuringDecodingInvalidatesDecoder) {
+  testFailureDuringDecode(
+      "/LayoutTests/images/resources/png-simple.png",
+      85u,   // idat offset for frame index 0
+      0u,    // try to decode frame index 0
+      true,  // expect the decoder to be invalidated after the failure
+      1u);   // expected frame count before failure
+}
+
+// For static images, frameIsCompleteAtIndex(0) should return true if and only
+// if the frame is successfully decoded, not when it is fully received.
+TEST(StaticPNGTests, VerifyFrameCompleteBehavior) {
+  auto decoder =
+      createDecoderWithPngData("/LayoutTests/images/resources/png-simple.png");
+  EXPECT_EQ(1u, decoder->frameCount());
+  EXPECT_FALSE(decoder->frameIsCompleteAtIndex(0));
+  EXPECT_EQ(ImageFrame::FrameComplete,
+            decoder->frameBufferAtIndex(0)->getStatus());
+  EXPECT_TRUE(decoder->frameIsCompleteAtIndex(0));
+}
+
+};  // namespace blink
diff --git a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.cc b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.cc
index a72e1459..74a311b1 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.cc
@@ -232,11 +232,6 @@
     queues_to_delete_.clear();
 
   LazyNow lazy_now(real_time_domain()->CreateLazyNow());
-  base::TimeTicks task_start_time;
-
-  if (!delegate_->IsNested() && task_time_observers_.might_have_observers())
-    task_start_time = lazy_now.Now();
-
   UpdateWorkQueues(lazy_now);
 
   for (int i = 0; i < work_batch_size_; i++) {
@@ -244,11 +239,7 @@
     if (!SelectWorkQueueToService(&work_queue))
       break;
 
-    // TaskQueueManager guarantees that task queue will not be deleted
-    // when we are in DoWork (but WorkQueue may be deleted).
-    internal::TaskQueueImpl* task_queue = work_queue->task_queue();
-
-    switch (ProcessTaskFromWorkQueue(work_queue)) {
+    switch (ProcessTaskFromWorkQueue(work_queue, &lazy_now)) {
       case ProcessTaskResult::DEFERRED:
         // If a task was deferred, try again with another task.
         continue;
@@ -258,18 +249,6 @@
         return;  // The TaskQueueManager got deleted, we must bail out.
     }
 
-    lazy_now = real_time_domain()->CreateLazyNow();
-    if (!delegate_->IsNested() && task_start_time != base::TimeTicks()) {
-      // Only report top level task durations.
-      base::TimeTicks task_end_time = lazy_now.Now();
-      for (auto& observer : task_time_observers_) {
-        observer.ReportTaskTime(task_queue,
-                                MonotonicTimeInSeconds(task_start_time),
-                                MonotonicTimeInSeconds(task_end_time));
-      }
-      task_start_time = task_end_time;
-    }
-
     work_queue = nullptr;  // The queue may have been unregistered.
 
     UpdateWorkQueues(lazy_now);
@@ -312,7 +291,8 @@
 }
 
 TaskQueueManager::ProcessTaskResult TaskQueueManager::ProcessTaskFromWorkQueue(
-    internal::WorkQueue* work_queue) {
+    internal::WorkQueue* work_queue,
+    LazyNow* lazy_now) {
   DCHECK(main_thread_checker_.CalledOnValidThread());
   scoped_refptr<DeletionSentinel> protect(deletion_sentinel_);
   internal::TaskQueueImpl::Task pending_task =
@@ -341,13 +321,23 @@
 
   MaybeRecordTaskDelayHistograms(pending_task, queue);
 
+  double task_start_time = 0;
   TRACE_TASK_EXECUTION("TaskQueueManager::ProcessTaskFromWorkQueue",
                        pending_task);
   if (queue->GetShouldNotifyObservers()) {
     for (auto& observer : task_observers_)
       observer.WillProcessTask(pending_task);
     queue->NotifyWillProcessTask(pending_task);
+
+    bool notify_time_observers =
+        !delegate_->IsNested() && task_time_observers_.might_have_observers();
+    if (notify_time_observers) {
+      task_start_time = MonotonicTimeInSeconds(lazy_now->Now());
+      for (auto& observer : task_time_observers_)
+        observer.willProcessTask(queue, task_start_time);
+    }
   }
+
   TRACE_EVENT1(tracing_category_, "TaskQueueManager::RunTask", "queue",
                queue->GetName());
   // NOTE when TaskQueues get unregistered a reference ends up getting retained
@@ -364,7 +354,15 @@
 
   currently_executing_task_queue_ = prev_executing_task_queue;
 
+  *lazy_now = real_time_domain()->CreateLazyNow();
+
   if (queue->GetShouldNotifyObservers()) {
+    if (task_start_time) {
+      double task_end_time = MonotonicTimeInSeconds(lazy_now->Now());
+      for (auto& observer : task_time_observers_)
+        observer.didProcessTask(queue, task_start_time, task_end_time);
+    }
+
     for (auto& observer : task_observers_)
       observer.DidProcessTask(pending_task);
     queue->NotifyDidProcessTask(pending_task);
diff --git a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.h b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.h
index 5509bd9..0fbfa7e 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.h
+++ b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.h
@@ -188,7 +188,8 @@
     EXECUTED,
     TASK_QUEUE_MANAGER_DELETED
   };
-  ProcessTaskResult ProcessTaskFromWorkQueue(internal::WorkQueue* work_queue);
+  ProcessTaskResult ProcessTaskFromWorkQueue(internal::WorkQueue* work_queue,
+                                             LazyNow*);
 
   bool RunsTasksOnCurrentThread() const;
   bool PostNonNestableDelayedTask(const tracked_objects::Location& from_here,
diff --git a/third_party/WebKit/Source/platform/scheduler/base/test_task_time_observer.h b/third_party/WebKit/Source/platform/scheduler/base/test_task_time_observer.h
index 7d2e64d..f448af7 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/test_task_time_observer.h
+++ b/third_party/WebKit/Source/platform/scheduler/base/test_task_time_observer.h
@@ -13,7 +13,8 @@
 
 class TestTaskTimeObserver : public TaskTimeObserver {
  public:
-  void ReportTaskTime(TaskQueue* task_queue,
+  void willProcessTask(TaskQueue* task_queue, double start_time) override {}
+  void didProcessTask(TaskQueue* task_queue,
                       double start_time,
                       double end_time) override {}
 };
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 0beb608e..752e9a1 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
@@ -1661,7 +1661,7 @@
   }
 }
 
-void RendererSchedulerImpl::ReportTaskTime(TaskQueue* task_queue,
+void RendererSchedulerImpl::didProcessTask(TaskQueue* task_queue,
                                            double start_time,
                                            double end_time) {
   // TODO(scheduler-dev): Remove conversions when Blink starts using
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 2d06c3ca..a9286e04 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
@@ -136,7 +136,8 @@
                                    const base::PendingTask& task) override;
 
   // TaskTimeObserver implementation:
-  void ReportTaskTime(TaskQueue* task_queue,
+  void willProcessTask(TaskQueue* task_queue, double start_time) override{};
+  void didProcessTask(TaskQueue* task_queue,
                       double start_time,
                       double end_time) override;
 
diff --git a/third_party/WebKit/Source/web/FrameLoaderClientImpl.cpp b/third_party/WebKit/Source/web/FrameLoaderClientImpl.cpp
index 1fae82c..362cd93 100644
--- a/third_party/WebKit/Source/web/FrameLoaderClientImpl.cpp
+++ b/third_party/WebKit/Source/web/FrameLoaderClientImpl.cpp
@@ -872,9 +872,9 @@
 }
 
 void FrameLoaderClientImpl::didSetFeaturePolicyHeader(
-    const String& headerValue) {
+    const WebParsedFeaturePolicy& parsedHeader) {
   if (m_webFrame->client())
-    m_webFrame->client()->didSetFeaturePolicyHeader(headerValue);
+    m_webFrame->client()->didSetFeaturePolicyHeader(parsedHeader);
 }
 
 void FrameLoaderClientImpl::didAddContentSecurityPolicy(
diff --git a/third_party/WebKit/Source/web/FrameLoaderClientImpl.h b/third_party/WebKit/Source/web/FrameLoaderClientImpl.h
index 3ddbeaaa..2a8ba08 100644
--- a/third_party/WebKit/Source/web/FrameLoaderClientImpl.h
+++ b/third_party/WebKit/Source/web/FrameLoaderClientImpl.h
@@ -186,7 +186,8 @@
   void didEnforceInsecureRequestPolicy(WebInsecureRequestPolicy) override;
   void didUpdateToUniqueOrigin() override;
   void didChangeSandboxFlags(Frame* childFrame, SandboxFlags) override;
-  void didSetFeaturePolicyHeader(const String& headerValue) override;
+  void didSetFeaturePolicyHeader(
+      const WebParsedFeaturePolicy& parsedHeader) override;
   void didAddContentSecurityPolicy(const String& headerValue,
                                    ContentSecurityPolicyHeaderType,
                                    ContentSecurityPolicyHeaderSource) override;
diff --git a/third_party/WebKit/Source/web/WebRemoteFrameImpl.cpp b/third_party/WebKit/Source/web/WebRemoteFrameImpl.cpp
index 0d8c7ccb..73c09aa 100644
--- a/third_party/WebKit/Source/web/WebRemoteFrameImpl.cpp
+++ b/third_party/WebKit/Source/web/WebRemoteFrameImpl.cpp
@@ -433,14 +433,14 @@
 }
 
 void WebRemoteFrameImpl::setReplicatedFeaturePolicyHeader(
-    const WebString& headerValue) const {
+    const WebParsedFeaturePolicy& parsedHeader) const {
   if (RuntimeEnabledFeatures::featurePolicyEnabled()) {
     FeaturePolicy* parentFeaturePolicy = nullptr;
     if (parent()) {
       Frame* parentFrame = frame()->client()->parent();
       parentFeaturePolicy = parentFrame->securityContext()->getFeaturePolicy();
     }
-    frame()->securityContext()->setFeaturePolicyFromHeader(headerValue,
+    frame()->securityContext()->setFeaturePolicyFromHeader(parsedHeader,
                                                            parentFeaturePolicy);
   }
 }
diff --git a/third_party/WebKit/Source/web/WebRemoteFrameImpl.h b/third_party/WebKit/Source/web/WebRemoteFrameImpl.h
index 9489567..4cf55f6 100644
--- a/third_party/WebKit/Source/web/WebRemoteFrameImpl.h
+++ b/third_party/WebKit/Source/web/WebRemoteFrameImpl.h
@@ -147,7 +147,8 @@
   void setReplicatedSandboxFlags(WebSandboxFlags) const override;
   void setReplicatedName(const WebString& name,
                          const WebString& uniqueName) const override;
-  void setReplicatedFeaturePolicyHeader(const WebString&) const override;
+  void setReplicatedFeaturePolicyHeader(
+      const WebParsedFeaturePolicy& parsedHeader) const override;
   void addReplicatedContentSecurityPolicyHeader(
       const WebString& headerValue,
       WebContentSecurityPolicyType,
diff --git a/third_party/WebKit/public/platform/WebDisplayItemList.h b/third_party/WebKit/public/platform/WebDisplayItemList.h
index 7bce0e8..0a46f5edd 100644
--- a/third_party/WebKit/public/platform/WebDisplayItemList.h
+++ b/third_party/WebKit/public/platform/WebDisplayItemList.h
@@ -13,11 +13,12 @@
 #include "WebVector.h"
 
 #include "third_party/skia/include/core/SkBlendMode.h"
+#include "third_party/skia/include/core/SkClipOp.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
-#include "third_party/skia/include/core/SkRegion.h"
 
 class SkColorFilter;
 class SkMatrix44;
+class SkPath;
 class SkPicture;
 struct SkRect;
 class SkRRect;
@@ -46,8 +47,7 @@
   virtual void appendClipItem(const WebRect& clipRect,
                               const WebVector<SkRRect>& roundedClipRects) {}
   virtual void appendEndClipItem() {}
-  virtual void appendClipPathItem(const SkPath&, SkRegion::Op, bool antialias) {
-  }
+  virtual void appendClipPathItem(const SkPath&, SkClipOp, bool antialias) {}
   virtual void appendEndClipPathItem() {}
   virtual void appendFloatClipItem(const WebFloatRect& clipRect) {}
   virtual void appendEndFloatClipItem() {}
diff --git a/third_party/WebKit/public/platform/WebFeaturePolicy.h b/third_party/WebKit/public/platform/WebFeaturePolicy.h
new file mode 100644
index 0000000..a2c03f7
--- /dev/null
+++ b/third_party/WebKit/public/platform/WebFeaturePolicy.h
@@ -0,0 +1,27 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WebFeaturePolicy_h
+#define WebFeaturePolicy_h
+
+#include "WebSecurityOrigin.h"
+#include "WebString.h"
+#include "WebVector.h"
+
+namespace blink {
+
+struct WebFeaturePolicy {
+  struct ParsedWhitelist {
+    ParsedWhitelist() : matchesAllOrigins(false) {}
+    WebString featureName;
+    bool matchesAllOrigins;
+    WebVector<WebSecurityOrigin> origins;
+  };
+};
+
+using WebParsedFeaturePolicy = WebVector<WebFeaturePolicy::ParsedWhitelist>;
+
+}  // namespace blink
+
+#endif  // WebFeaturePolicy_h
diff --git a/third_party/WebKit/public/platform/scheduler/base/task_time_observer.h b/third_party/WebKit/public/platform/scheduler/base/task_time_observer.h
index 81714dd..6f8c89c 100644
--- a/third_party/WebKit/public/platform/scheduler/base/task_time_observer.h
+++ b/third_party/WebKit/public/platform/scheduler/base/task_time_observer.h
@@ -19,11 +19,16 @@
   TaskTimeObserver() {}
   virtual ~TaskTimeObserver() {}
 
+  // Callback to be called when task is about to start.
+  // |task_queue| - TaskQueue on which this task will run,
+  // |start_time| - time in seconds when task started to run,
+  virtual void willProcessTask(TaskQueue* task_queue, double start_time) = 0;
+
   // Callback to be called when task is completed.
   // |task_queue| - TaskQueue on which this task was run,
   // |start_time| - time in seconds when task started to run,
   // |end_time| - time in seconds when task was completed.
-  virtual void ReportTaskTime(TaskQueue* task_queue,
+  virtual void didProcessTask(TaskQueue* task_queue,
                               double start_time,
                               double end_time) = 0;
 
diff --git a/third_party/WebKit/public/web/WebFrameClient.h b/third_party/WebKit/public/web/WebFrameClient.h
index 2593df06f..1dc6db0 100644
--- a/third_party/WebKit/public/web/WebFrameClient.h
+++ b/third_party/WebKit/public/web/WebFrameClient.h
@@ -50,6 +50,7 @@
 #include "public/platform/BlameContext.h"
 #include "public/platform/WebCommon.h"
 #include "public/platform/WebEffectiveConnectionType.h"
+#include "public/platform/WebFeaturePolicy.h"
 #include "public/platform/WebFileSystem.h"
 #include "public/platform/WebFileSystemType.h"
 #include "public/platform/WebInsecureRequestPolicy.h"
@@ -229,7 +230,8 @@
 
   // Called when a Feature-Policy HTTP header is encountered while loading the
   // frame's document.
-  virtual void didSetFeaturePolicyHeader(const WebString& headerValue) {}
+  virtual void didSetFeaturePolicyHeader(
+      const WebParsedFeaturePolicy& parsedHeader) {}
 
   // Called when a new Content Security Policy is added to the frame's
   // document.  This can be triggered by handling of HTTP headers, handling
diff --git a/third_party/WebKit/public/web/WebRemoteFrame.h b/third_party/WebKit/public/web/WebRemoteFrame.h
index b8307b9..28f973f 100644
--- a/third_party/WebKit/public/web/WebRemoteFrame.h
+++ b/third_party/WebKit/public/web/WebRemoteFrame.h
@@ -5,6 +5,7 @@
 #ifndef WebRemoteFrame_h
 #define WebRemoteFrame_h
 
+#include "public/platform/WebFeaturePolicy.h"
 #include "public/platform/WebInsecureRequestPolicy.h"
 #include "public/web/WebContentSecurityPolicy.h"
 #include "public/web/WebFrame.h"
@@ -59,7 +60,7 @@
                                  const WebString& uniqueName) const = 0;
 
   virtual void setReplicatedFeaturePolicyHeader(
-      const WebString& headerValue) const = 0;
+      const WebParsedFeaturePolicy& parsedHeader) const = 0;
 
   // Adds |header| to the set of replicated CSP headers.
   virtual void addReplicatedContentSecurityPolicyHeader(
diff --git a/tools/determinism/deterministic_build_whitelist.pyl b/tools/determinism/deterministic_build_whitelist.pyl
index cccd9f9..3a0c96f1 100644
--- a/tools/determinism/deterministic_build_whitelist.pyl
+++ b/tools/determinism/deterministic_build_whitelist.pyl
@@ -110,7 +110,7 @@
     'mac_installer_unittests',
     'macviews_interactive_ui_tests',
     'media_blink_unittests',
-    'media_mojo_shell_unittests',
+    'media_service_unittests',
     'media_mojo_unittests',
     'media_perftests',
     'media_pipeline_integration_unittests',
@@ -268,7 +268,7 @@
     'mash_init_library.dll',
     'media_blink_unittests.exe',
     'media_library.dll',
-    'media_mojo_shell_unittests.exe',
+    'media_service_unittests.exe',
     'media_mojo_unittests.exe',
     'media_perftests.exe',
     'media_pipeline_integration_unittests.exe',
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 8b5e656..be82630 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -95984,6 +95984,7 @@
   <int value="8" label="RequestCoordinator timed-out"/>
   <int value="9" label="Prerendering not started"/>
   <int value="10" label="Prerendering failed (non-retryable)"/>
+  <int value="11" label="Prerendering failed (don't start next request)"/>
 </enum>
 
 <enum name="OfflinePagesCctApiPrerenderAllowedStatus" type="int">
diff --git a/ui/aura/mus/window_manager_delegate.h b/ui/aura/mus/window_manager_delegate.h
index 4d135397..0dc8d618 100644
--- a/ui/aura/mus/window_manager_delegate.h
+++ b/ui/aura/mus/window_manager_delegate.h
@@ -14,7 +14,7 @@
 
 #include "base/callback_forward.h"
 #include "services/ui/public/interfaces/cursor.mojom.h"
-#include "services/ui/public/interfaces/event_matcher.mojom.h"
+#include "services/ui/public/interfaces/window_manager.mojom.h"
 #include "services/ui/public/interfaces/window_manager_constants.mojom.h"
 #include "services/ui/public/interfaces/window_tree_constants.mojom.h"
 #include "ui/aura/aura_export.h"
@@ -48,9 +48,9 @@
   virtual void SetNonClientCursor(Window* window,
                                   ui::mojom::Cursor non_client_cursor) = 0;
 
-  virtual void AddAccelerator(uint32_t id,
-                              ui::mojom::EventMatcherPtr event_matcher,
-                              const base::Callback<void(bool)>& callback) = 0;
+  virtual void AddAccelerators(
+      std::vector<ui::mojom::AcceleratorPtr> accelerators,
+      const base::Callback<void(bool)>& callback) = 0;
   virtual void RemoveAccelerator(uint32_t id) = 0;
   virtual void AddActivationParent(Window* window) = 0;
   virtual void RemoveActivationParent(Window* window) = 0;
diff --git a/ui/aura/mus/window_tree_client.cc b/ui/aura/mus/window_tree_client.cc
index 042d000..d318eab 100644
--- a/ui/aura/mus/window_tree_client.cc
+++ b/ui/aura/mus/window_tree_client.cc
@@ -1438,13 +1438,12 @@
       WindowMus::Get(window)->server_id(), cursor_id);
 }
 
-void WindowTreeClient::AddAccelerator(
-    uint32_t id,
-    ui::mojom::EventMatcherPtr event_matcher,
+void WindowTreeClient::AddAccelerators(
+    std::vector<ui::mojom::AcceleratorPtr> accelerators,
     const base::Callback<void(bool)>& callback) {
   if (window_manager_internal_client_) {
-    window_manager_internal_client_->AddAccelerators(
-        ui::CreateAcceleratorVector(id, std::move(event_matcher)), callback);
+    window_manager_internal_client_->AddAccelerators(std::move(accelerators),
+                                                     callback);
   }
 }
 
diff --git a/ui/aura/mus/window_tree_client.h b/ui/aura/mus/window_tree_client.h
index 6178a69..0945366 100644
--- a/ui/aura/mus/window_tree_client.h
+++ b/ui/aura/mus/window_tree_client.h
@@ -398,9 +398,8 @@
   void SetFrameDecorationValues(
       ui::mojom::FrameDecorationValuesPtr values) override;
   void SetNonClientCursor(Window* window, ui::mojom::Cursor cursor_id) override;
-  void AddAccelerator(uint32_t id,
-                      ui::mojom::EventMatcherPtr event_matcher,
-                      const base::Callback<void(bool)>& callback) override;
+  void AddAccelerators(std::vector<ui::mojom::AcceleratorPtr> accelerators,
+                       const base::Callback<void(bool)>& callback) override;
   void RemoveAccelerator(uint32_t id) override;
   void AddActivationParent(Window* window) override;
   void RemoveActivationParent(Window* window) override;
diff --git a/ui/compositor/clip_recorder.cc b/ui/compositor/clip_recorder.cc
index 1c0eb55..14650df 100644
--- a/ui/compositor/clip_recorder.cc
+++ b/ui/compositor/clip_recorder.cc
@@ -49,14 +49,14 @@
 void ClipRecorder::ClipPath(const gfx::Path& clip_path) {
   bool antialias = false;
   context_.list_->CreateAndAppendPairedBeginItem<cc::ClipPathDisplayItem>(
-      clip_path, SkRegion::kIntersect_Op, antialias);
+      clip_path, kIntersect_SkClipOp, antialias);
   RecordCloser(CLIP_PATH);
 }
 
 void ClipRecorder::ClipPathWithAntiAliasing(const gfx::Path& clip_path) {
   bool antialias = true;
   context_.list_->CreateAndAppendPairedBeginItem<cc::ClipPathDisplayItem>(
-      clip_path, SkRegion::kIntersect_Op, antialias);
+      clip_path, kIntersect_SkClipOp, antialias);
   RecordCloser(CLIP_PATH);
 }
 
diff --git a/ui/display/manager/display_manager.cc b/ui/display/manager/display_manager.cc
index 9d74e34..6d3e03ab0 100644
--- a/ui/display/manager/display_manager.cc
+++ b/ui/display/manager/display_manager.cc
@@ -394,7 +394,8 @@
     const gfx::Insets* overscan_insets,
     const gfx::Size& resolution_in_pixels,
     float device_scale_factor,
-    ui::ColorCalibrationProfile color_profile) {
+    ui::ColorCalibrationProfile color_profile,
+    const TouchCalibrationData* touch_calibration_data) {
   if (display_info_.find(display_id) == display_info_.end())
     display_info_[display_id] =
         ManagedDisplayInfo(display_id, std::string(), false);
@@ -415,6 +416,8 @@
     display_info_[display_id].set_configured_ui_scale(ui_scale);
   if (overscan_insets)
     display_info_[display_id].SetOverscanInsets(*overscan_insets);
+  if (touch_calibration_data)
+    display_info_[display_id].SetTouchCalibrationData(*touch_calibration_data);
   if (!resolution_in_pixels.IsEmpty()) {
     DCHECK(!Display::IsInternalDisplayId(display_id));
     // Default refresh rate, until OnNativeDisplaysChanged() updates us with the
@@ -984,6 +987,44 @@
 bool DisplayManager::SoftwareMirroringEnabled() const {
   return software_mirroring_enabled();
 }
+
+void DisplayManager::SetTouchCalibrationData(
+    int64_t display_id,
+    const TouchCalibrationData::CalibrationPointPairQuad& point_pair_quad,
+    const gfx::Size& display_bounds) {
+  bool update = false;
+  TouchCalibrationData calibration_data(point_pair_quad, display_bounds);
+  DisplayInfoList display_info_list;
+  for (const auto& display : active_display_list_) {
+    display::ManagedDisplayInfo info = GetDisplayInfo(display.id());
+    if (info.id() == display_id) {
+      info.SetTouchCalibrationData(calibration_data);
+      update = true;
+    }
+    display_info_list.push_back(info);
+  }
+  if (update)
+    UpdateDisplaysWith(display_info_list);
+  else
+    display_info_[display_id].SetTouchCalibrationData(calibration_data);
+}
+
+void DisplayManager::ClearTouchCalibrationData(int64_t display_id) {
+  bool update = false;
+  DisplayInfoList display_info_list;
+  for (const auto& display : active_display_list_) {
+    display::ManagedDisplayInfo info = GetDisplayInfo(display.id());
+    if (info.id() == display_id) {
+      info.clear_touch_calibration_data();
+      update = true;
+    }
+    display_info_list.push_back(info);
+  }
+  if (update)
+    UpdateDisplaysWith(display_info_list);
+  else
+    display_info_[display_id].clear_touch_calibration_data();
+}
 #endif
 
 void DisplayManager::SetDefaultMultiDisplayModeForCurrentDisplays(
diff --git a/ui/display/manager/display_manager.h b/ui/display/manager/display_manager.h
index 6b58000c1..8437a3f1 100644
--- a/ui/display/manager/display_manager.h
+++ b/ui/display/manager/display_manager.h
@@ -164,15 +164,19 @@
   bool SetDisplayMode(int64_t display_id,
                       const scoped_refptr<ManagedDisplayMode>& display_mode);
 
-  // Register per display properties. |overscan_insets| is null if the display
-  // has no custom overscan insets.
-  void RegisterDisplayProperty(int64_t display_id,
-                               Display::Rotation rotation,
-                               float ui_scale,
-                               const gfx::Insets* overscan_insets,
-                               const gfx::Size& resolution_in_pixels,
-                               float device_scale_factor,
-                               ui::ColorCalibrationProfile color_profile);
+  // Register per display properties.
+  // |overscan_insets| is null if the display has no custom overscan insets.
+  // |touch_calibration_data| is null if the display has no touch calibration
+  // associated data.
+  void RegisterDisplayProperty(
+      int64_t display_id,
+      Display::Rotation rotation,
+      float ui_scale,
+      const gfx::Insets* overscan_insets,
+      const gfx::Size& resolution_in_pixels,
+      float device_scale_factor,
+      ui::ColorCalibrationProfile color_profile,
+      const TouchCalibrationData* touch_calibration_data);
 
   // Register stored rotation properties for the internal display.
   void RegisterDisplayRotationProperties(bool rotation_lock,
@@ -295,6 +299,11 @@
 #if defined(OS_CHROMEOS)
   void SetSoftwareMirroring(bool enabled) override;
   bool SoftwareMirroringEnabled() const override;
+  void SetTouchCalibrationData(
+      int64_t display_id,
+      const TouchCalibrationData::CalibrationPointPairQuad& point_pair_quad,
+      const gfx::Size& display_bounds);
+  void ClearTouchCalibrationData(int64_t display_id);
 #endif
 
   // Sets/gets default multi display mode.
diff --git a/ui/display/manager/managed_display_info.cc b/ui/display/manager/managed_display_info.cc
index 73b33084..1dd52ba 100644
--- a/ui/display/manager/managed_display_info.cc
+++ b/ui/display/manager/managed_display_info.cc
@@ -87,6 +87,19 @@
     : point_pairs(calibration_data.point_pairs),
       bounds(calibration_data.bounds) {}
 
+bool TouchCalibrationData::operator==(TouchCalibrationData other) const {
+  if (bounds != other.bounds)
+    return false;
+  CalibrationPointPairQuad quad_1 = point_pairs;
+  CalibrationPointPairQuad& quad_2 = other.point_pairs;
+
+  // Make sure the point pairs are in the correct order.
+  std::sort(quad_1.begin(), quad_1.end(), CalibrationPointPairCompare);
+  std::sort(quad_2.begin(), quad_2.end(), CalibrationPointPairCompare);
+
+  return quad_1 == quad_2;
+}
+
 ManagedDisplayMode::ManagedDisplayMode()
     : refresh_rate_(0.0f),
       is_interlaced_(false),
diff --git a/ui/display/manager/managed_display_info.h b/ui/display/manager/managed_display_info.h
index 57c259b..ff530b0 100644
--- a/ui/display/manager/managed_display_info.h
+++ b/ui/display/manager/managed_display_info.h
@@ -6,7 +6,7 @@
 #define UI_DISPLAY_MANAGER_MANAGED_DISPLAY_INFO_H_
 
 #include <stdint.h>
-
+#include <algorithm>
 #include <array>
 #include <map>
 #include <string>
@@ -34,6 +34,15 @@
                        const gfx::Size& bounds);
   TouchCalibrationData(const TouchCalibrationData& calibration_data);
 
+  static bool CalibrationPointPairCompare(const CalibrationPointPair& pair_1,
+                                          const CalibrationPointPair& pair_2) {
+    return pair_1.first.y() < pair_2.first.y()
+               ? true
+               : pair_1.first.x() < pair_2.first.x();
+  }
+
+  bool operator==(TouchCalibrationData other) const;
+
   // Calibration point pairs used during calibration. Each point pair contains a
   // display point and the corresponding touch point.
   CalibrationPointPairQuad point_pairs;
diff --git a/ui/display/manager/managed_display_info_unittest.cc b/ui/display/manager/managed_display_info_unittest.cc
index b27dcc4..069c761 100644
--- a/ui/display/manager/managed_display_info_unittest.cc
+++ b/ui/display/manager/managed_display_info_unittest.cc
@@ -168,15 +168,7 @@
   info.SetTouchCalibrationData(expected_data);
 
   EXPECT_TRUE(info.has_touch_calibration_data());
-  TouchCalibrationData actual_data = info.GetTouchCalibrationData();
-  EXPECT_EQ(actual_data.bounds, size);
-  for (size_t i = 0; i < expected_data.point_pairs.size(); i++) {
-    EXPECT_EQ(actual_data.point_pairs[i].first,
-              expected_data.point_pairs[i].first);
-
-    EXPECT_EQ(actual_data.point_pairs[i].second,
-              expected_data.point_pairs[i].second);
-  }
+  EXPECT_EQ(expected_data, info.GetTouchCalibrationData());
 
   // Clear all touch calibration data for the display.
   info.clear_touch_calibration_data();
diff --git a/ui/gfx/canvas.cc b/ui/gfx/canvas.cc
index 5dd5774..9dd7aea 100644
--- a/ui/gfx/canvas.cc
+++ b/ui/gfx/canvas.cc
@@ -187,16 +187,16 @@
   canvas_->restore();
 }
 
-void Canvas::ClipRect(const Rect& rect, SkRegion::Op op) {
+void Canvas::ClipRect(const Rect& rect, SkClipOp op) {
   canvas_->clipRect(RectToSkRect(rect), op);
 }
 
-void Canvas::ClipRect(const RectF& rect, SkRegion::Op op) {
+void Canvas::ClipRect(const RectF& rect, SkClipOp op) {
   canvas_->clipRect(RectFToSkRect(rect), op);
 }
 
 void Canvas::ClipPath(const SkPath& path, bool do_anti_alias) {
-  canvas_->clipPath(path, SkRegion::kIntersect_Op, do_anti_alias);
+  canvas_->clipPath(path, kIntersect_SkClipOp, do_anti_alias);
 }
 
 bool Canvas::IsClipEmpty() const {
diff --git a/ui/gfx/canvas.h b/ui/gfx/canvas.h
index 66a5598..24d6dbe 100644
--- a/ui/gfx/canvas.h
+++ b/ui/gfx/canvas.h
@@ -196,8 +196,8 @@
   void Restore();
 
   // Applies |rect| to the current clip using the specified region |op|.
-  void ClipRect(const Rect& rect, SkRegion::Op op = SkRegion::kIntersect_Op);
-  void ClipRect(const RectF& rect, SkRegion::Op op = SkRegion::kIntersect_Op);
+  void ClipRect(const Rect& rect, SkClipOp op = kIntersect_SkClipOp);
+  void ClipRect(const RectF& rect, SkClipOp op = kIntersect_SkClipOp);
 
   // Adds |path| to the current clip. |do_anti_alias| is true if the clip
   // should be antialiased.
diff --git a/ui/gl/BUILD.gn b/ui/gl/BUILD.gn
index e7705f6..5050c891 100644
--- a/ui/gl/BUILD.gn
+++ b/ui/gl/BUILD.gn
@@ -154,8 +154,6 @@
 
   if (use_egl) {
     sources += [
-      "angle_platform_impl.cc",
-      "angle_platform_impl.h",
       "egl_util.cc",
       "egl_util.h",
       "gl_bindings_autogen_egl.cc",
@@ -221,6 +219,8 @@
   }
   if (is_win) {
     sources += [
+      "angle_platform_impl.cc",
+      "angle_platform_impl.h",
       "gl_bindings_autogen_wgl.cc",
       "gl_bindings_autogen_wgl.h",
       "gl_context_wgl.cc",
diff --git a/ui/gl/egl_api_unittest.cc b/ui/gl/egl_api_unittest.cc
index 772cf06..b9dbd15a 100644
--- a/ui/gl/egl_api_unittest.cc
+++ b/ui/gl/egl_api_unittest.cc
@@ -25,7 +25,6 @@
     g_driver_egl.fn.eglGetCurrentDisplayFn = &FakeGetCurrentDisplay;
     g_driver_egl.fn.eglGetDisplayFn = &FakeGetDisplay;
     g_driver_egl.fn.eglGetErrorFn = &FakeGetError;
-    g_driver_egl.fn.eglGetProcAddressFn = &FakeGetProcAddress;
   }
 
   void TearDown() override {
@@ -83,11 +82,6 @@
     return EGL_SUCCESS;
   }
 
-  static __eglMustCastToProperFunctionPointerType GL_BINDING_CALL
-  FakeGetProcAddress(const char* procname) {
-    return nullptr;
-  }
-
   std::pair<const char*, const char*> GetExtensions() {
     return std::make_pair(
         api_->eglQueryStringFn(EGL_NO_DISPLAY, EGL_EXTENSIONS),
diff --git a/ui/gl/gl_surface_egl.cc b/ui/gl/gl_surface_egl.cc
index 56032ca9..54ffb7dd 100644
--- a/ui/gl/gl_surface_egl.cc
+++ b/ui/gl/gl_surface_egl.cc
@@ -12,7 +12,6 @@
 #include <vector>
 
 #include "base/command_line.h"
-#include "base/lazy_instance.h"
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/message_loop/message_loop.h"
@@ -22,7 +21,6 @@
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "ui/gfx/geometry/rect.h"
-#include "ui/gl/angle_platform_impl.h"
 #include "ui/gl/egl_util.h"
 #include "ui/gl/gl_context.h"
 #include "ui/gl/gl_context_egl.h"
@@ -136,10 +134,6 @@
 bool g_egl_surface_orientation_supported = false;
 bool g_use_direct_composition = false;
 
-base::LazyInstance<ANGLEPlatformImpl> g_angle_platform_impl =
-    LAZY_INSTANCE_INITIALIZER;
-ANGLEPlatformShutdownFunc g_angle_platform_shutdown = nullptr;
-
 EGLDisplay GetPlatformANGLEDisplay(EGLNativeDisplayType native_display,
                                    EGLenum platform_type,
                                    bool warpDevice) {
@@ -539,11 +533,7 @@
 }
 
 // static
-void GLSurfaceEGL::ShutdownOneOff() {
-  if (g_angle_platform_shutdown) {
-    g_angle_platform_shutdown();
-  }
-
+void GLSurfaceEGL::ResetForTesting() {
   if (g_display != EGL_NO_DISPLAY)
     eglTerminate(g_display);
   g_display = EGL_NO_DISPLAY;
@@ -617,17 +607,6 @@
 
   g_native_display = native_display;
 
-  // Init ANGLE platform here, before we call GetPlatformDisplay().
-  ANGLEPlatformInitializeFunc angle_platform_init =
-      reinterpret_cast<ANGLEPlatformInitializeFunc>(
-          eglGetProcAddress("ANGLEPlatformInitialize"));
-  if (angle_platform_init) {
-    angle_platform_init(&g_angle_platform_impl.Get());
-
-    g_angle_platform_shutdown = reinterpret_cast<ANGLEPlatformShutdownFunc>(
-        eglGetProcAddress("ANGLEPlatformShutdown"));
-  }
-
   // If EGL_EXT_client_extensions not supported this call to eglQueryString
   // will return NULL.
   const char* client_extensions =
diff --git a/ui/gl/gl_surface_egl.h b/ui/gl/gl_surface_egl.h
index dc000eba..1a0e7055 100644
--- a/ui/gl/gl_surface_egl.h
+++ b/ui/gl/gl_surface_egl.h
@@ -78,7 +78,7 @@
   GLSurface::Format GetFormat() override;
 
   static bool InitializeOneOff(EGLNativeDisplayType native_display);
-  static void ShutdownOneOff();
+  static void ResetForTesting();
   static EGLDisplay GetHardwareDisplay();
   static EGLDisplay InitializeDisplay(EGLNativeDisplayType native_display);
   static EGLNativeDisplayType GetNativeDisplay();
diff --git a/ui/gl/init/gl_initializer_android.cc b/ui/gl/init/gl_initializer_android.cc
index f338cc0..d528ad4 100644
--- a/ui/gl/init/gl_initializer_android.cc
+++ b/ui/gl/init/gl_initializer_android.cc
@@ -97,7 +97,6 @@
 }
 
 void ShutdownGLPlatform() {
-  GLSurfaceEGL::ShutdownOneOff();
   ClearBindingsEGL();
   ClearBindingsGL();
   ClearBindingsOSMESA();
diff --git a/ui/gl/init/gl_initializer_ozone.cc b/ui/gl/init/gl_initializer_ozone.cc
index 213ef8b..624e43b 100644
--- a/ui/gl/init/gl_initializer_ozone.cc
+++ b/ui/gl/init/gl_initializer_ozone.cc
@@ -92,7 +92,6 @@
 }
 
 void ShutdownGLPlatform() {
-  GLSurfaceEGL::ShutdownOneOff();
   if (HasGLOzone()) {
     GetGLOzone()->ShutdownGL();
     return;
diff --git a/ui/gl/init/gl_initializer_win.cc b/ui/gl/init/gl_initializer_win.cc
index 89a3586..5b0fc28 100644
--- a/ui/gl/init/gl_initializer_win.cc
+++ b/ui/gl/init/gl_initializer_win.cc
@@ -11,6 +11,7 @@
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/files/file_path.h"
+#include "base/lazy_instance.h"
 #include "base/logging.h"
 #include "base/native_library.h"
 #include "base/path_service.h"
@@ -18,6 +19,8 @@
 #include "base/threading/thread_restrictions.h"
 #include "base/trace_event/trace_event.h"
 #include "base/win/windows_version.h"
+// TODO(jmadill): Apply to all platforms eventually
+#include "ui/gl/angle_platform_impl.h"
 #include "ui/gl/gl_bindings.h"
 #include "ui/gl/gl_egl_api_implementation.h"
 #include "ui/gl/gl_features.h"
@@ -35,6 +38,12 @@
 
 const wchar_t kD3DCompiler[] = L"D3DCompiler_47.dll";
 
+// TODO(jmadill): Apply to all platforms eventually
+base::LazyInstance<ANGLEPlatformImpl> g_angle_platform_impl =
+    LAZY_INSTANCE_INITIALIZER;
+
+ANGLEPlatformShutdownFunc g_angle_platform_shutdown = nullptr;
+
 bool LoadD3DXLibrary(const base::FilePath& module_path,
                      const base::FilePath::StringType& name) {
   base::NativeLibrary library =
@@ -143,6 +152,22 @@
   }
 #endif
 
+  if (!using_swift_shader) {
+    // Init ANGLE platform here, before we call GetPlatformDisplay().
+    // TODO(jmadill): Apply to all platforms eventually
+    ANGLEPlatformInitializeFunc angle_platform_init =
+        reinterpret_cast<ANGLEPlatformInitializeFunc>(
+            base::GetFunctionPointerFromNativeLibrary(
+                gles_library, "ANGLEPlatformInitialize"));
+    if (angle_platform_init) {
+      angle_platform_init(&g_angle_platform_impl.Get());
+
+      g_angle_platform_shutdown = reinterpret_cast<ANGLEPlatformShutdownFunc>(
+          base::GetFunctionPointerFromNativeLibrary(gles_library,
+                                                    "ANGLEPlatformShutdown"));
+    }
+  }
+
   GLGetProcAddressProc get_proc_address =
       reinterpret_cast<GLGetProcAddressProc>(
           base::GetFunctionPointerFromNativeLibrary(egl_library,
@@ -295,7 +320,11 @@
 }
 
 void ShutdownGLPlatform() {
-  GLSurfaceEGL::ShutdownOneOff();
+  // TODO(jmadill): Apply to all platforms eventually
+  if (g_angle_platform_shutdown) {
+    g_angle_platform_shutdown();
+  }
+
   ClearBindingsEGL();
   ClearBindingsGL();
   ClearBindingsOSMESA();
diff --git a/ui/gl/init/gl_initializer_x11.cc b/ui/gl/init/gl_initializer_x11.cc
index 27fca039..a41e5ce 100644
--- a/ui/gl/init/gl_initializer_x11.cc
+++ b/ui/gl/init/gl_initializer_x11.cc
@@ -186,7 +186,6 @@
 }
 
 void ShutdownGLPlatform() {
-  GLSurfaceEGL::ShutdownOneOff();
   ClearBindingsEGL();
   ClearBindingsGL();
   ClearBindingsGLX();
diff --git a/ui/ozone/common/gl_ozone_egl.cc b/ui/ozone/common/gl_ozone_egl.cc
index 7abf320..d63b305 100644
--- a/ui/ozone/common/gl_ozone_egl.cc
+++ b/ui/ozone/common/gl_ozone_egl.cc
@@ -41,7 +41,7 @@
 }
 
 void GLOzoneEGL::ShutdownGL() {
-  gl::GLSurfaceEGL::ShutdownOneOff();
+  gl::GLSurfaceEGL::ResetForTesting();
   gl::ClearBindingsGL();
   gl::ClearBindingsEGL();
 }
diff --git a/ui/views/animation/ink_drop_painted_layer_delegates.cc b/ui/views/animation/ink_drop_painted_layer_delegates.cc
index eec57980..a5de90c6b 100644
--- a/ui/views/animation/ink_drop_painted_layer_delegates.cc
+++ b/ui/views/animation/ink_drop_painted_layer_delegates.cc
@@ -167,8 +167,7 @@
 
   // Now the shadow.
   paint.setLooper(gfx::CreateShadowDrawLooperCorrectBlur(shadows_));
-  recorder.canvas()->sk_canvas()->clipRRect(r_rect, SkRegion::kDifference_Op,
-                                            true);
+  recorder.canvas()->sk_canvas()->clipRRect(r_rect, kDifference_SkClipOp, true);
   recorder.canvas()->sk_canvas()->drawRRect(r_rect, paint);
 }
 
diff --git a/ui/views/bubble/bubble_border.cc b/ui/views/bubble/bubble_border.cc
index b151db0..8b282fa 100644
--- a/ui/views/bubble/bubble_border.cc
+++ b/ui/views/bubble/bubble_border.cc
@@ -358,7 +358,7 @@
 
   // Clip the arrow bounds out to avoid painting the overlapping edge area.
   canvas->Save();
-  canvas->ClipRect(arrow_bounds, SkRegion::kDifference_Op);
+  canvas->ClipRect(arrow_bounds, kDifference_SkClipOp);
   Painter::PaintPainterAt(canvas, images_->border_painter.get(), bounds);
   canvas->Restore();
 
@@ -538,7 +538,7 @@
   paint.setAntiAlias(true);
 
   SkRRect r_rect = GetClientRect(view);
-  canvas->sk_canvas()->clipRRect(r_rect, SkRegion::kDifference_Op,
+  canvas->sk_canvas()->clipRRect(r_rect, kDifference_SkClipOp,
                                  true /*doAntiAlias*/);
 
   // The border is drawn outside the content area.
@@ -550,7 +550,7 @@
 
 void BubbleBorder::PaintNoAssets(const View& view, gfx::Canvas* canvas) {
   gfx::ScopedCanvas scoped(canvas);
-  canvas->sk_canvas()->clipRRect(GetClientRect(view), SkRegion::kDifference_Op,
+  canvas->sk_canvas()->clipRRect(GetClientRect(view), kDifference_SkClipOp,
                                  true /*doAntiAlias*/);
   canvas->sk_canvas()->drawColor(SK_ColorTRANSPARENT, SkBlendMode::kSrc);
 }