diff --git a/DEPS b/DEPS
index 7333333..fdd3893 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': '9a121cc6ad746c37611229dc0ec1805545c4d2e0',
+  'skia_revision': '7d3d8723319038d16456137ba932f238c1e65dbf',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -64,7 +64,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': 'c758d9dd3f94c2dee89e7fdd50195af08f7e456c',
+  'pdfium_revision': '8a24b25ee0b08128b28dfae0ee86b8348a51b40b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling openmax_dl
   # and whatever else without interference from each other.
@@ -96,7 +96,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': 'ea02aa5af9f1b334923b191aeaaf070cb3feebf1',
+  'catapult_revision': 'e650872309eb9d2060e3e2ea2f3e98d4b8aaaf1e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
diff --git a/cc/output/direct_renderer.h b/cc/output/direct_renderer.h
index 30c172d..864c1863 100644
--- a/cc/output/direct_renderer.h
+++ b/cc/output/direct_renderer.h
@@ -93,6 +93,10 @@
     DCLayerOverlayList dc_layer_overlay_list;
   };
 
+  void DisableColorChecksForTesting() {
+    disable_color_checks_for_testing_ = true;
+  }
+
  protected:
   friend class BspWalkActionDrawPolygon;
 
@@ -203,6 +207,7 @@
   RenderPassFilterList render_pass_background_filters_;
 
   bool visible_ = false;
+  bool disable_color_checks_for_testing_ = false;
 
   // For use in coordinate conversion, this stores the output rect, viewport
   // rect (= unflipped version of glViewport rect), the size of target
diff --git a/cc/output/gl_renderer.cc b/cc/output/gl_renderer.cc
index 9b2eb3e5..612c315 100644
--- a/cc/output/gl_renderer.cc
+++ b/cc/output/gl_renderer.cc
@@ -3003,19 +3003,19 @@
 
 void GLRenderer::SetUseProgram(const ProgramKey& program_key,
                                const gfx::ColorSpace& src_color_space) {
+  // The source color space for non-YUV draw quads should always be full-range
+  // RGB.
+  if (!disable_color_checks_for_testing_)
+    DCHECK_EQ(src_color_space, src_color_space.GetAsFullRangeRGB());
+
   // Ensure that we do not apply any color conversion unless the color correct
   // rendering flag has been specified. This is because media mailboxes will
   // provide YUV color spaces despite YUV to RGB conversion already having been
   // performed.
-  // TODO(ccameron): Ensure that media mailboxes be accurate.
-  // https://crbug.com/699243
-  // The source color space for non-YUV draw quads should always be full-range
-  // RGB.
   if (settings_->enable_color_correct_rendering) {
     SetUseProgram(program_key, src_color_space,
                   current_frame()->current_render_pass->color_space);
   } else {
-    DCHECK_EQ(src_color_space, src_color_space.GetAsFullRangeRGB());
     SetUseProgram(program_key, gfx::ColorSpace(), gfx::ColorSpace());
   }
 }
diff --git a/cc/output/renderer_pixeltest.cc b/cc/output/renderer_pixeltest.cc
index 1b27d652..f54ab1d 100644
--- a/cc/output/renderer_pixeltest.cc
+++ b/cc/output/renderer_pixeltest.cc
@@ -3420,6 +3420,8 @@
   std::vector<uint8_t> input_colors(4 * rect.width() * rect.height(), 0);
   std::vector<SkColor> expected_output_colors(rect.width() * rect.height());
 
+  renderer_->DisableColorChecksForTesting();
+
   // Set the input data to be:
   //   Row 0: Gradient of red from 0 to 255
   //   Row 1: Gradient of green from 0 to 255
diff --git a/cc/resources/video_resource_updater.cc b/cc/resources/video_resource_updater.cc
index 231ca41e..33c818c7 100644
--- a/cc/resources/video_resource_updater.cc
+++ b/cc/resources/video_resource_updater.cc
@@ -316,16 +316,17 @@
   const bool software_compositor = context_provider_ == NULL;
 
   ResourceFormat output_resource_format;
+  gfx::ColorSpace output_color_space = video_frame->ColorSpace();
   if (input_frame_format == media::PIXEL_FORMAT_Y16) {
     // Unable to display directly as yuv planes so convert it to RGBA for
     // compositing.
     output_resource_format = RGBA_8888;
+    output_color_space = output_color_space.GetAsFullRangeRGB();
   } else {
     // Can be composited directly from yuv planes.
     output_resource_format =
         resource_provider_->YuvResourceFormat(bits_per_channel);
   }
-  gfx::ColorSpace output_color_space = video_frame->ColorSpace();
 
   // If GPU compositing is enabled, but the output resource format
   // returned by the resource provider is RGBA_8888, then a GPU driver
@@ -341,7 +342,6 @@
   // Obviously, this is suboptimal and should be addressed once ubercompositor
   // starts shaping up.
   if (software_compositor || texture_needs_rgb_conversion) {
-    output_color_space = output_color_space.GetAsFullRangeRGB();
     output_resource_format = kRGBResourceFormat;
     output_plane_count = 1;
     bits_per_channel = 8;
@@ -636,7 +636,7 @@
                 << media::VideoPixelFormatToString(video_frame->format());
     return external_resources;
   }
-  if (external_resources.type == VideoFrameExternalResources::YUV_RESOURCE)
+  if (external_resources.type == VideoFrameExternalResources::RGB_RESOURCE)
     resource_color_space = resource_color_space.GetAsFullRangeRGB();
 
   const size_t num_planes = media::VideoFrame::NumPlanes(video_frame->format());
diff --git a/chrome/VERSION b/chrome/VERSION
index e1af029..18ecf8d3 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=59
 MINOR=0
-BUILD=3065
+BUILD=3066
 PATCH=0
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
index 09a9447..6df7874 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -1755,7 +1755,7 @@
 
     @Override
     public final void onBackPressed() {
-        RecordUserAction.record("SystemBack");
+        if (mNativeInitialized) RecordUserAction.record("SystemBack");
         if (VrShellDelegate.onBackPressed()) return;
         if (mCompositorViewHolder != null) {
             LayoutManager layoutManager = mCompositorViewHolder.getLayoutManager();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/externalauth/ExternalAuthUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/externalauth/ExternalAuthUtils.java
index f998065..db9ea63 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/externalauth/ExternalAuthUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/externalauth/ExternalAuthUtils.java
@@ -17,6 +17,7 @@
 import com.google.android.gms.common.ConnectionResult;
 import com.google.android.gms.common.GoogleApiAvailability;
 
+import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.VisibleForTesting;
@@ -89,6 +90,7 @@
         return true;
     }
 
+    // TODO(paulmiller): Remove; replaced by no-argument isChromeGoogleSigned().
     /**
      * Returns whether the current build of Chrome is a Google-signed package.
      *
@@ -100,12 +102,31 @@
     }
 
     /**
+     * Returns whether the current build of Chrome is a Google-signed package.
+     * @return whether the currently running application is signed with Google keys.
+     */
+    public boolean isChromeGoogleSigned() {
+        String packageName = ContextUtils.getApplicationContext().getPackageName();
+        return isGoogleSigned(null, packageName);
+    }
+
+    // TODO(paulmiller): Remove; replaced by isGoogleSigned(String).
+    /**
      * Returns whether the call is originating from a Google-signed package.
      * @param appContext the current context.
      * @param packageName The package name to inquire about.
      */
     public boolean isGoogleSigned(Context context, String packageName) {
         // This is overridden in a subclass.
+        return isGoogleSigned(packageName);
+    }
+
+    /**
+     * Returns whether the call is originating from a Google-signed package.
+     * @param packageName The package name to inquire about.
+     */
+    public boolean isGoogleSigned(String packageName) {
+        // This is overridden in a subclass.
         return false;
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
index 69275cf..eba27fa 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
@@ -334,6 +334,8 @@
             public void onShown(Tab tab) {
                 // Showing the NTP is only meaningful when the page has been loaded already.
                 if (mIsLoaded) recordNTPShown();
+
+                mNewTabPageView.getTileGroup().onSwitchToForeground();
             }
 
             @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java
index 6e45bd5..e87d966 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java
@@ -41,7 +41,6 @@
 import org.chromium.chrome.browser.suggestions.Tile;
 import org.chromium.chrome.browser.suggestions.TileGridLayout;
 import org.chromium.chrome.browser.suggestions.TileGroup;
-import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.chrome.browser.util.MathUtils;
@@ -249,12 +248,6 @@
         initializeLayoutChangeListeners();
         setSearchProviderHasLogo(searchProviderHasLogo);
 
-        tab.addObserver(new EmptyTabObserver() {
-            @Override
-            public void onShown(Tab tab) {
-                mTileGroup.onSwitchToForeground();
-            }
-        });
         mTileGroup.startObserving(getMaxTileRows(searchProviderHasLogo) * getMaxTileColumns());
 
         // Set up snippets
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/OmahaBase.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/OmahaBase.java
index d6c47da..41d19cd5c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omaha/OmahaBase.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/OmahaBase.java
@@ -477,7 +477,7 @@
             writer.write(request, 0, request.length());
             StreamUtil.closeQuietly(writer);
             checkServerResponseCode(urlConnection);
-        } catch (IOException e) {
+        } catch (IOException | SecurityException e) {
             throw new RequestFailureException("Failed to write request to server: ", e);
         }
 
diff --git a/chrome/browser/android/preferences/website_preference_bridge.cc b/chrome/browser/android/preferences/website_preference_bridge.cc
index a491d34..e8585d9 100644
--- a/chrome/browser/android/preferences/website_preference_bridge.cc
+++ b/chrome/browser/android/preferences/website_preference_bridge.cc
@@ -203,6 +203,14 @@
   GURL embedder_url =
       embedder ? GURL(ConvertJavaStringToUTF8(env, embedder)) : GURL();
   Profile* profile = GetActiveUserProfile(is_incognito);
+
+  // The permission may have been blocked due to being under embargo, so if it
+  // was changed away from BLOCK, clear embargo status if it exists.
+  if (setting != CONTENT_SETTING_BLOCK) {
+    PermissionDecisionAutoBlocker::GetForProfile(profile)->RemoveEmbargoByUrl(
+        origin_url, content_type);
+  }
+
   PermissionUtil::ScopedRevocationReporter scoped_revocation_reporter(
       profile, origin_url, embedder_url, content_type,
       PermissionSourceUI::SITE_SETTINGS);
@@ -331,6 +339,12 @@
   Profile* profile = GetActiveUserProfile(is_incognito);
   GURL url = GURL(ConvertJavaStringToUTF8(env, origin));
   ContentSetting setting = static_cast<ContentSetting>(value);
+
+  if (setting != CONTENT_SETTING_BLOCK) {
+    PermissionDecisionAutoBlocker::GetForProfile(profile)->RemoveEmbargoByUrl(
+        url, CONTENT_SETTINGS_TYPE_NOTIFICATIONS);
+  }
+
   switch (setting) {
     case CONTENT_SETTING_DEFAULT:
       DesktopNotificationProfileUtil::ClearSetting(profile, url);
diff --git a/chrome/browser/permissions/permission_decision_auto_blocker.cc b/chrome/browser/permissions/permission_decision_auto_blocker.cc
index d3c11ba..8d26e6d 100644
--- a/chrome/browser/permissions/permission_decision_auto_blocker.cc
+++ b/chrome/browser/permissions/permission_decision_auto_blocker.cc
@@ -360,6 +360,41 @@
   return false;
 }
 
+void PermissionDecisionAutoBlocker::RemoveEmbargoByUrl(
+    const GURL& url,
+    ContentSettingsType permission) {
+  if (!PermissionUtil::IsPermission(permission))
+    return;
+
+  // Don't proceed if |permission| was not under embargo for |url|.
+  PermissionResult result = GetEmbargoResult(url, permission);
+  if (result.source != PermissionStatusSource::MULTIPLE_DISMISSALS &&
+      result.source != PermissionStatusSource::SAFE_BROWSING_BLACKLIST) {
+    return;
+  }
+
+  HostContentSettingsMap* map =
+      HostContentSettingsMapFactory::GetForProfile(profile_);
+  std::unique_ptr<base::DictionaryValue> dict = GetOriginDict(map, url);
+  base::DictionaryValue* permission_dict = GetOrCreatePermissionDict(
+      dict.get(), PermissionUtil::GetPermissionString(permission));
+
+  // Deleting non-existent entries will return a false value. Since it should be
+  // impossible for a permission to have been embargoed for two different
+  // reasons at the same time, check that exactly one deletion was successful.
+  const bool dismissal_key_deleted =
+      permission_dict->RemoveWithoutPathExpansion(
+          kPermissionDismissalEmbargoKey, nullptr);
+  const bool blacklist_key_deleted =
+      permission_dict->RemoveWithoutPathExpansion(
+          kPermissionBlacklistEmbargoKey, nullptr);
+  DCHECK(dismissal_key_deleted != blacklist_key_deleted);
+
+  map->SetWebsiteSettingDefaultScope(
+      url, GURL(), CONTENT_SETTINGS_TYPE_PERMISSION_AUTOBLOCKER_DATA,
+      std::string(), std::move(dict));
+}
+
 void PermissionDecisionAutoBlocker::RemoveCountsByUrl(
     base::Callback<bool(const GURL& url)> filter) {
   HostContentSettingsMap* map =
diff --git a/chrome/browser/permissions/permission_decision_auto_blocker.h b/chrome/browser/permissions/permission_decision_auto_blocker.h
index 24e07b50..79bc114 100644
--- a/chrome/browser/permissions/permission_decision_auto_blocker.h
+++ b/chrome/browser/permissions/permission_decision_auto_blocker.h
@@ -95,6 +95,13 @@
   // embargo for |permission|.
   bool RecordIgnoreAndEmbargo(const GURL& url, ContentSettingsType permission);
 
+  // Clears any existing embargo status for |url|, |permission|. For permissions
+  // embargoed under repeated dismissals, this means a prompt will be shown to
+  // the user on next permission request. On blacklisted permissions, the next
+  // permission request will re-embargo the permission only if it is still
+  // blacklisted. This is a NO-OP for non-embargoed |url|, |permission| pairs.
+  void RemoveEmbargoByUrl(const GURL& url, ContentSettingsType permission);
+
   // Removes any recorded counts for urls which match |filter|.
   void RemoveCountsByUrl(base::Callback<bool(const GURL& url)> filter);
 
diff --git a/chrome/browser/permissions/permission_decision_auto_blocker_unittest.cc b/chrome/browser/permissions/permission_decision_auto_blocker_unittest.cc
index c6d4e90a..df1b097 100644
--- a/chrome/browser/permissions/permission_decision_auto_blocker_unittest.cc
+++ b/chrome/browser/permissions/permission_decision_auto_blocker_unittest.cc
@@ -160,6 +160,123 @@
   bool callback_was_run_;
 };
 
+// Check removing the the embargo for a single permission on a site works, and
+// that it doesn't interfere with other embargoed permissions or the same
+// permission embargoed on other sites.
+TEST_F(PermissionDecisionAutoBlockerUnitTest, RemoveEmbargoByUrl) {
+  GURL url1("https://www.google.com");
+  GURL url2("https://www.example.com");
+
+  // Record dismissals for location and notifications in |url1|.
+  EXPECT_FALSE(autoblocker()->RecordDismissAndEmbargo(
+      url1, CONTENT_SETTINGS_TYPE_GEOLOCATION));
+  EXPECT_FALSE(autoblocker()->RecordDismissAndEmbargo(
+      url1, CONTENT_SETTINGS_TYPE_GEOLOCATION));
+  EXPECT_TRUE(autoblocker()->RecordDismissAndEmbargo(
+      url1, CONTENT_SETTINGS_TYPE_GEOLOCATION));
+  EXPECT_FALSE(autoblocker()->RecordDismissAndEmbargo(
+      url1, CONTENT_SETTINGS_TYPE_NOTIFICATIONS));
+  EXPECT_FALSE(autoblocker()->RecordDismissAndEmbargo(
+      url1, CONTENT_SETTINGS_TYPE_NOTIFICATIONS));
+  EXPECT_TRUE(autoblocker()->RecordDismissAndEmbargo(
+      url1, CONTENT_SETTINGS_TYPE_NOTIFICATIONS));
+  // Record dismissals for location in |url2|.
+  EXPECT_FALSE(autoblocker()->RecordDismissAndEmbargo(
+      url2, CONTENT_SETTINGS_TYPE_GEOLOCATION));
+  EXPECT_FALSE(autoblocker()->RecordDismissAndEmbargo(
+      url2, CONTENT_SETTINGS_TYPE_GEOLOCATION));
+  EXPECT_TRUE(autoblocker()->RecordDismissAndEmbargo(
+      url2, CONTENT_SETTINGS_TYPE_GEOLOCATION));
+
+  // Verify all dismissals recorded above resulted in embargo.
+  PermissionResult result =
+      autoblocker()->GetEmbargoResult(url1, CONTENT_SETTINGS_TYPE_GEOLOCATION);
+  EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting);
+  EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result.source);
+  result = autoblocker()->GetEmbargoResult(url1,
+                                           CONTENT_SETTINGS_TYPE_NOTIFICATIONS);
+  EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting);
+  EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result.source);
+  result =
+      autoblocker()->GetEmbargoResult(url2, CONTENT_SETTINGS_TYPE_GEOLOCATION);
+  EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting);
+  EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result.source);
+
+  // Remove the embargo on notifications. Verify it is no longer under embargo,
+  // but location still is.
+  autoblocker()->RemoveEmbargoByUrl(url1, CONTENT_SETTINGS_TYPE_NOTIFICATIONS);
+  result =
+      autoblocker()->GetEmbargoResult(url1, CONTENT_SETTINGS_TYPE_GEOLOCATION);
+  EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting);
+  EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result.source);
+  result = autoblocker()->GetEmbargoResult(url1,
+                                           CONTENT_SETTINGS_TYPE_NOTIFICATIONS);
+  // If not under embargo, GetEmbargoResult() returns a setting of ASK.
+  EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting);
+  EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source);
+  // Verify |url2|'s embargo is still intact as well.
+  result =
+      autoblocker()->GetEmbargoResult(url2, CONTENT_SETTINGS_TYPE_GEOLOCATION);
+  EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting);
+  EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result.source);
+}
+
+// Test that removing embargo from blacklisted permissions also works.
+TEST_F(PermissionDecisionAutoBlockerUnitTest,
+       RemoveEmbargoByUrlForBlacklistedPermission) {
+  GURL url("https://www.example.com");
+
+  // Place under embargo and verify.
+  PlaceUnderBlacklistEmbargo(url, CONTENT_SETTINGS_TYPE_GEOLOCATION);
+  PermissionResult result =
+      autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_GEOLOCATION);
+  EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting);
+  EXPECT_EQ(PermissionStatusSource::SAFE_BROWSING_BLACKLIST, result.source);
+
+  // Remove embargo and verify.
+  autoblocker()->RemoveEmbargoByUrl(url, CONTENT_SETTINGS_TYPE_GEOLOCATION);
+  result =
+      autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_GEOLOCATION);
+  EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting);
+  EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source);
+}
+
+// Test it still only takes one more dismissal to re-trigger embargo after
+// removing the embargo status for a site.
+TEST_F(PermissionDecisionAutoBlockerUnitTest,
+       DismissAfterRemovingEmbargoByURL) {
+  GURL url("https://www.example.com");
+
+  // Record dismissals for location.
+  EXPECT_FALSE(autoblocker()->RecordDismissAndEmbargo(
+      url, CONTENT_SETTINGS_TYPE_GEOLOCATION));
+  EXPECT_FALSE(autoblocker()->RecordDismissAndEmbargo(
+      url, CONTENT_SETTINGS_TYPE_GEOLOCATION));
+  EXPECT_TRUE(autoblocker()->RecordDismissAndEmbargo(
+      url, CONTENT_SETTINGS_TYPE_GEOLOCATION));
+
+  // Verify location is under embargo.
+  PermissionResult result =
+      autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_GEOLOCATION);
+  EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting);
+  EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result.source);
+
+  // Remove embargo and verify this is true.
+  autoblocker()->RemoveEmbargoByUrl(url, CONTENT_SETTINGS_TYPE_GEOLOCATION);
+  result =
+      autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_GEOLOCATION);
+  EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting);
+  EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source);
+
+  // Record another dismissal and verify location is under embargo again.
+  autoblocker()->RecordDismissAndEmbargo(url,
+                                         CONTENT_SETTINGS_TYPE_GEOLOCATION);
+  result =
+      autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_GEOLOCATION);
+  EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting);
+  EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result.source);
+}
+
 TEST_F(PermissionDecisionAutoBlockerUnitTest, RemoveCountsByUrl) {
   GURL url1("https://www.google.com");
   GURL url2("https://www.example.com");
diff --git a/chrome/browser/resources/chromeos/login/oobe_screen_network.js b/chrome/browser/resources/chromeos/login/oobe_screen_network.js
index b5f2ebe..8f9416a1 100644
--- a/chrome/browser/resources/chromeos/login/oobe_screen_network.js
+++ b/chrome/browser/resources/chromeos/login/oobe_screen_network.js
@@ -176,6 +176,7 @@
      */
     updateLocalizedContent: function() {
       this.setMDMode_();
+      $('oobe-welcome-md').updateLocalizedContent();
     },
 
     /**
diff --git a/chrome/browser/resources/chromeos/login/oobe_welcome.js b/chrome/browser/resources/chromeos/login/oobe_welcome.js
index 0c69451..3d7dc53 100644
--- a/chrome/browser/resources/chromeos/login/oobe_welcome.js
+++ b/chrome/browser/resources/chromeos/login/oobe_welcome.js
@@ -101,6 +101,13 @@
 
   /** @override */
   ready: function() {
+    this.updateLocalizedContent();
+  },
+
+  /**
+   * This is called when UI strings are changed.
+   */
+  updateLocalizedContent: function() {
     CrOncStrings = {
       OncTypeCellular: loadTimeData.getString('OncTypeCellular'),
       OncTypeEthernet: loadTimeData.getString('OncTypeEthernet'),
diff --git a/chrome/browser/resources/engagement/site_engagement.js b/chrome/browser/resources/engagement/site_engagement.js
index f8c7cccc..8dab46a 100644
--- a/chrome/browser/resources/engagement/site_engagement.js
+++ b/chrome/browser/resources/engagement/site_engagement.js
@@ -4,11 +4,22 @@
 
 'use strict';
 
+// Allow a function to be provided by tests, which will be called when
+// the page has been populated with site engagement details.
+var resolvePageIsPopulated = null;
+var pageIsPopulatedPromise = new Promise((resolve, reject) => {
+  resolvePageIsPopulated = resolve;
+});
+
+function whenPageIsPopulatedForTest() {
+  return pageIsPopulatedPromise;
+}
+
 define('main', [
     'chrome/browser/engagement/site_engagement.mojom',
     'content/public/renderer/frame_interfaces',
-], function(siteEngagementMojom, frameInterfaces) {
-  return function() {
+], (siteEngagementMojom, frameInterfaces) => {
+  return () => {
     var uiHandler = new siteEngagementMojom.SiteEngagementUIHandlerPtr(
         frameInterfaces.getInterface(
             siteEngagementMojom.SiteEngagementUIHandler.name));
@@ -23,7 +34,7 @@
     var engagementTableHeader = $('engagement-table-header');
     var headers = engagementTableHeader.children;
     for (var i = 0; i < headers.length; i++) {
-      headers[i].addEventListener('click', function(e) {
+      headers[i].addEventListener('click', (e) => {
         var newSortKey = e.target.getAttribute('sort-key');
         if (sortKey == newSortKey) {
           sortReverse = !sortReverse;
@@ -117,7 +128,7 @@
      * Sort the engagement info based on |sortKey| and |sortReverse|.
      */
     function sortInfo() {
-      info.sort(function(a, b) {
+      info.sort((a, b) => {
         return (sortReverse ? -1 : 1) *
                compareTableItem(sortKey, a, b);
       });
@@ -151,10 +162,12 @@
       clearTable();
       sortInfo();
       // Round each score to 2 decimal places.
-      info.forEach(function(info) {
+      info.forEach((info) => {
         info.score = Number(Math.round(info.score * 100) / 100);
         engagementTableBody.appendChild(createRow(info));
       });
+
+      resolvePageIsPopulated();
     }
 
     /**
@@ -162,7 +175,7 @@
      */
     function updateEngagementTable() {
       // Populate engagement table.
-      uiHandler.getSiteEngagementInfo().then(function(response) {
+      uiHandler.getSiteEngagementInfo().then((response) => {
         info = response.info;
         renderTable(info);
       });
diff --git a/chrome/browser/resources/settings/about_page/detailed_build_info.html b/chrome/browser/resources/settings/about_page/detailed_build_info.html
index eea525b..4e86bf590 100644
--- a/chrome/browser/resources/settings/about_page/detailed_build_info.html
+++ b/chrome/browser/resources/settings/about_page/detailed_build_info.html
@@ -53,7 +53,9 @@
         </template>
       </div>
       <template is="dom-if" if="[[showChannelSwitcherDialog_]]" restamp>
-        <settings-channel-switcher-dialog></settings-channel-switcher-dialog>
+        <settings-channel-switcher-dialog
+            on-close="onChannelSwitcherDialogClosed_">
+        </settings-channel-switcher-dialog>
       </template>
     </div>
     <div class="settings-box two-line single-column"
diff --git a/chrome/browser/resources/settings/about_page/detailed_build_info.js b/chrome/browser/resources/settings/about_page/detailed_build_info.js
index 78d22e1..33a90b78 100644
--- a/chrome/browser/resources/settings/about_page/detailed_build_info.js
+++ b/chrome/browser/resources/settings/about_page/detailed_build_info.js
@@ -89,16 +89,12 @@
   onChangeChannelTap_: function(e) {
     e.preventDefault();
     this.showChannelSwitcherDialog_ = true;
-    // Async to wait for dialog to appear in the DOM.
-    this.async(function() {
-      var dialog = this.$$('settings-channel-switcher-dialog');
-      // Register listener to detect when the dialog is closed. Flip the boolean
-      // once closed to force a restamp next time it is shown such that the
-      // previous dialog's contents are cleared.
-      dialog.addEventListener('close', function() {
-        this.showChannelSwitcherDialog_ = false;
-        this.updateChannelInfo_();
-      }.bind(this));
-    }.bind(this));
+  },
+
+  /** @private */
+  onChannelSwitcherDialogClosed_: function() {
+    this.showChannelSwitcherDialog_ = false;
+    this.$$('paper-button').focus();
+    this.updateChannelInfo_();
   },
 });
diff --git a/chrome/browser/ui/page_info/page_info.cc b/chrome/browser/ui/page_info/page_info.cc
index 63002ba..05f6f8e 100644
--- a/chrome/browser/ui/page_info/page_info.cc
+++ b/chrome/browser/ui/page_info/page_info.cc
@@ -32,6 +32,7 @@
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/infobars/infobar_service.h"
 #include "chrome/browser/permissions/chooser_context_base.h"
+#include "chrome/browser/permissions/permission_decision_auto_blocker.h"
 #include "chrome/browser/permissions/permission_manager.h"
 #include "chrome/browser/permissions/permission_result.h"
 #include "chrome/browser/permissions/permission_uma_util.h"
@@ -340,9 +341,14 @@
   RecordPageInfoAction(PAGE_INFO_CHANGED_PERMISSION);
 
   PermissionUtil::ScopedRevocationReporter scoped_revocation_reporter(
-      this->profile_, this->site_url_, this->site_url_, type,
-      PermissionSourceUI::OIB);
+      profile_, site_url_, site_url_, type, PermissionSourceUI::OIB);
 
+  // The permission may have been blocked due to being under embargo, so if it
+  // was changed away from BLOCK, clear embargo status if it exists.
+  if (setting != CONTENT_SETTING_BLOCK) {
+    PermissionDecisionAutoBlocker::GetForProfile(profile_)->RemoveEmbargoByUrl(
+        site_url_, type);
+  }
   content_settings_->SetNarrowestContentSetting(site_url_, site_url_, type,
                                                 setting);
 
diff --git a/chrome/browser/ui/webui/engagement/site_engagement_ui_browsertest.cc b/chrome/browser/ui/webui/engagement/site_engagement_ui_browsertest.cc
new file mode 100644
index 0000000..b1d442e
--- /dev/null
+++ b/chrome/browser/ui/webui/engagement/site_engagement_ui_browsertest.cc
@@ -0,0 +1,102 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/strings/string_util.h"
+#include "chrome/browser/engagement/site_engagement_service.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/webui/chrome_web_ui_controller_factory.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "content/public/browser/child_process_security_policy.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/test/browser_test_utils.h"
+
+namespace {
+
+const GURL kExampleUrl = GURL("https://www.example.com/");
+
+}  // namespace
+
+class SiteEngagementUiBrowserTest : public InProcessBrowserTest {
+ protected:
+  // Returns the SiteEngagementService for the test browser profile.
+  SiteEngagementService* engagement_service() {
+    return SiteEngagementService::Get(browser()->profile());
+  }
+
+  // (Re)sets the base score for a URL's site engagement.
+  void ResetBaseScore(const GURL& url, double score) {
+    engagement_service()->ResetBaseScoreForURL(url, score);
+  }
+  void ResetBaseScoreToMax(const GURL& url) {
+    ResetBaseScore(url, engagement_service()->GetMaxPoints());
+  }
+
+  // Navigates the tab to the site engagement WebUI.
+  void NavigateToWebUi() {
+    content::WebContents* web_contents =
+        browser()->tab_strip_model()->GetActiveWebContents();
+
+    const GURL web_ui_url(base::JoinString(
+        {content::kChromeUIScheme, "://", chrome::kChromeUISiteEngagementHost},
+        ""));
+    EXPECT_TRUE(ChromeWebUIControllerFactory::GetInstance()->UseWebUIForURL(
+        web_contents->GetBrowserContext(), web_ui_url));
+    ui_test_utils::NavigateToURL(browser(), web_ui_url);
+    EXPECT_TRUE(
+        content::ChildProcessSecurityPolicy::GetInstance()->HasWebUIBindings(
+            web_contents->GetRenderProcessHost()->GetID()));
+
+    EXPECT_TRUE(content::WaitForLoadStop(web_contents));
+  }
+
+  // Waits for the tab to be populated with site engagement data.
+  void WaitUntilPagePopulated() {
+    ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
+        browser()->tab_strip_model()->GetActiveWebContents(),
+        "window.whenPageIsPopulatedForTest().then(() => {"
+        "  window.domAutomationController.send(true);"
+        "});",
+        &page_is_populated_));
+
+    ASSERT_TRUE(page_is_populated_);
+  }
+
+  // Verifies that a row exists for the specified site URL.
+  void ExpectPageContainsUrl(const GURL& url) {
+    ASSERT_TRUE(page_is_populated_);
+
+    bool found_url = false;
+    ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
+        browser()->tab_strip_model()->GetActiveWebContents(),
+        base::JoinString(
+            {"var origin_cells = "
+             "    Array.from(document.getElementsByClassName('origin-cell'));"
+             "window.domAutomationController.send(origin_cells.reduce("
+             "    (found, element) => {"
+             "      return found || (element.innerHTML == '",
+             url.spec(),
+             "');"
+             "    }, false));"},
+            ""),
+        &found_url));
+    EXPECT_TRUE(found_url);
+  }
+
+ private:
+  // True if the page contains site engagement data.
+  bool page_is_populated_ = false;
+};
+
+IN_PROC_BROWSER_TEST_F(SiteEngagementUiBrowserTest, Basic) {
+  ResetBaseScoreToMax(kExampleUrl);
+
+  NavigateToWebUi();
+  WaitUntilPagePopulated();
+
+  ExpectPageContainsUrl(kExampleUrl);
+}
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 8c25fec..4572d20 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1820,6 +1820,7 @@
       "../browser/ui/webui/chrome_url_data_manager_browsertest.cc",
       "../browser/ui/webui/chromeos/bluetooth_pairing_ui_browsertest-inl.h",
       "../browser/ui/webui/constrained_web_dialog_ui_browsertest.cc",
+      "../browser/ui/webui/engagement/site_engagement_ui_browsertest.cc",
       "../browser/ui/webui/extensions/extension_settings_browsertest.cc",
       "../browser/ui/webui/extensions/extension_settings_browsertest.h",
       "../browser/ui/webui/identity_internals_ui_browsertest.cc",
diff --git a/chrome/test/data/webui/cr_elements/cr_action_menu_test.js b/chrome/test/data/webui/cr_elements/cr_action_menu_test.js
index d36f8e9..9715fba 100644
--- a/chrome/test/data/webui/cr_elements/cr_action_menu_test.js
+++ b/chrome/test/data/webui/cr_elements/cr_action_menu_test.js
@@ -40,6 +40,10 @@
     MockInteractions.keyDownOn(menu, 'ArrowDown', [], 'ArrowDown');
   }
 
+  function up() {
+    MockInteractions.keyDownOn(menu, 'ArrowUp', [], 'ArrowUp');
+  }
+
   test('hidden or disabled items', function() {
     menu.showAt(document.querySelector('#dots'));
     down();
@@ -59,10 +63,6 @@
   });
 
   test('focus after down/up arrow', function() {
-    function up() {
-      MockInteractions.keyDownOn(menu, 'ArrowUp', [], 'ArrowUp');
-    }
-
     menu.showAt(document.querySelector('#dots'));
 
     // The menu should be focused when shown, but not on any of the items.
@@ -93,6 +93,14 @@
     assertEquals(items[0], menu.root.activeElement);
   });
 
+  test('pressing up arrow when no focus will focus last item', function(){
+    menu.showAt(document.querySelector('#dots'));
+    assertEquals(menu, document.activeElement);
+
+    up();
+    assertEquals(items[items.length - 1], menu.root.activeElement);
+  });
+
   test('close on resize', function() {
     menu.showAt(document.querySelector('#dots'));
     assertTrue(menu.open);
diff --git a/components/sync/engine_impl/syncer.cc b/components/sync/engine_impl/syncer.cc
index 5d28f4a9..6d8d71e 100644
--- a/components/sync/engine_impl/syncer.cc
+++ b/components/sync/engine_impl/syncer.cc
@@ -79,17 +79,18 @@
     sync_pb::GetUpdatesCallerInfo::GetUpdatesSource source,
     SyncCycle* cycle) {
   base::AutoReset<bool> is_syncing(&is_syncing_, true);
+
+  // It is possible during configuration that datatypes get unregistered from
+  // ModelTypeRegistry before scheduled configure sync cycle gets executed.
+  // This happens either because DataTypeController::LoadModels fail and type
+  // need to be stopped or during shutdown when all datatypes are stopped. When
+  // it happens we should adjust set of types to download to only include
+  // registered types.
+  request_types.RetainAll(cycle->context()->GetEnabledTypes());
   VLOG(1) << "Configuring types " << ModelTypeSetToString(request_types);
   HandleCycleBegin(cycle);
   ConfigureGetUpdatesDelegate configure_delegate(source);
 
-  // It is possible during shutdown that datatypes get unregistered from
-  // ModelTypeRegistry before scheduled configure sync cycle gets executed.
-  // When it happens we should adjust set of types to download to only include
-  // registered types.
-  if (cancelation_signal_->IsSignalled())
-    request_types.RetainAll(cycle->context()->GetEnabledTypes());
-
   GetUpdatesProcessor get_updates_processor(
       cycle->context()->model_type_registry()->update_handler_map(),
       configure_delegate);
diff --git a/components/sync/engine_impl/syncer_unittest.cc b/components/sync/engine_impl/syncer_unittest.cc
index ca131a0e..d404a389 100644
--- a/components/sync/engine_impl/syncer_unittest.cc
+++ b/components/sync/engine_impl/syncer_unittest.cc
@@ -5022,12 +5022,10 @@
   mock_server_->ClearUpdatesQueue();
 }
 
-// Tests that, after shutdown initiated, if set of types to download includes
-// unregistered type, DCHECK doesn't get triggered.
-TEST_F(SyncerTest, ConfigureUnregisteredTypesDuringShutdown) {
-  // Signal that shutdown is initiated.
-  cancelation_signal_.Signal();
-
+// Tests that if type is not registered with ModelTypeRegistry (e.g. because
+// type's LoadModels failed), Syncer::ConfigureSyncShare runs without triggering
+// DCHECK.
+TEST_F(SyncerTest, ConfigureFailedUnregisteredType) {
   // Simulate type being unregistered before configuration by including type
   // that isn't registered with ModelTypeRegistry.
   SyncShareConfigureTypes(ModelTypeSet(APPS));
diff --git a/content/test/gpu/generate_buildbot_json.py b/content/test/gpu/generate_buildbot_json.py
index 978ad7b5..88fc66e 100755
--- a/content/test/gpu/generate_buildbot_json.py
+++ b/content/test/gpu/generate_buildbot_json.py
@@ -1440,7 +1440,7 @@
       {
         # Run this on the FYI waterfall and optional tryservers.
         'predicate': Predicates.FYI_AND_OPTIONAL,
-        'os_types': ['win', 'linux'],
+        'os_types': ['win', 'linux', 'mac'],
       },
     ],
     'disabled_tester_configs': [
diff --git a/device/gamepad/OWNERS b/device/gamepad/OWNERS
index b1bcb4c0..49f893e 100644
--- a/device/gamepad/OWNERS
+++ b/device/gamepad/OWNERS
@@ -1,5 +1,6 @@
 scottmg@chromium.org
 bajones@chromium.org
+mattreynolds@chromium.org
 
 # TEAM: device-dev@chromium.org
 # COMPONENT: IO>Gamepad
diff --git a/extensions/browser/value_store/lazy_leveldb.cc b/extensions/browser/value_store/lazy_leveldb.cc
index 8176b83..ca5501d 100644
--- a/extensions/browser/value_store/lazy_leveldb.cc
+++ b/extensions/browser/value_store/lazy_leveldb.cc
@@ -253,6 +253,9 @@
 
 ValueStore::Status LazyLevelDb::ToValueStoreError(
     const leveldb::Status& status) {
+  if (status.ok())
+    return ValueStore::Status();
+
   CHECK(!status.IsNotFound());  // not an error
 
   std::string message = status.ToString();
diff --git a/gpu/ipc/service/direct_composition_surface_win.cc b/gpu/ipc/service/direct_composition_surface_win.cc
index b794e77..c54a894a 100644
--- a/gpu/ipc/service/direct_composition_surface_win.cc
+++ b/gpu/ipc/service/direct_composition_surface_win.cc
@@ -432,6 +432,8 @@
       d3d11_device_.get(), swap_chain_handle_.Get(), &desc, nullptr,
       swap_chain_.Receive());
 
+  bool yuy2_swapchain = true;
+
   if (FAILED(hr)) {
     // This should not be hit in production but is a simple fallback for
     // testing on systems without YUY2 swapchain support.
@@ -443,6 +445,7 @@
         d3d11_device_.get(), swap_chain_handle_.Get(), &desc, nullptr,
         swap_chain_.Receive());
     CHECK(SUCCEEDED(hr));
+    yuy2_swapchain = false;
   } else {
     // This is a sensible default colorspace for most videos.
     // TODO(jbauman): Use correct colorspace.
@@ -452,6 +455,8 @@
         DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709);
     CHECK(SUCCEEDED(hr));
   }
+  UMA_HISTOGRAM_BOOLEAN("GPU.DirectComposition.SwapchainFormat",
+                        yuy2_swapchain);
   out_view_.Reset();
 }
 
diff --git a/media/capture/mojo/image_capture.mojom b/media/capture/mojo/image_capture.mojom
index bf5f7cac..dbbc451b 100644
--- a/media/capture/mojo/image_capture.mojom
+++ b/media/capture/mojo/image_capture.mojom
@@ -42,6 +42,7 @@
 
   Range zoom;
 
+  bool supports_torch;
   bool torch;
 
   // https://w3c.github.io/mediacapture-image/##photocapabilities-section
diff --git a/media/capture/video/android/java/src/org/chromium/media/PhotoCapabilities.java b/media/capture/video/android/java/src/org/chromium/media/PhotoCapabilities.java
index 5d73b74..890d581 100644
--- a/media/capture/video/android/java/src/org/chromium/media/PhotoCapabilities.java
+++ b/media/capture/video/android/java/src/org/chromium/media/PhotoCapabilities.java
@@ -36,6 +36,7 @@
     public final double stepExposureCompensation;
     public final int whiteBalanceMode;
     public final int[] fillLightModes;
+    public final boolean supportsTorch;
     public final boolean torch;
     public final boolean redEyeReduction;
     public final int maxColorTemperature;
@@ -49,7 +50,7 @@
             double stepZoom, int focusMode, int exposureMode, double maxExposureCompensation,
             double minExposureCompensation, double currentExposureCompensation,
             double stepExposureCompensation, int whiteBalanceMode, int[] fillLightModes,
-            boolean torch, boolean redEyeReduction, int maxColorTemperature,
+            boolean supportsTorch, boolean torch, boolean redEyeReduction, int maxColorTemperature,
             int minColorTemperature, int currentColorTemperature, int stepColorTemperature) {
         this.maxIso = maxIso;
         this.minIso = minIso;
@@ -75,6 +76,7 @@
         this.stepExposureCompensation = stepExposureCompensation;
         this.whiteBalanceMode = whiteBalanceMode;
         this.fillLightModes = fillLightModes;
+        this.supportsTorch = supportsTorch;
         this.torch = torch;
         this.redEyeReduction = redEyeReduction;
         this.maxColorTemperature = maxColorTemperature;
@@ -204,6 +206,11 @@
     }
 
     @CalledByNative
+    public boolean getSupportsTorch() {
+        return supportsTorch;
+    }
+
+    @CalledByNative
     public boolean getTorch() {
         return torch;
     }
@@ -258,6 +265,7 @@
         public double stepExposureCompensation;
         public int whiteBalanceMode;
         public int[] fillLightModes;
+        public boolean supportsTorch;
         public boolean torch;
         public boolean redEyeReduction;
         public int maxColorTemperature;
@@ -387,6 +395,11 @@
             return this;
         }
 
+        public Builder setSupportsTorch(boolean supportsTorch) {
+            this.supportsTorch = supportsTorch;
+            return this;
+        }
+
         public Builder setTorch(boolean torch) {
             this.torch = torch;
             return this;
@@ -422,8 +435,8 @@
                     currentHeight, stepHeight, maxWidth, minWidth, currentWidth, stepWidth, maxZoom,
                     minZoom, currentZoom, stepZoom, focusMode, exposureMode,
                     maxExposureCompensation, minExposureCompensation, currentExposureCompensation,
-                    stepExposureCompensation, whiteBalanceMode, fillLightModes, torch,
-                    redEyeReduction, maxColorTemperature, minColorTemperature,
+                    stepExposureCompensation, whiteBalanceMode, fillLightModes, supportsTorch,
+                    torch, redEyeReduction, maxColorTemperature, minColorTemperature,
                     currentColorTemperature, stepColorTemperature);
         }
     }
diff --git a/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera.java b/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera.java
index 016ae4c..488f42b 100644
--- a/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera.java
+++ b/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera.java
@@ -542,8 +542,11 @@
 
         final List<String> flashModes = parameters.getSupportedFlashModes();
         if (flashModes != null) {
-            builder.setTorch(
+            builder.setSupportsTorch(
                     flashModes.contains(android.hardware.Camera.Parameters.FLASH_MODE_TORCH));
+            builder.setTorch(parameters.getFlashMode()
+                    == android.hardware.Camera.Parameters.FLASH_MODE_TORCH);
+
             builder.setRedEyeReduction(
                     flashModes.contains(android.hardware.Camera.Parameters.FLASH_MODE_RED_EYE));
 
diff --git a/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera2.java b/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera2.java
index a73751b5..d8c92d75f 100644
--- a/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera2.java
+++ b/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera2.java
@@ -797,12 +797,15 @@
         builder.setStepColorTemperature(1);
 
         if (!cameraCharacteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE)) {
-            builder.setTorch(false);
+            builder.setSupportsTorch(false);
             builder.setRedEyeReduction(false);
         } else {
             // There's no way to query if torch and/or red eye reduction modes are available using
             // Camera2 API but since there's a Flash unit, we assume so.
-            builder.setTorch(true);
+            builder.setSupportsTorch(true);
+            builder.setTorch(mPreviewRequest.get(CaptureRequest.FLASH_MODE)
+                    == CameraMetadata.FLASH_MODE_TORCH);
+
             builder.setRedEyeReduction(true);
 
             final int[] flashModes =
diff --git a/media/capture/video/android/photo_capabilities.cc b/media/capture/video/android/photo_capabilities.cc
index 0b9ef77..adb0377e 100644
--- a/media/capture/video/android/photo_capabilities.cc
+++ b/media/capture/video/android/photo_capabilities.cc
@@ -165,6 +165,12 @@
   return modes;
 }
 
+bool PhotoCapabilities::getSupportsTorch() const {
+  DCHECK(!object_.is_null());
+  return Java_PhotoCapabilities_getSupportsTorch(AttachCurrentThread(),
+                                                 object_);
+}
+
 bool PhotoCapabilities::getTorch() const {
   DCHECK(!object_.is_null());
   return Java_PhotoCapabilities_getTorch(AttachCurrentThread(), object_);
diff --git a/media/capture/video/android/photo_capabilities.h b/media/capture/video/android/photo_capabilities.h
index 37b9933..aea99af 100644
--- a/media/capture/video/android/photo_capabilities.h
+++ b/media/capture/video/android/photo_capabilities.h
@@ -65,6 +65,7 @@
   double getStepExposureCompensation() const;
   AndroidMeteringMode getWhiteBalanceMode() const;
   std::vector<AndroidFillLightMode> getFillLightModes() const;
+  bool getSupportsTorch() const;
   bool getTorch() const;
   bool getRedEyeReduction() const;
   int getMinColorTemperature() const;
diff --git a/media/capture/video/android/video_capture_device_android.cc b/media/capture/video/android/video_capture_device_android.cc
index c95f2ee..fa6f03b 100644
--- a/media/capture/video/android/video_capture_device_android.cc
+++ b/media/capture/video/android/video_capture_device_android.cc
@@ -542,6 +542,7 @@
   photo_capabilities->zoom->min = caps.getMinZoom();
   photo_capabilities->zoom->step = caps.getStepZoom();
 
+  photo_capabilities->supports_torch = caps.getSupportsTorch();
   photo_capabilities->torch = caps.getTorch();
 
   photo_capabilities->red_eye_reduction =
diff --git a/media/capture/video/fake_video_capture_device.cc b/media/capture/video/fake_video_capture_device.cc
index e7ef7159..56d9f71 100644
--- a/media/capture/video/fake_video_capture_device.cc
+++ b/media/capture/video/fake_video_capture_device.cc
@@ -472,6 +472,7 @@
   photo_capabilities->zoom->min = kMinZoom;
   photo_capabilities->zoom->step = kZoomStep;
 
+  photo_capabilities->supports_torch = false;
   photo_capabilities->torch = false;
 
   photo_capabilities->red_eye_reduction = mojom::RedEyeReduction::NEVER;
diff --git a/media/capture/video/fake_video_capture_device_unittest.cc b/media/capture/video/fake_video_capture_device_unittest.cc
index fd52e19b5..bd57e153 100644
--- a/media/capture/video/fake_video_capture_device_unittest.cc
+++ b/media/capture/video/fake_video_capture_device_unittest.cc
@@ -459,6 +459,7 @@
   EXPECT_EQ(0, capabilities->sharpness->current);
   EXPECT_EQ(0, capabilities->sharpness->step);
 
+  EXPECT_FALSE(capabilities->supports_torch);
   EXPECT_FALSE(capabilities->torch);
 
   EXPECT_EQ(mojom::RedEyeReduction::NEVER, capabilities->red_eye_reduction);
diff --git a/media/remoting/BUILD.gn b/media/remoting/BUILD.gn
index bf628d7..455a0c4 100644
--- a/media/remoting/BUILD.gn
+++ b/media/remoting/BUILD.gn
@@ -80,13 +80,19 @@
   sources = [
     "courier_renderer_unittest.cc",
     "demuxer_stream_adapter_unittest.cc",
+    "end2end_test_renderer.cc",
+    "end2end_test_renderer.h",
     "fake_media_resource.cc",
     "fake_media_resource.h",
     "fake_remoter.cc",
     "fake_remoter.h",
     "proto_utils_unittest.cc",
+    "receiver.cc",
+    "receiver.h",
     "renderer_controller_unittest.cc",
     "rpc_broker_unittest.cc",
+    "stream_provider.cc",
+    "stream_provider.h",
   ]
 
   deps = [
diff --git a/media/remoting/end2end_test_renderer.cc b/media/remoting/end2end_test_renderer.cc
new file mode 100644
index 0000000..283f137
--- /dev/null
+++ b/media/remoting/end2end_test_renderer.cc
@@ -0,0 +1,217 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/remoting/end2end_test_renderer.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "media/mojo/interfaces/remoting.mojom.h"
+#include "media/remoting/courier_renderer.h"
+#include "media/remoting/proto_utils.h"
+#include "media/remoting/receiver.h"
+#include "media/remoting/renderer_controller.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+
+namespace media {
+namespace remoting {
+
+namespace {
+
+class TestStreamSender final : public mojom::RemotingDataStreamSender {
+ public:
+  using SendFrameToSinkCallback =
+      base::Callback<void(const std::vector<uint8_t>& data,
+                          DemuxerStream::Type type)>;
+  TestStreamSender(mojom::RemotingDataStreamSenderRequest request,
+                   mojo::ScopedDataPipeConsumerHandle handle,
+                   DemuxerStream::Type type,
+                   const SendFrameToSinkCallback& callback)
+      : binding_(this, std::move(request)),
+        consumer_handle_(std::move(handle)),
+        type_(type),
+        send_frame_to_sink_cb_(callback) {}
+
+  ~TestStreamSender() override {}
+
+  // mojom::RemotingDataStreamSender implementation.
+
+  void ConsumeDataChunk(uint32_t offset,
+                        uint32_t size,
+                        uint32_t total_payload_size) override {
+    next_frame_data_.resize(total_payload_size);
+    MojoResult result = mojo::ReadDataRaw(
+        consumer_handle_.get(), next_frame_data_.data() + offset, &size,
+        MOJO_READ_DATA_FLAG_ALL_OR_NONE);
+    CHECK(result == MOJO_RESULT_OK);
+  }
+
+  void SendFrame() override {
+    if (!send_frame_to_sink_cb_.is_null())
+      send_frame_to_sink_cb_.Run(next_frame_data_, type_);
+    next_frame_data_.resize(0);
+  }
+
+  void CancelInFlightData() override { next_frame_data_.resize(0); }
+
+ private:
+  mojo::Binding<RemotingDataStreamSender> binding_;
+  mojo::ScopedDataPipeConsumerHandle consumer_handle_;
+  const DemuxerStream::Type type_;
+  const SendFrameToSinkCallback send_frame_to_sink_cb_;
+  std::vector<uint8_t> next_frame_data_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestStreamSender);
+};
+
+class TestRemoter final : public mojom::Remoter {
+ public:
+  using SendMessageToSinkCallback =
+      base::Callback<void(const std::vector<uint8_t>& message)>;
+  TestRemoter(
+      mojom::RemotingSourcePtr source,
+      const SendMessageToSinkCallback& send_message_to_sink_cb,
+      const TestStreamSender::SendFrameToSinkCallback& send_frame_to_sink_cb)
+      : source_(std::move(source)),
+        send_message_to_sink_cb_(send_message_to_sink_cb),
+        send_frame_to_sink_cb_(send_frame_to_sink_cb) {}
+
+  ~TestRemoter() override {}
+
+  // mojom::Remoter implementation.
+
+  void Start() override { source_->OnStarted(); }
+
+  void StartDataStreams(
+      mojo::ScopedDataPipeConsumerHandle audio_pipe,
+      mojo::ScopedDataPipeConsumerHandle video_pipe,
+      mojom::RemotingDataStreamSenderRequest audio_sender_request,
+      mojom::RemotingDataStreamSenderRequest video_sender_request) override {
+    if (audio_pipe.is_valid()) {
+      audio_stream_sender_.reset(new TestStreamSender(
+          std::move(audio_sender_request), std::move(audio_pipe),
+          DemuxerStream::AUDIO, send_frame_to_sink_cb_));
+    }
+    if (video_pipe.is_valid()) {
+      video_stream_sender_.reset(new TestStreamSender(
+          std::move(video_sender_request), std::move(video_pipe),
+          DemuxerStream::VIDEO, send_frame_to_sink_cb_));
+    }
+  }
+
+  void Stop(mojom::RemotingStopReason reason) override {
+    source_->OnStopped(reason);
+  }
+
+  void SendMessageToSink(const std::vector<uint8_t>& message) override {
+    if (!send_message_to_sink_cb_.is_null())
+      send_message_to_sink_cb_.Run(message);
+  }
+
+  // Called when receives RPC messages from receiver.
+  void OnMessageFromSink(const std::vector<uint8_t>& message) {
+    source_->OnMessageFromSink(message);
+  }
+
+ private:
+  mojom::RemotingSourcePtr source_;
+  const SendMessageToSinkCallback send_message_to_sink_cb_;
+  const TestStreamSender::SendFrameToSinkCallback send_frame_to_sink_cb_;
+  std::unique_ptr<TestStreamSender> audio_stream_sender_;
+  std::unique_ptr<TestStreamSender> video_stream_sender_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestRemoter);
+};
+
+scoped_refptr<SharedSession> CreateSharedSession(
+    const TestRemoter::SendMessageToSinkCallback& send_message_to_sink_cb,
+    const TestStreamSender::SendFrameToSinkCallback& send_frame_to_sink_cb) {
+  mojom::RemotingSourcePtr remoting_source;
+  mojom::RemotingSourceRequest remoting_source_request(&remoting_source);
+  mojom::RemoterPtr remoter;
+  std::unique_ptr<TestRemoter> test_remoter = base::MakeUnique<TestRemoter>(
+      std::move(remoting_source), send_message_to_sink_cb,
+      send_frame_to_sink_cb);
+  mojo::MakeStrongBinding(std::move(test_remoter), mojo::MakeRequest(&remoter));
+  return new SharedSession(std::move(remoting_source_request),
+                           std::move(remoter));
+}
+
+}  // namespace
+
+End2EndTestRenderer::End2EndTestRenderer(std::unique_ptr<Renderer> renderer)
+    : receiver_rpc_broker_(base::Bind(&End2EndTestRenderer::OnMessageFromSink,
+                                      base::Unretained(this))),
+      receiver_(new Receiver(std::move(renderer), &receiver_rpc_broker_)),
+      weak_factory_(this) {
+  shared_session_ =
+      CreateSharedSession(base::Bind(&End2EndTestRenderer::SendMessageToSink,
+                                     weak_factory_.GetWeakPtr()),
+                          base::Bind(&End2EndTestRenderer::SendFrameToSink,
+                                     weak_factory_.GetWeakPtr()));
+  controller_.reset(new RendererController(shared_session_));
+  courier_renderer_.reset(new CourierRenderer(
+      base::ThreadTaskRunnerHandle::Get(), controller_->GetWeakPtr(), nullptr));
+}
+
+End2EndTestRenderer::~End2EndTestRenderer() {}
+
+void End2EndTestRenderer::Initialize(MediaResource* media_resource,
+                                     RendererClient* client,
+                                     const PipelineStatusCB& init_cb) {
+  courier_renderer_->Initialize(media_resource, client, init_cb);
+}
+
+void End2EndTestRenderer::SetCdm(CdmContext* cdm_context,
+                                 const CdmAttachedCB& cdc_attached_cb) {
+  // TODO(xjz): Add the implementation when media remoting starts supporting
+  // encrypted contents.
+  NOTIMPLEMENTED() << "Media Remoting doesn't support EME for now.";
+}
+
+void End2EndTestRenderer::Flush(const base::Closure& flush_cb) {
+  courier_renderer_->Flush(flush_cb);
+}
+
+void End2EndTestRenderer::StartPlayingFrom(base::TimeDelta time) {
+  courier_renderer_->StartPlayingFrom(time);
+}
+
+void End2EndTestRenderer::SetPlaybackRate(double playback_rate) {
+  courier_renderer_->SetPlaybackRate(playback_rate);
+}
+
+void End2EndTestRenderer::SetVolume(float volume) {
+  courier_renderer_->SetVolume(volume);
+}
+
+base::TimeDelta End2EndTestRenderer::GetMediaTime() {
+  return courier_renderer_->GetMediaTime();
+}
+
+void End2EndTestRenderer::SendMessageToSink(
+    const std::vector<uint8_t>& message) {
+  std::unique_ptr<pb::RpcMessage> rpc(new pb::RpcMessage());
+  if (!rpc->ParseFromArray(message.data(), message.size())) {
+    VLOG(1) << __func__ << ": Received corrupted Rpc message.";
+    return;
+  }
+  receiver_rpc_broker_.ProcessMessageFromRemote(std::move(rpc));
+}
+
+void End2EndTestRenderer::SendFrameToSink(const std::vector<uint8_t>& frame,
+                                          DemuxerStream::Type type) {
+  scoped_refptr<DecoderBuffer> decoder_buffer =
+      ByteArrayToDecoderBuffer(frame.data(), frame.size());
+  receiver_->OnReceivedBuffer(type, decoder_buffer);
+}
+
+void End2EndTestRenderer::OnMessageFromSink(
+    std::unique_ptr<std::vector<uint8_t>> message) {
+  shared_session_->OnMessageFromSink(*message);
+}
+
+}  // namespace remoting
+}  // namespace media
diff --git a/media/remoting/end2end_test_renderer.h b/media/remoting/end2end_test_renderer.h
new file mode 100644
index 0000000..5ef04b1e
--- /dev/null
+++ b/media/remoting/end2end_test_renderer.h
@@ -0,0 +1,71 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_REMOTING_END2END_RENDERER_H_
+#define MEDIA_REMOTING_END2END_RENDERER_H_
+
+#include <vector>
+
+#include "base/memory/weak_ptr.h"
+#include "media/base/demuxer_stream.h"
+#include "media/base/renderer.h"
+#include "media/remoting/rpc_broker.h"
+
+namespace media {
+namespace remoting {
+
+class SharedSession;
+class RendererController;
+class CourierRenderer;
+class Receiver;
+
+// Simulates the media remoting pipeline.
+class End2EndTestRenderer final : public Renderer {
+ public:
+  explicit End2EndTestRenderer(std::unique_ptr<Renderer> renderer);
+  ~End2EndTestRenderer() override;
+
+  // Renderer implementation.
+  void Initialize(MediaResource* media_resource,
+                  RendererClient* client,
+                  const PipelineStatusCB& init_cb) override;
+  void SetCdm(CdmContext* cdm_context,
+              const CdmAttachedCB& cdm_attached_cb) override;
+  void Flush(const base::Closure& flush_cb) override;
+  void StartPlayingFrom(base::TimeDelta time) override;
+  void SetPlaybackRate(double playback_rate) override;
+  void SetVolume(float volume) override;
+  base::TimeDelta GetMediaTime() override;
+
+ private:
+  // Called to send RPC messages to |receiver_|.
+  void SendMessageToSink(const std::vector<uint8_t>& message);
+
+  // Called to send frame data to |receiver_|.
+  void SendFrameToSink(const std::vector<uint8_t>& data,
+                       DemuxerStream::Type type);
+
+  // Called when receives RPC messages from |receiver_|.
+  void OnMessageFromSink(std::unique_ptr<std::vector<uint8_t>> message);
+
+  // The session that is used by |controller_| to create the data pipes.
+  scoped_refptr<SharedSession> shared_session_;
+  std::unique_ptr<RendererController> controller_;
+  std::unique_ptr<CourierRenderer> courier_renderer_;
+
+  // The RpcBroker to handle the RPC messages to/from |receiver_|.
+  RpcBroker receiver_rpc_broker_;
+
+  // A receiver that renders media streams.
+  std::unique_ptr<Receiver> receiver_;
+
+  base::WeakPtrFactory<End2EndTestRenderer> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(End2EndTestRenderer);
+};
+
+}  // namespace remoting
+}  // namespace media
+
+#endif  // MEDIA_REMOTING_END2END_RENDERER_H_
diff --git a/media/remoting/receiver.cc b/media/remoting/receiver.cc
new file mode 100644
index 0000000..90dd78b2
--- /dev/null
+++ b/media/remoting/receiver.cc
@@ -0,0 +1,304 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/remoting/receiver.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "media/base/decoder_buffer.h"
+#include "media/base/renderer.h"
+#include "media/remoting/proto_enum_utils.h"
+#include "media/remoting/stream_provider.h"
+
+namespace media {
+namespace remoting {
+namespace {
+
+// The period to send the TimeUpdate RPC message to update the media time on
+// sender side.
+constexpr base::TimeDelta kTimeUpdateInterval =
+    base::TimeDelta::FromMilliseconds(250);
+
+}  // namespace
+
+Receiver::Receiver(std::unique_ptr<Renderer> renderer, RpcBroker* rpc_broker)
+    : renderer_(std::move(renderer)),
+      rpc_broker_(rpc_broker),
+      rpc_handle_(rpc_broker_->GetUniqueHandle()),
+      weak_factory_(this) {
+  DCHECK(renderer_);
+  DCHECK(rpc_broker_);
+  rpc_broker_->RegisterMessageReceiverCallback(
+      rpc_handle_,
+      base::Bind(&Receiver::OnReceivedRpc, weak_factory_.GetWeakPtr()));
+  rpc_broker_->RegisterMessageReceiverCallback(
+      RpcBroker::kAcquireHandle,
+      base::Bind(&Receiver::OnReceivedRpc, weak_factory_.GetWeakPtr()));
+}
+
+Receiver::~Receiver() {
+  rpc_broker_->UnregisterMessageReceiverCallback(rpc_handle_);
+  rpc_broker_->UnregisterMessageReceiverCallback(RpcBroker::kAcquireHandle);
+}
+
+void Receiver::OnReceivedRpc(std::unique_ptr<pb::RpcMessage> message) {
+  DCHECK(message);
+  switch (message->proc()) {
+    case pb::RpcMessage::RPC_ACQUIRE_RENDERER:
+      AcquireRenderer(std::move(message));
+      break;
+    case pb::RpcMessage::RPC_R_FLUSHUNTIL:
+      FlushUntil(std::move(message));
+      break;
+    case pb::RpcMessage::RPC_R_STARTPLAYINGFROM:
+      StartPlayingFrom(std::move(message));
+      break;
+    case pb::RpcMessage::RPC_R_SETPLAYBACKRATE:
+      SetPlaybackRate(std::move(message));
+      break;
+    case pb::RpcMessage::RPC_R_SETVOLUME:
+      SetVolume(std::move(message));
+      break;
+    case pb::RpcMessage::RPC_R_INITIALIZE:
+      Initialize(std::move(message));
+      break;
+    default:
+      VLOG(1) << __func__ << ": Unknow RPC message. proc=" << message->proc();
+  }
+}
+
+void Receiver::AcquireRenderer(std::unique_ptr<pb::RpcMessage> message) {
+  DVLOG(3) << __func__ << ": Receives RPC_ACQUIRE_RENDERER with remote_handle= "
+           << message->integer_value();
+
+  remote_handle_ = message->integer_value();
+  if (stream_provider_) {
+    VLOG(1) << "Acquire renderer error: Already aquired.";
+    OnError(PipelineStatus::PIPELINE_ERROR_DECODE);
+    return;
+  }
+
+  stream_provider_.reset(new StreamProvider(
+      rpc_broker_, base::Bind(&Receiver::OnError, weak_factory_.GetWeakPtr(),
+                              PipelineStatus::PIPELINE_ERROR_DECODE)));
+
+  DVLOG(3) << __func__
+           << ": Issues RPC_ACQUIRE_RENDERER_DONE RPC message. remote_handle="
+           << remote_handle_ << " rpc_handle=" << rpc_handle_;
+  std::unique_ptr<pb::RpcMessage> rpc(new pb::RpcMessage());
+  rpc->set_handle(remote_handle_);
+  rpc->set_proc(pb::RpcMessage::RPC_ACQUIRE_RENDERER_DONE);
+  rpc->set_integer_value(rpc_handle_);
+  rpc_broker_->SendMessageToRemote(std::move(rpc));
+}
+
+void Receiver::Initialize(std::unique_ptr<pb::RpcMessage> message) {
+  DCHECK(stream_provider_);
+  DVLOG(3) << __func__ << ": Receives RPC_R_INITIALIZE with callback handle= "
+           << message->renderer_initialize_rpc().callback_handle();
+  DCHECK(message->renderer_initialize_rpc().callback_handle() ==
+         remote_handle_);
+  if (!stream_provider_)
+    OnRendererInitialized(PipelineStatus::PIPELINE_ERROR_INITIALIZATION_FAILED);
+
+  stream_provider_->Initialize(
+      message->renderer_initialize_rpc().audio_demuxer_handle(),
+      message->renderer_initialize_rpc().video_demuxer_handle(),
+      base::Bind(&Receiver::OnStreamInitialized, weak_factory_.GetWeakPtr()));
+}
+
+void Receiver::OnStreamInitialized() {
+  DCHECK(stream_provider_);
+  renderer_->Initialize(
+      stream_provider_.get(), this,
+      base::Bind(&Receiver::OnRendererInitialized, weak_factory_.GetWeakPtr()));
+}
+
+void Receiver::OnRendererInitialized(PipelineStatus status) {
+  DVLOG(3) << __func__ << ": Issues RPC_R_INITIALIZE_CALLBACK RPC message."
+           << "remote_handle=" << remote_handle_;
+
+  std::unique_ptr<pb::RpcMessage> rpc(new pb::RpcMessage());
+  rpc->set_handle(remote_handle_);
+  rpc->set_proc(pb::RpcMessage::RPC_R_INITIALIZE_CALLBACK);
+  rpc->set_boolean_value(status == PIPELINE_OK);
+  rpc_broker_->SendMessageToRemote(std::move(rpc));
+}
+
+void Receiver::SetPlaybackRate(std::unique_ptr<pb::RpcMessage> message) {
+  const double playback_rate = message->double_value();
+  DVLOG(3) << __func__
+           << ": Receives RPC_R_SETPLAYBACKRATE with rate=" << playback_rate;
+  renderer_->SetPlaybackRate(playback_rate);
+
+  if (playback_rate == 0.0) {
+    if (time_update_timer_.IsRunning()) {
+      time_update_timer_.Stop();
+      // Send one final media time update since the sender will not get any
+      // until playback resumes.
+      SendMediaTimeUpdate();
+    }
+  } else {
+    ScheduleMediaTimeUpdates();
+  }
+}
+
+void Receiver::FlushUntil(std::unique_ptr<pb::RpcMessage> message) {
+  DVLOG(3) << __func__ << ": Receives RPC_R_FLUSHUNTIL RPC message.";
+
+  const pb::RendererFlushUntil flush_message =
+      message->renderer_flushuntil_rpc();
+  DCHECK_EQ(flush_message.callback_handle(), remote_handle_);
+  if (stream_provider_) {
+    if (flush_message.has_audio_count()) {
+      stream_provider_->FlushUntil(DemuxerStream::AUDIO,
+                                   flush_message.audio_count());
+    }
+    if (flush_message.has_video_count()) {
+      stream_provider_->FlushUntil(DemuxerStream::VIDEO,
+                                   flush_message.video_count());
+    }
+  }
+  time_update_timer_.Stop();
+  renderer_->Flush(
+      base::Bind(&Receiver::OnFlushDone, weak_factory_.GetWeakPtr()));
+}
+
+void Receiver::OnFlushDone() {
+  DVLOG(3) << __func__ << ": Issues RPC_R_FLUSHUNTIL_CALLBACK RPC message.";
+
+  std::unique_ptr<pb::RpcMessage> rpc(new pb::RpcMessage());
+  rpc->set_handle(remote_handle_);
+  rpc->set_proc(pb::RpcMessage::RPC_R_FLUSHUNTIL_CALLBACK);
+  rpc_broker_->SendMessageToRemote(std::move(rpc));
+}
+
+void Receiver::StartPlayingFrom(std::unique_ptr<pb::RpcMessage> message) {
+  DVLOG(3) << __func__ << ": Receives RPC_R_STARTPLAYINGFROM message.";
+  base::TimeDelta time =
+      base::TimeDelta::FromMicroseconds(message->integer64_value());
+  renderer_->StartPlayingFrom(time);
+  ScheduleMediaTimeUpdates();
+}
+
+void Receiver::ScheduleMediaTimeUpdates() {
+  if (time_update_timer_.IsRunning())
+    return;
+  SendMediaTimeUpdate();
+  time_update_timer_.Start(
+      FROM_HERE, kTimeUpdateInterval,
+      base::Bind(&Receiver::SendMediaTimeUpdate, weak_factory_.GetWeakPtr()));
+}
+
+void Receiver::SetVolume(std::unique_ptr<pb::RpcMessage> message) {
+  DVLOG(3) << __func__ << ": Receives RPC_R_SETVOLUME message.";
+  renderer_->SetVolume(message->double_value());
+}
+
+void Receiver::SendMediaTimeUpdate() {
+  // Issues RPC_RC_ONTIMEUPDATE RPC message.
+  std::unique_ptr<pb::RpcMessage> rpc(new pb::RpcMessage());
+  rpc->set_handle(remote_handle_);
+  rpc->set_proc(pb::RpcMessage::RPC_RC_ONTIMEUPDATE);
+  auto* message = rpc->mutable_rendererclient_ontimeupdate_rpc();
+  base::TimeDelta media_time = renderer_->GetMediaTime();
+  message->set_time_usec(media_time.InMicroseconds());
+  base::TimeDelta max_time = media_time;
+  message->set_max_time_usec(max_time.InMicroseconds());
+  DVLOG(3) << __func__ << ": Issues RPC_RC_ONTIMEUPDATE message."
+           << " media_time = " << media_time.InMicroseconds()
+           << " max_time= " << max_time.InMicroseconds();
+  rpc_broker_->SendMessageToRemote(std::move(rpc));
+}
+
+void Receiver::OnReceivedBuffer(DemuxerStream::Type type,
+                                scoped_refptr<DecoderBuffer> buffer) {
+  DVLOG(3) << __func__
+           << ": type=" << (type == DemuxerStream::AUDIO ? "Audio" : "Video");
+  DCHECK(stream_provider_);
+  stream_provider_->AppendBuffer(type, buffer);
+}
+
+void Receiver::OnError(PipelineStatus status) {
+  VLOG(1) << __func__ << ": Issues RPC_RC_ONERROR message.";
+  std::unique_ptr<pb::RpcMessage> rpc(new pb::RpcMessage());
+  rpc->set_handle(remote_handle_);
+  rpc->set_proc(pb::RpcMessage::RPC_RC_ONERROR);
+  rpc_broker_->SendMessageToRemote(std::move(rpc));
+}
+
+void Receiver::OnEnded() {
+  DVLOG(3) << __func__ << ": Issues RPC_RC_ONENDED message.";
+  std::unique_ptr<pb::RpcMessage> rpc(new pb::RpcMessage());
+  rpc->set_handle(remote_handle_);
+  rpc->set_proc(pb::RpcMessage::RPC_RC_ONENDED);
+  rpc_broker_->SendMessageToRemote(std::move(rpc));
+  time_update_timer_.Stop();
+}
+
+void Receiver::OnStatisticsUpdate(const PipelineStatistics& stats) {
+  DVLOG(3) << __func__ << ": Issues RPC_RC_ONSTATISTICSUPDATE message.";
+  std::unique_ptr<pb::RpcMessage> rpc(new pb::RpcMessage());
+  rpc->set_handle(remote_handle_);
+  rpc->set_proc(pb::RpcMessage::RPC_RC_ONSTATISTICSUPDATE);
+  auto* message = rpc->mutable_rendererclient_onstatisticsupdate_rpc();
+  message->set_audio_bytes_decoded(stats.audio_bytes_decoded);
+  message->set_video_bytes_decoded(stats.video_bytes_decoded);
+  message->set_video_frames_decoded(stats.video_frames_decoded);
+  message->set_video_frames_dropped(stats.video_frames_dropped);
+  message->set_audio_memory_usage(stats.audio_memory_usage);
+  message->set_video_memory_usage(stats.video_memory_usage);
+  rpc_broker_->SendMessageToRemote(std::move(rpc));
+}
+
+void Receiver::OnBufferingStateChange(BufferingState state) {
+  DVLOG(3) << __func__
+           << ": Issues RPC_RC_ONBUFFERINGSTATECHANGE message: state=" << state;
+  std::unique_ptr<pb::RpcMessage> rpc(new pb::RpcMessage());
+  rpc->set_handle(remote_handle_);
+  rpc->set_proc(pb::RpcMessage::RPC_RC_ONBUFFERINGSTATECHANGE);
+  auto* message = rpc->mutable_rendererclient_onbufferingstatechange_rpc();
+  message->set_state(ToProtoMediaBufferingState(state).value());
+  rpc_broker_->SendMessageToRemote(std::move(rpc));
+}
+
+void Receiver::OnWaitingForDecryptionKey() {
+  DVLOG(3) << __func__ << ": Issues RPC_RC_ONWAITINGFORDECRYPTIONKEY message.";
+  std::unique_ptr<pb::RpcMessage> rpc(new pb::RpcMessage());
+  rpc->set_handle(remote_handle_);
+  rpc->set_proc(pb::RpcMessage::RPC_RC_ONWAITINGFORDECRYPTIONKEY);
+  rpc_broker_->SendMessageToRemote(std::move(rpc));
+}
+
+void Receiver::OnVideoNaturalSizeChange(const gfx::Size& size) {
+  DVLOG(3) << __func__ << ": Issues RPC_RC_ONVIDEONATURALSIZECHANGE message.";
+  std::unique_ptr<pb::RpcMessage> rpc(new pb::RpcMessage());
+  rpc->set_handle(remote_handle_);
+  rpc->set_proc(pb::RpcMessage::RPC_RC_ONVIDEONATURALSIZECHANGE);
+  auto* message = rpc->mutable_rendererclient_onvideonatualsizechange_rpc();
+  message->set_width(size.width());
+  message->set_height(size.height());
+  rpc_broker_->SendMessageToRemote(std::move(rpc));
+}
+
+void Receiver::OnVideoOpacityChange(bool opaque) {
+  DVLOG(3) << __func__ << ": Issues RPC_RC_ONVIDEOOPACITYCHANGE message.";
+  std::unique_ptr<pb::RpcMessage> rpc(new pb::RpcMessage());
+  rpc->set_handle(remote_handle_);
+  rpc->set_proc(pb::RpcMessage::RPC_RC_ONVIDEOOPACITYCHANGE);
+  rpc->set_boolean_value(opaque);
+  rpc_broker_->SendMessageToRemote(std::move(rpc));
+}
+
+void Receiver::OnDurationChange(base::TimeDelta duration) {
+  DVLOG(3) << __func__ << ": Issues RPC_RC_ONDURATIONCHANGE message.";
+  std::unique_ptr<pb::RpcMessage> rpc(new pb::RpcMessage());
+  rpc->set_handle(remote_handle_);
+  rpc->set_proc(pb::RpcMessage::RPC_RC_ONDURATIONCHANGE);
+  rpc->set_integer_value(duration.InMicroseconds());
+  rpc_broker_->SendMessageToRemote(std::move(rpc));
+}
+
+}  // namespace remoting
+}  // namespace media
diff --git a/media/remoting/receiver.h b/media/remoting/receiver.h
new file mode 100644
index 0000000..6068ca82
--- /dev/null
+++ b/media/remoting/receiver.h
@@ -0,0 +1,88 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_REMOTING_RECEIVER_H_
+#define MEDIA_REMOTING_RECEIVER_H_
+
+#include "base/memory/weak_ptr.h"
+#include "base/timer/timer.h"
+#include "media/base/buffering_state.h"
+#include "media/base/demuxer_stream.h"
+#include "media/base/renderer_client.h"
+#include "media/remoting/rpc_broker.h"
+
+namespace media {
+class Renderer;
+class DecoderBuffer;
+}  // namespace media
+
+namespace media {
+namespace remoting {
+
+class RpcBroker;
+class StreamProvider;
+
+// Media remoting receiver. Media streams are rendered by |renderer|.
+// |rpc_broker| outlives this class.
+class Receiver final : public RendererClient {
+ public:
+  Receiver(std::unique_ptr<Renderer> renderer, RpcBroker* rpc_broker);
+  ~Receiver();
+
+  // RendererClient implementation.
+  void OnError(PipelineStatus status) override;
+  void OnEnded() override;
+  void OnStatisticsUpdate(const PipelineStatistics& stats) override;
+  void OnBufferingStateChange(BufferingState state) override;
+  void OnWaitingForDecryptionKey() override;
+  void OnVideoNaturalSizeChange(const gfx::Size& size) override;
+  void OnVideoOpacityChange(bool opaque) override;
+  void OnDurationChange(base::TimeDelta duration) override;
+
+  void OnReceivedRpc(std::unique_ptr<pb::RpcMessage> message);
+  void OnReceivedBuffer(DemuxerStream::Type type,
+                        scoped_refptr<DecoderBuffer> buffer);
+
+ private:
+  // RPC message handlers.
+  void AcquireRenderer(std::unique_ptr<pb::RpcMessage> message);
+  void Initialize(std::unique_ptr<pb::RpcMessage> message);
+  void SetPlaybackRate(std::unique_ptr<pb::RpcMessage> message);
+  void FlushUntil(std::unique_ptr<pb::RpcMessage> message);
+  void StartPlayingFrom(std::unique_ptr<pb::RpcMessage> message);
+  void SetVolume(std::unique_ptr<pb::RpcMessage> message);
+
+  // Initialization callbacks.
+  void OnStreamInitialized();
+  void OnRendererInitialized(PipelineStatus status);
+
+  void OnFlushDone();
+
+  // Periodically send the UpdateTime RPC message to update the media time.
+  void ScheduleMediaTimeUpdates();
+  void SendMediaTimeUpdate();
+
+  const std::unique_ptr<Renderer> renderer_;
+  RpcBroker* const rpc_broker_;  // Outlives this class.
+
+  // The CourierRenderer handle on sender side. Set when AcauireRenderer() is
+  // called.
+  int remote_handle_ = RpcBroker::kInvalidHandle;
+
+  int rpc_handle_ = RpcBroker::kInvalidHandle;
+
+  std::unique_ptr<StreamProvider> stream_provider_;
+
+  // The timer to periodically update the media time.
+  base::RepeatingTimer time_update_timer_;
+
+  base::WeakPtrFactory<Receiver> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(Receiver);
+};
+
+}  // namespace remoting
+}  // namespace media
+
+#endif  // MEDIA_REMOTING_RECEIVER_H_
diff --git a/media/remoting/stream_provider.cc b/media/remoting/stream_provider.cc
new file mode 100644
index 0000000..3bd99591
--- /dev/null
+++ b/media/remoting/stream_provider.cc
@@ -0,0 +1,485 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/remoting/stream_provider.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/callback_helpers.h"
+#include "base/logging.h"
+#include "media/base/decoder_buffer.h"
+#include "media/base/video_rotation.h"
+#include "media/remoting/proto_enum_utils.h"
+#include "media/remoting/proto_utils.h"
+
+namespace media {
+namespace remoting {
+
+namespace {
+// The number of frames requested in each ReadUntil RPC message.
+constexpr int kNumFramesInEachReadUntil = 10;
+}
+
+// An implementation of media::DemuxerStream on Media Remoting receiver.
+// Receives data from mojo data pipe, and returns one frame or/and status when
+// Read() is called.
+class MediaStream final : public DemuxerStream {
+ public:
+  MediaStream(RpcBroker* rpc_broker,
+              Type type,
+              int remote_handle,
+              const base::Closure& error_callback);
+  ~MediaStream() override;
+
+  // DemuxerStream implementation.
+  void Read(const ReadCB& read_cb) override;
+  AudioDecoderConfig audio_decoder_config() override;
+  VideoDecoderConfig video_decoder_config() override;
+  DemuxerStream::Type type() const override;
+  Liveness liveness() const override;
+  bool SupportsConfigChanges() override;
+  VideoRotation video_rotation() override;
+
+  void Initialize(const base::Closure& init_done_cb);
+  void FlushUntil(int count);
+  void AppendBuffer(scoped_refptr<DecoderBuffer> buffer);
+
+ private:
+  // RPC messages handlers.
+  void OnReceivedRpc(std::unique_ptr<pb::RpcMessage> message);
+  void OnInitializeCallback(std::unique_ptr<pb::RpcMessage> message);
+  void OnReadUntilCallback(std::unique_ptr<pb::RpcMessage> message);
+
+  // Issues the ReadUntil RPC message when read is pending and buffer is empty.
+  void SendReadUntil();
+
+  // Run and reset the read callback.
+  void CompleteRead(DemuxerStream::Status status);
+
+  // Update the audio/video decoder config When config changes in the mid
+  // stream, the new config will be stored in
+  // |next_audio/video_decoder_config_|. Old config will be droped when all
+  // associated frames are consumed.
+  void UpdateConfig(const pb::AudioDecoderConfig* audio_message,
+                    const pb::VideoDecoderConfig* video_message);
+
+  // Called when any error occurs.
+  void OnError(const std::string& error);
+
+  RpcBroker* const rpc_broker_;  // Outlives this class.
+  const Type type_;
+  const int remote_handle_;
+  const int rpc_handle_;
+
+  // Set when Initialize() is called, and will be run only once after
+  // initialization is done.
+  base::Closure init_done_callback_;
+
+  // The read until count in the last ReadUntil RPC message.
+  int last_read_until_count_ = 0;
+
+  // Indicates whether Audio/VideoDecoderConfig changed and the frames with the
+  // old config are not yet consumed. The new config is stored in the end of
+  // |audio/video_decoder_config_|;
+  bool config_changed_ = false;
+
+  // Indicates whether a ReadUntil RPC message was sent without receiving the
+  // ReadUntilCallback message yet.
+  bool read_until_sent_ = false;
+
+  // Set when Read() is called. Run only once when read completes.
+  ReadCB read_complete_callback_;
+
+  base::Closure error_callback_;  // Called only once when first error occurs.
+
+  std::deque<scoped_refptr<DecoderBuffer>> buffers_;
+
+  // Current audio/video config.
+  AudioDecoderConfig audio_decoder_config_;
+  VideoDecoderConfig video_decoder_config_;
+
+  // Stores the new auido/video config when config changes.
+  AudioDecoderConfig next_audio_decoder_config_;
+  VideoDecoderConfig next_video_decoder_config_;
+
+  base::WeakPtrFactory<MediaStream> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(MediaStream);
+};
+
+MediaStream::MediaStream(RpcBroker* rpc_broker,
+                         Type type,
+                         int remote_handle,
+                         const base::Closure& error_callback)
+    : rpc_broker_(rpc_broker),
+      type_(type),
+      remote_handle_(remote_handle),
+      rpc_handle_(rpc_broker_->GetUniqueHandle()),
+      error_callback_(error_callback),
+      weak_factory_(this) {
+  DCHECK(remote_handle_ != RpcBroker::kInvalidHandle);
+  rpc_broker_->RegisterMessageReceiverCallback(
+      rpc_handle_,
+      base::Bind(&MediaStream::OnReceivedRpc, weak_factory_.GetWeakPtr()));
+}
+
+MediaStream::~MediaStream() {
+  rpc_broker_->UnregisterMessageReceiverCallback(rpc_handle_);
+}
+
+void MediaStream::Initialize(const base::Closure& init_done_cb) {
+  DCHECK(!init_done_cb.is_null());
+  if (!init_done_callback_.is_null()) {
+    OnError("Duplicate initialization");
+    return;
+  }
+  init_done_callback_ = init_done_cb;
+
+  DVLOG(3) << __func__ << "Issues RpcMessage::RPC_DS_INITIALIZE with "
+           << "remote_handle=" << remote_handle_
+           << " rpc_handle=" << rpc_handle_;
+  std::unique_ptr<pb::RpcMessage> rpc(new pb::RpcMessage());
+  rpc->set_handle(remote_handle_);
+  rpc->set_proc(pb::RpcMessage::RPC_DS_INITIALIZE);
+  rpc->set_integer_value(rpc_handle_);
+  rpc_broker_->SendMessageToRemote(std::move(rpc));
+}
+
+void MediaStream::OnReceivedRpc(std::unique_ptr<pb::RpcMessage> message) {
+  DCHECK(message->handle() == rpc_handle_);
+
+  switch (message->proc()) {
+    case pb::RpcMessage::RPC_DS_INITIALIZE_CALLBACK:
+      OnInitializeCallback(std::move(message));
+      break;
+    case pb::RpcMessage::RPC_DS_READUNTIL_CALLBACK:
+      OnReadUntilCallback(std::move(message));
+      break;
+    default:
+      VLOG(3) << __func__ << "Unknow RPC message.";
+  }
+}
+
+void MediaStream::OnInitializeCallback(
+    std::unique_ptr<pb::RpcMessage> message) {
+  DVLOG(3) << __func__ << "Receives RPC_DS_INITIALIZE_CALLBACK message.";
+  const pb::DemuxerStreamInitializeCallback callback_message =
+      message->demuxerstream_initializecb_rpc();
+  if (callback_message.type() != type_) {
+    OnError("Wrong type");
+    return;
+  }
+  if ((type_ == DemuxerStream::AUDIO &&
+       audio_decoder_config_.IsValidConfig()) ||
+      (type_ == DemuxerStream::VIDEO &&
+       video_decoder_config_.IsValidConfig())) {
+    OnError("Duplicate Iniitialize");
+    return;
+  }
+  if (init_done_callback_.is_null()) {
+    OnError("Iniitialize callback missing");
+    return;
+  }
+
+  if (type_ == DemuxerStream::AUDIO &&
+      callback_message.has_audio_decoder_config()) {
+    const pb::AudioDecoderConfig audio_message =
+        callback_message.audio_decoder_config();
+    UpdateConfig(&audio_message, nullptr);
+  } else if (type_ == DemuxerStream::VIDEO &&
+             callback_message.has_video_decoder_config()) {
+    const pb::VideoDecoderConfig video_message =
+        callback_message.video_decoder_config();
+    UpdateConfig(nullptr, &video_message);
+  } else {
+    OnError("config missing");
+    return;
+  }
+  base::ResetAndReturn(&init_done_callback_).Run();
+}
+
+void MediaStream::OnReadUntilCallback(std::unique_ptr<pb::RpcMessage> message) {
+  DVLOG(3) << __func__ << ": Receives RPC_DS_READUNTIL_CALLBACK message.";
+  if (!read_until_sent_) {
+    OnError("Unexpected ReadUntilCallback");
+    return;
+  }
+  read_until_sent_ = false;
+  const pb::DemuxerStreamReadUntilCallback callback_message =
+      message->demuxerstream_readuntilcb_rpc();
+  last_read_until_count_ = callback_message.count();
+  if (ToDemuxerStreamStatus(callback_message.status()) == kConfigChanged) {
+    config_changed_ = true;
+    if (callback_message.has_audio_decoder_config()) {
+      const pb::AudioDecoderConfig audio_message =
+          callback_message.audio_decoder_config();
+      UpdateConfig(&audio_message, nullptr);
+    }
+    if (callback_message.has_video_decoder_config()) {
+      const pb::VideoDecoderConfig video_message =
+          callback_message.video_decoder_config();
+      UpdateConfig(nullptr, &video_message);
+    }
+    if (buffers_.empty() && !read_complete_callback_.is_null())
+      CompleteRead(DemuxerStream::kConfigChanged);
+    return;
+  }
+  if (buffers_.empty() && !read_complete_callback_.is_null())
+    SendReadUntil();
+}
+
+void MediaStream::UpdateConfig(const pb::AudioDecoderConfig* audio_message,
+                               const pb::VideoDecoderConfig* video_message) {
+  if (type_ == AUDIO) {
+    DCHECK(audio_message && !video_message);
+    AudioDecoderConfig audio_config;
+    ConvertProtoToAudioDecoderConfig(*audio_message, &audio_config);
+    if (!audio_config.IsValidConfig()) {
+      OnError("Invalid audio config");
+      return;
+    }
+    if (config_changed_) {
+      DCHECK(audio_decoder_config_.IsValidConfig());
+      DCHECK(!next_audio_decoder_config_.IsValidConfig());
+      next_audio_decoder_config_ = audio_config;
+    } else {
+      DCHECK(!audio_decoder_config_.IsValidConfig());
+      audio_decoder_config_ = audio_config;
+    }
+  } else if (type_ == VIDEO) {
+    DCHECK(video_message && !audio_message);
+    VideoDecoderConfig video_config;
+    ConvertProtoToVideoDecoderConfig(*video_message, &video_config);
+    if (!video_config.IsValidConfig()) {
+      OnError("Invalid video config");
+      return;
+    }
+    if (config_changed_) {
+      DCHECK(video_decoder_config_.IsValidConfig());
+      DCHECK(!next_video_decoder_config_.IsValidConfig());
+      next_video_decoder_config_ = video_config;
+    } else {
+      DCHECK(!video_decoder_config_.IsValidConfig());
+      video_decoder_config_ = video_config;
+    }
+  } else {
+    NOTREACHED() << ": Only supports video or audio stream.";
+  }
+}
+
+void MediaStream::SendReadUntil() {
+  if (read_until_sent_)
+    return;
+  DVLOG(3) << "Issues RPC_DS_READUNTIL RPC message to remote_handle_="
+           << remote_handle_ << " with callback handle=" << rpc_handle_
+           << " count=" << last_read_until_count_;
+
+  std::unique_ptr<pb::RpcMessage> rpc(new pb::RpcMessage());
+  rpc->set_handle(remote_handle_);
+  rpc->set_proc(pb::RpcMessage::RPC_DS_READUNTIL);
+  auto* message = rpc->mutable_demuxerstream_readuntil_rpc();
+  last_read_until_count_ += kNumFramesInEachReadUntil;
+  message->set_count(last_read_until_count_);
+  message->set_callback_handle(rpc_handle_);
+  rpc_broker_->SendMessageToRemote(std::move(rpc));
+  read_until_sent_ = true;
+}
+
+void MediaStream::FlushUntil(int count) {
+  while (!buffers_.empty()) {
+    buffers_.pop_front();
+  }
+
+  last_read_until_count_ = count;
+  if (!read_complete_callback_.is_null())
+    CompleteRead(DemuxerStream::kAborted);
+  read_until_sent_ = false;
+}
+
+void MediaStream::Read(const ReadCB& read_cb) {
+  DCHECK(read_complete_callback_.is_null());
+  DCHECK(!read_cb.is_null());
+  read_complete_callback_ = read_cb;
+  if (buffers_.empty() && config_changed_) {
+    CompleteRead(DemuxerStream::kConfigChanged);
+    return;
+  }
+
+  // Wait for more data.
+  if (buffers_.empty()) {
+    SendReadUntil();
+    return;
+  }
+
+  CompleteRead(DemuxerStream::kOk);
+}
+
+void MediaStream::CompleteRead(DemuxerStream::Status status) {
+  DVLOG(3) << __func__ << ": " << status;
+  switch (status) {
+    case DemuxerStream::kConfigChanged:
+      if (type_ == AUDIO) {
+        DCHECK(next_audio_decoder_config_.IsValidConfig());
+        audio_decoder_config_ = next_audio_decoder_config_;
+#if DCHECK_IS_ON()
+        next_audio_decoder_config_ = AudioDecoderConfig();
+#endif  // DCHECK_IS_ON()
+      } else {
+        DCHECK(next_video_decoder_config_.IsValidConfig());
+        video_decoder_config_ = next_video_decoder_config_;
+#if DCHECK_IS_ON()
+        next_video_decoder_config_ = VideoDecoderConfig();
+#endif  // DCHECK_IS_ON()
+      }
+      config_changed_ = false;
+      base::ResetAndReturn(&read_complete_callback_).Run(status, nullptr);
+      return;
+    case DemuxerStream::kAborted:
+      base::ResetAndReturn(&read_complete_callback_).Run(status, nullptr);
+      return;
+    case DemuxerStream::kOk:
+      DCHECK(!buffers_.empty());
+      scoped_refptr<DecoderBuffer> frame_data = buffers_.front();
+      buffers_.pop_front();
+      base::ResetAndReturn(&read_complete_callback_).Run(status, frame_data);
+      return;
+  }
+}
+
+AudioDecoderConfig MediaStream::audio_decoder_config() {
+  DVLOG(3) << __func__;
+  DCHECK(type_ == DemuxerStream::AUDIO);
+  return audio_decoder_config_;
+}
+
+VideoDecoderConfig MediaStream::video_decoder_config() {
+  DVLOG(3) << __func__;
+  DCHECK(type_ == DemuxerStream::VIDEO);
+  return video_decoder_config_;
+}
+
+DemuxerStream::Type MediaStream::type() const {
+  return type_;
+}
+
+DemuxerStream::Liveness MediaStream::liveness() const {
+  return DemuxerStream::LIVENESS_LIVE;
+}
+
+bool MediaStream::SupportsConfigChanges() {
+  return true;
+}
+
+VideoRotation MediaStream::video_rotation() {
+  return VideoRotation::VIDEO_ROTATION_0;
+}
+
+void MediaStream::AppendBuffer(scoped_refptr<DecoderBuffer> buffer) {
+  DVLOG(3) << __func__;
+  buffers_.push_back(buffer);
+  if (!read_complete_callback_.is_null())
+    CompleteRead(DemuxerStream::kOk);
+}
+
+void MediaStream::OnError(const std::string& error) {
+  VLOG(1) << __func__ << ": " << error;
+  if (error_callback_.is_null())
+    return;
+  base::ResetAndReturn(&error_callback_).Run();
+}
+
+StreamProvider::StreamProvider(RpcBroker* rpc_broker,
+                               const base::Closure& error_callback)
+    : rpc_broker_(rpc_broker),
+      error_callback_(error_callback),
+      weak_factory_(this) {}
+
+StreamProvider::~StreamProvider() {}
+
+void StreamProvider::Initialize(int remote_audio_handle,
+                                int remote_video_handle,
+                                const base::Closure& callback) {
+  DVLOG(3) << __func__ << ": remote_audio_handle=" << remote_audio_handle
+           << " remote_video_handle=" << remote_video_handle;
+  if (!init_done_callback_.is_null()) {
+    OnError("Duplicate initialization.");
+    return;
+  }
+  if (remote_audio_handle == RpcBroker::kInvalidHandle &&
+      remote_video_handle == RpcBroker::kInvalidHandle) {
+    OnError("Invalid handle.");
+    return;
+  }
+
+  init_done_callback_ = callback;
+  if (remote_audio_handle != RpcBroker::kInvalidHandle) {
+    audio_stream_.reset(new MediaStream(
+        rpc_broker_, DemuxerStream::AUDIO, remote_audio_handle,
+        base::Bind(&StreamProvider::OnError, weak_factory_.GetWeakPtr(),
+                   "Media stream error")));
+    audio_stream_->Initialize(base::Bind(
+        &StreamProvider::AudioStreamInitialized, weak_factory_.GetWeakPtr()));
+  }
+  if (remote_video_handle != RpcBroker::kInvalidHandle) {
+    video_stream_.reset(new MediaStream(
+        rpc_broker_, DemuxerStream::VIDEO, remote_video_handle,
+        base::Bind(&StreamProvider::OnError, weak_factory_.GetWeakPtr(),
+                   "Media stream error")));
+    video_stream_->Initialize(base::Bind(
+        &StreamProvider::VideoStreamInitialized, weak_factory_.GetWeakPtr()));
+  }
+}
+
+void StreamProvider::OnError(const std::string& error) {
+  VLOG(1) << __func__ << ": " << error;
+  if (error_callback_.is_null())
+    return;
+  base::ResetAndReturn(&error_callback_).Run();
+}
+
+void StreamProvider::AudioStreamInitialized() {
+  DCHECK(!init_done_callback_.is_null());
+  audio_stream_initialized_ = true;
+  if (video_stream_initialized_ || !video_stream_)
+    base::ResetAndReturn(&init_done_callback_).Run();
+}
+
+void StreamProvider::VideoStreamInitialized() {
+  DCHECK(!init_done_callback_.is_null());
+  video_stream_initialized_ = true;
+  if (audio_stream_initialized_ || !audio_stream_)
+    base::ResetAndReturn(&init_done_callback_).Run();
+}
+
+std::vector<DemuxerStream*> StreamProvider::GetAllStreams() {
+  std::vector<DemuxerStream*> streams;
+  if (audio_stream_)
+    streams.push_back(audio_stream_.get());
+  if (video_stream_)
+    streams.push_back(video_stream_.get());
+  return streams;
+}
+
+void StreamProvider::AppendBuffer(DemuxerStream::Type type,
+                                  scoped_refptr<DecoderBuffer> buffer) {
+  if (type == DemuxerStream::AUDIO)
+    audio_stream_->AppendBuffer(buffer);
+  else if (type == DemuxerStream::VIDEO)
+    video_stream_->AppendBuffer(buffer);
+  else
+    NOTREACHED() << ": Only supports video or audio stream.";
+}
+
+void StreamProvider::FlushUntil(DemuxerStream::Type type, int count) {
+  DVLOG(3) << __func__ << ": type=" << type << " count=" << count;
+  if (type == DemuxerStream::AUDIO)
+    audio_stream_->FlushUntil(count);
+  else if (type == DemuxerStream::VIDEO)
+    video_stream_->FlushUntil(count);
+  else
+    NOTREACHED() << ": Only supports video or audio stream.";
+}
+
+}  // namespace remoting
+}  // namespace media
diff --git a/media/remoting/stream_provider.h b/media/remoting/stream_provider.h
new file mode 100644
index 0000000..a7696dcf
--- /dev/null
+++ b/media/remoting/stream_provider.h
@@ -0,0 +1,69 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_REMOTING_STREAM_PROVIDER_H_
+#define MEDIA_REMOTING_STREAM_PROVIDER_H_
+
+#include <deque>
+
+#include "base/memory/weak_ptr.h"
+#include "media/base/audio_decoder_config.h"
+#include "media/base/demuxer_stream.h"
+#include "media/base/media_resource.h"
+#include "media/base/video_decoder_config.h"
+#include "media/remoting/rpc_broker.h"
+
+namespace media {
+namespace remoting {
+
+class MediaStream;
+
+// The media stream provider for Media Remoting receiver.
+class StreamProvider final : public MediaResource {
+ public:
+  StreamProvider(RpcBroker* rpc_broker, const base::Closure& error_callback);
+
+  ~StreamProvider() override;
+
+  // MediaResource implemenation.
+  std::vector<DemuxerStream*> GetAllStreams() override;
+  void SetStreamStatusChangeCB(const StreamStatusChangeCB& cb) override {}
+
+  void Initialize(int remote_audio_handle,
+                  int remote_video_handle,
+                  const base::Closure& callback);
+  void AppendBuffer(DemuxerStream::Type type,
+                    scoped_refptr<DecoderBuffer> buffer);
+  void FlushUntil(DemuxerStream::Type type, int count);
+
+ private:
+  // Called when audio/video stream is initialized.
+  void AudioStreamInitialized();
+  void VideoStreamInitialized();
+
+  // Called when any error occurs.
+  void OnError(const std::string& error);
+
+  RpcBroker* const rpc_broker_;  // Outlives this class.
+  std::unique_ptr<MediaStream> video_stream_;
+  std::unique_ptr<MediaStream> audio_stream_;
+  bool audio_stream_initialized_ = false;
+  bool video_stream_initialized_ = false;
+
+  // Set when Initialize() is called, and will run only once when both video
+  // and audio streams are initialized or error occurs.
+  base::Closure init_done_callback_;
+
+  // Run only once when first error occurs;
+  base::Closure error_callback_;
+
+  base::WeakPtrFactory<StreamProvider> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(StreamProvider);
+};
+
+}  // namespace remoting
+}  // namespace media
+
+#endif  // MEDIA_REMOTING_STREAM_PROVIDER_H_
diff --git a/media/test/BUILD.gn b/media/test/BUILD.gn
index 1d426da..472e8ef2 100644
--- a/media/test/BUILD.gn
+++ b/media/test/BUILD.gn
@@ -46,6 +46,13 @@
       "//testing/gmock",
       "//testing/gtest",
     ]
+
+    if (enable_media_remoting) {
+      deps += [
+        "//media/remoting:media_remoting_proto",
+        "//media/remoting:media_remoting_tests",
+      ]
+    }
   }
 }
 
diff --git a/media/test/pipeline_integration_test.cc b/media/test/pipeline_integration_test.cc
index 051a758..39724c8f 100644
--- a/media/test/pipeline_integration_test.cc
+++ b/media/test/pipeline_integration_test.cc
@@ -80,12 +80,6 @@
 #define MAYBE_TEXT(test) test
 #endif
 
-#if defined(DISABLE_CLOCKLESS_TESTS)
-#define MAYBE_CLOCKLESS(test) DISABLED_##test
-#else
-#define MAYBE_CLOCKLESS(test) test
-#endif
-
 using testing::_;
 using testing::AnyNumber;
 using testing::AtLeast;
@@ -94,6 +88,8 @@
 
 namespace media {
 
+namespace {
+
 const char kSourceId[] = "SourceId";
 
 const char kWebM[] = "video/webm; codecs=\"vp8,vorbis\"";
@@ -665,6 +661,8 @@
   bool NeedsBitstreamConversion() const override { return true; }
 };
 
+}  // namespace
+
 // TODO(xhwang): These tests have been disabled for some time as apptests and no
 //               longer pass. They need to be reconstituted as shell tests.
 //               Currently there are compile issues which must be resolved,
@@ -756,7 +754,7 @@
       EXPECT_CALL(*this, OnWaitingForDecryptionKey()).Times(0);
     }
 
-    pipeline_->Start(demuxer_.get(), CreateRenderer(), this,
+    pipeline_->Start(demuxer_.get(), renderer_factory_->CreateRenderer(), this,
                      base::Bind(&PipelineIntegrationTest::OnStatusCallback,
                                 base::Unretained(this)));
 
@@ -801,12 +799,14 @@
 };
 
 struct PlaybackTestData {
+  const PipelineType type;
   const std::string filename;
   const uint32_t start_time_ms;
   const uint32_t duration_ms;
 };
 
 struct MSEPlaybackTestData {
+  const PipelineType type;
   const std::string filename;
   const std::string mimetype;
   const size_t append_bytes;
@@ -824,15 +824,29 @@
 
 class BasicPlaybackTest : public PipelineIntegrationTest,
                           public testing::WithParamInterface<PlaybackTestData> {
+ public:
+  BasicPlaybackTest() {
+#if BUILDFLAG(ENABLE_MEDIA_REMOTING)
+    if (GetParam().type == PipelineType::MediaRemoting)
+      SetUpRemotingPipeline();
+#endif  // BUILDFLAG(ENABLE_MEDIA_REMOTING)
+  }
 };
 
 class BasicMSEPlaybackTest
     : public ::testing::WithParamInterface<MSEPlaybackTestData>,
-      public PipelineIntegrationTest {};
+      public PipelineIntegrationTest {
+ public:
+  BasicMSEPlaybackTest() {
+#if BUILDFLAG(ENABLE_MEDIA_REMOTING)
+    if (GetParam().type == PipelineType::MediaRemoting)
+      SetUpRemotingPipeline();
+#endif  // BUILDFLAG(ENABLE_MEDIA_REMOTING)
+  }
+};
 
 TEST_P(BasicPlaybackTest, PlayToEnd) {
   PlaybackTestData data = GetParam();
-
   ASSERT_EQ(PIPELINE_OK,
             Start(data.filename, kClockless | kUnreliableDuration));
   EXPECT_EQ(data.start_time_ms, demuxer_->GetStartTime().InMilliseconds());
@@ -844,7 +858,6 @@
 
 TEST_P(BasicMSEPlaybackTest, PlayToEnd) {
   MSEPlaybackTestData data = GetParam();
-
   MockMediaSource source(data.filename, data.mimetype, data.append_bytes);
   // TODO -- ADD uint8_t test_type to StartWithMSE and pass clockless flags
   ASSERT_EQ(PIPELINE_OK,
@@ -867,11 +880,20 @@
 
 #if BUILDFLAG(USE_PROPRIETARY_CODECS)
 
+// Any new/changed entries should be made for both the ::Media and
+// ::MediaRemoting types. If you don't think something applies, please contact
+// one of the media/remoting/OWNERS.
 const PlaybackTestData kADTSTests[] = {
-    {"bear-audio-main-aac.aac", 0, 2724},
-    {"bear-audio-lc-aac.aac", 0, 2858},
-    {"bear-audio-implicit-he-aac-v1.aac", 0, 2812},
-    {"bear-audio-implicit-he-aac-v2.aac", 0, 3047},
+    {PipelineType::Media, "bear-audio-main-aac.aac", 0, 2724},
+    {PipelineType::Media, "bear-audio-lc-aac.aac", 0, 2858},
+    {PipelineType::Media, "bear-audio-implicit-he-aac-v1.aac", 0, 2812},
+    {PipelineType::Media, "bear-audio-implicit-he-aac-v2.aac", 0, 3047},
+#if BUILDFLAG(ENABLE_MEDIA_REMOTING)
+    {PipelineType::MediaRemoting, "bear-audio-main-aac.aac", 0, 2724},
+    {PipelineType::MediaRemoting, "bear-audio-lc-aac.aac", 0, 2858},
+    {PipelineType::MediaRemoting, "bear-audio-implicit-he-aac-v1.aac", 0, 2812},
+    {PipelineType::MediaRemoting, "bear-audio-implicit-he-aac-v2.aac", 0, 3047},
+#endif  // BUILDFLAG(ENABLE_MEDIA_REMOTING)
 };
 
 // TODO(chcunningham): Migrate other basic playback tests to TEST_P.
@@ -879,11 +901,28 @@
                         BasicPlaybackTest,
                         testing::ValuesIn(kADTSTests));
 
+// Any new/changed entries should be made for both the ::Media and
+// ::MediaRemoting types. If you don't think something applies, please contact
+// one of the media/remoting/OWNERS.
 const MSEPlaybackTestData kMediaSourceADTSTests[] = {
-    {"bear-audio-main-aac.aac", kADTS, kAppendWholeFile, 2773},
-    {"bear-audio-lc-aac.aac", kADTS, kAppendWholeFile, 2794},
-    {"bear-audio-implicit-he-aac-v1.aac", kADTS, kAppendWholeFile, 2858},
-    {"bear-audio-implicit-he-aac-v2.aac", kADTS, kAppendWholeFile, 2901},
+    {PipelineType::Media, "bear-audio-main-aac.aac", kADTS, kAppendWholeFile,
+     2773},
+    {PipelineType::Media, "bear-audio-lc-aac.aac", kADTS, kAppendWholeFile,
+     2794},
+    {PipelineType::Media, "bear-audio-implicit-he-aac-v1.aac", kADTS,
+     kAppendWholeFile, 2858},
+    {PipelineType::Media, "bear-audio-implicit-he-aac-v2.aac", kADTS,
+     kAppendWholeFile, 2901},
+#if BUILDFLAG(ENABLE_MEDIA_REMOTING)
+    {PipelineType::MediaRemoting, "bear-audio-main-aac.aac", kADTS,
+     kAppendWholeFile, 2773},
+    {PipelineType::MediaRemoting, "bear-audio-lc-aac.aac", kADTS,
+     kAppendWholeFile, 2794},
+    {PipelineType::MediaRemoting, "bear-audio-implicit-he-aac-v1.aac", kADTS,
+     kAppendWholeFile, 2858},
+    {PipelineType::MediaRemoting, "bear-audio-implicit-he-aac-v2.aac", kADTS,
+     kAppendWholeFile, 2901},
+#endif  // BUILDFLAG(ENABLE_MEDIA_REMOTING)
 };
 
 // TODO(chcunningham): Migrate other basic MSE playback tests to TEST_P.
@@ -893,7 +932,29 @@
 
 #endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
 
-TEST_F(PipelineIntegrationTest, BasicPlayback) {
+struct IntegrationTestData {
+  const PipelineType type;
+};
+
+// Tells gtest how to print our PlaybackTestData structure.
+std::ostream& operator<<(std::ostream& os, const IntegrationTestData& data) {
+  return os << (data.type == PipelineType::Media ? "Media" : "MediaRemoting");
+}
+
+// These tests are used to test both media pipeline and media remoting pipeline.
+class CommonPipelineIntegrationTest
+    : public PipelineIntegrationTest,
+      public testing::WithParamInterface<IntegrationTestData> {
+ public:
+  CommonPipelineIntegrationTest() {
+#if BUILDFLAG(ENABLE_MEDIA_REMOTING)
+    if (GetParam().type == PipelineType::MediaRemoting)
+      SetUpRemotingPipeline();
+#endif  // BUILDFLAG(ENABLE_MEDIA_REMOTING)
+  }
+};
+
+TEST_P(CommonPipelineIntegrationTest, BasicPlayback) {
   ASSERT_EQ(PIPELINE_OK, Start("bear-320x240.webm"));
 
   Play();
@@ -901,7 +962,7 @@
   ASSERT_TRUE(WaitUntilOnEnded());
 }
 
-TEST_F(PipelineIntegrationTest, BasicPlaybackOpusOgg) {
+TEST_P(CommonPipelineIntegrationTest, BasicPlaybackOpusOgg) {
   ASSERT_EQ(PIPELINE_OK, Start("bear-opus.ogg"));
 
   Play();
@@ -909,7 +970,7 @@
   ASSERT_TRUE(WaitUntilOnEnded());
 }
 
-TEST_F(PipelineIntegrationTest, BasicPlaybackHashed) {
+TEST_P(CommonPipelineIntegrationTest, BasicPlaybackHashed) {
   ASSERT_EQ(PIPELINE_OK, Start("bear-320x240.webm", kHashed));
 
   Play();
@@ -925,7 +986,8 @@
   return base::TimeDelta::FromMilliseconds(milliseconds);
 }
 
-TEST_F(PipelineIntegrationTest, PlaybackWithAudioTrackDisabledThenEnabled) {
+TEST_P(CommonPipelineIntegrationTest,
+       PlaybackWithAudioTrackDisabledThenEnabled) {
   ASSERT_EQ(PIPELINE_OK, Start("bear-320x240.webm", kHashed));
 
   // Disable audio.
@@ -959,7 +1021,8 @@
   EXPECT_HASH_EQ("-1.53,0.21,1.23,1.56,-0.34,-0.94,", GetAudioHash());
 }
 
-TEST_F(PipelineIntegrationTest, PlaybackWithVideoTrackDisabledThenEnabled) {
+TEST_P(CommonPipelineIntegrationTest,
+       PlaybackWithVideoTrackDisabledThenEnabled) {
   ASSERT_EQ(PIPELINE_OK, Start("bear-320x240.webm", kHashed));
 
   // Disable video.
@@ -999,13 +1062,13 @@
   EXPECT_HASH_EQ("fd59357dfd9c144ab4fb8181b2de32c3", GetVideoHash());
 }
 
-TEST_F(PipelineIntegrationTest, TrackStatusChangesBeforePipelineStarted) {
+TEST_P(CommonPipelineIntegrationTest, TrackStatusChangesBeforePipelineStarted) {
   std::vector<MediaTrack::Id> empty_track_ids;
   pipeline_->OnEnabledAudioTracksChanged(empty_track_ids);
   pipeline_->OnSelectedVideoTrackChanged(base::nullopt);
 }
 
-TEST_F(PipelineIntegrationTest, TrackStatusChangesAfterPipelineEnded) {
+TEST_P(CommonPipelineIntegrationTest, TrackStatusChangesAfterPipelineEnded) {
   ASSERT_EQ(PIPELINE_OK, Start("bear-320x240.webm", kHashed));
   Play();
   ASSERT_TRUE(WaitUntilOnEnded());
@@ -1021,7 +1084,7 @@
   pipeline_->OnSelectedVideoTrackChanged(MediaTrack::Id("1"));
 }
 
-TEST_F(PipelineIntegrationTest, TrackStatusChangesWhileSuspended) {
+TEST_P(CommonPipelineIntegrationTest, TrackStatusChangesWhileSuspended) {
   ASSERT_EQ(PIPELINE_OK, Start("bear-320x240.webm", kHashed));
   Play();
 
@@ -1059,6 +1122,1051 @@
   ASSERT_TRUE(WaitUntilOnEnded());
 }
 
+TEST_P(CommonPipelineIntegrationTest, PipelineStoppedWhileAudioRestartPending) {
+  ASSERT_EQ(PIPELINE_OK, Start("bear-320x240.webm"));
+  Play();
+
+  // Disable audio track first, to re-enable it later and stop the pipeline
+  // (which destroys the media renderer) while audio restart is pending.
+  std::vector<MediaTrack::Id> track_ids;
+  pipeline_->OnEnabledAudioTracksChanged(track_ids);
+  ASSERT_TRUE(WaitUntilCurrentTimeIsAfter(TimestampMs(200)));
+
+  track_ids.push_back("2");
+  pipeline_->OnEnabledAudioTracksChanged(track_ids);
+  Stop();
+}
+
+TEST_P(CommonPipelineIntegrationTest, PipelineStoppedWhileVideoRestartPending) {
+  ASSERT_EQ(PIPELINE_OK, Start("bear-320x240.webm"));
+  Play();
+
+  // Disable video track first, to re-enable it later and stop the pipeline
+  // (which destroys the media renderer) while video restart is pending.
+  pipeline_->OnSelectedVideoTrackChanged(base::nullopt);
+  ASSERT_TRUE(WaitUntilCurrentTimeIsAfter(TimestampMs(200)));
+
+  pipeline_->OnSelectedVideoTrackChanged(MediaTrack::Id("1"));
+  Stop();
+}
+
+TEST_P(CommonPipelineIntegrationTest, BasicPlaybackOpusOggTrimmingHashed) {
+#if defined(DISABLE_CLOCKLESS_TESTS)
+  return;
+#endif  // defined(DISABLE_CLOCKLESS_TESTS)
+
+  ASSERT_EQ(PIPELINE_OK,
+            Start("opus-trimming-test.webm", kHashed | kClockless));
+
+  Play();
+
+  ASSERT_TRUE(WaitUntilOnEnded());
+  EXPECT_HASH_EQ(kOpusEndTrimmingHash_1, GetAudioHash());
+
+  // Seek within the pre-skip section, this should not cause a beep.
+  ASSERT_TRUE(Seek(base::TimeDelta::FromSeconds(1)));
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+  EXPECT_HASH_EQ(kOpusEndTrimmingHash_2, GetAudioHash());
+
+  // Seek somewhere outside of the pre-skip / end-trim section, demxuer should
+  // correctly preroll enough to accurately decode this segment.
+  ASSERT_TRUE(Seek(base::TimeDelta::FromMilliseconds(6360)));
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+  EXPECT_HASH_EQ(kOpusEndTrimmingHash_3, GetAudioHash());
+}
+
+TEST_P(CommonPipelineIntegrationTest, BasicPlaybackOpusWebmTrimmingHashed) {
+#if defined(DISABLE_CLOCKLESS_TESTS)
+  return;
+#endif  // defined(DISABLE_CLOCKLESS_TESTS)
+
+  ASSERT_EQ(PIPELINE_OK,
+            Start("opus-trimming-test.webm", kHashed | kClockless));
+
+  Play();
+
+  ASSERT_TRUE(WaitUntilOnEnded());
+  EXPECT_HASH_EQ(kOpusEndTrimmingHash_1, GetAudioHash());
+
+  // Seek within the pre-skip section, this should not cause a beep.
+  ASSERT_TRUE(Seek(base::TimeDelta::FromSeconds(1)));
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+  EXPECT_HASH_EQ(kOpusEndTrimmingHash_2, GetAudioHash());
+
+  // Seek somewhere outside of the pre-skip / end-trim section, demxuer should
+  // correctly preroll enough to accurately decode this segment.
+  ASSERT_TRUE(Seek(base::TimeDelta::FromMilliseconds(6360)));
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+  EXPECT_HASH_EQ(kOpusEndTrimmingHash_3, GetAudioHash());
+}
+
+TEST_P(CommonPipelineIntegrationTest,
+       BasicPlaybackOpusWebmTrimmingHashed_MediaSource) {
+#if defined(DISABLE_CLOCKLESS_TESTS)
+  return;
+#endif  // defined(DISABLE_CLOCKLESS_TESTS)
+
+  MockMediaSource source("opus-trimming-test.webm", kOpusAudioOnlyWebM,
+                         kAppendWholeFile);
+  EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(
+                             &source, kClockless | kHashed, nullptr));
+  source.EndOfStream();
+
+  Play();
+
+  ASSERT_TRUE(WaitUntilOnEnded());
+  EXPECT_HASH_EQ(kOpusEndTrimmingHash_1, GetAudioHash());
+
+  // Seek within the pre-skip section, this should not cause a beep.
+  base::TimeDelta seek_time = base::TimeDelta::FromSeconds(1);
+  source.Seek(seek_time);
+  ASSERT_TRUE(Seek(seek_time));
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+  EXPECT_HASH_EQ(kOpusEndTrimmingHash_2, GetAudioHash());
+
+  // Seek somewhere outside of the pre-skip / end-trim section, demuxer should
+  // correctly preroll enough to accurately decode this segment.
+  seek_time = base::TimeDelta::FromMilliseconds(6360);
+  source.Seek(seek_time);
+  ASSERT_TRUE(Seek(seek_time));
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+  EXPECT_HASH_EQ(kOpusEndTrimmingHash_3, GetAudioHash());
+}
+
+TEST_P(CommonPipelineIntegrationTest,
+       BasicPlaybackOpusPrerollExceedsCodecDelay) {
+#if defined(DISABLE_CLOCKLESS_TESTS)
+  return;
+#endif  // defined(DISABLE_CLOCKLESS_TESTS)
+
+  ASSERT_EQ(PIPELINE_OK, Start("bear-opus.webm", kHashed | kClockless));
+
+  AudioDecoderConfig config =
+      demuxer_->GetFirstStream(DemuxerStream::AUDIO)->audio_decoder_config();
+
+  // Verify that this file's preroll is not eclipsed by the codec delay so we
+  // can detect when preroll is not properly performed.
+  base::TimeDelta codec_delay = base::TimeDelta::FromSecondsD(
+      static_cast<double>(config.codec_delay()) / config.samples_per_second());
+  ASSERT_GT(config.seek_preroll(), codec_delay);
+
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+  EXPECT_HASH_EQ(kOpusSmallCodecDelayHash_1, GetAudioHash());
+
+  // Seek halfway through the file to invoke seek preroll.
+  ASSERT_TRUE(Seek(base::TimeDelta::FromSecondsD(1.414)));
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+  EXPECT_HASH_EQ(kOpusSmallCodecDelayHash_2, GetAudioHash());
+}
+
+TEST_P(CommonPipelineIntegrationTest,
+       BasicPlaybackOpusPrerollExceedsCodecDelay_MediaSource) {
+#if defined(DISABLE_CLOCKLESS_TESTS)
+  return;
+#endif  // defined(DISABLE_CLOCKLESS_TESTS)
+
+  MockMediaSource source("bear-opus.webm", kOpusAudioOnlyWebM,
+                         kAppendWholeFile);
+  EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(
+                             &source, kClockless | kHashed, nullptr));
+  source.EndOfStream();
+
+  AudioDecoderConfig config =
+      demuxer_->GetFirstStream(DemuxerStream::AUDIO)->audio_decoder_config();
+
+  // Verify that this file's preroll is not eclipsed by the codec delay so we
+  // can detect when preroll is not properly performed.
+  base::TimeDelta codec_delay = base::TimeDelta::FromSecondsD(
+      static_cast<double>(config.codec_delay()) / config.samples_per_second());
+  ASSERT_GT(config.seek_preroll(), codec_delay);
+
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+  EXPECT_HASH_EQ(kOpusSmallCodecDelayHash_1, GetAudioHash());
+
+  // Seek halfway through the file to invoke seek preroll.
+  base::TimeDelta seek_time = base::TimeDelta::FromSecondsD(1.414);
+  source.Seek(seek_time);
+  ASSERT_TRUE(Seek(seek_time));
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+  EXPECT_HASH_EQ(kOpusSmallCodecDelayHash_2, GetAudioHash());
+}
+
+TEST_P(CommonPipelineIntegrationTest, BasicPlaybackLive) {
+  ASSERT_EQ(PIPELINE_OK, Start("bear-320x240-live.webm", kHashed));
+
+  // Live stream does not have duration in the initialization segment.
+  // It will be set after the entire file is available.
+  EXPECT_CALL(*this, OnDurationChange()).Times(1);
+
+  Play();
+
+  ASSERT_TRUE(WaitUntilOnEnded());
+
+  EXPECT_HASH_EQ("f0be120a90a811506777c99a2cdf7cc1", GetVideoHash());
+  EXPECT_HASH_EQ("-3.59,-2.06,-0.43,2.15,0.77,-0.95,", GetAudioHash());
+  EXPECT_EQ(kLiveTimelineOffset(), demuxer_->GetTimelineOffset());
+}
+
+TEST_P(CommonPipelineIntegrationTest, S32PlaybackHashed) {
+  ASSERT_EQ(PIPELINE_OK, Start("sfx_s32le.wav", kHashed));
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+  EXPECT_HASH_EQ(std::string(kNullVideoHash), GetVideoHash());
+  EXPECT_HASH_EQ("3.03,2.86,2.99,3.31,3.57,4.06,", GetAudioHash());
+}
+
+TEST_P(CommonPipelineIntegrationTest, F32PlaybackHashed) {
+  ASSERT_EQ(PIPELINE_OK, Start("sfx_f32le.wav", kHashed));
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+  EXPECT_HASH_EQ(std::string(kNullVideoHash), GetVideoHash());
+  EXPECT_HASH_EQ("3.03,2.86,2.99,3.31,3.57,4.06,", GetAudioHash());
+}
+
+TEST_P(CommonPipelineIntegrationTest, FlacPlaybackHashed) {
+  ASSERT_EQ(PIPELINE_OK, Start("sfx.flac", kHashed));
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+  EXPECT_HASH_EQ(std::string(kNullVideoHash), GetVideoHash());
+  EXPECT_HASH_EQ("3.03,2.86,2.99,3.31,3.57,4.06,", GetAudioHash());
+}
+
+TEST_P(CommonPipelineIntegrationTest, BasicPlayback_MediaSource) {
+  MockMediaSource source("bear-320x240.webm", kWebM, 219229);
+  EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
+  source.EndOfStream();
+
+  EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
+  EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds());
+  EXPECT_EQ(k320WebMFileDurationMs,
+            pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds());
+
+  Play();
+
+  ASSERT_TRUE(WaitUntilOnEnded());
+
+  EXPECT_TRUE(demuxer_->GetTimelineOffset().is_null());
+  source.Shutdown();
+  Stop();
+}
+
+TEST_P(CommonPipelineIntegrationTest, BasicPlayback_MediaSource_Live) {
+  MockMediaSource source("bear-320x240-live.webm", kWebM, 219221);
+  EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
+  source.EndOfStream();
+
+  EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
+  EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds());
+  EXPECT_EQ(k320WebMFileDurationMs,
+            pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds());
+
+  Play();
+
+  ASSERT_TRUE(WaitUntilOnEnded());
+
+  EXPECT_EQ(kLiveTimelineOffset(), demuxer_->GetTimelineOffset());
+  source.Shutdown();
+  Stop();
+}
+
+TEST_P(CommonPipelineIntegrationTest, BasicPlayback_MediaSource_VP9_WebM) {
+  MockMediaSource source("bear-vp9.webm", kWebMVP9, 67504);
+  EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
+  source.EndOfStream();
+
+  EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
+  EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds());
+  EXPECT_EQ(kVP9WebMFileDurationMs,
+            pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds());
+
+  Play();
+
+  ASSERT_TRUE(WaitUntilOnEnded());
+  source.Shutdown();
+  Stop();
+}
+
+TEST_P(CommonPipelineIntegrationTest,
+       BasicPlayback_MediaSource_VP9_BlockGroup_WebM) {
+  MockMediaSource source("bear-vp9-blockgroup.webm", kWebMVP9, 67871);
+  EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
+  source.EndOfStream();
+
+  EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
+  EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds());
+  EXPECT_EQ(kVP9WebMFileDurationMs,
+            pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds());
+
+  Play();
+
+  ASSERT_TRUE(WaitUntilOnEnded());
+  source.Shutdown();
+  Stop();
+}
+
+TEST_P(CommonPipelineIntegrationTest, BasicPlayback_MediaSource_VP8A_WebM) {
+  MockMediaSource source("bear-vp8a.webm", kVideoOnlyWebM, kAppendWholeFile);
+  EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
+  source.EndOfStream();
+
+  EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
+  EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds());
+  EXPECT_EQ(kVP8AWebMFileDurationMs,
+            pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds());
+
+  Play();
+
+  ASSERT_TRUE(WaitUntilOnEnded());
+  source.Shutdown();
+  Stop();
+}
+
+TEST_P(CommonPipelineIntegrationTest, BasicPlayback_MediaSource_Opus_WebM) {
+  MockMediaSource source("bear-opus-end-trimming.webm", kOpusAudioOnlyWebM,
+                         kAppendWholeFile);
+  EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
+  source.EndOfStream();
+
+  EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
+  EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds());
+  EXPECT_EQ(kOpusEndTrimmingWebMFileDurationMs,
+            pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds());
+  Play();
+
+  ASSERT_TRUE(WaitUntilOnEnded());
+  source.Shutdown();
+  Stop();
+}
+
+// Flaky. http://crbug.com/304776
+TEST_P(CommonPipelineIntegrationTest, DISABLED_MediaSource_Opus_Seeking_WebM) {
+  MockMediaSource source("bear-opus-end-trimming.webm", kOpusAudioOnlyWebM,
+                         kAppendWholeFile);
+  EXPECT_EQ(PIPELINE_OK,
+            StartPipelineWithMediaSource(&source, kHashed, nullptr));
+
+  EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
+  EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds());
+  EXPECT_EQ(kOpusEndTrimmingWebMFileDurationMs,
+            pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds());
+
+  base::TimeDelta start_seek_time = base::TimeDelta::FromMilliseconds(1000);
+  base::TimeDelta seek_time = base::TimeDelta::FromMilliseconds(2000);
+
+  Play();
+  ASSERT_TRUE(WaitUntilCurrentTimeIsAfter(start_seek_time));
+  source.Seek(seek_time, 0x1D5, 34017);
+  source.EndOfStream();
+  ASSERT_TRUE(Seek(seek_time));
+
+  ASSERT_TRUE(WaitUntilOnEnded());
+
+  EXPECT_HASH_EQ("0.76,0.20,-0.82,-0.58,-1.29,-0.29,", GetAudioHash());
+
+  source.Shutdown();
+  Stop();
+}
+
+TEST_P(CommonPipelineIntegrationTest, MediaSource_ConfigChange_WebM) {
+  MockMediaSource source("bear-320x240-16x9-aspect.webm", kWebM,
+                         kAppendWholeFile);
+  EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
+
+  EXPECT_CALL(*this, OnVideoNaturalSizeChange(gfx::Size(640, 360))).Times(1);
+  scoped_refptr<DecoderBuffer> second_file =
+      ReadTestDataFile("bear-640x360.webm");
+  ASSERT_TRUE(source.AppendAtTime(base::TimeDelta::FromSeconds(kAppendTimeSec),
+                                  second_file->data(),
+                                  second_file->data_size()));
+  source.EndOfStream();
+
+  Play();
+  EXPECT_TRUE(WaitUntilOnEnded());
+
+  EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
+  EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds());
+  EXPECT_EQ(kAppendTimeMs + k640WebMFileDurationMs,
+            pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds());
+
+  source.Shutdown();
+  Stop();
+}
+
+TEST_P(CommonPipelineIntegrationTest,
+       MediaSource_Remove_Updates_BufferedRanges) {
+  const char* input_filename = "bear-320x240.webm";
+  MockMediaSource source(input_filename, kWebM, kAppendWholeFile);
+  EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
+
+  auto buffered_ranges = pipeline_->GetBufferedTimeRanges();
+  EXPECT_EQ(1u, buffered_ranges.size());
+  EXPECT_EQ(0, buffered_ranges.start(0).InMilliseconds());
+  EXPECT_EQ(k320WebMFileDurationMs, buffered_ranges.end(0).InMilliseconds());
+
+  source.RemoveRange(base::TimeDelta::FromMilliseconds(1000),
+                     base::TimeDelta::FromMilliseconds(k320WebMFileDurationMs));
+  base::RunLoop().RunUntilIdle();
+
+  buffered_ranges = pipeline_->GetBufferedTimeRanges();
+  EXPECT_EQ(1u, buffered_ranges.size());
+  EXPECT_EQ(0, buffered_ranges.start(0).InMilliseconds());
+  EXPECT_EQ(1001, buffered_ranges.end(0).InMilliseconds());
+
+  source.Shutdown();
+  Stop();
+}
+
+// This test case imitates media playback with advancing media_time and
+// continuously adding new data. At some point we should reach the buffering
+// limit, after that MediaSource should evict some buffered data and that
+// evicted data shold be reflected in the change of media::Pipeline buffered
+// ranges (returned by GetBufferedTimeRanges). At that point the buffered ranges
+// will no longer start at 0.
+TEST_P(CommonPipelineIntegrationTest, MediaSource_FillUp_Buffer) {
+  const char* input_filename = "bear-320x240.webm";
+  MockMediaSource source(input_filename, kWebM, kAppendWholeFile);
+  EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
+  source.SetMemoryLimits(1048576);
+
+  scoped_refptr<DecoderBuffer> file = ReadTestDataFile(input_filename);
+
+  auto buffered_ranges = pipeline_->GetBufferedTimeRanges();
+  EXPECT_EQ(1u, buffered_ranges.size());
+  do {
+    // Advance media_time to the end of the currently buffered data
+    base::TimeDelta media_time = buffered_ranges.end(0);
+    source.Seek(media_time);
+    // Ask MediaSource to evict buffered data if buffering limit has been
+    // reached (the data will be evicted from the front of the buffered range).
+    source.EvictCodedFrames(media_time, file->data_size());
+    ASSERT_TRUE(
+        source.AppendAtTime(media_time, file->data(), file->data_size()));
+    base::RunLoop().RunUntilIdle();
+
+    buffered_ranges = pipeline_->GetBufferedTimeRanges();
+  } while (buffered_ranges.size() == 1 &&
+           buffered_ranges.start(0) == base::TimeDelta::FromSeconds(0));
+
+  EXPECT_EQ(1u, buffered_ranges.size());
+  source.Shutdown();
+  Stop();
+}
+
+#if defined(ARCH_CPU_X86_FAMILY) && !defined(OS_ANDROID)
+TEST_P(CommonPipelineIntegrationTest, BasicPlaybackHi10PVP9) {
+  ASSERT_EQ(PIPELINE_OK, Start("bear-320x180-hi10p-vp9.webm", kClockless));
+
+  Play();
+
+  ASSERT_TRUE(WaitUntilOnEnded());
+}
+
+TEST_P(CommonPipelineIntegrationTest, BasicPlaybackHi12PVP9) {
+  ASSERT_EQ(PIPELINE_OK, Start("bear-320x180-hi12p-vp9.webm", kClockless));
+
+  Play();
+
+  ASSERT_TRUE(WaitUntilOnEnded());
+}
+#endif
+
+#if BUILDFLAG(USE_PROPRIETARY_CODECS)
+
+TEST_P(CommonPipelineIntegrationTest, BasicPlaybackHi10P) {
+  ASSERT_EQ(PIPELINE_OK, Start("bear-320x180-hi10p.mp4", kClockless));
+
+  Play();
+
+  ASSERT_TRUE(WaitUntilOnEnded());
+}
+
+TEST_P(CommonPipelineIntegrationTest, BasicFallback) {
+  ScopedVector<VideoDecoder> failing_video_decoder;
+  failing_video_decoder.push_back(new FailingVideoDecoder());
+
+  ASSERT_EQ(PIPELINE_OK,
+            Start("bear.mp4", kClockless, std::move(failing_video_decoder)));
+
+  Play();
+
+  ASSERT_TRUE(WaitUntilOnEnded());
+};
+
+TEST_P(CommonPipelineIntegrationTest, MediaSource_ADTS) {
+  MockMediaSource source("sfx.adts", kADTS, kAppendWholeFile);
+  EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
+  source.EndOfStream();
+
+  EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
+  EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds());
+  EXPECT_EQ(325, pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds());
+
+  Play();
+
+  EXPECT_TRUE(WaitUntilOnEnded());
+}
+
+TEST_P(CommonPipelineIntegrationTest, MediaSource_ADTS_TimestampOffset) {
+  MockMediaSource source("sfx.adts", kADTS, kAppendWholeFile);
+  EXPECT_EQ(PIPELINE_OK,
+            StartPipelineWithMediaSource(&source, kHashed, nullptr));
+  EXPECT_EQ(325, source.last_timestamp_offset().InMilliseconds());
+
+  // Trim off multiple frames off the beginning of the segment which will cause
+  // the first decoded frame to be incorrect if preroll isn't implemented.
+  const base::TimeDelta adts_preroll_duration =
+      base::TimeDelta::FromSecondsD(2.5 * 1024 / 44100);
+  const base::TimeDelta append_time =
+      source.last_timestamp_offset() - adts_preroll_duration;
+
+  scoped_refptr<DecoderBuffer> second_file = ReadTestDataFile("sfx.adts");
+  source.AppendAtTimeWithWindow(
+      append_time, append_time + adts_preroll_duration, kInfiniteDuration,
+      second_file->data(), second_file->data_size());
+  source.EndOfStream();
+
+  Play();
+  EXPECT_TRUE(WaitUntilOnEnded());
+
+  EXPECT_EQ(592, source.last_timestamp_offset().InMilliseconds());
+  EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
+  EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds());
+  EXPECT_EQ(592, pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds());
+
+  // Verify preroll is stripped.
+  EXPECT_HASH_EQ("-0.25,0.67,0.04,0.14,-0.49,-0.41,", GetAudioHash());
+}
+
+TEST_P(CommonPipelineIntegrationTest, BasicPlaybackHashed_MP3) {
+  ASSERT_EQ(PIPELINE_OK, Start("sfx.mp3", kHashed));
+
+  Play();
+
+  ASSERT_TRUE(WaitUntilOnEnded());
+
+  // Verify codec delay and preroll are stripped.
+  EXPECT_HASH_EQ("1.30,2.72,4.56,5.08,3.74,2.03,", GetAudioHash());
+}
+
+TEST_P(CommonPipelineIntegrationTest, MediaSource_MP3) {
+  MockMediaSource source("sfx.mp3", kMP3, kAppendWholeFile);
+  EXPECT_EQ(PIPELINE_OK,
+            StartPipelineWithMediaSource(&source, kHashed, nullptr));
+  source.EndOfStream();
+
+  EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
+  EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds());
+  EXPECT_EQ(313, pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds());
+
+  Play();
+
+  EXPECT_TRUE(WaitUntilOnEnded());
+
+  // Verify that codec delay was stripped.
+  EXPECT_HASH_EQ("1.01,2.71,4.18,4.32,3.04,1.12,", GetAudioHash());
+}
+
+TEST_P(CommonPipelineIntegrationTest, MediaSource_MP3_TimestampOffset) {
+  MockMediaSource source("sfx.mp3", kMP3, kAppendWholeFile);
+  EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
+  EXPECT_EQ(313, source.last_timestamp_offset().InMilliseconds());
+
+  // There are 576 silent frames at the start of this mp3.  The second append
+  // should trim them off.
+  const base::TimeDelta mp3_preroll_duration =
+      base::TimeDelta::FromSecondsD(576.0 / 44100);
+  const base::TimeDelta append_time =
+      source.last_timestamp_offset() - mp3_preroll_duration;
+
+  scoped_refptr<DecoderBuffer> second_file = ReadTestDataFile("sfx.mp3");
+  source.AppendAtTimeWithWindow(append_time, append_time + mp3_preroll_duration,
+                                kInfiniteDuration, second_file->data(),
+                                second_file->data_size());
+  source.EndOfStream();
+
+  Play();
+  EXPECT_TRUE(WaitUntilOnEnded());
+
+  EXPECT_EQ(613, source.last_timestamp_offset().InMilliseconds());
+  EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
+  EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds());
+  EXPECT_EQ(613, pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds());
+}
+
+TEST_P(CommonPipelineIntegrationTest, MediaSource_MP3_Icecast) {
+  MockMediaSource source("icy_sfx.mp3", kMP3, kAppendWholeFile);
+  EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
+  source.EndOfStream();
+
+  Play();
+
+  EXPECT_TRUE(WaitUntilOnEnded());
+}
+
+TEST_P(CommonPipelineIntegrationTest, MediaSource_ConfigChange_MP4) {
+  MockMediaSource source("bear-640x360-av_frag.mp4", kMP4, kAppendWholeFile);
+  EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
+
+  EXPECT_CALL(*this, OnVideoNaturalSizeChange(gfx::Size(1280, 720))).Times(1);
+  scoped_refptr<DecoderBuffer> second_file =
+      ReadTestDataFile("bear-1280x720-av_frag.mp4");
+  ASSERT_TRUE(source.AppendAtTime(base::TimeDelta::FromSeconds(kAppendTimeSec),
+                                  second_file->data(),
+                                  second_file->data_size()));
+  source.EndOfStream();
+
+  Play();
+  EXPECT_TRUE(WaitUntilOnEnded());
+
+  EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
+  EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds());
+  EXPECT_EQ(kAppendTimeMs + k1280IsoFileDurationMs,
+            pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds());
+
+  source.Shutdown();
+  Stop();
+}
+#endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
+
+TEST_P(CommonPipelineIntegrationTest, BasicPlayback_16x9AspectRatio) {
+  ASSERT_EQ(PIPELINE_OK, Start("bear-320x240-16x9-aspect.webm"));
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+}
+
+#if BUILDFLAG(USE_PROPRIETARY_CODECS)
+TEST_P(CommonPipelineIntegrationTest, Mp2ts_AAC_HE_SBR_Audio) {
+  MockMediaSource source("bear-1280x720-aac_he.ts", kMP2AudioSBR,
+                         kAppendWholeFile);
+#if BUILDFLAG(ENABLE_MSE_MPEG2TS_STREAM_PARSER)
+  EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
+  source.EndOfStream();
+  ASSERT_EQ(PIPELINE_OK, pipeline_status_);
+
+  // Check that SBR is taken into account correctly by mpeg2ts parser. When an
+  // SBR stream is parsed as non-SBR stream, then audio frame durations are
+  // calculated incorrectly and that leads to gaps in buffered ranges (so this
+  // check will fail) and eventually leads to stalled playback.
+  EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
+#else
+  EXPECT_EQ(
+      DEMUXER_ERROR_COULD_NOT_OPEN,
+      StartPipelineWithMediaSource(&source, kExpectDemuxerFailure, nullptr));
+#endif
+}
+
+TEST_P(CommonPipelineIntegrationTest, Mpeg2ts_MP3Audio_Mp4a_6B) {
+  MockMediaSource source("bear-audio-mp4a.6B.ts",
+                         "video/mp2t; codecs=\"mp4a.6B\"", kAppendWholeFile);
+#if BUILDFLAG(ENABLE_MSE_MPEG2TS_STREAM_PARSER)
+  EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
+  source.EndOfStream();
+  ASSERT_EQ(PIPELINE_OK, pipeline_status_);
+#else
+  EXPECT_EQ(
+      DEMUXER_ERROR_COULD_NOT_OPEN,
+      StartPipelineWithMediaSource(&source, kExpectDemuxerFailure, nullptr));
+#endif
+}
+
+TEST_P(CommonPipelineIntegrationTest, Mpeg2ts_MP3Audio_Mp4a_69) {
+  MockMediaSource source("bear-audio-mp4a.69.ts",
+                         "video/mp2t; codecs=\"mp4a.69\"", kAppendWholeFile);
+#if BUILDFLAG(ENABLE_MSE_MPEG2TS_STREAM_PARSER)
+  EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
+  source.EndOfStream();
+  ASSERT_EQ(PIPELINE_OK, pipeline_status_);
+#else
+  EXPECT_EQ(
+      DEMUXER_ERROR_COULD_NOT_OPEN,
+      StartPipelineWithMediaSource(&source, kExpectDemuxerFailure, nullptr));
+#endif
+}
+
+TEST_P(CommonPipelineIntegrationTest,
+       BasicPlayback_MediaSource_VideoOnly_MP4_AVC3) {
+  MockMediaSource source("bear-1280x720-v_frag-avc3.mp4", kMP4VideoAVC3,
+                         kAppendWholeFile);
+  EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
+  source.EndOfStream();
+
+  EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
+  EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds());
+  EXPECT_EQ(k1280IsoAVC3FileDurationMs,
+            pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds());
+
+  Play();
+
+  ASSERT_TRUE(WaitUntilOnEnded());
+  source.Shutdown();
+  Stop();
+}
+
+TEST_P(CommonPipelineIntegrationTest,
+       BasicPlayback_MediaSource_VideoOnly_MP4_VP9) {
+  MockMediaSource source("bear-320x240-v_frag-vp9.mp4", kMP4VideoVP9,
+                         kAppendWholeFile);
+  if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kEnableVp9InMp4)) {
+    ASSERT_EQ(ChunkDemuxer::kNotSupported, source.AddId());
+    return;
+  }
+
+  EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
+  source.EndOfStream();
+  ASSERT_EQ(PIPELINE_OK, pipeline_status_);
+
+  Play();
+
+  ASSERT_TRUE(WaitUntilOnEnded());
+  source.Shutdown();
+  Stop();
+}
+
+TEST_P(CommonPipelineIntegrationTest,
+       BasicPlayback_MediaSource_VideoOnly_MP4_HEVC1) {
+  // HEVC demuxing might be enabled even on platforms that don't support HEVC
+  // decoding. For those cases we'll get DECODER_ERROR_NOT_SUPPORTED, which
+  // indicates indicates that we did pass media mime type checks and attempted
+  // to actually demux and decode the stream. On platforms that support both
+  // demuxing and decoding we'll get PIPELINE_OK.
+  MockMediaSource source("bear-320x240-v_frag-hevc.mp4", kMP4VideoHEVC1,
+                         kAppendWholeFile);
+#if BUILDFLAG(ENABLE_HEVC_DEMUXING)
+  PipelineStatus status = StartPipelineWithMediaSource(&source);
+  EXPECT_TRUE(status == PIPELINE_OK || status == DECODER_ERROR_NOT_SUPPORTED);
+#else
+  EXPECT_EQ(
+      DEMUXER_ERROR_COULD_NOT_OPEN,
+      StartPipelineWithMediaSource(&source, kExpectDemuxerFailure, nullptr));
+#endif
+}
+
+TEST_P(CommonPipelineIntegrationTest,
+       BasicPlayback_MediaSource_VideoOnly_MP4_HEVC2) {
+  // HEVC demuxing might be enabled even on platforms that don't support HEVC
+  // decoding. For those cases we'll get DECODER_ERROR_NOT_SUPPORTED, which
+  // indicates indicates that we did pass media mime type checks and attempted
+  // to actually demux and decode the stream. On platforms that support both
+  // demuxing and decoding we'll get PIPELINE_OK.
+  MockMediaSource source("bear-320x240-v_frag-hevc.mp4", kMP4VideoHEVC2,
+                         kAppendWholeFile);
+#if BUILDFLAG(ENABLE_HEVC_DEMUXING)
+  PipelineStatus status = StartPipelineWithMediaSource(&source);
+  EXPECT_TRUE(status == PIPELINE_OK || status == DECODER_ERROR_NOT_SUPPORTED);
+#else
+  EXPECT_EQ(
+      DEMUXER_ERROR_COULD_NOT_OPEN,
+      StartPipelineWithMediaSource(&source, kExpectDemuxerFailure, nullptr));
+#endif
+}
+
+#endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
+
+TEST_P(CommonPipelineIntegrationTest, SeekWhilePaused) {
+  ASSERT_EQ(PIPELINE_OK, Start("bear-320x240.webm"));
+
+  base::TimeDelta duration(pipeline_->GetMediaDuration());
+  base::TimeDelta start_seek_time(duration / 4);
+  base::TimeDelta seek_time(duration * 3 / 4);
+
+  Play();
+  ASSERT_TRUE(WaitUntilCurrentTimeIsAfter(start_seek_time));
+  Pause();
+  ASSERT_TRUE(Seek(seek_time));
+  EXPECT_EQ(seek_time, pipeline_->GetMediaTime());
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+
+  // Make sure seeking after reaching the end works as expected.
+  Pause();
+  ASSERT_TRUE(Seek(seek_time));
+  EXPECT_EQ(seek_time, pipeline_->GetMediaTime());
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+}
+
+TEST_P(CommonPipelineIntegrationTest, SeekWhilePlaying) {
+  ASSERT_EQ(PIPELINE_OK, Start("bear-320x240.webm"));
+
+  base::TimeDelta duration(pipeline_->GetMediaDuration());
+  base::TimeDelta start_seek_time(duration / 4);
+  base::TimeDelta seek_time(duration * 3 / 4);
+
+  Play();
+  ASSERT_TRUE(WaitUntilCurrentTimeIsAfter(start_seek_time));
+  ASSERT_TRUE(Seek(seek_time));
+  EXPECT_GE(pipeline_->GetMediaTime(), seek_time);
+  ASSERT_TRUE(WaitUntilOnEnded());
+
+  // Make sure seeking after reaching the end works as expected.
+  ASSERT_TRUE(Seek(seek_time));
+  EXPECT_GE(pipeline_->GetMediaTime(), seek_time);
+  ASSERT_TRUE(WaitUntilOnEnded());
+}
+
+TEST_P(CommonPipelineIntegrationTest, SuspendWhilePaused) {
+  ASSERT_EQ(PIPELINE_OK, Start("bear-320x240.webm"));
+
+  base::TimeDelta duration(pipeline_->GetMediaDuration());
+  base::TimeDelta start_seek_time(duration / 4);
+  base::TimeDelta seek_time(duration * 3 / 4);
+
+  Play();
+  ASSERT_TRUE(WaitUntilCurrentTimeIsAfter(start_seek_time));
+  Pause();
+
+  // Suspend while paused.
+  ASSERT_TRUE(Suspend());
+
+  // Resuming the pipeline will create a new Renderer,
+  // which in turn will trigger video size and opacity notifications.
+  EXPECT_CALL(*this, OnVideoNaturalSizeChange(gfx::Size(320, 240))).Times(1);
+  EXPECT_CALL(*this, OnVideoOpacityChange(true)).Times(1);
+
+  ASSERT_TRUE(Resume(seek_time));
+  EXPECT_GE(pipeline_->GetMediaTime(), seek_time);
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+}
+
+TEST_P(CommonPipelineIntegrationTest, SuspendWhilePlaying) {
+  ASSERT_EQ(PIPELINE_OK, Start("bear-320x240.webm"));
+
+  base::TimeDelta duration(pipeline_->GetMediaDuration());
+  base::TimeDelta start_seek_time(duration / 4);
+  base::TimeDelta seek_time(duration * 3 / 4);
+
+  Play();
+  ASSERT_TRUE(WaitUntilCurrentTimeIsAfter(start_seek_time));
+  ASSERT_TRUE(Suspend());
+
+  // Resuming the pipeline will create a new Renderer,
+  // which in turn will trigger video size and opacity notifications.
+  EXPECT_CALL(*this, OnVideoNaturalSizeChange(gfx::Size(320, 240))).Times(1);
+  EXPECT_CALL(*this, OnVideoOpacityChange(true)).Times(1);
+
+  ASSERT_TRUE(Resume(seek_time));
+  EXPECT_GE(pipeline_->GetMediaTime(), seek_time);
+  ASSERT_TRUE(WaitUntilOnEnded());
+}
+
+#if BUILDFLAG(USE_PROPRIETARY_CODECS)
+TEST_P(CommonPipelineIntegrationTest, Rotated_Metadata_0) {
+  ASSERT_EQ(PIPELINE_OK, Start("bear_rotate_0.mp4"));
+  ASSERT_EQ(VIDEO_ROTATION_0, metadata_.video_rotation);
+}
+
+TEST_P(CommonPipelineIntegrationTest, Rotated_Metadata_90) {
+  ASSERT_EQ(PIPELINE_OK, Start("bear_rotate_90.mp4"));
+  ASSERT_EQ(VIDEO_ROTATION_90, metadata_.video_rotation);
+}
+
+TEST_P(CommonPipelineIntegrationTest, Rotated_Metadata_180) {
+  ASSERT_EQ(PIPELINE_OK, Start("bear_rotate_180.mp4"));
+  ASSERT_EQ(VIDEO_ROTATION_180, metadata_.video_rotation);
+}
+
+TEST_P(CommonPipelineIntegrationTest, Rotated_Metadata_270) {
+  ASSERT_EQ(PIPELINE_OK, Start("bear_rotate_270.mp4"));
+  ASSERT_EQ(VIDEO_ROTATION_270, metadata_.video_rotation);
+}
+#endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
+
+// Verify audio decoder & renderer can handle aborted demuxer reads.
+TEST_P(CommonPipelineIntegrationTest, ChunkDemuxerAbortRead_AudioOnly) {
+  ASSERT_TRUE(TestSeekDuringRead("bear-320x240-audio-only.webm", kAudioOnlyWebM,
+                                 16384, base::TimeDelta::FromMilliseconds(464),
+                                 base::TimeDelta::FromMilliseconds(617), 0x10CA,
+                                 19730));
+}
+
+// Verify video decoder & renderer can handle aborted demuxer reads.
+TEST_P(CommonPipelineIntegrationTest, ChunkDemuxerAbortRead_VideoOnly) {
+  ASSERT_TRUE(TestSeekDuringRead("bear-320x240-video-only.webm", kVideoOnlyWebM,
+                                 32768, base::TimeDelta::FromMilliseconds(167),
+                                 base::TimeDelta::FromMilliseconds(1668),
+                                 0x1C896, 65536));
+}
+
+// Verify that Opus audio in WebM containers can be played back.
+TEST_P(CommonPipelineIntegrationTest, BasicPlayback_AudioOnly_Opus_WebM) {
+  ASSERT_EQ(PIPELINE_OK, Start("bear-opus-end-trimming.webm"));
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+}
+
+// Verify that VP9 video in WebM containers can be played back.
+TEST_P(CommonPipelineIntegrationTest, BasicPlayback_VideoOnly_VP9_WebM) {
+  ASSERT_EQ(PIPELINE_OK, Start("bear-vp9.webm"));
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+}
+
+// Verify that VP9 video and Opus audio in the same WebM container can be played
+// back.
+TEST_P(CommonPipelineIntegrationTest, BasicPlayback_VP9_Opus_WebM) {
+  ASSERT_EQ(PIPELINE_OK, Start("bear-vp9-opus.webm"));
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+}
+
+// Verify that VP8 video with alpha channel can be played back.
+TEST_P(CommonPipelineIntegrationTest, BasicPlayback_VP8A_WebM) {
+  ASSERT_EQ(PIPELINE_OK, Start("bear-vp8a.webm"));
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+  EXPECT_VIDEO_FORMAT_EQ(last_video_frame_format_, PIXEL_FORMAT_YV12A);
+}
+
+// Verify that VP8A video with odd width/height can be played back.
+TEST_P(CommonPipelineIntegrationTest, BasicPlayback_VP8A_Odd_WebM) {
+  ASSERT_EQ(PIPELINE_OK, Start("bear-vp8a-odd-dimensions.webm"));
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+  EXPECT_VIDEO_FORMAT_EQ(last_video_frame_format_, PIXEL_FORMAT_YV12A);
+}
+
+// Verify that VP9 video with odd width/height can be played back.
+TEST_P(CommonPipelineIntegrationTest, BasicPlayback_VP9_Odd_WebM) {
+  ASSERT_EQ(PIPELINE_OK, Start("bear-vp9-odd-dimensions.webm"));
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+}
+
+// Verify that VP9 video with alpha channel can be played back.
+TEST_P(CommonPipelineIntegrationTest, BasicPlayback_VP9A_WebM) {
+  ASSERT_EQ(PIPELINE_OK, Start("bear-vp9a.webm"));
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+  EXPECT_VIDEO_FORMAT_EQ(last_video_frame_format_, PIXEL_FORMAT_YV12A);
+}
+
+// Verify that VP9A video with odd width/height can be played back.
+TEST_P(CommonPipelineIntegrationTest, BasicPlayback_VP9A_Odd_WebM) {
+  ASSERT_EQ(PIPELINE_OK, Start("bear-vp9a-odd-dimensions.webm"));
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+  EXPECT_VIDEO_FORMAT_EQ(last_video_frame_format_, PIXEL_FORMAT_YV12A);
+}
+
+// Verify that VP9 video with 4:4:4 subsampling can be played back.
+TEST_P(CommonPipelineIntegrationTest, P444_VP9_WebM) {
+  ASSERT_EQ(PIPELINE_OK, Start("bear-320x240-P444.webm"));
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+  EXPECT_VIDEO_FORMAT_EQ(last_video_frame_format_, PIXEL_FORMAT_YV24);
+}
+
+// Verify that frames of VP9 video in the BT.709 color space have the YV12HD
+// format.
+TEST_P(CommonPipelineIntegrationTest, BT709_VP9_WebM) {
+  ASSERT_EQ(PIPELINE_OK, Start("bear-vp9-bt709.webm"));
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+  EXPECT_VIDEO_FORMAT_EQ(last_video_frame_format_, PIXEL_FORMAT_YV12);
+  EXPECT_COLOR_SPACE_EQ(last_video_frame_color_space_, COLOR_SPACE_HD_REC709);
+}
+
+TEST_P(CommonPipelineIntegrationTest, HD_VP9_WebM) {
+  ASSERT_EQ(PIPELINE_OK, Start("bear-1280x720.webm", kClockless));
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+}
+
+// Verify that videos with an odd frame size playback successfully.
+TEST_P(CommonPipelineIntegrationTest, BasicPlayback_OddVideoSize) {
+  ASSERT_EQ(PIPELINE_OK, Start("butterfly-853x480.webm"));
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+}
+
+// Verify that OPUS audio in a webm which reports a 44.1kHz sample rate plays
+// correctly at 48kHz
+TEST_P(CommonPipelineIntegrationTest, BasicPlayback_Opus441kHz) {
+  ASSERT_EQ(PIPELINE_OK, Start("sfx-opus-441.webm"));
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+  EXPECT_EQ(48000, demuxer_->GetFirstStream(DemuxerStream::AUDIO)
+                       ->audio_decoder_config()
+                       .samples_per_second());
+}
+
+// Same as above but using MediaSource.
+TEST_P(CommonPipelineIntegrationTest, BasicPlayback_MediaSource_Opus441kHz) {
+  MockMediaSource source("sfx-opus-441.webm", kOpusAudioOnlyWebM,
+                         kAppendWholeFile);
+  EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
+  source.EndOfStream();
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+  source.Shutdown();
+  Stop();
+  EXPECT_EQ(48000, demuxer_->GetFirstStream(DemuxerStream::AUDIO)
+                       ->audio_decoder_config()
+                       .samples_per_second());
+}
+
+// Ensures audio-only playback with missing or negative timestamps works.  Tests
+// the common live-streaming case for chained ogg.  See http://crbug.com/396864.
+TEST_P(CommonPipelineIntegrationTest, BasicPlaybackChainedOgg) {
+  ASSERT_EQ(PIPELINE_OK, Start("double-sfx.ogg", kUnreliableDuration));
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+  ASSERT_EQ(base::TimeDelta(), demuxer_->GetStartTime());
+}
+
+// Tests that we signal ended even when audio runs longer than video track.
+TEST_P(CommonPipelineIntegrationTest, BasicPlaybackAudioLongerThanVideo) {
+  ASSERT_EQ(PIPELINE_OK, Start("bear_audio_longer_than_video.ogv"));
+  // Audio track is 2000ms. Video track is 1001ms. Duration should be higher
+  // of the two.
+  EXPECT_EQ(2000, pipeline_->GetMediaDuration().InMilliseconds());
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+}
+
+// Tests that we signal ended even when audio runs shorter than video track.
+TEST_P(CommonPipelineIntegrationTest, BasicPlaybackAudioShorterThanVideo) {
+  ASSERT_EQ(PIPELINE_OK, Start("bear_audio_shorter_than_video.ogv"));
+  // Audio track is 500ms. Video track is 1001ms. Duration should be higher of
+  // the two.
+  EXPECT_EQ(1001, pipeline_->GetMediaDuration().InMilliseconds());
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+}
+
+TEST_P(CommonPipelineIntegrationTest, BasicPlaybackPositiveStartTime) {
+  ASSERT_EQ(PIPELINE_OK, Start("nonzero-start-time.webm"));
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+  ASSERT_EQ(base::TimeDelta::FromMicroseconds(396000),
+            demuxer_->GetStartTime());
+}
+
+const IntegrationTestData kIntegrationTests[] = {
+    {PipelineType::Media},
+#if BUILDFLAG(ENABLE_MEDIA_REMOTING)
+    {PipelineType::MediaRemoting},
+#endif  // BUILDFLAG(ENABLE_MEDIA_REMOTING)
+};
+
+INSTANTIATE_TEST_CASE_P(,
+                        CommonPipelineIntegrationTest,
+                        testing::ValuesIn(kIntegrationTests));
+
+// Media Remoting currently doesn't support stream status change without
+// restarting pipeline.
 TEST_F(PipelineIntegrationTest, ReinitRenderersWhileAudioTrackIsDisabled) {
   ASSERT_EQ(PIPELINE_OK, Start("bear-320x240.webm"));
   Play();
@@ -1106,199 +2214,6 @@
   Stop();
 }
 
-TEST_F(PipelineIntegrationTest, PipelineStoppedWhileAudioRestartPending) {
-  ASSERT_EQ(PIPELINE_OK, Start("bear-320x240.webm"));
-  Play();
-
-  // Disable audio track first, to re-enable it later and stop the pipeline
-  // (which destroys the media renderer) while audio restart is pending.
-  std::vector<MediaTrack::Id> track_ids;
-  pipeline_->OnEnabledAudioTracksChanged(track_ids);
-  ASSERT_TRUE(WaitUntilCurrentTimeIsAfter(TimestampMs(200)));
-
-  track_ids.push_back("2");
-  pipeline_->OnEnabledAudioTracksChanged(track_ids);
-  Stop();
-}
-
-TEST_F(PipelineIntegrationTest, PipelineStoppedWhileVideoRestartPending) {
-  ASSERT_EQ(PIPELINE_OK, Start("bear-320x240.webm"));
-  Play();
-
-  // Disable video track first, to re-enable it later and stop the pipeline
-  // (which destroys the media renderer) while video restart is pending.
-  pipeline_->OnSelectedVideoTrackChanged(base::nullopt);
-  ASSERT_TRUE(WaitUntilCurrentTimeIsAfter(TimestampMs(200)));
-
-  pipeline_->OnSelectedVideoTrackChanged(MediaTrack::Id("1"));
-  Stop();
-}
-
-TEST_F(PipelineIntegrationTest,
-       MAYBE_CLOCKLESS(BasicPlaybackOpusOggTrimmingHashed)) {
-  ASSERT_EQ(PIPELINE_OK,
-            Start("opus-trimming-test.webm", kHashed | kClockless));
-
-  Play();
-
-  ASSERT_TRUE(WaitUntilOnEnded());
-  EXPECT_HASH_EQ(kOpusEndTrimmingHash_1, GetAudioHash());
-
-  // Seek within the pre-skip section, this should not cause a beep.
-  ASSERT_TRUE(Seek(base::TimeDelta::FromSeconds(1)));
-  Play();
-  ASSERT_TRUE(WaitUntilOnEnded());
-  EXPECT_HASH_EQ(kOpusEndTrimmingHash_2, GetAudioHash());
-
-  // Seek somewhere outside of the pre-skip / end-trim section, demxuer should
-  // correctly preroll enough to accurately decode this segment.
-  ASSERT_TRUE(Seek(base::TimeDelta::FromMilliseconds(6360)));
-  Play();
-  ASSERT_TRUE(WaitUntilOnEnded());
-  EXPECT_HASH_EQ(kOpusEndTrimmingHash_3, GetAudioHash());
-}
-
-TEST_F(PipelineIntegrationTest,
-       MAYBE_CLOCKLESS(BasicPlaybackOpusWebmTrimmingHashed)) {
-  ASSERT_EQ(PIPELINE_OK,
-            Start("opus-trimming-test.webm", kHashed | kClockless));
-
-  Play();
-
-  ASSERT_TRUE(WaitUntilOnEnded());
-  EXPECT_HASH_EQ(kOpusEndTrimmingHash_1, GetAudioHash());
-
-  // Seek within the pre-skip section, this should not cause a beep.
-  ASSERT_TRUE(Seek(base::TimeDelta::FromSeconds(1)));
-  Play();
-  ASSERT_TRUE(WaitUntilOnEnded());
-  EXPECT_HASH_EQ(kOpusEndTrimmingHash_2, GetAudioHash());
-
-  // Seek somewhere outside of the pre-skip / end-trim section, demxuer should
-  // correctly preroll enough to accurately decode this segment.
-  ASSERT_TRUE(Seek(base::TimeDelta::FromMilliseconds(6360)));
-  Play();
-  ASSERT_TRUE(WaitUntilOnEnded());
-  EXPECT_HASH_EQ(kOpusEndTrimmingHash_3, GetAudioHash());
-}
-
-TEST_F(PipelineIntegrationTest,
-       MAYBE_CLOCKLESS(BasicPlaybackOpusWebmTrimmingHashed_MediaSource)) {
-  MockMediaSource source("opus-trimming-test.webm", kOpusAudioOnlyWebM,
-                         kAppendWholeFile);
-  EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(
-                             &source, kClockless | kHashed, nullptr));
-  source.EndOfStream();
-
-  Play();
-
-  ASSERT_TRUE(WaitUntilOnEnded());
-  EXPECT_HASH_EQ(kOpusEndTrimmingHash_1, GetAudioHash());
-
-  // Seek within the pre-skip section, this should not cause a beep.
-  base::TimeDelta seek_time = base::TimeDelta::FromSeconds(1);
-  source.Seek(seek_time);
-  ASSERT_TRUE(Seek(seek_time));
-  Play();
-  ASSERT_TRUE(WaitUntilOnEnded());
-  EXPECT_HASH_EQ(kOpusEndTrimmingHash_2, GetAudioHash());
-
-  // Seek somewhere outside of the pre-skip / end-trim section, demuxer should
-  // correctly preroll enough to accurately decode this segment.
-  seek_time = base::TimeDelta::FromMilliseconds(6360);
-  source.Seek(seek_time);
-  ASSERT_TRUE(Seek(seek_time));
-  Play();
-  ASSERT_TRUE(WaitUntilOnEnded());
-  EXPECT_HASH_EQ(kOpusEndTrimmingHash_3, GetAudioHash());
-}
-
-TEST_F(PipelineIntegrationTest,
-       MAYBE_CLOCKLESS(BasicPlaybackOpusPrerollExceedsCodecDelay)) {
-  ASSERT_EQ(PIPELINE_OK, Start("bear-opus.webm", kHashed | kClockless));
-
-  AudioDecoderConfig config =
-      demuxer_->GetFirstStream(DemuxerStream::AUDIO)->audio_decoder_config();
-
-  // Verify that this file's preroll is not eclipsed by the codec delay so we
-  // can detect when preroll is not properly performed.
-  base::TimeDelta codec_delay = base::TimeDelta::FromSecondsD(
-      static_cast<double>(config.codec_delay()) / config.samples_per_second());
-  ASSERT_GT(config.seek_preroll(), codec_delay);
-
-  Play();
-  ASSERT_TRUE(WaitUntilOnEnded());
-  EXPECT_HASH_EQ(kOpusSmallCodecDelayHash_1, GetAudioHash());
-
-  // Seek halfway through the file to invoke seek preroll.
-  ASSERT_TRUE(Seek(base::TimeDelta::FromSecondsD(1.414)));
-  Play();
-  ASSERT_TRUE(WaitUntilOnEnded());
-  EXPECT_HASH_EQ(kOpusSmallCodecDelayHash_2, GetAudioHash());
-}
-
-TEST_F(PipelineIntegrationTest,
-       MAYBE_CLOCKLESS(BasicPlaybackOpusPrerollExceedsCodecDelay_MediaSource)) {
-  MockMediaSource source("bear-opus.webm", kOpusAudioOnlyWebM,
-                         kAppendWholeFile);
-  EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(
-                             &source, kClockless | kHashed, nullptr));
-  source.EndOfStream();
-
-  AudioDecoderConfig config =
-      demuxer_->GetFirstStream(DemuxerStream::AUDIO)->audio_decoder_config();
-
-  // Verify that this file's preroll is not eclipsed by the codec delay so we
-  // can detect when preroll is not properly performed.
-  base::TimeDelta codec_delay = base::TimeDelta::FromSecondsD(
-      static_cast<double>(config.codec_delay()) / config.samples_per_second());
-  ASSERT_GT(config.seek_preroll(), codec_delay);
-
-  Play();
-  ASSERT_TRUE(WaitUntilOnEnded());
-  EXPECT_HASH_EQ(kOpusSmallCodecDelayHash_1, GetAudioHash());
-
-  // Seek halfway through the file to invoke seek preroll.
-  base::TimeDelta seek_time = base::TimeDelta::FromSecondsD(1.414);
-  source.Seek(seek_time);
-  ASSERT_TRUE(Seek(seek_time));
-  Play();
-  ASSERT_TRUE(WaitUntilOnEnded());
-  EXPECT_HASH_EQ(kOpusSmallCodecDelayHash_2, GetAudioHash());
-}
-
-TEST_F(PipelineIntegrationTest, BasicPlaybackLive) {
-  ASSERT_EQ(PIPELINE_OK, Start("bear-320x240-live.webm", kHashed));
-
-  // Live stream does not have duration in the initialization segment.
-  // It will be set after the entire file is available.
-  EXPECT_CALL(*this, OnDurationChange()).Times(1);
-
-  Play();
-
-  ASSERT_TRUE(WaitUntilOnEnded());
-
-  EXPECT_HASH_EQ("f0be120a90a811506777c99a2cdf7cc1", GetVideoHash());
-  EXPECT_HASH_EQ("-3.59,-2.06,-0.43,2.15,0.77,-0.95,", GetAudioHash());
-  EXPECT_EQ(kLiveTimelineOffset(), demuxer_->GetTimelineOffset());
-}
-
-TEST_F(PipelineIntegrationTest, S32PlaybackHashed) {
-  ASSERT_EQ(PIPELINE_OK, Start("sfx_s32le.wav", kHashed));
-  Play();
-  ASSERT_TRUE(WaitUntilOnEnded());
-  EXPECT_HASH_EQ(std::string(kNullVideoHash), GetVideoHash());
-  EXPECT_HASH_EQ("3.03,2.86,2.99,3.31,3.57,4.06,", GetAudioHash());
-}
-
-TEST_F(PipelineIntegrationTest, F32PlaybackHashed) {
-  ASSERT_EQ(PIPELINE_OK, Start("sfx_f32le.wav", kHashed));
-  Play();
-  ASSERT_TRUE(WaitUntilOnEnded());
-  EXPECT_HASH_EQ(std::string(kNullVideoHash), GetVideoHash());
-  EXPECT_HASH_EQ("3.03,2.86,2.99,3.31,3.57,4.06,", GetAudioHash());
-}
-
 TEST_F(PipelineIntegrationTest, MAYBE_EME(BasicPlaybackEncrypted)) {
   FakeEncryptedMedia encrypted_media(new KeyProvidingApp());
   set_encrypted_media_init_data_cb(
@@ -1314,233 +2229,6 @@
   Stop();
 }
 
-TEST_F(PipelineIntegrationTest, FlacPlaybackHashed) {
-  ASSERT_EQ(PIPELINE_OK, Start("sfx.flac", kHashed));
-  Play();
-  ASSERT_TRUE(WaitUntilOnEnded());
-  EXPECT_HASH_EQ(std::string(kNullVideoHash), GetVideoHash());
-  EXPECT_HASH_EQ("3.03,2.86,2.99,3.31,3.57,4.06,", GetAudioHash());
-}
-
-TEST_F(PipelineIntegrationTest, BasicPlayback_MediaSource) {
-  MockMediaSource source("bear-320x240.webm", kWebM, 219229);
-  EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
-  source.EndOfStream();
-
-  EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
-  EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds());
-  EXPECT_EQ(k320WebMFileDurationMs,
-            pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds());
-
-  Play();
-
-  ASSERT_TRUE(WaitUntilOnEnded());
-
-  EXPECT_TRUE(demuxer_->GetTimelineOffset().is_null());
-  source.Shutdown();
-  Stop();
-}
-
-TEST_F(PipelineIntegrationTest, BasicPlayback_MediaSource_Live) {
-  MockMediaSource source("bear-320x240-live.webm", kWebM, 219221);
-  EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
-  source.EndOfStream();
-
-  EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
-  EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds());
-  EXPECT_EQ(k320WebMFileDurationMs,
-            pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds());
-
-  Play();
-
-  ASSERT_TRUE(WaitUntilOnEnded());
-
-  EXPECT_EQ(kLiveTimelineOffset(), demuxer_->GetTimelineOffset());
-  source.Shutdown();
-  Stop();
-}
-
-TEST_F(PipelineIntegrationTest, BasicPlayback_MediaSource_VP9_WebM) {
-  MockMediaSource source("bear-vp9.webm", kWebMVP9, 67504);
-  EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
-  source.EndOfStream();
-
-  EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
-  EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds());
-  EXPECT_EQ(kVP9WebMFileDurationMs,
-            pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds());
-
-  Play();
-
-  ASSERT_TRUE(WaitUntilOnEnded());
-  source.Shutdown();
-  Stop();
-}
-
-TEST_F(PipelineIntegrationTest, BasicPlayback_MediaSource_VP9_BlockGroup_WebM) {
-  MockMediaSource source("bear-vp9-blockgroup.webm", kWebMVP9, 67871);
-  EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
-  source.EndOfStream();
-
-  EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
-  EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds());
-  EXPECT_EQ(kVP9WebMFileDurationMs,
-            pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds());
-
-  Play();
-
-  ASSERT_TRUE(WaitUntilOnEnded());
-  source.Shutdown();
-  Stop();
-}
-
-TEST_F(PipelineIntegrationTest, BasicPlayback_MediaSource_VP8A_WebM) {
-  MockMediaSource source("bear-vp8a.webm", kVideoOnlyWebM, kAppendWholeFile);
-  EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
-  source.EndOfStream();
-
-  EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
-  EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds());
-  EXPECT_EQ(kVP8AWebMFileDurationMs,
-            pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds());
-
-  Play();
-
-  ASSERT_TRUE(WaitUntilOnEnded());
-  source.Shutdown();
-  Stop();
-}
-
-TEST_F(PipelineIntegrationTest, BasicPlayback_MediaSource_Opus_WebM) {
-  MockMediaSource source("bear-opus-end-trimming.webm", kOpusAudioOnlyWebM,
-                         kAppendWholeFile);
-  EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
-  source.EndOfStream();
-
-  EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
-  EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds());
-  EXPECT_EQ(kOpusEndTrimmingWebMFileDurationMs,
-            pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds());
-  Play();
-
-  ASSERT_TRUE(WaitUntilOnEnded());
-  source.Shutdown();
-  Stop();
-}
-
-// Flaky. http://crbug.com/304776
-TEST_F(PipelineIntegrationTest, DISABLED_MediaSource_Opus_Seeking_WebM) {
-  MockMediaSource source("bear-opus-end-trimming.webm", kOpusAudioOnlyWebM,
-                         kAppendWholeFile);
-  EXPECT_EQ(PIPELINE_OK,
-            StartPipelineWithMediaSource(&source, kHashed, nullptr));
-
-  EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
-  EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds());
-  EXPECT_EQ(kOpusEndTrimmingWebMFileDurationMs,
-            pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds());
-
-  base::TimeDelta start_seek_time = base::TimeDelta::FromMilliseconds(1000);
-  base::TimeDelta seek_time = base::TimeDelta::FromMilliseconds(2000);
-
-  Play();
-  ASSERT_TRUE(WaitUntilCurrentTimeIsAfter(start_seek_time));
-  source.Seek(seek_time, 0x1D5, 34017);
-  source.EndOfStream();
-  ASSERT_TRUE(Seek(seek_time));
-
-  ASSERT_TRUE(WaitUntilOnEnded());
-
-  EXPECT_HASH_EQ("0.76,0.20,-0.82,-0.58,-1.29,-0.29,", GetAudioHash());
-
-  source.Shutdown();
-  Stop();
-}
-
-TEST_F(PipelineIntegrationTest, MediaSource_ConfigChange_WebM) {
-  MockMediaSource source("bear-320x240-16x9-aspect.webm", kWebM,
-                         kAppendWholeFile);
-  EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
-
-  EXPECT_CALL(*this, OnVideoNaturalSizeChange(gfx::Size(640, 360))).Times(1);
-  scoped_refptr<DecoderBuffer> second_file =
-      ReadTestDataFile("bear-640x360.webm");
-  ASSERT_TRUE(source.AppendAtTime(base::TimeDelta::FromSeconds(kAppendTimeSec),
-                                  second_file->data(),
-                                  second_file->data_size()));
-  source.EndOfStream();
-
-  Play();
-  EXPECT_TRUE(WaitUntilOnEnded());
-
-  EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
-  EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds());
-  EXPECT_EQ(kAppendTimeMs + k640WebMFileDurationMs,
-            pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds());
-
-  source.Shutdown();
-  Stop();
-}
-
-TEST_F(PipelineIntegrationTest, MediaSource_Remove_Updates_BufferedRanges) {
-  const char* input_filename = "bear-320x240.webm";
-  MockMediaSource source(input_filename, kWebM, kAppendWholeFile);
-  EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
-
-  auto buffered_ranges = pipeline_->GetBufferedTimeRanges();
-  EXPECT_EQ(1u, buffered_ranges.size());
-  EXPECT_EQ(0, buffered_ranges.start(0).InMilliseconds());
-  EXPECT_EQ(k320WebMFileDurationMs, buffered_ranges.end(0).InMilliseconds());
-
-  source.RemoveRange(base::TimeDelta::FromMilliseconds(1000),
-                     base::TimeDelta::FromMilliseconds(k320WebMFileDurationMs));
-  base::RunLoop().RunUntilIdle();
-
-  buffered_ranges = pipeline_->GetBufferedTimeRanges();
-  EXPECT_EQ(1u, buffered_ranges.size());
-  EXPECT_EQ(0, buffered_ranges.start(0).InMilliseconds());
-  EXPECT_EQ(1001, buffered_ranges.end(0).InMilliseconds());
-
-  source.Shutdown();
-  Stop();
-}
-
-// This test case imitates media playback with advancing media_time and
-// continuously adding new data. At some point we should reach the buffering
-// limit, after that MediaSource should evict some buffered data and that
-// evicted data shold be reflected in the change of media::Pipeline buffered
-// ranges (returned by GetBufferedTimeRanges). At that point the buffered ranges
-// will no longer start at 0.
-TEST_F(PipelineIntegrationTest, MediaSource_FillUp_Buffer) {
-  const char* input_filename = "bear-320x240.webm";
-  MockMediaSource source(input_filename, kWebM, kAppendWholeFile);
-  EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
-  source.SetMemoryLimits(1048576);
-
-  scoped_refptr<DecoderBuffer> file = ReadTestDataFile(input_filename);
-
-  auto buffered_ranges = pipeline_->GetBufferedTimeRanges();
-  EXPECT_EQ(1u, buffered_ranges.size());
-  do {
-    // Advance media_time to the end of the currently buffered data
-    base::TimeDelta media_time = buffered_ranges.end(0);
-    source.Seek(media_time);
-    // Ask MediaSource to evict buffered data if buffering limit has been
-    // reached (the data will be evicted from the front of the buffered range).
-    source.EvictCodedFrames(media_time, file->data_size());
-    ASSERT_TRUE(
-        source.AppendAtTime(media_time, file->data(), file->data_size()));
-    base::RunLoop().RunUntilIdle();
-
-    buffered_ranges = pipeline_->GetBufferedTimeRanges();
-  } while (buffered_ranges.size() == 1 &&
-           buffered_ranges.start(0) == base::TimeDelta::FromSeconds(0));
-
-  EXPECT_EQ(1u, buffered_ranges.size());
-  source.Shutdown();
-  Stop();
-}
-
 TEST_F(PipelineIntegrationTest,
        MAYBE_EME(MediaSource_ConfigChange_Encrypted_WebM)) {
   MockMediaSource source("bear-320x240-16x9-aspect-av_enc-av.webm", kWebM,
@@ -1552,7 +2240,6 @@
   EXPECT_CALL(*this, OnVideoNaturalSizeChange(gfx::Size(640, 360))).Times(1);
   scoped_refptr<DecoderBuffer> second_file =
       ReadTestDataFile("bear-640x360-av_enc-av.webm");
-
   ASSERT_TRUE(source.AppendAtTime(base::TimeDelta::FromSeconds(kAppendTimeSec),
                                   second_file->data(),
                                   second_file->data_size()));
@@ -1630,102 +2317,7 @@
   Stop();
 }
 
-#if defined(ARCH_CPU_X86_FAMILY) && !defined(OS_ANDROID)
-TEST_F(PipelineIntegrationTest, BasicPlaybackHi10PVP9) {
-  ASSERT_EQ(PIPELINE_OK, Start("bear-320x180-hi10p-vp9.webm", kClockless));
-
-  Play();
-
-  ASSERT_TRUE(WaitUntilOnEnded());
-}
-
-TEST_F(PipelineIntegrationTest, BasicPlaybackHi12PVP9) {
-  ASSERT_EQ(PIPELINE_OK, Start("bear-320x180-hi12p-vp9.webm", kClockless));
-
-  Play();
-
-  ASSERT_TRUE(WaitUntilOnEnded());
-}
-#endif
-
 #if BUILDFLAG(USE_PROPRIETARY_CODECS)
-
-TEST_F(PipelineIntegrationTest, BasicPlaybackHi10P) {
-  ASSERT_EQ(PIPELINE_OK, Start("bear-320x180-hi10p.mp4", kClockless));
-
-  Play();
-
-  ASSERT_TRUE(WaitUntilOnEnded());
-}
-
-TEST_F(PipelineIntegrationTest, BasicFallback) {
-  ScopedVector<VideoDecoder> failing_video_decoder;
-  failing_video_decoder.push_back(new FailingVideoDecoder());
-
-  ASSERT_EQ(PIPELINE_OK,
-            Start("bear.mp4", kClockless, std::move(failing_video_decoder)));
-
-  Play();
-
-  ASSERT_TRUE(WaitUntilOnEnded());
-};
-
-TEST_F(PipelineIntegrationTest, MediaSource_ADTS) {
-  MockMediaSource source("sfx.adts", kADTS, kAppendWholeFile);
-  EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
-  source.EndOfStream();
-
-  EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
-  EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds());
-  EXPECT_EQ(325, pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds());
-
-  Play();
-
-  EXPECT_TRUE(WaitUntilOnEnded());
-}
-
-TEST_F(PipelineIntegrationTest, MediaSource_ADTS_TimestampOffset) {
-  MockMediaSource source("sfx.adts", kADTS, kAppendWholeFile);
-  EXPECT_EQ(PIPELINE_OK,
-            StartPipelineWithMediaSource(&source, kHashed, nullptr));
-  EXPECT_EQ(325, source.last_timestamp_offset().InMilliseconds());
-
-  // Trim off multiple frames off the beginning of the segment which will cause
-  // the first decoded frame to be incorrect if preroll isn't implemented.
-  const base::TimeDelta adts_preroll_duration =
-      base::TimeDelta::FromSecondsD(2.5 * 1024 / 44100);
-  const base::TimeDelta append_time =
-      source.last_timestamp_offset() - adts_preroll_duration;
-
-  scoped_refptr<DecoderBuffer> second_file = ReadTestDataFile("sfx.adts");
-  source.AppendAtTimeWithWindow(
-      append_time, append_time + adts_preroll_duration, kInfiniteDuration,
-      second_file->data(), second_file->data_size());
-  source.EndOfStream();
-
-  Play();
-  EXPECT_TRUE(WaitUntilOnEnded());
-
-  EXPECT_EQ(592, source.last_timestamp_offset().InMilliseconds());
-  EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
-  EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds());
-  EXPECT_EQ(592, pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds());
-
-  // Verify preroll is stripped.
-  EXPECT_HASH_EQ("-0.25,0.67,0.04,0.14,-0.49,-0.41,", GetAudioHash());
-}
-
-TEST_F(PipelineIntegrationTest, BasicPlaybackHashed_MP3) {
-  ASSERT_EQ(PIPELINE_OK, Start("sfx.mp3", kHashed));
-
-  Play();
-
-  ASSERT_TRUE(WaitUntilOnEnded());
-
-  // Verify codec delay and preroll are stripped.
-  EXPECT_HASH_EQ("1.30,2.72,4.56,5.08,3.74,2.03,", GetAudioHash());
-}
-
 #if !defined(DISABLE_CLOCKLESS_TESTS)
 class Mp3FastSeekParams {
  public:
@@ -1768,26 +2360,22 @@
   EXPECT_HASH_EQ(config.hash, GetAudioHash());
 }
 
-// CBR seeks should always be fast and accurate.
 INSTANTIATE_TEST_CASE_P(
     CBRSeek_HasTOC,
     Mp3FastSeekIntegrationTest,
     ::testing::Values(Mp3FastSeekParams("bear-audio-10s-CBR-has-TOC.mp3",
                                         "-0.71,0.36,2.96,2.68,2.11,-1.08,")));
-
 INSTANTIATE_TEST_CASE_P(
     CBRSeeks_NoTOC,
     Mp3FastSeekIntegrationTest,
     ::testing::Values(Mp3FastSeekParams("bear-audio-10s-CBR-no-TOC.mp3",
                                         "0.95,0.56,1.34,0.47,1.77,0.84,")));
-
 // VBR seeks can be fast *OR* accurate, but not both. We chose fast.
 INSTANTIATE_TEST_CASE_P(
     VBRSeeks_HasTOC,
     Mp3FastSeekIntegrationTest,
     ::testing::Values(Mp3FastSeekParams("bear-audio-10s-VBR-has-TOC.mp3",
                                         "-0.15,-0.83,0.54,1.00,1.94,0.93,")));
-
 INSTANTIATE_TEST_CASE_P(
     VBRSeeks_NoTOC,
     Mp3FastSeekIntegrationTest,
@@ -1795,85 +2383,6 @@
                                         "-0.22,0.80,1.19,0.73,-0.31,-1.12,")));
 #endif  // !defined(DISABLE_CLOCKLESS_TESTS)
 
-TEST_F(PipelineIntegrationTest, MediaSource_MP3) {
-  MockMediaSource source("sfx.mp3", kMP3, kAppendWholeFile);
-  EXPECT_EQ(PIPELINE_OK,
-            StartPipelineWithMediaSource(&source, kHashed, nullptr));
-  source.EndOfStream();
-
-  EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
-  EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds());
-  EXPECT_EQ(313, pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds());
-
-  Play();
-
-  EXPECT_TRUE(WaitUntilOnEnded());
-
-  // Verify that codec delay was stripped.
-  EXPECT_HASH_EQ("1.01,2.71,4.18,4.32,3.04,1.12,", GetAudioHash());
-}
-
-TEST_F(PipelineIntegrationTest, MediaSource_MP3_TimestampOffset) {
-  MockMediaSource source("sfx.mp3", kMP3, kAppendWholeFile);
-  EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
-  EXPECT_EQ(313, source.last_timestamp_offset().InMilliseconds());
-
-  // There are 576 silent frames at the start of this mp3.  The second append
-  // should trim them off.
-  const base::TimeDelta mp3_preroll_duration =
-      base::TimeDelta::FromSecondsD(576.0 / 44100);
-  const base::TimeDelta append_time =
-      source.last_timestamp_offset() - mp3_preroll_duration;
-
-  scoped_refptr<DecoderBuffer> second_file = ReadTestDataFile("sfx.mp3");
-  source.AppendAtTimeWithWindow(append_time, append_time + mp3_preroll_duration,
-                                kInfiniteDuration, second_file->data(),
-                                second_file->data_size());
-  source.EndOfStream();
-
-  Play();
-  EXPECT_TRUE(WaitUntilOnEnded());
-
-  EXPECT_EQ(613, source.last_timestamp_offset().InMilliseconds());
-  EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
-  EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds());
-  EXPECT_EQ(613, pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds());
-}
-
-TEST_F(PipelineIntegrationTest, MediaSource_MP3_Icecast) {
-  MockMediaSource source("icy_sfx.mp3", kMP3, kAppendWholeFile);
-  EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
-  source.EndOfStream();
-
-  Play();
-
-  EXPECT_TRUE(WaitUntilOnEnded());
-}
-
-TEST_F(PipelineIntegrationTest, MediaSource_ConfigChange_MP4) {
-  MockMediaSource source("bear-640x360-av_frag.mp4", kMP4, kAppendWholeFile);
-  EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
-
-  EXPECT_CALL(*this, OnVideoNaturalSizeChange(gfx::Size(1280, 720))).Times(1);
-  scoped_refptr<DecoderBuffer> second_file =
-      ReadTestDataFile("bear-1280x720-av_frag.mp4");
-  ASSERT_TRUE(source.AppendAtTime(base::TimeDelta::FromSeconds(kAppendTimeSec),
-                                  second_file->data(),
-                                  second_file->data_size()));
-  source.EndOfStream();
-
-  Play();
-  EXPECT_TRUE(WaitUntilOnEnded());
-
-  EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
-  EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds());
-  EXPECT_EQ(kAppendTimeMs + k1280IsoFileDurationMs,
-            pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds());
-
-  source.Shutdown();
-  Stop();
-}
-
 TEST_F(PipelineIntegrationTest,
        MAYBE_EME(MediaSource_ConfigChange_Encrypted_MP4_CENC_VideoOnly)) {
   MockMediaSource source("bear-640x360-v_frag-cenc.mp4", kMP4Video,
@@ -2003,12 +2512,6 @@
 }
 #endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
 
-TEST_F(PipelineIntegrationTest, BasicPlayback_16x9AspectRatio) {
-  ASSERT_EQ(PIPELINE_OK, Start("bear-320x240-16x9-aspect.webm"));
-  Play();
-  ASSERT_TRUE(WaitUntilOnEnded());
-}
-
 TEST_F(PipelineIntegrationTest, MAYBE_EME(EncryptedPlayback_WebM)) {
   MockMediaSource source("bear-320x240-av_enc-av.webm", kWebM, 219816);
   FakeEncryptedMedia encrypted_media(new KeyProvidingApp());
@@ -2114,54 +2617,6 @@
   Stop();
 }
 
-TEST_F(PipelineIntegrationTest, Mp2ts_AAC_HE_SBR_Audio) {
-  MockMediaSource source("bear-1280x720-aac_he.ts", kMP2AudioSBR,
-                         kAppendWholeFile);
-#if BUILDFLAG(ENABLE_MSE_MPEG2TS_STREAM_PARSER)
-  EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
-  source.EndOfStream();
-  ASSERT_EQ(PIPELINE_OK, pipeline_status_);
-
-  // Check that SBR is taken into account correctly by mpeg2ts parser. When an
-  // SBR stream is parsed as non-SBR stream, then audio frame durations are
-  // calculated incorrectly and that leads to gaps in buffered ranges (so this
-  // check will fail) and eventually leads to stalled playback.
-  EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
-#else
-  EXPECT_EQ(
-      DEMUXER_ERROR_COULD_NOT_OPEN,
-      StartPipelineWithMediaSource(&source, kExpectDemuxerFailure, nullptr));
-#endif
-}
-
-TEST_F(PipelineIntegrationTest, Mpeg2ts_MP3Audio_Mp4a_6B) {
-  MockMediaSource source("bear-audio-mp4a.6B.ts",
-                         "video/mp2t; codecs=\"mp4a.6B\"", kAppendWholeFile);
-#if BUILDFLAG(ENABLE_MSE_MPEG2TS_STREAM_PARSER)
-  EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
-  source.EndOfStream();
-  ASSERT_EQ(PIPELINE_OK, pipeline_status_);
-#else
-  EXPECT_EQ(
-      DEMUXER_ERROR_COULD_NOT_OPEN,
-      StartPipelineWithMediaSource(&source, kExpectDemuxerFailure, nullptr));
-#endif
-}
-
-TEST_F(PipelineIntegrationTest, Mpeg2ts_MP3Audio_Mp4a_69) {
-  MockMediaSource source("bear-audio-mp4a.69.ts",
-                         "video/mp2t; codecs=\"mp4a.69\"", kAppendWholeFile);
-#if BUILDFLAG(ENABLE_MSE_MPEG2TS_STREAM_PARSER)
-  EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
-  source.EndOfStream();
-  ASSERT_EQ(PIPELINE_OK, pipeline_status_);
-#else
-  EXPECT_EQ(
-      DEMUXER_ERROR_COULD_NOT_OPEN,
-      StartPipelineWithMediaSource(&source, kExpectDemuxerFailure, nullptr));
-#endif
-}
-
 TEST_F(PipelineIntegrationTest,
        MAYBE_EME(EncryptedPlayback_NoEncryptedFrames_MP4_CENC_AudioOnly)) {
   MockMediaSource source("bear-1280x720-a_frag-cenc_clear-all.mp4", kMP4Audio,
@@ -2275,269 +2730,8 @@
   Stop();
 }
 
-TEST_F(PipelineIntegrationTest, BasicPlayback_MediaSource_VideoOnly_MP4_AVC3) {
-  MockMediaSource source("bear-1280x720-v_frag-avc3.mp4", kMP4VideoAVC3,
-                         kAppendWholeFile);
-  EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
-  source.EndOfStream();
-
-  EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
-  EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds());
-  EXPECT_EQ(k1280IsoAVC3FileDurationMs,
-            pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds());
-
-  Play();
-
-  ASSERT_TRUE(WaitUntilOnEnded());
-  source.Shutdown();
-  Stop();
-}
-
-TEST_F(PipelineIntegrationTest, BasicPlayback_MediaSource_VideoOnly_MP4_VP9) {
-  MockMediaSource source("bear-320x240-v_frag-vp9.mp4", kMP4VideoVP9,
-                         kAppendWholeFile);
-  if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kEnableVp9InMp4)) {
-    ASSERT_EQ(ChunkDemuxer::kNotSupported, source.AddId());
-    return;
-  }
-
-  EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
-  source.EndOfStream();
-  ASSERT_EQ(PIPELINE_OK, pipeline_status_);
-
-  Play();
-
-  ASSERT_TRUE(WaitUntilOnEnded());
-  source.Shutdown();
-  Stop();
-}
-
-TEST_F(PipelineIntegrationTest, BasicPlayback_MediaSource_VideoOnly_MP4_HEVC1) {
-  // HEVC demuxing might be enabled even on platforms that don't support HEVC
-  // decoding. For those cases we'll get DECODER_ERROR_NOT_SUPPORTED, which
-  // indicates indicates that we did pass media mime type checks and attempted
-  // to actually demux and decode the stream. On platforms that support both
-  // demuxing and decoding we'll get PIPELINE_OK.
-  MockMediaSource source("bear-320x240-v_frag-hevc.mp4", kMP4VideoHEVC1,
-                         kAppendWholeFile);
-#if BUILDFLAG(ENABLE_HEVC_DEMUXING)
-  PipelineStatus status = StartPipelineWithMediaSource(&source);
-  EXPECT_TRUE(status == PIPELINE_OK || status == DECODER_ERROR_NOT_SUPPORTED);
-#else
-  EXPECT_EQ(
-      DEMUXER_ERROR_COULD_NOT_OPEN,
-      StartPipelineWithMediaSource(&source, kExpectDemuxerFailure, nullptr));
-#endif
-}
-
-TEST_F(PipelineIntegrationTest, BasicPlayback_MediaSource_VideoOnly_MP4_HEVC2) {
-  // HEVC demuxing might be enabled even on platforms that don't support HEVC
-  // decoding. For those cases we'll get DECODER_ERROR_NOT_SUPPORTED, which
-  // indicates indicates that we did pass media mime type checks and attempted
-  // to actually demux and decode the stream. On platforms that support both
-  // demuxing and decoding we'll get PIPELINE_OK.
-  MockMediaSource source("bear-320x240-v_frag-hevc.mp4", kMP4VideoHEVC2,
-                         kAppendWholeFile);
-#if BUILDFLAG(ENABLE_HEVC_DEMUXING)
-  PipelineStatus status = StartPipelineWithMediaSource(&source);
-  EXPECT_TRUE(status == PIPELINE_OK || status == DECODER_ERROR_NOT_SUPPORTED);
-#else
-  EXPECT_EQ(
-      DEMUXER_ERROR_COULD_NOT_OPEN,
-      StartPipelineWithMediaSource(&source, kExpectDemuxerFailure, nullptr));
-#endif
-}
-
 #endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
 
-TEST_F(PipelineIntegrationTest, SeekWhilePaused) {
-  ASSERT_EQ(PIPELINE_OK, Start("bear-320x240.webm"));
-
-  base::TimeDelta duration(pipeline_->GetMediaDuration());
-  base::TimeDelta start_seek_time(duration / 4);
-  base::TimeDelta seek_time(duration * 3 / 4);
-
-  Play();
-  ASSERT_TRUE(WaitUntilCurrentTimeIsAfter(start_seek_time));
-  Pause();
-  ASSERT_TRUE(Seek(seek_time));
-  EXPECT_EQ(seek_time, pipeline_->GetMediaTime());
-  Play();
-  ASSERT_TRUE(WaitUntilOnEnded());
-
-  // Make sure seeking after reaching the end works as expected.
-  Pause();
-  ASSERT_TRUE(Seek(seek_time));
-  EXPECT_EQ(seek_time, pipeline_->GetMediaTime());
-  Play();
-  ASSERT_TRUE(WaitUntilOnEnded());
-}
-
-TEST_F(PipelineIntegrationTest, SeekWhilePlaying) {
-  ASSERT_EQ(PIPELINE_OK, Start("bear-320x240.webm"));
-
-  base::TimeDelta duration(pipeline_->GetMediaDuration());
-  base::TimeDelta start_seek_time(duration / 4);
-  base::TimeDelta seek_time(duration * 3 / 4);
-
-  Play();
-  ASSERT_TRUE(WaitUntilCurrentTimeIsAfter(start_seek_time));
-  ASSERT_TRUE(Seek(seek_time));
-  EXPECT_GE(pipeline_->GetMediaTime(), seek_time);
-  ASSERT_TRUE(WaitUntilOnEnded());
-
-  // Make sure seeking after reaching the end works as expected.
-  ASSERT_TRUE(Seek(seek_time));
-  EXPECT_GE(pipeline_->GetMediaTime(), seek_time);
-  ASSERT_TRUE(WaitUntilOnEnded());
-}
-
-TEST_F(PipelineIntegrationTest, SuspendWhilePaused) {
-  ASSERT_EQ(PIPELINE_OK, Start("bear-320x240.webm"));
-
-  base::TimeDelta duration(pipeline_->GetMediaDuration());
-  base::TimeDelta start_seek_time(duration / 4);
-  base::TimeDelta seek_time(duration * 3 / 4);
-
-  Play();
-  ASSERT_TRUE(WaitUntilCurrentTimeIsAfter(start_seek_time));
-  Pause();
-
-  // Suspend while paused.
-  ASSERT_TRUE(Suspend());
-
-  // Resuming the pipeline will create a new Renderer,
-  // which in turn will trigger video size and opacity notifications.
-  EXPECT_CALL(*this, OnVideoNaturalSizeChange(gfx::Size(320, 240))).Times(1);
-  EXPECT_CALL(*this, OnVideoOpacityChange(true)).Times(1);
-
-  ASSERT_TRUE(Resume(seek_time));
-  EXPECT_GE(pipeline_->GetMediaTime(), seek_time);
-  Play();
-  ASSERT_TRUE(WaitUntilOnEnded());
-}
-
-TEST_F(PipelineIntegrationTest, SuspendWhilePlaying) {
-  ASSERT_EQ(PIPELINE_OK, Start("bear-320x240.webm"));
-
-  base::TimeDelta duration(pipeline_->GetMediaDuration());
-  base::TimeDelta start_seek_time(duration / 4);
-  base::TimeDelta seek_time(duration * 3 / 4);
-
-  Play();
-  ASSERT_TRUE(WaitUntilCurrentTimeIsAfter(start_seek_time));
-  ASSERT_TRUE(Suspend());
-
-  // Resuming the pipeline will create a new Renderer,
-  // which in turn will trigger video size and opacity notifications.
-  EXPECT_CALL(*this, OnVideoNaturalSizeChange(gfx::Size(320, 240))).Times(1);
-  EXPECT_CALL(*this, OnVideoOpacityChange(true)).Times(1);
-
-  ASSERT_TRUE(Resume(seek_time));
-  EXPECT_GE(pipeline_->GetMediaTime(), seek_time);
-  ASSERT_TRUE(WaitUntilOnEnded());
-}
-
-#if BUILDFLAG(USE_PROPRIETARY_CODECS)
-TEST_F(PipelineIntegrationTest, Rotated_Metadata_0) {
-  ASSERT_EQ(PIPELINE_OK, Start("bear_rotate_0.mp4"));
-  ASSERT_EQ(VIDEO_ROTATION_0, metadata_.video_rotation);
-}
-
-TEST_F(PipelineIntegrationTest, Rotated_Metadata_90) {
-  ASSERT_EQ(PIPELINE_OK, Start("bear_rotate_90.mp4"));
-  ASSERT_EQ(VIDEO_ROTATION_90, metadata_.video_rotation);
-}
-
-TEST_F(PipelineIntegrationTest, Rotated_Metadata_180) {
-  ASSERT_EQ(PIPELINE_OK, Start("bear_rotate_180.mp4"));
-  ASSERT_EQ(VIDEO_ROTATION_180, metadata_.video_rotation);
-}
-
-TEST_F(PipelineIntegrationTest, Rotated_Metadata_270) {
-  ASSERT_EQ(PIPELINE_OK, Start("bear_rotate_270.mp4"));
-  ASSERT_EQ(VIDEO_ROTATION_270, metadata_.video_rotation);
-}
-#endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
-
-// Verify audio decoder & renderer can handle aborted demuxer reads.
-TEST_F(PipelineIntegrationTest, ChunkDemuxerAbortRead_AudioOnly) {
-  ASSERT_TRUE(TestSeekDuringRead("bear-320x240-audio-only.webm", kAudioOnlyWebM,
-                                 16384, base::TimeDelta::FromMilliseconds(464),
-                                 base::TimeDelta::FromMilliseconds(617), 0x10CA,
-                                 19730));
-}
-
-// Verify video decoder & renderer can handle aborted demuxer reads.
-TEST_F(PipelineIntegrationTest, ChunkDemuxerAbortRead_VideoOnly) {
-  ASSERT_TRUE(TestSeekDuringRead("bear-320x240-video-only.webm", kVideoOnlyWebM,
-                                 32768, base::TimeDelta::FromMilliseconds(167),
-                                 base::TimeDelta::FromMilliseconds(1668),
-                                 0x1C896, 65536));
-}
-
-// Verify that Opus audio in WebM containers can be played back.
-TEST_F(PipelineIntegrationTest, BasicPlayback_AudioOnly_Opus_WebM) {
-  ASSERT_EQ(PIPELINE_OK, Start("bear-opus-end-trimming.webm"));
-  Play();
-  ASSERT_TRUE(WaitUntilOnEnded());
-}
-
-// Verify that VP9 video in WebM containers can be played back.
-TEST_F(PipelineIntegrationTest, BasicPlayback_VideoOnly_VP9_WebM) {
-  ASSERT_EQ(PIPELINE_OK, Start("bear-vp9.webm"));
-  Play();
-  ASSERT_TRUE(WaitUntilOnEnded());
-}
-
-// Verify that VP9 video and Opus audio in the same WebM container can be played
-// back.
-TEST_F(PipelineIntegrationTest, BasicPlayback_VP9_Opus_WebM) {
-  ASSERT_EQ(PIPELINE_OK, Start("bear-vp9-opus.webm"));
-  Play();
-  ASSERT_TRUE(WaitUntilOnEnded());
-}
-
-// Verify that VP8 video with alpha channel can be played back.
-TEST_F(PipelineIntegrationTest, BasicPlayback_VP8A_WebM) {
-  ASSERT_EQ(PIPELINE_OK, Start("bear-vp8a.webm"));
-  Play();
-  ASSERT_TRUE(WaitUntilOnEnded());
-  EXPECT_VIDEO_FORMAT_EQ(last_video_frame_format_, PIXEL_FORMAT_YV12A);
-}
-
-// Verify that VP8A video with odd width/height can be played back.
-TEST_F(PipelineIntegrationTest, BasicPlayback_VP8A_Odd_WebM) {
-  ASSERT_EQ(PIPELINE_OK, Start("bear-vp8a-odd-dimensions.webm"));
-  Play();
-  ASSERT_TRUE(WaitUntilOnEnded());
-  EXPECT_VIDEO_FORMAT_EQ(last_video_frame_format_, PIXEL_FORMAT_YV12A);
-}
-
-// Verify that VP9 video with odd width/height can be played back.
-TEST_F(PipelineIntegrationTest, BasicPlayback_VP9_Odd_WebM) {
-  ASSERT_EQ(PIPELINE_OK, Start("bear-vp9-odd-dimensions.webm"));
-  Play();
-  ASSERT_TRUE(WaitUntilOnEnded());
-}
-
-// Verify that VP9 video with alpha channel can be played back.
-TEST_F(PipelineIntegrationTest, BasicPlayback_VP9A_WebM) {
-  ASSERT_EQ(PIPELINE_OK, Start("bear-vp9a.webm"));
-  Play();
-  ASSERT_TRUE(WaitUntilOnEnded());
-  EXPECT_VIDEO_FORMAT_EQ(last_video_frame_format_, PIXEL_FORMAT_YV12A);
-}
-
-// Verify that VP9A video with odd width/height can be played back.
-TEST_F(PipelineIntegrationTest, BasicPlayback_VP9A_Odd_WebM) {
-  ASSERT_EQ(PIPELINE_OK, Start("bear-vp9a-odd-dimensions.webm"));
-  Play();
-  ASSERT_TRUE(WaitUntilOnEnded());
-  EXPECT_VIDEO_FORMAT_EQ(last_video_frame_format_, PIXEL_FORMAT_YV12A);
-}
-
 // Verify that VP8 video with inband text track can be played back.
 TEST_F(PipelineIntegrationTest, MAYBE_TEXT(BasicPlayback_VP8_WebVTT_WebM)) {
   EXPECT_CALL(*this, OnAddTextTrack(_, _));
@@ -2546,73 +2740,6 @@
   ASSERT_TRUE(WaitUntilOnEnded());
 }
 
-// Verify that VP9 video with 4:4:4 subsampling can be played back.
-TEST_F(PipelineIntegrationTest, P444_VP9_WebM) {
-  ASSERT_EQ(PIPELINE_OK, Start("bear-320x240-P444.webm"));
-  Play();
-  ASSERT_TRUE(WaitUntilOnEnded());
-  EXPECT_VIDEO_FORMAT_EQ(last_video_frame_format_, PIXEL_FORMAT_YV24);
-}
-
-// Verify that frames of VP9 video in the BT.709 color space have the YV12HD
-// format.
-TEST_F(PipelineIntegrationTest, BT709_VP9_WebM) {
-  ASSERT_EQ(PIPELINE_OK, Start("bear-vp9-bt709.webm"));
-  Play();
-  ASSERT_TRUE(WaitUntilOnEnded());
-  EXPECT_VIDEO_FORMAT_EQ(last_video_frame_format_, PIXEL_FORMAT_YV12);
-  EXPECT_COLOR_SPACE_EQ(last_video_frame_color_space_, COLOR_SPACE_HD_REC709);
-}
-
-TEST_F(PipelineIntegrationTest, HD_VP9_WebM) {
-  ASSERT_EQ(PIPELINE_OK, Start("bear-1280x720.webm", kClockless));
-  Play();
-  ASSERT_TRUE(WaitUntilOnEnded());
-}
-
-// Verify that videos with an odd frame size playback successfully.
-TEST_F(PipelineIntegrationTest, BasicPlayback_OddVideoSize) {
-  ASSERT_EQ(PIPELINE_OK, Start("butterfly-853x480.webm"));
-  Play();
-  ASSERT_TRUE(WaitUntilOnEnded());
-}
-
-// Verify that OPUS audio in a webm which reports a 44.1kHz sample rate plays
-// correctly at 48kHz
-TEST_F(PipelineIntegrationTest, BasicPlayback_Opus441kHz) {
-  ASSERT_EQ(PIPELINE_OK, Start("sfx-opus-441.webm"));
-  Play();
-  ASSERT_TRUE(WaitUntilOnEnded());
-
-  EXPECT_EQ(48000, demuxer_->GetFirstStream(DemuxerStream::AUDIO)
-                       ->audio_decoder_config()
-                       .samples_per_second());
-}
-
-// Same as above but using MediaSource.
-TEST_F(PipelineIntegrationTest, BasicPlayback_MediaSource_Opus441kHz) {
-  MockMediaSource source("sfx-opus-441.webm", kOpusAudioOnlyWebM,
-                         kAppendWholeFile);
-  EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
-  source.EndOfStream();
-  Play();
-  ASSERT_TRUE(WaitUntilOnEnded());
-  source.Shutdown();
-  Stop();
-  EXPECT_EQ(48000, demuxer_->GetFirstStream(DemuxerStream::AUDIO)
-                       ->audio_decoder_config()
-                       .samples_per_second());
-}
-
-// Ensures audio-only playback with missing or negative timestamps works.  Tests
-// the common live-streaming case for chained ogg.  See http://crbug.com/396864.
-TEST_F(PipelineIntegrationTest, BasicPlaybackChainedOgg) {
-  ASSERT_EQ(PIPELINE_OK, Start("double-sfx.ogg", kUnreliableDuration));
-  Play();
-  ASSERT_TRUE(WaitUntilOnEnded());
-  ASSERT_EQ(base::TimeDelta(), demuxer_->GetStartTime());
-}
-
 // Ensures audio-video playback with missing or negative timestamps fails softly
 // instead of crashing.  See http://crbug.com/396864.
 TEST_F(PipelineIntegrationTest, BasicPlaybackChainedOggVideo) {
@@ -2622,32 +2749,4 @@
   ASSERT_EQ(base::TimeDelta(), demuxer_->GetStartTime());
 }
 
-// Tests that we signal ended even when audio runs longer than video track.
-TEST_F(PipelineIntegrationTest, BasicPlaybackAudioLongerThanVideo) {
-  ASSERT_EQ(PIPELINE_OK, Start("bear_audio_longer_than_video.ogv"));
-  // Audio track is 2000ms. Video track is 1001ms. Duration should be higher
-  // of the two.
-  EXPECT_EQ(2000, pipeline_->GetMediaDuration().InMilliseconds());
-  Play();
-  ASSERT_TRUE(WaitUntilOnEnded());
-}
-
-// Tests that we signal ended even when audio runs shorter than video track.
-TEST_F(PipelineIntegrationTest, BasicPlaybackAudioShorterThanVideo) {
-  ASSERT_EQ(PIPELINE_OK, Start("bear_audio_shorter_than_video.ogv"));
-  // Audio track is 500ms. Video track is 1001ms. Duration should be higher of
-  // the two.
-  EXPECT_EQ(1001, pipeline_->GetMediaDuration().InMilliseconds());
-  Play();
-  ASSERT_TRUE(WaitUntilOnEnded());
-}
-
-TEST_F(PipelineIntegrationTest, BasicPlaybackPositiveStartTime) {
-  ASSERT_EQ(PIPELINE_OK, Start("nonzero-start-time.webm"));
-  Play();
-  ASSERT_TRUE(WaitUntilOnEnded());
-  ASSERT_EQ(base::TimeDelta::FromMicroseconds(396000),
-            demuxer_->GetStartTime());
-}
-
 }  // namespace media
diff --git a/media/test/pipeline_integration_test_base.cc b/media/test/pipeline_integration_test_base.cc
index 6a8b8f728..e455b10 100644
--- a/media/test/pipeline_integration_test_base.cc
+++ b/media/test/pipeline_integration_test_base.cc
@@ -30,6 +30,10 @@
 #include "media/filters/vpx_video_decoder.h"
 #endif
 
+#if BUILDFLAG(ENABLE_MEDIA_REMOTING)
+#include "media/remoting/end2end_test_renderer.h"  // nogncheck
+#endif  // BUILDFLAG(ENABLE_MEDIA_REMOTING)
+
 using ::testing::_;
 using ::testing::AnyNumber;
 using ::testing::AtLeast;
@@ -44,6 +48,61 @@
 const char kNullVideoHash[] = "d41d8cd98f00b204e9800998ecf8427e";
 const char kNullAudioHash[] = "0.00,0.00,0.00,0.00,0.00,0.00,";
 
+namespace {
+
+class RendererFactoryImpl final : public PipelineTestRendererFactory {
+ public:
+  explicit RendererFactoryImpl(PipelineIntegrationTestBase* integration_test)
+      : integration_test_(integration_test) {}
+  ~RendererFactoryImpl() override {}
+
+  // PipelineTestRendererFactory implementation.
+  std::unique_ptr<Renderer> CreateRenderer(
+      ScopedVector<VideoDecoder> prepend_video_decoders =
+          ScopedVector<VideoDecoder>(),
+      ScopedVector<AudioDecoder> prepend_audio_decoders =
+          ScopedVector<AudioDecoder>()) override {
+    return integration_test_->CreateRenderer(std::move(prepend_video_decoders),
+                                             std::move(prepend_audio_decoders));
+  }
+
+ private:
+  PipelineIntegrationTestBase* integration_test_;
+
+  DISALLOW_COPY_AND_ASSIGN(RendererFactoryImpl);
+};
+
+#if BUILDFLAG(ENABLE_MEDIA_REMOTING)
+class RemotingTestRendererFactory final : public PipelineTestRendererFactory {
+ public:
+  explicit RemotingTestRendererFactory(
+      std::unique_ptr<PipelineTestRendererFactory> renderer_factory)
+      : default_renderer_factory_(std::move(renderer_factory)) {}
+  ~RemotingTestRendererFactory() override {}
+
+  // PipelineTestRendererFactory implementation.
+  std::unique_ptr<Renderer> CreateRenderer(
+      ScopedVector<VideoDecoder> prepend_video_decoders =
+          ScopedVector<VideoDecoder>(),
+      ScopedVector<AudioDecoder> prepend_audio_decoders =
+          ScopedVector<AudioDecoder>()) override {
+    std::unique_ptr<Renderer> renderer_impl =
+        default_renderer_factory_->CreateRenderer(
+            std::move(prepend_video_decoders),
+            std::move(prepend_audio_decoders));
+    return base::MakeUnique<remoting::End2EndTestRenderer>(
+        std::move(renderer_impl));
+  }
+
+ private:
+  std::unique_ptr<PipelineTestRendererFactory> default_renderer_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(RemotingTestRendererFactory);
+};
+#endif  // BUILDFLAG(ENABLE_MEDIA_REMOTING)
+
+}  // namespace
+
 PipelineIntegrationTestBase::PipelineIntegrationTestBase()
     : hashing_enabled_(false),
       clockless_playback_(false),
@@ -52,7 +111,8 @@
       pipeline_status_(PIPELINE_OK),
       last_video_frame_format_(PIXEL_FORMAT_UNKNOWN),
       last_video_frame_color_space_(COLOR_SPACE_UNSPECIFIED),
-      current_duration_(kInfiniteDuration) {
+      current_duration_(kInfiniteDuration),
+      renderer_factory_(new RendererFactoryImpl(this)) {
   ResetVideoHash();
   EXPECT_CALL(*this, OnVideoAverageKeyframeDistanceUpdate()).Times(AnyNumber());
 }
@@ -179,10 +239,12 @@
   EXPECT_CALL(*this, OnWaitingForDecryptionKey()).Times(0);
 
   pipeline_->Start(
-      demuxer_.get(), CreateRenderer(std::move(prepend_video_decoders),
-                                     std::move(prepend_audio_decoders)),
-      this, base::Bind(&PipelineIntegrationTestBase::OnStatusCallback,
-                       base::Unretained(this)));
+      demuxer_.get(),
+      renderer_factory_->CreateRenderer(std::move(prepend_video_decoders),
+                                        std::move(prepend_audio_decoders)),
+      this,
+      base::Bind(&PipelineIntegrationTestBase::OnStatusCallback,
+                 base::Unretained(this)));
   base::RunLoop().Run();
   return pipeline_status_;
 }
@@ -260,7 +322,7 @@
 
   EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH))
       .WillOnce(InvokeWithoutArgs(&message_loop_, &base::MessageLoop::QuitNow));
-  pipeline_->Resume(CreateRenderer(), seek_time,
+  pipeline_->Resume(renderer_factory_->CreateRenderer(), seek_time,
                     base::Bind(&PipelineIntegrationTestBase::OnSeeked,
                                base::Unretained(this), seek_time));
   base::RunLoop().Run();
@@ -453,4 +515,12 @@
   return now_;
 }
 
+#if BUILDFLAG(ENABLE_MEDIA_REMOTING)
+void PipelineIntegrationTestBase::SetUpRemotingPipeline() {
+  std::unique_ptr<PipelineTestRendererFactory> factory =
+      std::move(renderer_factory_);
+  renderer_factory_.reset(new RemotingTestRendererFactory(std::move(factory)));
+}
+#endif  // BUILDFLAG(ENABLE_MEDIA_REMOTING)
+
 }  // namespace media
diff --git a/media/test/pipeline_integration_test_base.h b/media/test/pipeline_integration_test_base.h
index 580e963c..9ab7ab58 100644
--- a/media/test/pipeline_integration_test_base.h
+++ b/media/test/pipeline_integration_test_base.h
@@ -48,6 +48,22 @@
   base::TimeTicks now_;
 };
 
+class PipelineTestRendererFactory {
+ public:
+  virtual ~PipelineTestRendererFactory() {}
+  // Creates and returns a Renderer.
+  virtual std::unique_ptr<Renderer> CreateRenderer(
+      ScopedVector<VideoDecoder> prepend_video_decoders =
+          ScopedVector<VideoDecoder>(),
+      ScopedVector<AudioDecoder> prepend_audio_decoders =
+          ScopedVector<AudioDecoder>()) = 0;
+};
+
+enum PipelineType {
+  Media,          // Test the general media pipeline.
+  MediaRemoting,  // Test Media Remoting pipeline.
+};
+
 // Integration tests for Pipeline. Real demuxers, real decoders, and
 // base renderer implementations are used to verify pipeline functionality. The
 // renderers used in these tests rely heavily on the AudioRendererBase &
@@ -129,30 +145,11 @@
     encrypted_media_init_data_cb_ = encrypted_media_init_data_cb;
   }
 
+  std::unique_ptr<Renderer> CreateRenderer(
+      ScopedVector<VideoDecoder> prepend_video_decoders,
+      ScopedVector<AudioDecoder> prepend_audio_decoders);
+
  protected:
-  base::MessageLoop message_loop_;
-  base::MD5Context md5_context_;
-  bool hashing_enabled_;
-  bool clockless_playback_;
-
-  // TaskScheduler is used only for FFmpegDemuxer.
-  std::unique_ptr<base::test::ScopedTaskScheduler> task_scheduler_;
-  std::unique_ptr<Demuxer> demuxer_;
-  std::unique_ptr<DataSource> data_source_;
-  std::unique_ptr<PipelineImpl> pipeline_;
-  scoped_refptr<NullAudioSink> audio_sink_;
-  scoped_refptr<ClocklessAudioSink> clockless_audio_sink_;
-  std::unique_ptr<NullVideoSink> video_sink_;
-  bool ended_;
-  PipelineStatus pipeline_status_;
-  Demuxer::EncryptedMediaInitDataCB encrypted_media_init_data_cb_;
-  VideoPixelFormat last_video_frame_format_;
-  ColorSpace last_video_frame_color_space_;
-  DummyTickClock dummy_clock_;
-  PipelineMetadata metadata_;
-  scoped_refptr<VideoFrame> last_frame_;
-  base::TimeDelta current_duration_;
-
   PipelineStatus StartInternal(
       std::unique_ptr<DataSource> data_source,
       CdmContext* cdm_context,
@@ -183,13 +180,6 @@
   // Creates Demuxer and sets |demuxer_|.
   void CreateDemuxer(std::unique_ptr<DataSource> data_source);
 
-  // Creates and returns a Renderer.
-  virtual std::unique_ptr<Renderer> CreateRenderer(
-      ScopedVector<VideoDecoder> prepend_video_decoders =
-          ScopedVector<VideoDecoder>(),
-      ScopedVector<AudioDecoder> prepend_audio_decoders =
-          ScopedVector<AudioDecoder>());
-
   void OnVideoFramePaint(const scoped_refptr<VideoFrame>& frame);
 
   void CheckDuration();
@@ -197,6 +187,13 @@
   // Return the media start time from |demuxer_|.
   base::TimeDelta GetStartTime();
 
+#if BUILDFLAG(ENABLE_MEDIA_REMOTING)
+  // Proxy all control and data flows through a media remoting RPC pipeline, to
+  // test that an end-to-end media remoting pipeline works the same as a normal,
+  // local pipeline.
+  void SetUpRemotingPipeline();
+#endif  // BUILDFLAG(ENABLE_MEDIA_REMOTING)
+
   MOCK_METHOD1(DecryptorAttached, void(bool));
   // Pipeline::Client overrides.
   void OnError(PipelineStatus status) override;
@@ -211,6 +208,33 @@
   MOCK_METHOD1(OnVideoNaturalSizeChange, void(const gfx::Size&));
   MOCK_METHOD1(OnVideoOpacityChange, void(bool));
   MOCK_METHOD0(OnVideoAverageKeyframeDistanceUpdate, void());
+
+  base::MessageLoop message_loop_;
+  base::MD5Context md5_context_;
+  bool hashing_enabled_;
+  bool clockless_playback_;
+
+  // TaskScheduler is used only for FFmpegDemuxer.
+  std::unique_ptr<base::test::ScopedTaskScheduler> task_scheduler_;
+  std::unique_ptr<Demuxer> demuxer_;
+  std::unique_ptr<DataSource> data_source_;
+  std::unique_ptr<PipelineImpl> pipeline_;
+  scoped_refptr<NullAudioSink> audio_sink_;
+  scoped_refptr<ClocklessAudioSink> clockless_audio_sink_;
+  std::unique_ptr<NullVideoSink> video_sink_;
+  bool ended_;
+  PipelineStatus pipeline_status_;
+  Demuxer::EncryptedMediaInitDataCB encrypted_media_init_data_cb_;
+  VideoPixelFormat last_video_frame_format_;
+  ColorSpace last_video_frame_color_space_;
+  DummyTickClock dummy_clock_;
+  PipelineMetadata metadata_;
+  scoped_refptr<VideoFrame> last_frame_;
+  base::TimeDelta current_duration_;
+  std::unique_ptr<PipelineTestRendererFactory> renderer_factory_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(PipelineIntegrationTestBase);
 };
 
 }  // namespace media
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 3151bda..f5f99c3 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -1522,9 +1522,11 @@
       "spdy/multiplexed_session.cc",
       "spdy/multiplexed_session.h",
       "spdy/platform/api/spdy_estimate_memory_usage.h",
+      "spdy/platform/api/spdy_string.h",
       "spdy/platform/api/spdy_string_piece.h",
       "spdy/platform/api/spdy_string_utils.h",
       "spdy/platform/impl/spdy_estimate_memory_usage_impl.h",
+      "spdy/platform/impl/spdy_string_impl.h",
       "spdy/platform/impl/spdy_string_piece_impl.h",
       "spdy/platform/impl/spdy_string_utils_impl.h",
       "spdy/priority_write_scheduler.h",
diff --git a/net/spdy/array_output_buffer.h b/net/spdy/array_output_buffer.h
index a363e56d..f6c805ab 100644
--- a/net/spdy/array_output_buffer.h
+++ b/net/spdy/array_output_buffer.h
@@ -5,7 +5,7 @@
 #ifndef NET_SPDY_ARRAY_OUTPUT_BUFFER_H_
 #define NET_SPDY_ARRAY_OUTPUT_BUFFER_H_
 
-#include <string.h>
+#include <cstddef>
 #include "net/spdy/zero_copy_output_buffer.h"
 
 namespace net {
diff --git a/net/spdy/bidirectional_stream_spdy_impl_unittest.cc b/net/spdy/bidirectional_stream_spdy_impl_unittest.cc
index a56ae16..a37ad01 100644
--- a/net/spdy/bidirectional_stream_spdy_impl_unittest.cc
+++ b/net/spdy/bidirectional_stream_spdy_impl_unittest.cc
@@ -5,7 +5,6 @@
 #include "net/spdy/bidirectional_stream_spdy_impl.h"
 
 #include <memory>
-#include <string>
 
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
@@ -21,6 +20,7 @@
 #include "net/http/http_response_info.h"
 #include "net/log/test_net_log.h"
 #include "net/socket/socket_test_util.h"
+#include "net/spdy/platform/api/spdy_string.h"
 #include "net/spdy/spdy_session.h"
 #include "net/spdy/spdy_test_util_common.h"
 #include "net/test/cert_test_util.h"
@@ -196,7 +196,7 @@
   }
 
   // Const getters for internal states.
-  const std::string& data_received() const { return data_received_; }
+  const SpdyString& data_received() const { return data_received_; }
   int bytes_read() const { return bytes_read_; }
   int error() const { return error_; }
   const SpdyHeaderBlock& response_headers() const { return response_headers_; }
@@ -214,7 +214,7 @@
   std::unique_ptr<BidirectionalStreamSpdyImpl> stream_;
   scoped_refptr<IOBuffer> read_buf_;
   int read_buf_len_;
-  std::string data_received_;
+  SpdyString data_received_;
   std::unique_ptr<base::RunLoop> loop_;
   SpdyHeaderBlock response_headers_;
   SpdyHeaderBlock trailers_;
@@ -313,7 +313,7 @@
   sequenced_data_->RunUntilPaused();
 
   scoped_refptr<StringIOBuffer> write_buffer(
-      new StringIOBuffer(std::string(kBodyData, kBodyDataSize)));
+      new StringIOBuffer(SpdyString(kBodyData, kBodyDataSize)));
   delegate->SendData(write_buffer.get(), write_buffer->size(), true);
   sequenced_data_->Resume();
   base::RunLoop().RunUntilIdle();
@@ -465,7 +465,7 @@
   sequenced_data_->RunUntilPaused();
   // Make a write pending before receiving RST_STREAM.
   scoped_refptr<StringIOBuffer> write_buffer(
-      new StringIOBuffer(std::string(kBodyData, kBodyDataSize)));
+      new StringIOBuffer(SpdyString(kBodyData, kBodyDataSize)));
   delegate->SendData(write_buffer.get(), write_buffer->size(), false);
   sequenced_data_->Resume();
   base::RunLoop().RunUntilIdle();
diff --git a/net/spdy/buffered_spdy_framer.h b/net/spdy/buffered_spdy_framer.h
index f4502257..9c6c0d9d 100644
--- a/net/spdy/buffered_spdy_framer.h
+++ b/net/spdy/buffered_spdy_framer.h
@@ -9,11 +9,11 @@
 #include <stdint.h>
 
 #include <memory>
-#include <string>
 
 #include "base/macros.h"
 #include "net/base/net_export.h"
 #include "net/spdy/header_coalescer.h"
+#include "net/spdy/platform/api/spdy_string.h"
 #include "net/spdy/platform/api/spdy_string_piece.h"
 #include "net/spdy/spdy_alt_svc_wire_format.h"
 #include "net/spdy/spdy_framer.h"
@@ -31,7 +31,7 @@
 
   // Called if an error is detected in a HTTP2 stream.
   virtual void OnStreamError(SpdyStreamId stream_id,
-                             const std::string& description) = 0;
+                             const SpdyString& description) = 0;
 
   // Called after all the header data for HEADERS control frame is received.
   virtual void OnHeaders(SpdyStreamId stream_id,
@@ -261,7 +261,7 @@
   struct GoAwayFields {
     SpdyStreamId last_accepted_stream_id;
     SpdyErrorCode error_code;
-    std::string debug_data;
+    SpdyString debug_data;
 
     // Returns the estimate of dynamically allocated memory in bytes.
     size_t EstimateMemoryUsage() const;
diff --git a/net/spdy/buffered_spdy_framer_unittest.cc b/net/spdy/buffered_spdy_framer_unittest.cc
index 870ab36..770f21c4 100644
--- a/net/spdy/buffered_spdy_framer_unittest.cc
+++ b/net/spdy/buffered_spdy_framer_unittest.cc
@@ -34,7 +34,7 @@
   }
 
   void OnStreamError(SpdyStreamId stream_id,
-                     const std::string& description) override {
+                     const SpdyString& description) override {
     VLOG(1) << "SpdyFramer Error on stream: " << stream_id << " "
             << description;
     error_count_++;
@@ -167,11 +167,11 @@
   // OnGoAway parameters.
   SpdyStreamId goaway_last_accepted_stream_id_;
   SpdyErrorCode goaway_error_code_;
-  std::string goaway_debug_data_;
+  SpdyString goaway_debug_data_;
 
   // OnAltSvc parameters.
   SpdyStreamId altsvc_stream_id_;
-  std::string altsvc_origin_;
+  SpdyString altsvc_origin_;
   SpdyAltSvcWireFormat::AlternativeServiceVector altsvc_vector_;
 };
 
@@ -194,7 +194,7 @@
 
 TEST_F(BufferedSpdyFramerTest, HeaderListTooLarge) {
   SpdyHeaderBlock headers;
-  std::string long_header_value(256 * 1024, 'x');
+  SpdyString long_header_value(256 * 1024, 'x');
   headers["foo"] = long_header_value;
   SpdyHeadersIR headers_ir(/*stream_id=*/1, std::move(headers));
 
diff --git a/net/spdy/fuzzing/hpack_example_generator.cc b/net/spdy/fuzzing/hpack_example_generator.cc
index e73681b..ba6e43f 100644
--- a/net/spdy/fuzzing/hpack_example_generator.cc
+++ b/net/spdy/fuzzing/hpack_example_generator.cc
@@ -10,6 +10,7 @@
 #include "net/spdy/fuzzing/hpack_fuzz_util.h"
 #include "net/spdy/hpack/hpack_constants.h"
 #include "net/spdy/hpack/hpack_encoder.h"
+#include "net/spdy/platform/api/spdy_string.h"
 #include "net/spdy/spdy_protocol.h"
 
 namespace {
@@ -23,8 +24,8 @@
 }  // namespace
 
 using net::HpackFuzzUtil;
+using net::SpdyString;
 using std::map;
-using std::string;
 
 // Generates a configurable number of header sets (using HpackFuzzUtil), and
 // sequentially encodes each header set with an HpackEncoder. Encoded header
@@ -43,7 +44,7 @@
                << " --" << kExampleCount << "=1000";
     return -1;
   }
-  string file_to_write = command_line.GetSwitchValueASCII(kFileToWrite);
+  SpdyString file_to_write = command_line.GetSwitchValueASCII(kFileToWrite);
 
   int example_count = 0;
   base::StringToInt(command_line.GetSwitchValueASCII(kExampleCount),
@@ -62,10 +63,10 @@
     net::SpdyHeaderBlock headers =
         HpackFuzzUtil::NextGeneratedHeaderSet(&context);
 
-    string buffer;
+    SpdyString buffer;
     CHECK(encoder.EncodeHeaderSet(headers, &buffer));
 
-    string prefix = HpackFuzzUtil::HeaderBlockPrefix(buffer.size());
+    SpdyString prefix = HpackFuzzUtil::HeaderBlockPrefix(buffer.size());
 
     CHECK_LT(0, file_out.WriteAtCurrentPos(prefix.data(), prefix.size()));
     CHECK_LT(0, file_out.WriteAtCurrentPos(buffer.data(), buffer.size()));
diff --git a/net/spdy/fuzzing/hpack_fuzz_util.cc b/net/spdy/fuzzing/hpack_fuzz_util.cc
index 75b03a3..90b633e 100644
--- a/net/spdy/fuzzing/hpack_fuzz_util.cc
+++ b/net/spdy/fuzzing/hpack_fuzz_util.cc
@@ -33,7 +33,6 @@
 
 using base::RandBytesAsString;
 using std::map;
-using std::string;
 
 HpackFuzzUtil::GeneratorContext::GeneratorContext() {}
 HpackFuzzUtil::GeneratorContext::~GeneratorContext() {}
@@ -82,7 +81,7 @@
                                           kHeaderIndexMax);
     size_t value_index = SampleExponential(kHeaderIndexMean,
                                            kHeaderIndexMax);
-    string name, value;
+    SpdyString name, value;
     if (name_index >= context->names.size()) {
       context->names.push_back(
           RandBytesAsString(1 + SampleExponential(kNameLengthMean,
@@ -132,9 +131,9 @@
 }
 
 // static
-string HpackFuzzUtil::HeaderBlockPrefix(size_t block_size) {
+SpdyString HpackFuzzUtil::HeaderBlockPrefix(size_t block_size) {
   uint32_t length = base::HostToNet32(static_cast<uint32_t>(block_size));
-  return string(reinterpret_cast<char*>(&length), sizeof(uint32_t));
+  return SpdyString(reinterpret_cast<char*>(&length), sizeof(uint32_t));
 }
 
 // static
@@ -157,7 +156,7 @@
     return false;
   }
   // Second stage: Re-encode the decoded header block. This must succeed.
-  string second_stage_out;
+  SpdyString second_stage_out;
   CHECK(context->second_stage->EncodeHeaderSet(
       context->first_stage->decoded_block(), &second_stage_out));
 
diff --git a/net/spdy/fuzzing/hpack_fuzz_util.h b/net/spdy/fuzzing/hpack_fuzz_util.h
index 9f1a991..163b8b9 100644
--- a/net/spdy/fuzzing/hpack_fuzz_util.h
+++ b/net/spdy/fuzzing/hpack_fuzz_util.h
@@ -9,12 +9,12 @@
 #include <stdint.h>
 
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "net/base/net_export.h"
 #include "net/spdy/hpack/hpack_decoder.h"
 #include "net/spdy/hpack/hpack_encoder.h"
+#include "net/spdy/platform/api/spdy_string.h"
 #include "net/spdy/platform/api/spdy_string_piece.h"
 
 namespace net {
@@ -26,8 +26,8 @@
   struct NET_EXPORT_PRIVATE GeneratorContext {
     GeneratorContext();
     ~GeneratorContext();
-    std::vector<std::string> names;
-    std::vector<std::string> values;
+    std::vector<SpdyString> names;
+    std::vector<SpdyString> values;
   };
 
   // Initializes a GeneratorContext with a random seed and name/value fixtures.
@@ -40,7 +40,7 @@
   // upper-bounded by |sanity_bound|.
   static size_t SampleExponential(size_t mean, size_t sanity_bound);
 
-  // Holds an input string, and manages an offset into that string.
+  // Holds an input SpdyString, and manages an offset into that SpdyString.
   struct NET_EXPORT_PRIVATE Input {
     Input();  // Initializes |offset| to zero.
     ~Input();
@@ -52,7 +52,7 @@
       return input.data() + offset;
     }
 
-    std::string input;
+    SpdyString input;
     size_t offset;
   };
 
@@ -62,7 +62,7 @@
 
   // Returns the serialized header block length prefix for a block of
   // |block_size| bytes.
-  static std::string HeaderBlockPrefix(size_t block_size);
+  static SpdyString HeaderBlockPrefix(size_t block_size);
 
   // A FuzzerContext holds fuzzer input, as well as each of the decoder and
   // encoder stages which fuzzed header blocks are processed through.
diff --git a/net/spdy/fuzzing/hpack_fuzz_util_test.cc b/net/spdy/fuzzing/hpack_fuzz_util_test.cc
index d5f0677..bbc72dfd 100644
--- a/net/spdy/fuzzing/hpack_fuzz_util_test.cc
+++ b/net/spdy/fuzzing/hpack_fuzz_util_test.cc
@@ -18,7 +18,6 @@
 namespace test {
 
 using std::map;
-using std::string;
 using test::a2b_hex;
 
 TEST(HpackFuzzUtilTest, GeneratorContextInitialization) {
@@ -85,15 +84,16 @@
 }
 
 TEST(HpackFuzzUtilTest, SerializedHeaderBlockPrefixes) {
-  EXPECT_EQ(string("\x00\x00\x00\x00", 4), HpackFuzzUtil::HeaderBlockPrefix(0));
-  EXPECT_EQ(string("\x00\x00\x00\x05", 4), HpackFuzzUtil::HeaderBlockPrefix(5));
+  EXPECT_EQ(SpdyString("\x00\x00\x00\x00", 4),
+            HpackFuzzUtil::HeaderBlockPrefix(0));
+  EXPECT_EQ(SpdyString("\x00\x00\x00\x05", 4),
+            HpackFuzzUtil::HeaderBlockPrefix(5));
   EXPECT_EQ("\x4f\xb3\x0a\x91", HpackFuzzUtil::HeaderBlockPrefix(1337133713));
 }
 
 TEST(HpackFuzzUtilTest, PassValidInputThroughAllStages) {
   // Example lifted from HpackDecoderTest.SectionD4RequestHuffmanExamples.
-  string input = a2b_hex("828684418cf1e3c2e5f23a6ba0ab90f4"
-                         "ff");
+  SpdyString input = a2b_hex("828684418cf1e3c2e5f23a6ba0ab90f4ff");
 
   HpackFuzzUtil::FuzzerContext context;
   HpackFuzzUtil::InitializeFuzzerContext(&context);
@@ -135,7 +135,7 @@
 
 TEST(HpackFuzzUtilTest, FlipBitsMutatesBuffer) {
   char buffer[] = "testbuffer1234567890";
-  string unmodified(buffer, arraysize(buffer) - 1);
+  SpdyString unmodified(buffer, arraysize(buffer) - 1);
 
   EXPECT_EQ(unmodified, buffer);
   HpackFuzzUtil::FlipBits(reinterpret_cast<uint8_t*>(buffer),
diff --git a/net/spdy/header_coalescer.cc b/net/spdy/header_coalescer.cc
index 6048d2c..21589983 100644
--- a/net/spdy/header_coalescer.cc
+++ b/net/spdy/header_coalescer.cc
@@ -9,6 +9,7 @@
 #include "base/strings/string_util.h"
 #include "net/http/http_util.h"
 #include "net/spdy/platform/api/spdy_estimate_memory_usage.h"
+#include "net/spdy/platform/api/spdy_string.h"
 
 namespace net {
 
@@ -61,7 +62,7 @@
   } else {
     // This header had multiple values, so it must be reconstructed.
     SpdyStringPiece v = iter->second;
-    std::string s(v.data(), v.length());
+    SpdyString s(v.data(), v.length());
     if (key == "cookie") {
       // Obeys section 8.1.2.5 in RFC 7540 for cookie reconstruction.
       s.append("; ");
diff --git a/net/spdy/header_coalescer_test.cc b/net/spdy/header_coalescer_test.cc
index f0f696bc..a9587612 100644
--- a/net/spdy/header_coalescer_test.cc
+++ b/net/spdy/header_coalescer_test.cc
@@ -4,8 +4,7 @@
 
 #include "header_coalescer.h"
 
-#include <string>
-
+#include "net/spdy/platform/api/spdy_string.h"
 #include "net/spdy/platform/api/spdy_string_utils.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -41,7 +40,7 @@
 TEST_F(HeaderCoalescerTest, HeaderBlockTooLarge) {
   // 3 byte key, 256 * 1024 - 40 byte value, 32 byte overhead:
   // less than 256 * 1024 bytes in total.
-  std::string data(256 * 1024 - 40, 'a');
+  SpdyString data(256 * 1024 - 40, 'a');
   header_coalescer_.OnHeader("foo", data);
   EXPECT_FALSE(header_coalescer_.error_seen());
 
diff --git a/net/spdy/hpack/hpack_decoder.cc b/net/spdy/hpack/hpack_decoder.cc
index c06e5e1..611e2c5c 100644
--- a/net/spdy/hpack/hpack_decoder.cc
+++ b/net/spdy/hpack/hpack_decoder.cc
@@ -14,8 +14,6 @@
 
 namespace net {
 
-using std::string;
-
 HpackDecoder::HpackDecoder()
     : handler_(nullptr),
       total_header_bytes_(0),
@@ -271,7 +269,7 @@
                                            bool is_key,
                                            SpdyStringPiece* output) {
   if (input_stream->MatchPrefixAndConsume(kStringLiteralHuffmanEncoded)) {
-    string* buffer = is_key ? &key_buffer_ : &value_buffer_;
+    SpdyString* buffer = is_key ? &key_buffer_ : &value_buffer_;
     bool result = input_stream->DecodeNextHuffmanString(buffer);
     *output = SpdyStringPiece(*buffer);
     return result;
diff --git a/net/spdy/hpack/hpack_decoder.h b/net/spdy/hpack/hpack_decoder.h
index 8fa9e196..6df4d1a 100644
--- a/net/spdy/hpack/hpack_decoder.h
+++ b/net/spdy/hpack/hpack_decoder.h
@@ -10,7 +10,6 @@
 
 #include <map>
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "base/macros.h"
@@ -18,6 +17,7 @@
 #include "net/spdy/hpack/hpack_decoder_interface.h"
 #include "net/spdy/hpack/hpack_header_table.h"
 #include "net/spdy/hpack/hpack_input_stream.h"
+#include "net/spdy/platform/api/spdy_string.h"
 #include "net/spdy/platform/api/spdy_string_piece.h"
 #include "net/spdy/spdy_headers_handler_interface.h"
 #include "net/spdy/spdy_protocol.h"
@@ -116,11 +116,11 @@
   // TODO(jgraettinger): Buffer for headers data, and storage for the last-
   // processed headers block. Both will be removed with the switch to
   // SpdyHeadersHandlerInterface.
-  std::string headers_block_buffer_;
+  SpdyString headers_block_buffer_;
   SpdyHeaderBlock decoded_block_;
 
   // Scratch space for storing decoded literals.
-  std::string key_buffer_, value_buffer_;
+  SpdyString key_buffer_, value_buffer_;
 
   // If non-NULL, handles decoded headers.
   SpdyHeadersHandlerInterface* handler_;
diff --git a/net/spdy/hpack/hpack_decoder3_test.cc b/net/spdy/hpack/hpack_decoder3_test.cc
index 85752aa2..73279aa 100644
--- a/net/spdy/hpack/hpack_decoder3_test.cc
+++ b/net/spdy/hpack/hpack_decoder3_test.cc
@@ -8,7 +8,6 @@
 
 #include <stdint.h>
 
-#include <string>
 #include <tuple>
 #include <utility>
 #include <vector>
@@ -23,11 +22,11 @@
 #include "net/spdy/hpack/hpack_encoder.h"
 #include "net/spdy/hpack/hpack_huffman_table.h"
 #include "net/spdy/hpack/hpack_output_stream.h"
+#include "net/spdy/platform/api/spdy_string.h"
 #include "net/spdy/spdy_test_utils.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using std::string;
 using ::testing::ElementsAre;
 using ::testing::Pair;
 
@@ -174,8 +173,8 @@
 
   void expectEntry(size_t index,
                    size_t size,
-                   const string& name,
-                   const string& value) {
+                   const SpdyString& name,
+                   const SpdyString& value) {
     const HpackStringPair* entry = decoder_peer_.GetTableEntry(index);
     EXPECT_EQ(name, entry->name) << "index " << index;
     EXPECT_EQ(value, entry->value);
@@ -183,7 +182,7 @@
   }
 
   SpdyHeaderBlock MakeHeaderBlock(
-      const std::vector<std::pair<string, string>>& headers) {
+      const std::vector<std::pair<SpdyString, SpdyString>>& headers) {
     SpdyHeaderBlock result;
     for (const auto& kv : headers) {
       result.AppendValueOrAddHeader(kv.first, kv.second);
@@ -214,12 +213,12 @@
   // limit is rejected.
   HandleControlFrameHeadersStart();
   const size_t kMaxBufferSizeBytes = 50;
-  const string a_value = string(49, 'x');
+  const SpdyString a_value = SpdyString(49, 'x');
   decoder_.set_max_decode_buffer_size_bytes(kMaxBufferSizeBytes);
   HpackBlockBuilder hbb;
   hbb.AppendLiteralNameAndValue(HpackEntryType::kNeverIndexedLiteralHeader,
                                 false, "a", false, a_value);
-  const string& s = hbb.buffer();
+  const SpdyString& s = hbb.buffer();
   EXPECT_GT(s.size(), kMaxBufferSizeBytes);
 
   // Any one in input buffer must not exceed kMaxBufferSizeBytes.
@@ -234,8 +233,8 @@
 TEST_P(HpackDecoder3Test, NameTooLong) {
   // Verify that a name longer than the allowed size generates an error.
   const size_t kMaxBufferSizeBytes = 50;
-  const string name = string(2 * kMaxBufferSizeBytes, 'x');
-  const string value = "abc";
+  const SpdyString name = SpdyString(2 * kMaxBufferSizeBytes, 'x');
+  const SpdyString value = "abc";
 
   decoder_.set_max_decode_buffer_size_bytes(kMaxBufferSizeBytes);
 
@@ -244,7 +243,7 @@
                                 false, name, false, value);
 
   const size_t fragment_size = (3 * kMaxBufferSizeBytes) / 2;
-  const string fragment = hbb.buffer().substr(0, fragment_size);
+  const SpdyString fragment = hbb.buffer().substr(0, fragment_size);
 
   HandleControlFrameHeadersStart();
   EXPECT_FALSE(HandleControlFrameHeadersData(fragment));
@@ -253,8 +252,8 @@
 TEST_P(HpackDecoder3Test, HeaderTooLongToBuffer) {
   // Verify that a header longer than the allowed size generates an error if
   // it isn't all in one input buffer.
-  const string name = "some-key";
-  const string value = "some-value";
+  const SpdyString name = "some-key";
+  const SpdyString value = "some-value";
   const size_t kMaxBufferSizeBytes = name.size() + value.size() - 2;
   decoder_.set_max_decode_buffer_size_bytes(kMaxBufferSizeBytes);
 
@@ -262,7 +261,7 @@
   hbb.AppendLiteralNameAndValue(HpackEntryType::kNeverIndexedLiteralHeader,
                                 false, name, false, value);
   const size_t fragment_size = hbb.size() - 1;
-  const string fragment = hbb.buffer().substr(0, fragment_size);
+  const SpdyString fragment = hbb.buffer().substr(0, fragment_size);
 
   HandleControlFrameHeadersStart();
   EXPECT_FALSE(HandleControlFrameHeadersData(fragment));
@@ -274,7 +273,7 @@
 
   // No need to wait for more data.
   EXPECT_TRUE(HandleControlFrameHeadersData("\x82\x85\x82"));
-  std::vector<std::pair<string, string>> expected_headers = {
+  std::vector<std::pair<SpdyString, SpdyString>> expected_headers = {
       {":method", "GET"}, {":path", "/index.html"}, {":method", "GET"}};
 
   SpdyHeaderBlock expected_block1 = MakeHeaderBlock(expected_headers);
@@ -315,7 +314,7 @@
 
   // Already-delimited headers are passed through.
   decoder_peer_.HandleHeaderRepresentation("passed-through",
-                                           string("foo\0baz", 7));
+                                           SpdyString("foo\0baz", 7));
 
   // Other headers are joined on \0. Case matters.
   decoder_peer_.HandleHeaderRepresentation("joined", "not joined");
@@ -402,7 +401,7 @@
 TEST_P(HpackDecoder3Test, ContextUpdateMaximumSize) {
   EXPECT_EQ(kDefaultHeaderTableSizeSetting,
             decoder_peer_.header_table_size_limit());
-  string input;
+  SpdyString input;
   {
     // Maximum-size update with size 126. Succeeds.
     HpackOutputStream output_stream;
@@ -439,7 +438,7 @@
 
 // Two HeaderTableSizeUpdates may appear at the beginning of the block
 TEST_P(HpackDecoder3Test, TwoTableSizeUpdates) {
-  string input;
+  SpdyString input;
   {
     // Should accept two table size updates, update to second one
     HpackOutputStream output_stream;
@@ -456,7 +455,7 @@
 
 // Three HeaderTableSizeUpdates should result in an error
 TEST_P(HpackDecoder3Test, ThreeTableSizeUpdatesError) {
-  string input;
+  SpdyString input;
   {
     // Should reject three table size updates, update to second one
     HpackOutputStream output_stream;
@@ -477,7 +476,7 @@
 // HeaderTableSizeUpdates may only appear at the beginning of the block
 // Any other updates should result in an error
 TEST_P(HpackDecoder3Test, TableSizeUpdateSecondError) {
-  string input;
+  SpdyString input;
   {
     // Should reject a table size update appearing after a different entry
     // The table size should remain as the default
@@ -497,7 +496,7 @@
 // HeaderTableSizeUpdates may only appear at the beginning of the block
 // Any other updates should result in an error
 TEST_P(HpackDecoder3Test, TableSizeUpdateFirstThirdError) {
-  string input;
+  SpdyString input;
   {
     // Should reject the second table size update
     // if a different entry appears after the first update
@@ -588,7 +587,7 @@
   //                                         | www.example.com
   //                                         | -> :authority: www.example.com
 
-  string first = a2b_hex("418cf1e3c2e5f23a6ba0ab90f4ff");
+  SpdyString first = a2b_hex("418cf1e3c2e5f23a6ba0ab90f4ff");
   EXPECT_TRUE(DecodeHeaderBlock(first));
   first = a2b_hex("418cf1e3c2e5f23a6ba0ab90f4");
   EXPECT_FALSE(DecodeHeaderBlock(first));
@@ -608,7 +607,7 @@
   //                                         | www.example.com
   //                                         | -> :authority: www.example.com
 
-  string first = a2b_hex("418cf1e3c2e5f23a6ba0ab90f4ff");
+  SpdyString first = a2b_hex("418cf1e3c2e5f23a6ba0ab90f4ff");
   EXPECT_TRUE(DecodeHeaderBlock(first));
   first = a2b_hex("418df1e3c2e5f23a6ba0ab90f4ffff");
   EXPECT_FALSE(DecodeHeaderBlock(first));
@@ -625,7 +624,7 @@
   expected_header_set[":path"] = "/";
   expected_header_set[":authority"] = "www.example.com";
 
-  string encoded_header_set;
+  SpdyString encoded_header_set;
   EXPECT_TRUE(
       encoder.EncodeHeaderSet(expected_header_set, &encoded_header_set));
 
@@ -656,7 +655,7 @@
   //                                         |     Decoded:
   //                                         | www.example.com
   //                                         | -> :authority: www.example.com
-  string first = a2b_hex("828684418cf1e3c2e5f23a6ba0ab90f4ff");
+  SpdyString first = a2b_hex("828684418cf1e3c2e5f23a6ba0ab90f4ff");
   const SpdyHeaderBlock& first_header_set = DecodeBlockExpectingSuccess(first);
 
   EXPECT_THAT(first_header_set,
@@ -693,7 +692,7 @@
   //                                         | no-cache
   //                                         | -> cache-control: no-cache
 
-  string second = a2b_hex("828684be5886a8eb10649cbf");
+  SpdyString second = a2b_hex("828684be5886a8eb10649cbf");
   const SpdyHeaderBlock& second_header_set =
       DecodeBlockExpectingSuccess(second);
 
@@ -735,7 +734,8 @@
   //                                         |     Decoded:
   //                                         | custom-value
   //                                         | -> custom-key: custom-value
-  string third = a2b_hex("828785bf408825a849e95ba97d7f8925a849e95bb8e8b4bf");
+  SpdyString third =
+      a2b_hex("828785bf408825a849e95ba97d7f8925a849e95bb8e8b4bf");
   const SpdyHeaderBlock& third_header_set = DecodeBlockExpectingSuccess(third);
 
   EXPECT_THAT(
@@ -804,7 +804,7 @@
   //                                         | -> location: https://www.e
   //                                         |    xample.com
 
-  string first = a2b_hex(
+  SpdyString first = a2b_hex(
       "488264025885aec3771a4b6196d07abe"
       "941054d444a8200595040b8166e082a6"
       "2d1bff6e919d29ad171863c78f0b97c8"
@@ -847,7 +847,7 @@
   //                                         |   idx = 63
   //                                         | -> location:
   //                                         |   https://www.example.com
-  string second = a2b_hex("4883640effc1c0bf");
+  SpdyString second = a2b_hex("4883640effc1c0bf");
   const SpdyHeaderBlock& second_header_set =
       DecodeBlockExpectingSuccess(second);
 
@@ -919,7 +919,7 @@
   //                                         | -> set-cookie: foo=ASDJKHQ
   //                                         |   KBZXOQWEOPIUAXQWEOIU;
   //                                         |   max-age=3600; version=1
-  string third = a2b_hex(
+  SpdyString third = a2b_hex(
       "88c16196d07abe941054d444a8200595"
       "040b8166e084a62d1bffc05a839bd9ab"
       "77ad94e7821dd7f2e6c7b335dfdfcd5b"
@@ -1000,7 +1000,7 @@
 
   // SpdyHeaderBlock stores these 6 strings as '\0' separated values.
   // Make sure that is what happened.
-  string joined_values = expected_header_set[name].as_string();
+  SpdyString joined_values = expected_header_set[name].as_string();
   EXPECT_EQ(joined_values.size(),
             2 * value1.size() + 2 * value2.size() + 2 * value3.size() + 5);
 
diff --git a/net/spdy/hpack/hpack_decoder_test.cc b/net/spdy/hpack/hpack_decoder_test.cc
index dae54cf..70eb50c 100644
--- a/net/spdy/hpack/hpack_decoder_test.cc
+++ b/net/spdy/hpack/hpack_decoder_test.cc
@@ -5,7 +5,6 @@
 #include "net/spdy/hpack/hpack_decoder.h"
 
 #include <map>
-#include <string>
 
 #include "base/logging.h"
 #include "net/spdy/hpack/hpack_encoder.h"
@@ -20,8 +19,6 @@
 namespace net {
 namespace test {
 
-using std::string;
-
 class HpackDecoderPeer {
  public:
   explicit HpackDecoderPeer(HpackDecoder* decoder) : decoder_(decoder) {}
@@ -40,7 +37,7 @@
     return decoder_->DecodeNextStringLiteral(in, is_header_key, str);
   }
 
-  const string& headers_block_buffer() const {
+  const SpdyString& headers_block_buffer() const {
     return decoder_->headers_block_buffer_;
   }
 
@@ -50,7 +47,6 @@
 
 namespace {
 
-using std::string;
 using test::a2b_hex;
 
 using testing::ElementsAre;
@@ -93,8 +89,8 @@
 
   void expectEntry(size_t index,
                    size_t size,
-                   const string& name,
-                   const string& value) {
+                   const SpdyString& name,
+                   const SpdyString& value) {
     const HpackEntry* entry = decoder_peer_.header_table()->GetByIndex(index);
     EXPECT_EQ(name, entry->name()) << "index " << index;
     EXPECT_EQ(value, entry->value());
@@ -120,26 +116,26 @@
   decoder_.set_max_decode_buffer_size_bytes(kMaxBufferSizeBytes);
 
   // Strings under threshold are concatenated in the buffer.
-  string first_input;
+  SpdyString first_input;
   first_input.push_back(0x00);  // Literal name and value, unindexed
   first_input.push_back(0x7f);  // Name length = 127
   ASSERT_EQ(2u, first_input.size());
   EXPECT_TRUE(decoder_.HandleControlFrameHeadersData(first_input.data(),
                                                      first_input.size()));
   // Further 38 bytes to make 40 total buffered bytes.
-  string second_input = string(38, 'x');
+  SpdyString second_input = SpdyString(38, 'x');
   EXPECT_TRUE(decoder_.HandleControlFrameHeadersData(second_input.data(),
                                                      second_input.size()));
   // A string which would push the buffer over the threshold is refused.
   const int kThirdInputSize =
       kMaxBufferSizeBytes - (first_input.size() + second_input.size()) + 1;
-  string third_input = string(kThirdInputSize, 'y');
+  SpdyString third_input = SpdyString(kThirdInputSize, 'y');
   ASSERT_GT(first_input.size() + second_input.size() + third_input.size(),
             kMaxBufferSizeBytes);
   EXPECT_FALSE(decoder_.HandleControlFrameHeadersData(third_input.data(),
                                                       third_input.size()));
 
-  string expected(first_input);
+  SpdyString expected(first_input);
   expected.append(second_input);
   EXPECT_EQ(expected, decoder_peer_.headers_block_buffer());
 }
@@ -177,7 +173,7 @@
 
   // Already-delimited headers are passed through.
   decoder_peer_.HandleHeaderRepresentation("passed-through",
-                                           string("foo\0baz", 7));
+                                           SpdyString("foo\0baz", 7));
 
   // Other headers are joined on \0. Case matters.
   decoder_peer_.HandleHeaderRepresentation("joined", "not joined");
@@ -240,7 +236,7 @@
 }
 
 TEST_P(HpackDecoderTest, DecodeNextNameLiteralWithHuffmanEncoding) {
-  string input = a2b_hex("008825a849e95ba97d7f");
+  SpdyString input = a2b_hex("008825a849e95ba97d7f");
   HpackInputStream input_stream(input);
 
   SpdyStringPiece string_piece;
@@ -257,7 +253,7 @@
   // CHECK(huffman_table_.Initialize(kHpackHuffmanCode,
   //                                 arraysize(kHpackHuffmanCode)));
   // Put two copies of the same huffman encoding into input.
-  string input = a2b_hex("008825a849e95ba97d7f008825a849e95ba97d7f");
+  SpdyString input = a2b_hex("008825a849e95ba97d7f008825a849e95ba97d7f");
   input.resize(input.size() - 1);  // Remove the last byte.
   HpackInputStream input_stream(input);
 
@@ -352,7 +348,7 @@
 TEST_P(HpackDecoderTest, ContextUpdateMaximumSize) {
   EXPECT_EQ(kDefaultHeaderTableSizeSetting,
             decoder_peer_.header_table()->max_size());
-  string input;
+  SpdyString input;
   {
     // Maximum-size update with size 126. Succeeds.
     HpackOutputStream output_stream;
@@ -389,7 +385,7 @@
 
 // Two HeaderTableSizeUpdates may appear at the beginning of the block
 TEST_P(HpackDecoderTest, TwoTableSizeUpdates) {
-  string input;
+  SpdyString input;
   {
     // Should accept two table size updates, update to second one
     HpackOutputStream output_stream;
@@ -406,7 +402,7 @@
 
 // Three HeaderTableSizeUpdates should result in an error
 TEST_P(HpackDecoderTest, ThreeTableSizeUpdatesError) {
-  string input;
+  SpdyString input;
   {
     // Should reject three table size updates, update to second one
     HpackOutputStream output_stream;
@@ -427,7 +423,7 @@
 // HeaderTableSizeUpdates may only appear at the beginning of the block
 // Any other updates should result in an error
 TEST_P(HpackDecoderTest, TableSizeUpdateSecondError) {
-  string input;
+  SpdyString input;
   {
     // Should reject a table size update appearing after a different entry
     // The table size should remain as the default
@@ -447,7 +443,7 @@
 // HeaderTableSizeUpdates may only appear at the beginning of the block
 // Any other updates should result in an error
 TEST_P(HpackDecoderTest, TableSizeUpdateFirstThirdError) {
-  string input;
+  SpdyString input;
   {
     // Should reject the second table size update
     // if a different entry appears after the first update
@@ -552,7 +548,7 @@
   expected_header_set[":path"] = "/";
   expected_header_set[":authority"] = "www.example.com";
 
-  string encoded_header_set;
+  SpdyString encoded_header_set;
   EXPECT_TRUE(
       encoder.EncodeHeaderSet(expected_header_set, &encoded_header_set));
 
@@ -581,7 +577,7 @@
   //                                         |     Decoded:
   //                                         | www.example.com
   //                                         | -> :authority: www.example.com
-  string first = a2b_hex("828684418cf1e3c2e5f23a6ba0ab90f4ff");
+  SpdyString first = a2b_hex("828684418cf1e3c2e5f23a6ba0ab90f4ff");
   const SpdyHeaderBlock& first_header_set = DecodeBlockExpectingSuccess(first);
 
   EXPECT_THAT(
@@ -614,7 +610,7 @@
   //                                         | no-cache
   //                                         | -> cache-control: no-cache
 
-  string second = a2b_hex("828684be5886a8eb10649cbf");
+  SpdyString second = a2b_hex("828684be5886a8eb10649cbf");
   const SpdyHeaderBlock& second_header_set =
       DecodeBlockExpectingSuccess(second);
 
@@ -652,7 +648,7 @@
   //                                         |     Decoded:
   //                                         | custom-value
   //                                         | -> custom-key: custom-value
-  string third = a2b_hex(
+  SpdyString third = a2b_hex(
       "828785bf408825a849e95ba97d7f89"
       "25a849e95bb8e8b4bf");
   const SpdyHeaderBlock& third_header_set = DecodeBlockExpectingSuccess(third);
@@ -716,7 +712,7 @@
   //                                         | -> location: https://www.e
   //                                         |    xample.com
 
-  string first = a2b_hex(
+  SpdyString first = a2b_hex(
       "488264025885aec3771a4b6196d07abe"
       "941054d444a8200595040b8166e082a6"
       "2d1bff6e919d29ad171863c78f0b97c8"
@@ -756,7 +752,7 @@
   //                                         |   idx = 63
   //                                         | -> location:
   //                                         |   https://www.example.com
-  string second = a2b_hex("4883640effc1c0bf");
+  SpdyString second = a2b_hex("4883640effc1c0bf");
   const SpdyHeaderBlock& second_header_set =
       DecodeBlockExpectingSuccess(second);
 
@@ -825,7 +821,7 @@
   //                                         | -> set-cookie: foo=ASDJKHQ
   //                                         |   KBZXOQWEOPIUAXQWEOIU;
   //                                         |   max-age=3600; version=1
-  string third = a2b_hex(
+  SpdyString third = a2b_hex(
       "88c16196d07abe941054d444a8200595"
       "040b8166e084a62d1bffc05a839bd9ab"
       "77ad94e7821dd7f2e6c7b335dfdfcd5b"
diff --git a/net/spdy/hpack/hpack_encoder.cc b/net/spdy/hpack/hpack_encoder.cc
index a9d9df6..48129d1 100644
--- a/net/spdy/hpack/hpack_encoder.cc
+++ b/net/spdy/hpack/hpack_encoder.cc
@@ -17,8 +17,6 @@
 
 namespace net {
 
-using std::string;
-
 class HpackEncoder::RepresentationIterator {
  public:
   // |pseudo_headers| and |regular_headers| must outlive the iterator.
@@ -88,13 +86,13 @@
 HpackEncoder::~HpackEncoder() {}
 
 void HpackEncoder::EncodeHeaderSet(const Representations& representations,
-                                   string* output) {
+                                   SpdyString* output) {
   RepresentationIterator iter(representations);
   EncodeRepresentations(&iter, output);
 }
 
 bool HpackEncoder::EncodeHeaderSet(const SpdyHeaderBlock& header_set,
-                                   string* output) {
+                                   SpdyString* output) {
   // Separate header set into pseudo-headers and regular headers.
   Representations pseudo_headers;
   Representations regular_headers;
@@ -139,7 +137,7 @@
 }
 
 void HpackEncoder::EncodeRepresentations(RepresentationIterator* iter,
-                                         string* output) {
+                                         SpdyString* output) {
   MaybeEmitTableSize();
   while (iter->HasNext()) {
     const auto header = iter->Next();
@@ -298,7 +296,7 @@
 
   // Encodes up to max_encoded_bytes of the current header block into the
   // given output string.
-  void Next(size_t max_encoded_bytes, string* output) override;
+  void Next(size_t max_encoded_bytes, SpdyString* output) override;
 
  private:
   HpackEncoder* encoder_;
@@ -335,7 +333,8 @@
   encoder_->MaybeEmitTableSize();
 }
 
-void HpackEncoder::Encoderator::Next(size_t max_encoded_bytes, string* output) {
+void HpackEncoder::Encoderator::Next(size_t max_encoded_bytes,
+                                     SpdyString* output) {
   SPDY_BUG_IF(!has_next_)
       << "Encoderator::Next called with nothing left to encode.";
   const bool use_compression = encoder_->enable_compression_;
diff --git a/net/spdy/hpack/hpack_encoder.h b/net/spdy/hpack/hpack_encoder.h
index 2312c23..f9f93208 100644
--- a/net/spdy/hpack/hpack_encoder.h
+++ b/net/spdy/hpack/hpack_encoder.h
@@ -10,7 +10,6 @@
 #include <functional>
 #include <map>
 #include <memory>
-#include <string>
 #include <utility>
 #include <vector>
 
@@ -18,6 +17,7 @@
 #include "net/base/net_export.h"
 #include "net/spdy/hpack/hpack_header_table.h"
 #include "net/spdy/hpack/hpack_output_stream.h"
+#include "net/spdy/platform/api/spdy_string.h"
 #include "net/spdy/platform/api/spdy_string_piece.h"
 #include "net/spdy/spdy_protocol.h"
 
@@ -52,11 +52,11 @@
 
   // Encodes a sequence of Representations into the given string.
   void EncodeHeaderSet(const Representations& representations,
-                       std::string* output);
+                       SpdyString* output);
 
   // Encodes the given header set into the given string. Returns
   // whether or not the encoding was successful.
-  bool EncodeHeaderSet(const SpdyHeaderBlock& header_set, std::string* output);
+  bool EncodeHeaderSet(const SpdyHeaderBlock& header_set, SpdyString* output);
 
   class NET_EXPORT_PRIVATE ProgressiveEncoder {
    public:
@@ -67,7 +67,7 @@
 
     // Encodes up to max_encoded_bytes of the current header block into the
     // given output string.
-    virtual void Next(size_t max_encoded_bytes, std::string* output) = 0;
+    virtual void Next(size_t max_encoded_bytes, SpdyString* output) = 0;
   };
 
   // Returns a ProgressiveEncoder which must be outlived by both the given
@@ -109,7 +109,7 @@
   class Encoderator;
 
   // Encodes a sequence of header name-value pairs as a single header block.
-  void EncodeRepresentations(RepresentationIterator* iter, std::string* output);
+  void EncodeRepresentations(RepresentationIterator* iter, SpdyString* output);
 
   // Emits a static/dynamic indexed representation (Section 7.1).
   void EmitIndex(const HpackEntry* entry);
diff --git a/net/spdy/hpack/hpack_encoder_test.cc b/net/spdy/hpack/hpack_encoder_test.cc
index 705965b..7623f0a 100644
--- a/net/spdy/hpack/hpack_encoder_test.cc
+++ b/net/spdy/hpack/hpack_encoder_test.cc
@@ -5,7 +5,6 @@
 #include "net/spdy/hpack/hpack_encoder.h"
 
 #include <map>
-#include <string>
 
 #include "base/rand_util.h"
 #include "net/base/arena.h"
@@ -15,7 +14,6 @@
 
 namespace net {
 
-using std::string;
 using testing::ElementsAre;
 
 namespace test {
@@ -46,7 +44,7 @@
     return encoder_->huffman_table_;
   }
   void EmitString(SpdyStringPiece str) { encoder_->EmitString(str); }
-  void TakeString(string* out) { encoder_->output_stream_.TakeString(out); }
+  void TakeString(SpdyString* out) { encoder_->output_stream_.TakeString(out); }
   static void CookieToCrumbs(SpdyStringPiece cookie,
                              std::vector<SpdyStringPiece>* out) {
     Representations tmp;
@@ -73,7 +71,7 @@
   // non-incremental encoding path.
   static bool EncodeHeaderSet(HpackEncoder* encoder,
                               const SpdyHeaderBlock& header_set,
-                              string* output,
+                              SpdyString* output,
                               bool use_incremental) {
     if (use_incremental) {
       return EncodeIncremental(encoder, header_set, output);
@@ -84,13 +82,13 @@
 
   static bool EncodeIncremental(HpackEncoder* encoder,
                                 const SpdyHeaderBlock& header_set,
-                                string* output) {
+                                SpdyString* output) {
     std::unique_ptr<HpackEncoder::ProgressiveEncoder> encoderator =
         encoder->EncodeHeaderSet(header_set);
-    string output_buffer;
+    SpdyString output_buffer;
     encoderator->Next(base::RandInt(0, 15), &output_buffer);
     while (encoderator->HasNext()) {
-      string second_buffer;
+      SpdyString second_buffer;
       encoderator->Next(base::RandInt(0, 15), &second_buffer);
       output_buffer.append(second_buffer);
     }
@@ -184,7 +182,7 @@
     expected_.AppendUint32(size);
   }
   void CompareWithExpectedEncoding(const SpdyHeaderBlock& header_set) {
-    string expected_out, actual_out;
+    SpdyString expected_out, actual_out;
     expected_.TakeString(&expected_out);
     EXPECT_TRUE(test::HpackEncoderPeer::EncodeHeaderSet(
         &encoder_, header_set, &actual_out, use_incremental_));
@@ -320,7 +318,7 @@
   expected_.AppendUint32(6);
   expected_.AppendBytes("@@@@@@");
 
-  string expected_out, actual_out;
+  SpdyString expected_out, actual_out;
   expected_.TakeString(&expected_out);
   peer_.TakeString(&actual_out);
   EXPECT_EQ(expected_out, actual_out);
@@ -509,7 +507,7 @@
 TEST_P(HpackEncoderTest, CrumbleNullByteDelimitedValue) {
   SpdyHeaderBlock headers;
   // A header field to be crumbled: "spam: foo\0bar".
-  headers["spam"] = string("foo\0bar", 7);
+  headers["spam"] = SpdyString("foo\0bar", 7);
 
   ExpectIndexedLiteral("spam", "foo");
   expected_.AppendPrefix(kLiteralIncrementalIndexOpcode);
diff --git a/net/spdy/hpack/hpack_entry.cc b/net/spdy/hpack/hpack_entry.cc
index 4b6d500..582d55d2 100644
--- a/net/spdy/hpack/hpack_entry.cc
+++ b/net/spdy/hpack/hpack_entry.cc
@@ -74,9 +74,9 @@
   return Size(name(), value());
 }
 
-std::string HpackEntry::GetDebugString() const {
-  return "{ name: \"" + std::string(name_ref_) + "\", value: \"" +
-         std::string(value_ref_) +
+SpdyString HpackEntry::GetDebugString() const {
+  return "{ name: \"" + SpdyString(name_ref_) + "\", value: \"" +
+         SpdyString(value_ref_) +
          "\", index: " + base::SizeTToString(insertion_index_) +
          (IsStatic() ? " static" : (IsLookup() ? " lookup" : " dynamic")) +
          " }";
diff --git a/net/spdy/hpack/hpack_entry.h b/net/spdy/hpack/hpack_entry.h
index 8e7993a..0d4287f 100644
--- a/net/spdy/hpack/hpack_entry.h
+++ b/net/spdy/hpack/hpack_entry.h
@@ -7,10 +7,9 @@
 
 #include <stddef.h>
 
-#include <string>
-
 #include "base/macros.h"
 #include "net/base/net_export.h"
+#include "net/spdy/platform/api/spdy_string.h"
 #include "net/spdy/platform/api/spdy_string_piece.h"
 
 // All section references below are to
@@ -71,7 +70,7 @@
   static size_t Size(SpdyStringPiece name, SpdyStringPiece value);
   size_t Size() const;
 
-  std::string GetDebugString() const;
+  SpdyString GetDebugString() const;
 
   int64_t time_added() const { return time_added_; }
   void set_time_added(int64_t now) { time_added_ = now; }
@@ -87,8 +86,8 @@
   };
 
   // These members are not used for LOOKUP entries.
-  std::string name_;
-  std::string value_;
+  SpdyString name_;
+  SpdyString value_;
 
   // These members are always valid. For DYNAMIC and STATIC entries, they
   // always point to |name_| and |value_|.
diff --git a/net/spdy/hpack/hpack_entry_test.cc b/net/spdy/hpack/hpack_entry_test.cc
index eb7de1c3..eda78d0 100644
--- a/net/spdy/hpack/hpack_entry_test.cc
+++ b/net/spdy/hpack/hpack_entry_test.cc
@@ -4,16 +4,12 @@
 
 #include "net/spdy/hpack/hpack_entry.h"
 
-#include <string>
-
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace net {
 
 namespace {
 
-using std::string;
-
 class HpackEntryTest : public ::testing::Test {
  protected:
   HpackEntryTest()
@@ -46,7 +42,7 @@
     return name_.size() + value_.size() + HpackEntry::kSizeOverhead;
   }
 
-  string name_, value_;
+  SpdyString name_, value_;
 
  private:
   // Referenced by HpackEntry instances.
diff --git a/net/spdy/hpack/hpack_header_table_test.cc b/net/spdy/hpack/hpack_header_table_test.cc
index e5fb694..f46968e 100644
--- a/net/spdy/hpack/hpack_header_table_test.cc
+++ b/net/spdy/hpack/hpack_header_table_test.cc
@@ -6,19 +6,18 @@
 
 #include <algorithm>
 #include <set>
-#include <string>
 #include <vector>
 
 #include "base/macros.h"
 #include "net/spdy/hpack/hpack_constants.h"
 #include "net/spdy/hpack/hpack_entry.h"
+#include "net/spdy/platform/api/spdy_string.h"
 #include "net/spdy/spdy_flags.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace net {
 
 using std::distance;
-using std::string;
 
 namespace test {
 
@@ -77,8 +76,8 @@
   // Returns an entry whose Size() is equal to the given one.
   static HpackEntry MakeEntryOfSize(uint32_t size) {
     EXPECT_GE(size, HpackEntry::kSizeOverhead);
-    string name((size - HpackEntry::kSizeOverhead) / 2, 'n');
-    string value(size - HpackEntry::kSizeOverhead - name.size(), 'v');
+    SpdyString name((size - HpackEntry::kSizeOverhead) / 2, 'n');
+    SpdyString value(size - HpackEntry::kSizeOverhead - name.size(), 'v');
     HpackEntry entry(name, value, false, 0);
     EXPECT_EQ(size, entry.Size());
     return entry;
@@ -124,7 +123,7 @@
     }
   }
 
-  HpackEntry DynamicEntry(const string& name, const string& value) {
+  HpackEntry DynamicEntry(const SpdyString& name, const SpdyString& value) {
     peer_.AddDynamicEntry(name, value);
     return peer_.dynamic_entries().back();
   }
@@ -259,7 +258,7 @@
 }
 
 TEST_F(HpackHeaderTableTest, SetSizes) {
-  string key = "key", value = "value";
+  SpdyString key = "key", value = "value";
   const HpackEntry* entry1 = table_.TryAddEntry(key, value);
   const HpackEntry* entry2 = table_.TryAddEntry(key, value);
   const HpackEntry* entry3 = table_.TryAddEntry(key, value);
@@ -291,7 +290,7 @@
 }
 
 TEST_F(HpackHeaderTableTest, EvictionCountForEntry) {
-  string key = "key", value = "value";
+  SpdyString key = "key", value = "value";
   const HpackEntry* entry1 = table_.TryAddEntry(key, value);
   const HpackEntry* entry2 = table_.TryAddEntry(key, value);
   size_t entry3_size = HpackEntry::Size(key, value);
@@ -308,7 +307,7 @@
 }
 
 TEST_F(HpackHeaderTableTest, EvictionCountToReclaim) {
-  string key = "key", value = "value";
+  SpdyString key = "key", value = "value";
   const HpackEntry* entry1 = table_.TryAddEntry(key, value);
   const HpackEntry* entry2 = table_.TryAddEntry(key, value);
 
diff --git a/net/spdy/hpack/hpack_huffman_decoder.cc b/net/spdy/hpack/hpack_huffman_decoder.cc
index d933067..49f9dae 100644
--- a/net/spdy/hpack/hpack_huffman_decoder.cc
+++ b/net/spdy/hpack/hpack_huffman_decoder.cc
@@ -303,7 +303,7 @@
 // long strings, and a later portion dealing with the last few bytes of strings.
 // TODO(jamessynge): Determine if that is worth it by adding some counters to
 // measure the distribution of string sizes seen in practice.
-bool HpackHuffmanDecoder::DecodeString(HpackInputStream* in, std::string* out) {
+bool HpackHuffmanDecoder::DecodeString(HpackInputStream* in, SpdyString* out) {
   out->clear();
 
   // Load |bits| with the leading bits of the input stream, left justified
diff --git a/net/spdy/hpack/hpack_huffman_decoder.h b/net/spdy/hpack/hpack_huffman_decoder.h
index 50c3dc4..8027014 100644
--- a/net/spdy/hpack/hpack_huffman_decoder.h
+++ b/net/spdy/hpack/hpack_huffman_decoder.h
@@ -8,10 +8,9 @@
 #include <stddef.h>
 #include <stdint.h>
 
-#include <string>
-
 #include "net/base/net_export.h"
 #include "net/spdy/hpack/hpack_input_stream.h"
+#include "net/spdy/platform/api/spdy_string.h"
 
 namespace net {
 namespace test {
@@ -35,7 +34,7 @@
   // DecodeString() halts when |in| runs out of input, in which case true is
   // returned. It also halts (returning false) if an invalid Huffman code
   // prefix is read.
-  static bool DecodeString(HpackInputStream* in, std::string* out);
+  static bool DecodeString(HpackInputStream* in, SpdyString* out);
 
  private:
   friend class test::HpackHuffmanDecoderPeer;
diff --git a/net/spdy/hpack/hpack_huffman_decoder_test.cc b/net/spdy/hpack/hpack_huffman_decoder_test.cc
index 7133c38..6b86b5fb 100644
--- a/net/spdy/hpack/hpack_huffman_decoder_test.cc
+++ b/net/spdy/hpack/hpack_huffman_decoder_test.cc
@@ -72,7 +72,7 @@
         HpackHuffmanDecoderPeer::CanonicalToSource(canonical));
   }
 
-  void EncodeString(SpdyStringPiece input, std::string* encoded) {
+  void EncodeString(SpdyStringPiece input, SpdyString* encoded) {
     HpackOutputStream output_stream;
     table_.EncodeString(input, &output_stream);
     encoded->clear();
@@ -81,8 +81,8 @@
     EXPECT_EQ(encoded->size(), table_.EncodedSize(input));
   }
 
-  std::string EncodeString(SpdyStringPiece input) {
-    std::string result;
+  SpdyString EncodeString(SpdyStringPiece input) {
+    SpdyString result;
     EncodeString(input, &result);
     return result;
   }
@@ -162,8 +162,8 @@
 }
 
 TEST_F(HpackHuffmanDecoderTest, SpecRequestExamples) {
-  std::string buffer;
-  std::string test_table[] = {
+  SpdyString buffer;
+  SpdyString test_table[] = {
       a2b_hex("f1e3c2e5f23a6ba0ab90f4ff"),
       "www.example.com",
       a2b_hex("a8eb10649cbf"),
@@ -175,8 +175,8 @@
   };
   // Round-trip each test example.
   for (size_t i = 0; i != arraysize(test_table); i += 2) {
-    const std::string& encodedFixture(test_table[i]);
-    const std::string& decodedFixture(test_table[i + 1]);
+    const SpdyString& encodedFixture(test_table[i]);
+    const SpdyString& decodedFixture(test_table[i + 1]);
     HpackInputStream input_stream(encodedFixture);
     EXPECT_TRUE(HpackHuffmanDecoder::DecodeString(&input_stream, &buffer));
     EXPECT_EQ(decodedFixture, buffer);
@@ -186,9 +186,9 @@
 }
 
 TEST_F(HpackHuffmanDecoderTest, SpecResponseExamples) {
-  std::string buffer;
+  SpdyString buffer;
   // clang-format off
-  std::string test_table[] = {
+  SpdyString test_table[] = {
     a2b_hex("6402"),
     "302",
     a2b_hex("aec3771a4b"),
@@ -207,8 +207,8 @@
   // clang-format on
   // Round-trip each test example.
   for (size_t i = 0; i != arraysize(test_table); i += 2) {
-    const std::string& encodedFixture(test_table[i]);
-    const std::string& decodedFixture(test_table[i + 1]);
+    const SpdyString& encodedFixture(test_table[i]);
+    const SpdyString& decodedFixture(test_table[i + 1]);
     HpackInputStream input_stream(encodedFixture);
     EXPECT_TRUE(HpackHuffmanDecoder::DecodeString(&input_stream, &buffer));
     EXPECT_EQ(decodedFixture, buffer);
@@ -222,8 +222,8 @@
     char c = static_cast<char>(i);
     char storage[3] = {c, c, c};
     SpdyStringPiece input(storage, arraysize(storage));
-    std::string buffer_in = EncodeString(input);
-    std::string buffer_out;
+    SpdyString buffer_in = EncodeString(input);
+    SpdyString buffer_out;
     HpackInputStream input_stream(buffer_in);
     EXPECT_TRUE(HpackHuffmanDecoder::DecodeString(&input_stream, &buffer_out));
     EXPECT_EQ(input, buffer_out);
@@ -233,9 +233,9 @@
 // Creates 256 input strings, each with a unique byte value i used to sandwich
 // all the other higher byte values.
 TEST_F(HpackHuffmanDecoderTest, RoundTripSymbolSequences) {
-  std::string input;
-  std::string encoded;
-  std::string decoded;
+  SpdyString input;
+  SpdyString encoded;
+  SpdyString decoded;
   for (size_t i = 0; i != 256; i++) {
     input.clear();
     auto ic = static_cast<char>(i);
diff --git a/net/spdy/hpack/hpack_huffman_table.cc b/net/spdy/hpack/hpack_huffman_table.cc
index 2a25c45..a398d263 100644
--- a/net/spdy/hpack/hpack_huffman_table.cc
+++ b/net/spdy/hpack/hpack_huffman_table.cc
@@ -16,8 +16,6 @@
 
 namespace net {
 
-using std::string;
-
 namespace {
 
 // How many bits to index in the root decode table.
@@ -266,7 +264,7 @@
 }
 
 bool HpackHuffmanTable::GenericDecodeString(HpackInputStream* in,
-                                            string* out) const {
+                                            SpdyString* out) const {
   // Number of decode iterations required for a 32-bit code.
   const int kDecodeIterations = static_cast<int>(
       std::ceil((32.f - kDecodeTableRootBits) / kDecodeTableBranchBits));
diff --git a/net/spdy/hpack/hpack_huffman_table.h b/net/spdy/hpack/hpack_huffman_table.h
index c93dcfbd..b2937a18 100644
--- a/net/spdy/hpack/hpack_huffman_table.h
+++ b/net/spdy/hpack/hpack_huffman_table.h
@@ -8,11 +8,11 @@
 #include <stdint.h>
 
 #include <cstddef>
-#include <string>
 #include <vector>
 
 #include "net/base/net_export.h"
 #include "net/spdy/hpack/hpack_constants.h"
+#include "net/spdy/platform/api/spdy_string.h"
 #include "net/spdy/platform/api/spdy_string_piece.h"
 
 namespace net {
@@ -91,8 +91,7 @@
   // otherwise be overflowed.
   // DEPRECATED: HpackHuffmanDecoder is now used for decoding strings encoded
   // according to the Huffman Table in the HPACK spec.
-  bool GenericDecodeString(HpackInputStream* in,
-                           std::string* out) const;
+  bool GenericDecodeString(HpackInputStream* in, SpdyString* out) const;
 
   // Returns the estimate of dynamically allocated memory in bytes.
   size_t EstimateMemoryUsage() const;
diff --git a/net/spdy/hpack/hpack_huffman_table_test.cc b/net/spdy/hpack/hpack_huffman_table_test.cc
index ddbc8e0..3becdbb 100644
--- a/net/spdy/hpack/hpack_huffman_table_test.cc
+++ b/net/spdy/hpack/hpack_huffman_table_test.cc
@@ -7,7 +7,6 @@
 #include <stdint.h>
 
 #include <bitset>
-#include <string>
 #include <utility>
 
 #include "base/logging.h"
@@ -20,7 +19,6 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using std::string;
 using testing::ElementsAreArray;
 using testing::Pointwise;
 
@@ -66,8 +64,8 @@
  protected:
   GenericHuffmanTableTest() : table_(), peer_(table_) {}
 
-  string EncodeString(SpdyStringPiece input) {
-    string result;
+  SpdyString EncodeString(SpdyStringPiece input) {
+    SpdyString result;
     HpackOutputStream output_stream;
     table_.EncodeString(input, &output_stream);
 
@@ -88,10 +86,10 @@
          lhs.length == rhs.length && lhs.symbol_id == rhs.symbol_id;
 }
 
-uint32_t bits32(const string& bitstring) {
+uint32_t bits32(const SpdyString& bitstring) {
   return std::bitset<32>(bitstring).to_ulong();
 }
-char bits8(const string& bitstring) {
+char bits8(const SpdyString& bitstring) {
   return static_cast<char>(std::bitset<8>(bitstring).to_ulong());
 }
 
@@ -234,10 +232,10 @@
                            bits8("01001100")};
   SpdyStringPiece expect(expect_storage, arraysize(expect_storage));
 
-  string buffer_in = EncodeString(input);
+  SpdyString buffer_in = EncodeString(input);
   EXPECT_EQ(expect, buffer_in);
 
-  string buffer_out;
+  SpdyString buffer_out;
   HpackInputStream input_stream(buffer_in);
   EXPECT_TRUE(table_.GenericDecodeString(&input_stream, &buffer_out));
   EXPECT_EQ(buffer_out, input);
@@ -296,7 +294,7 @@
       {bits32("10011100000000000000000000000000"), 16, 8}};
   EXPECT_TRUE(table_.Initialize(code, arraysize(code)));
 
-  string buffer;
+  SpdyString buffer;
   {
     // This example works: (2) 00 (3) 010 (2) 00 (6) 100110 (pad) 100.
     char input_storage[] = {bits8("00010001"), bits8("00110100")};
@@ -338,7 +336,7 @@
     EXPECT_TRUE(table_.IsInitialized());
   }
 
-  void DecodeStringTwice(const string& encoded, string* out) {
+  void DecodeStringTwice(const SpdyString& encoded, SpdyString* out) {
     // First decode with HpackHuffmanTable.
     {
       HpackInputStream input_stream(encoded);
@@ -348,7 +346,7 @@
     // the same.
     {
       HpackInputStream input_stream(encoded);
-      string buf;
+      SpdyString buf;
       EXPECT_TRUE(HpackHuffmanDecoder::DecodeString(&input_stream, &buf));
       EXPECT_EQ(*out, buf);
     }
@@ -360,8 +358,8 @@
 }
 
 TEST_F(HpackHuffmanTableTest, SpecRequestExamples) {
-  string buffer;
-  string test_table[] = {
+  SpdyString buffer;
+  SpdyString test_table[] = {
       a2b_hex("f1e3c2e5f23a6ba0ab90f4ff"),
       "www.example.com",
       a2b_hex("a8eb10649cbf"),
@@ -373,8 +371,8 @@
   };
   // Round-trip each test example.
   for (size_t i = 0; i != arraysize(test_table); i += 2) {
-    const string& encodedFixture(test_table[i]);
-    const string& decodedFixture(test_table[i + 1]);
+    const SpdyString& encodedFixture(test_table[i]);
+    const SpdyString& decodedFixture(test_table[i + 1]);
     DecodeStringTwice(encodedFixture, &buffer);
     EXPECT_EQ(decodedFixture, buffer);
     buffer = EncodeString(decodedFixture);
@@ -383,23 +381,27 @@
 }
 
 TEST_F(HpackHuffmanTableTest, SpecResponseExamples) {
-  string buffer;
-  string test_table[] = {
-      a2b_hex("6402"), "302", a2b_hex("aec3771a4b"), "private",
+  SpdyString buffer;
+  SpdyString 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"),
+      "https://www.example.com",
+      a2b_hex("94e7821dd7f2e6c7b335dfdfcd5b3960"
+              "d5af27087f3672c1ab270fb5291f9587"
+              "316065c003ed4ee5b1063d5007"),
       "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1",
   };
   // Round-trip each test example.
   for (size_t i = 0; i != arraysize(test_table); i += 2) {
-    const string& encodedFixture(test_table[i]);
-    const string& decodedFixture(test_table[i + 1]);
+    const SpdyString& encodedFixture(test_table[i]);
+    const SpdyString& decodedFixture(test_table[i + 1]);
     DecodeStringTwice(encodedFixture, &buffer);
     EXPECT_EQ(decodedFixture, buffer);
     buffer = EncodeString(decodedFixture);
@@ -412,8 +414,8 @@
     char c = static_cast<char>(i);
     char storage[3] = {c, c, c};
     SpdyStringPiece input(storage, arraysize(storage));
-    string buffer_in = EncodeString(input);
-    string buffer_out;
+    SpdyString buffer_in = EncodeString(input);
+    SpdyString buffer_out;
     DecodeStringTwice(buffer_in, &buffer_out);
     EXPECT_EQ(input, buffer_out);
   }
@@ -427,21 +429,21 @@
   }
   SpdyStringPiece input(storage, arraysize(storage));
 
-  string buffer_in = EncodeString(input);
-  string buffer_out;
+  SpdyString buffer_in = EncodeString(input);
+  SpdyString buffer_out;
   DecodeStringTwice(buffer_in, &buffer_out);
   EXPECT_EQ(input, buffer_out);
 }
 
 TEST_F(HpackHuffmanTableTest, EncodedSizeAgreesWithEncodeString) {
-  string test_table[] = {
+  SpdyString test_table[] = {
       "",
       "Mon, 21 Oct 2013 20:13:21 GMT",
       "https://www.example.com",
       "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1",
-      string(1, '\0'),
-      string("foo\0bar", 7),
-      string(256, '\0'),
+      SpdyString(1, '\0'),
+      SpdyString("foo\0bar", 7),
+      SpdyString(256, '\0'),
   };
   for (size_t i = 0; i != 256; ++i) {
     // Expand last |test_table| entry to cover all codes.
@@ -449,7 +451,7 @@
   }
 
   HpackOutputStream output_stream;
-  string encoding;
+  SpdyString encoding;
   for (size_t i = 0; i != arraysize(test_table); ++i) {
     table_.EncodeString(test_table[i], &output_stream);
     output_stream.TakeString(&encoding);
diff --git a/net/spdy/hpack/hpack_input_stream.cc b/net/spdy/hpack/hpack_input_stream.cc
index 0d0dfc2..9cc27f5 100644
--- a/net/spdy/hpack/hpack_input_stream.cc
+++ b/net/spdy/hpack/hpack_input_stream.cc
@@ -12,8 +12,6 @@
 
 namespace net {
 
-using std::string;
-
 HpackInputStream::HpackInputStream(SpdyStringPiece buffer)
     : buffer_(buffer),
       bit_offset_(0),
@@ -135,7 +133,7 @@
   return true;
 }
 
-bool HpackInputStream::DecodeNextHuffmanString(string* str) {
+bool HpackInputStream::DecodeNextHuffmanString(SpdyString* str) {
   uint32_t encoded_size = 0;
   if (!DecodeNextUint32(&encoded_size)) {
     if (!need_more_data_) {
diff --git a/net/spdy/hpack/hpack_input_stream.h b/net/spdy/hpack/hpack_input_stream.h
index 61041210..5eff562 100644
--- a/net/spdy/hpack/hpack_input_stream.h
+++ b/net/spdy/hpack/hpack_input_stream.h
@@ -8,13 +8,13 @@
 #include <stddef.h>
 #include <stdint.h>
 
-#include <string>
 #include <utility>
 
 #include "base/macros.h"
 #include "net/base/net_export.h"
 #include "net/spdy/hpack/hpack_constants.h"
 #include "net/spdy/hpack/hpack_huffman_table.h"
+#include "net/spdy/platform/api/spdy_string.h"
 #include "net/spdy/platform/api/spdy_string_piece.h"
 
 // All section references below are to
@@ -49,7 +49,7 @@
 
   bool DecodeNextUint32(uint32_t* I);
   bool DecodeNextIdentityString(SpdyStringPiece* str);
-  bool DecodeNextHuffmanString(std::string* str);
+  bool DecodeNextHuffmanString(SpdyString* str);
 
   // Stores input bits into the most-significant, unfilled bits of |out|.
   // |peeked_count| is the number of filled bits in |out| which have been
diff --git a/net/spdy/hpack/hpack_input_stream_test.cc b/net/spdy/hpack/hpack_input_stream_test.cc
index 96e9e2c..7f1df805 100644
--- a/net/spdy/hpack/hpack_input_stream_test.cc
+++ b/net/spdy/hpack/hpack_input_stream_test.cc
@@ -5,7 +5,6 @@
 #include "net/spdy/hpack/hpack_input_stream.h"
 
 #include <bitset>
-#include <string>
 #include <vector>
 
 #include "base/logging.h"
@@ -18,7 +17,6 @@
 
 namespace test {
 
-using std::string;
 using test::a2b_hex;
 
 // Hex representation of encoded length and Huffman string.
@@ -73,7 +71,7 @@
   EXPECT_FALSE(input_stream.DecodeNextUint32(&I));
 }
 
-uint32_t bits32(const string& bitstring) {
+uint32_t bits32(const SpdyString& bitstring) {
   return std::bitset<32>(bitstring).to_ulong();
 }
 
@@ -83,7 +81,7 @@
 
 TEST(HpackInputStreamTest, OneByteIntegersEightBitPrefix) {
   // Minimum.
-  EXPECT_EQ(0x00u, DecodeValidUint32(8, string("\x00", 1)));
+  EXPECT_EQ(0x00u, DecodeValidUint32(8, SpdyString("\x00", 1)));
   EXPECT_EQ(0x7fu, DecodeValidUint32(8, "\x7f"));
   // Maximum.
   EXPECT_EQ(0xfeu, DecodeValidUint32(8, "\xfe"));
@@ -93,7 +91,7 @@
 
 TEST(HpackInputStreamTest, TwoByteIntegersEightBitPrefix) {
   // Minimum.
-  EXPECT_EQ(0xffu, DecodeValidUint32(8, string("\xff\x00", 2)));
+  EXPECT_EQ(0xffu, DecodeValidUint32(8, SpdyString("\xff\x00", 2)));
   EXPECT_EQ(0x0100u, DecodeValidUint32(8, "\xff\x01"));
   // Maximum.
   EXPECT_EQ(0x017eu, DecodeValidUint32(8, "\xff\x7f"));
@@ -166,19 +164,19 @@
 
 TEST(HpackInputStreamTest, OneByteIntegersOneToSevenBitPrefixes) {
   // Minimums.
-  EXPECT_EQ(0x00u, DecodeValidUint32(7, string("\x00", 1)));
+  EXPECT_EQ(0x00u, DecodeValidUint32(7, SpdyString("\x00", 1)));
   EXPECT_EQ(0x00u, DecodeValidUint32(7, "\x80"));
-  EXPECT_EQ(0x00u, DecodeValidUint32(6, string("\x00", 1)));
+  EXPECT_EQ(0x00u, DecodeValidUint32(6, SpdyString("\x00", 1)));
   EXPECT_EQ(0x00u, DecodeValidUint32(6, "\xc0"));
-  EXPECT_EQ(0x00u, DecodeValidUint32(5, string("\x00", 1)));
+  EXPECT_EQ(0x00u, DecodeValidUint32(5, SpdyString("\x00", 1)));
   EXPECT_EQ(0x00u, DecodeValidUint32(5, "\xe0"));
-  EXPECT_EQ(0x00u, DecodeValidUint32(4, string("\x00", 1)));
+  EXPECT_EQ(0x00u, DecodeValidUint32(4, SpdyString("\x00", 1)));
   EXPECT_EQ(0x00u, DecodeValidUint32(4, "\xf0"));
-  EXPECT_EQ(0x00u, DecodeValidUint32(3, string("\x00", 1)));
+  EXPECT_EQ(0x00u, DecodeValidUint32(3, SpdyString("\x00", 1)));
   EXPECT_EQ(0x00u, DecodeValidUint32(3, "\xf8"));
-  EXPECT_EQ(0x00u, DecodeValidUint32(2, string("\x00", 1)));
+  EXPECT_EQ(0x00u, DecodeValidUint32(2, SpdyString("\x00", 1)));
   EXPECT_EQ(0x00u, DecodeValidUint32(2, "\xfc"));
-  EXPECT_EQ(0x00u, DecodeValidUint32(1, string("\x00", 1)));
+  EXPECT_EQ(0x00u, DecodeValidUint32(1, SpdyString("\x00", 1)));
   EXPECT_EQ(0x00u, DecodeValidUint32(1, "\xfe"));
 
   // Maximums.
@@ -194,7 +192,7 @@
   EXPECT_EQ(0x06u, DecodeValidUint32(3, "\xfe"));
   EXPECT_EQ(0x02u, DecodeValidUint32(2, "\x02"));
   EXPECT_EQ(0x02u, DecodeValidUint32(2, "\xfe"));
-  EXPECT_EQ(0x00u, DecodeValidUint32(1, string("\x00", 1)));
+  EXPECT_EQ(0x00u, DecodeValidUint32(1, SpdyString("\x00", 1)));
   EXPECT_EQ(0x00u, DecodeValidUint32(1, "\xfe"));
 
   // Invalid.
@@ -216,20 +214,20 @@
 
 TEST(HpackInputStreamTest, TwoByteIntegersOneToSevenBitPrefixes) {
   // Minimums.
-  EXPECT_EQ(0x7fu, DecodeValidUint32(7, string("\x7f\x00", 2)));
-  EXPECT_EQ(0x7fu, DecodeValidUint32(7, string("\xff\x00", 2)));
-  EXPECT_EQ(0x3fu, DecodeValidUint32(6, string("\x3f\x00", 2)));
-  EXPECT_EQ(0x3fu, DecodeValidUint32(6, string("\xff\x00", 2)));
-  EXPECT_EQ(0x1fu, DecodeValidUint32(5, string("\x1f\x00", 2)));
-  EXPECT_EQ(0x1fu, DecodeValidUint32(5, string("\xff\x00", 2)));
-  EXPECT_EQ(0x0fu, DecodeValidUint32(4, string("\x0f\x00", 2)));
-  EXPECT_EQ(0x0fu, DecodeValidUint32(4, string("\xff\x00", 2)));
-  EXPECT_EQ(0x07u, DecodeValidUint32(3, string("\x07\x00", 2)));
-  EXPECT_EQ(0x07u, DecodeValidUint32(3, string("\xff\x00", 2)));
-  EXPECT_EQ(0x03u, DecodeValidUint32(2, string("\x03\x00", 2)));
-  EXPECT_EQ(0x03u, DecodeValidUint32(2, string("\xff\x00", 2)));
-  EXPECT_EQ(0x01u, DecodeValidUint32(1, string("\x01\x00", 2)));
-  EXPECT_EQ(0x01u, DecodeValidUint32(1, string("\xff\x00", 2)));
+  EXPECT_EQ(0x7fu, DecodeValidUint32(7, SpdyString("\x7f\x00", 2)));
+  EXPECT_EQ(0x7fu, DecodeValidUint32(7, SpdyString("\xff\x00", 2)));
+  EXPECT_EQ(0x3fu, DecodeValidUint32(6, SpdyString("\x3f\x00", 2)));
+  EXPECT_EQ(0x3fu, DecodeValidUint32(6, SpdyString("\xff\x00", 2)));
+  EXPECT_EQ(0x1fu, DecodeValidUint32(5, SpdyString("\x1f\x00", 2)));
+  EXPECT_EQ(0x1fu, DecodeValidUint32(5, SpdyString("\xff\x00", 2)));
+  EXPECT_EQ(0x0fu, DecodeValidUint32(4, SpdyString("\x0f\x00", 2)));
+  EXPECT_EQ(0x0fu, DecodeValidUint32(4, SpdyString("\xff\x00", 2)));
+  EXPECT_EQ(0x07u, DecodeValidUint32(3, SpdyString("\x07\x00", 2)));
+  EXPECT_EQ(0x07u, DecodeValidUint32(3, SpdyString("\xff\x00", 2)));
+  EXPECT_EQ(0x03u, DecodeValidUint32(2, SpdyString("\x03\x00", 2)));
+  EXPECT_EQ(0x03u, DecodeValidUint32(2, SpdyString("\xff\x00", 2)));
+  EXPECT_EQ(0x01u, DecodeValidUint32(1, SpdyString("\x01\x00", 2)));
+  EXPECT_EQ(0x01u, DecodeValidUint32(1, SpdyString("\xff\x00", 2)));
 
   // Maximums.
   EXPECT_EQ(0xfeu, DecodeValidUint32(7, "\x7f\x7f"));
@@ -517,7 +515,7 @@
 }
 
 TEST(HpackInputStreamTest, DecodeNextHuffmanString) {
-  string output, input(a2b_hex(kEncodedHuffmanFixture));
+  SpdyString output, input(a2b_hex(kEncodedHuffmanFixture));
   HpackInputStream input_stream(input);
   HpackInputStreamPeer input_stream_peer(&input_stream);
 
@@ -530,7 +528,7 @@
 }
 
 TEST(HpackInputStreamTest, DecodeNextHuffmanStringNotEnoughInput) {
-  string output, input(a2b_hex(kEncodedHuffmanFixture));
+  SpdyString output, input(a2b_hex(kEncodedHuffmanFixture));
   input[0]++;  // Input prefix is one byte larger than available input.
   HpackInputStream input_stream(input);
 
@@ -761,7 +759,7 @@
 }
 
 TEST(HpackInputStreamTest, IncompleteHeaderDecodeNextHuffmanString) {
-  string output, input(a2b_hex(kEncodedHuffmanFixture));
+  SpdyString output, input(a2b_hex(kEncodedHuffmanFixture));
   input.resize(input.size() - 1);  // Remove last byte.
   HpackInputStream input_stream1(input);
   HpackInputStreamPeer input_stream1_peer(&input_stream1);
diff --git a/net/spdy/hpack/hpack_output_stream.cc b/net/spdy/hpack/hpack_output_stream.cc
index 86f0f5bf..c3673cb 100644
--- a/net/spdy/hpack/hpack_output_stream.cc
+++ b/net/spdy/hpack/hpack_output_stream.cc
@@ -11,8 +11,6 @@
 
 namespace net {
 
-using std::string;
-
 HpackOutputStream::HpackOutputStream() : bit_offset_(0) {}
 
 HpackOutputStream::~HpackOutputStream() {}
@@ -65,7 +63,7 @@
   }
 }
 
-void HpackOutputStream::TakeString(string* output) {
+void HpackOutputStream::TakeString(SpdyString* output) {
   // This must hold, since all public functions cause the buffer to
   // end on a byte boundary.
   DCHECK_EQ(bit_offset_, 0u);
@@ -74,10 +72,10 @@
   bit_offset_ = 0;
 }
 
-void HpackOutputStream::BoundedTakeString(size_t max_size, string* output) {
+void HpackOutputStream::BoundedTakeString(size_t max_size, SpdyString* output) {
   if (buffer_.size() > max_size) {
     // Save off overflow bytes to temporary string (causes a copy).
-    string overflow(buffer_.data() + max_size, buffer_.size() - max_size);
+    SpdyString overflow(buffer_.data() + max_size, buffer_.size() - max_size);
 
     // Resize buffer down to the given limit.
     buffer_.resize(max_size);
diff --git a/net/spdy/hpack/hpack_output_stream.h b/net/spdy/hpack/hpack_output_stream.h
index fe252ec..05dce985 100644
--- a/net/spdy/hpack/hpack_output_stream.h
+++ b/net/spdy/hpack/hpack_output_stream.h
@@ -9,11 +9,11 @@
 #include <stdint.h>
 
 #include <map>
-#include <string>
 
 #include "base/macros.h"
 #include "net/base/net_export.h"
 #include "net/spdy/hpack/hpack_constants.h"
+#include "net/spdy/platform/api/spdy_string.h"
 #include "net/spdy/platform/api/spdy_string_piece.h"
 
 // All section references below are to
@@ -50,11 +50,11 @@
   void AppendUint32(uint32_t I);
 
   // Swaps the internal buffer with |output|, then resets state.
-  void TakeString(std::string* output);
+  void TakeString(SpdyString* output);
 
   // Gives up to |max_size| bytes of the internal buffer to |output|. Resets
   // internal state with the overflow.
-  void BoundedTakeString(size_t max_size, std::string* output);
+  void BoundedTakeString(size_t max_size, SpdyString* output);
 
   // Size in bytes of stream's internal buffer.
   size_t size() const { return buffer_.size(); }
@@ -64,7 +64,7 @@
 
  private:
   // The internal bit buffer.
-  std::string buffer_;
+  SpdyString buffer_;
 
   // If 0, the buffer ends on a byte boundary. If non-zero, the buffer
   // ends on the nth most significant bit. Guaranteed to be < 8.
diff --git a/net/spdy/hpack/hpack_output_stream_test.cc b/net/spdy/hpack/hpack_output_stream_test.cc
index aa09640..13af4e47 100644
--- a/net/spdy/hpack/hpack_output_stream_test.cc
+++ b/net/spdy/hpack/hpack_output_stream_test.cc
@@ -12,13 +12,11 @@
 
 namespace {
 
-using std::string;
-
 // Make sure that AppendBits() appends bits starting from the most
 // significant bit, and that it can handle crossing a byte boundary.
 TEST(HpackOutputStreamTest, AppendBits) {
   HpackOutputStream output_stream;
-  string expected_str;
+  SpdyString expected_str;
 
   output_stream.AppendBits(0x1, 1);
   expected_str.append(1, 0x00);
@@ -39,20 +37,20 @@
 
   output_stream.AppendBits(0x0, 7);
 
-  string str;
+  SpdyString str;
   output_stream.TakeString(&str);
   EXPECT_EQ(expected_str, str);
 }
 
 // Utility function to return I as a string encoded with an N-bit
 // prefix.
-string EncodeUint32(uint8_t N, uint32_t I) {
+SpdyString EncodeUint32(uint8_t N, uint32_t I) {
   HpackOutputStream output_stream;
   if (N < 8) {
     output_stream.AppendBits(0x00, 8 - N);
   }
   output_stream.AppendUint32(I);
-  string str;
+  SpdyString str;
   output_stream.TakeString(&str);
   return str;
 }
@@ -63,7 +61,7 @@
 
 TEST(HpackOutputStreamTest, OneByteIntegersEightBitPrefix) {
   // Minimum.
-  EXPECT_EQ(string("\x00", 1), EncodeUint32(8, 0x00));
+  EXPECT_EQ(SpdyString("\x00", 1), EncodeUint32(8, 0x00));
   EXPECT_EQ("\x7f", EncodeUint32(8, 0x7f));
   // Maximum.
   EXPECT_EQ("\xfe", EncodeUint32(8, 0xfe));
@@ -71,7 +69,7 @@
 
 TEST(HpackOutputStreamTest, TwoByteIntegersEightBitPrefix) {
   // Minimum.
-  EXPECT_EQ(string("\xff\x00", 2), EncodeUint32(8, 0xff));
+  EXPECT_EQ(SpdyString("\xff\x00", 2), EncodeUint32(8, 0xff));
   EXPECT_EQ("\xff\x01", EncodeUint32(8, 0x0100));
   // Maximum.
   EXPECT_EQ("\xff\x7f", EncodeUint32(8, 0x017e));
@@ -114,13 +112,13 @@
 
 TEST(HpackOutputStreamTest, OneByteIntegersOneToSevenBitPrefixes) {
   // Minimums.
-  EXPECT_EQ(string("\x00", 1), EncodeUint32(7, 0x00));
-  EXPECT_EQ(string("\x00", 1), EncodeUint32(6, 0x00));
-  EXPECT_EQ(string("\x00", 1), EncodeUint32(5, 0x00));
-  EXPECT_EQ(string("\x00", 1), EncodeUint32(4, 0x00));
-  EXPECT_EQ(string("\x00", 1), EncodeUint32(3, 0x00));
-  EXPECT_EQ(string("\x00", 1), EncodeUint32(2, 0x00));
-  EXPECT_EQ(string("\x00", 1), EncodeUint32(1, 0x00));
+  EXPECT_EQ(SpdyString("\x00", 1), EncodeUint32(7, 0x00));
+  EXPECT_EQ(SpdyString("\x00", 1), EncodeUint32(6, 0x00));
+  EXPECT_EQ(SpdyString("\x00", 1), EncodeUint32(5, 0x00));
+  EXPECT_EQ(SpdyString("\x00", 1), EncodeUint32(4, 0x00));
+  EXPECT_EQ(SpdyString("\x00", 1), EncodeUint32(3, 0x00));
+  EXPECT_EQ(SpdyString("\x00", 1), EncodeUint32(2, 0x00));
+  EXPECT_EQ(SpdyString("\x00", 1), EncodeUint32(1, 0x00));
 
   // Maximums.
   EXPECT_EQ("\x7e", EncodeUint32(7, 0x7e));
@@ -129,18 +127,18 @@
   EXPECT_EQ("\x0e", EncodeUint32(4, 0x0e));
   EXPECT_EQ("\x06", EncodeUint32(3, 0x06));
   EXPECT_EQ("\x02", EncodeUint32(2, 0x02));
-  EXPECT_EQ(string("\x00", 1), EncodeUint32(1, 0x00));
+  EXPECT_EQ(SpdyString("\x00", 1), EncodeUint32(1, 0x00));
 }
 
 TEST(HpackOutputStreamTest, TwoByteIntegersOneToSevenBitPrefixes) {
   // Minimums.
-  EXPECT_EQ(string("\x7f\x00", 2), EncodeUint32(7, 0x7f));
-  EXPECT_EQ(string("\x3f\x00", 2), EncodeUint32(6, 0x3f));
-  EXPECT_EQ(string("\x1f\x00", 2), EncodeUint32(5, 0x1f));
-  EXPECT_EQ(string("\x0f\x00", 2), EncodeUint32(4, 0x0f));
-  EXPECT_EQ(string("\x07\x00", 2), EncodeUint32(3, 0x07));
-  EXPECT_EQ(string("\x03\x00", 2), EncodeUint32(2, 0x03));
-  EXPECT_EQ(string("\x01\x00", 2), EncodeUint32(1, 0x01));
+  EXPECT_EQ(SpdyString("\x7f\x00", 2), EncodeUint32(7, 0x7f));
+  EXPECT_EQ(SpdyString("\x3f\x00", 2), EncodeUint32(6, 0x3f));
+  EXPECT_EQ(SpdyString("\x1f\x00", 2), EncodeUint32(5, 0x1f));
+  EXPECT_EQ(SpdyString("\x0f\x00", 2), EncodeUint32(4, 0x0f));
+  EXPECT_EQ(SpdyString("\x07\x00", 2), EncodeUint32(3, 0x07));
+  EXPECT_EQ(SpdyString("\x03\x00", 2), EncodeUint32(2, 0x03));
+  EXPECT_EQ(SpdyString("\x01\x00", 2), EncodeUint32(1, 0x01));
 
   // Maximums.
   EXPECT_EQ("\x7f\x7f", EncodeUint32(7, 0xfe));
@@ -238,9 +236,9 @@
   HpackOutputStream output_stream;
   output_stream.AppendBits(0x7f, 7);
   output_stream.AppendUint32(0x01);
-  string str;
+  SpdyString str;
   output_stream.TakeString(&str);
-  EXPECT_EQ(string("\xff\x00", 2), str);
+  EXPECT_EQ(SpdyString("\xff\x00", 2), str);
 }
 
 TEST(HpackOutputStreamTest, AppendBytes) {
@@ -249,7 +247,7 @@
   output_stream.AppendBytes("buffer1");
   output_stream.AppendBytes("buffer2");
 
-  string str;
+  SpdyString str;
   output_stream.TakeString(&str);
   EXPECT_EQ("buffer1buffer2", str);
 }
@@ -260,7 +258,7 @@
   output_stream.AppendBytes("buffer12");
   output_stream.AppendBytes("buffer456");
 
-  string str;
+  SpdyString str;
   output_stream.BoundedTakeString(9, &str);
   EXPECT_EQ("buffer12b", str);
 
diff --git a/net/spdy/hpack/hpack_round_trip_test.cc b/net/spdy/hpack/hpack_round_trip_test.cc
index b932cffa9..e8756db 100644
--- a/net/spdy/hpack/hpack_round_trip_test.cc
+++ b/net/spdy/hpack/hpack_round_trip_test.cc
@@ -4,21 +4,19 @@
 
 #include <cmath>
 #include <ctime>
-#include <string>
 #include <vector>
 
 #include "base/rand_util.h"
 #include "net/spdy/hpack/hpack_constants.h"
 #include "net/spdy/hpack/hpack_decoder.h"
 #include "net/spdy/hpack/hpack_encoder.h"
+#include "net/spdy/platform/api/spdy_string.h"
 #include "net/spdy/spdy_test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace net {
 namespace test {
 
-using std::string;
-
 namespace {
 
 // Supports testing with the input split at every byte boundary.
@@ -35,7 +33,7 @@
   }
 
   bool RoundTrip(const SpdyHeaderBlock& header_set) {
-    string encoded;
+    SpdyString encoded;
     encoder_.EncodeHeaderSet(header_set, &encoded);
 
     bool success = true;
@@ -110,7 +108,7 @@
     headers["set-cookie"] =
         "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU;"
         " max-age=3600; version=1";
-    headers["multivalue"] = string("foo\0bar", 7);
+    headers["multivalue"] = SpdyString("foo\0bar", 7);
     EXPECT_TRUE(RoundTrip(headers));
   }
 }
@@ -143,7 +141,7 @@
     headers[":scheme"] = "https";
     headers["custom-key"] = "custom-value";
     headers["cookie"] = "baz=bing; fizzle=fazzle; garbage";
-    headers["multivalue"] = string("foo\0bar", 7);
+    headers["multivalue"] = SpdyString("foo\0bar", 7);
     EXPECT_TRUE(RoundTrip(headers));
   }
 }
@@ -152,7 +150,7 @@
   // Grow vectors of names & values, which are seeded with fixtures and then
   // expanded with dynamically generated data. Samples are taken using the
   // exponential distribution.
-  std::vector<string> pseudo_header_names, random_header_names;
+  std::vector<SpdyString> pseudo_header_names, random_header_names;
   pseudo_header_names.push_back(":authority");
   pseudo_header_names.push_back(":path");
   pseudo_header_names.push_back(":status");
@@ -160,7 +158,7 @@
   // TODO(jgraettinger): Enable "cookie" as a name fixture. Crumbs may be
   // reconstructed in any order, which breaks the simple validation used here.
 
-  std::vector<string> values;
+  std::vector<SpdyString> values;
   values.push_back("/");
   values.push_back("/index.html");
   values.push_back("200");
@@ -183,7 +181,7 @@
         std::min(header_count, 1 + SampleExponential(7, 50));
     EXPECT_LE(pseudo_header_count, header_count);
     for (size_t j = 0; j != header_count; ++j) {
-      string name, value;
+      SpdyString name, value;
       // Pseudo headers must be added before regular headers.
       if (j < pseudo_header_count) {
         // Choose one of the defined pseudo headers at random.
@@ -207,7 +205,7 @@
       // Randomly reuse an existing value, or generate a new one.
       size_t value_index = SampleExponential(20, 200);
       if (value_index >= values.size()) {
-        string newvalue =
+        SpdyString newvalue =
             base::RandBytesAsString(1 + SampleExponential(15, 75));
         // Currently order is not preserved in the encoder.  In particular,
         // when a value is decomposed at \0 delimiters, its parts might get
diff --git a/net/spdy/http2_frame_decoder_adapter.cc b/net/spdy/http2_frame_decoder_adapter.cc
index 5e21f784..8b74e6ec 100644
--- a/net/spdy/http2_frame_decoder_adapter.cc
+++ b/net/spdy/http2_frame_decoder_adapter.cc
@@ -13,7 +13,6 @@
 
 #include <cstdint>
 #include <cstring>
-#include <string>
 #include <utility>
 
 #include "base/logging.h"
@@ -28,6 +27,7 @@
 #include "net/spdy/hpack/hpack_decoder_interface.h"
 #include "net/spdy/hpack/hpack_header_table.h"
 #include "net/spdy/platform/api/spdy_estimate_memory_usage.h"
+#include "net/spdy/platform/api/spdy_string.h"
 #include "net/spdy/spdy_alt_svc_wire_format.h"
 #include "net/spdy/spdy_bug_tracker.h"
 #include "net/spdy/spdy_frame_builder.h"
@@ -35,8 +35,6 @@
 #include "net/spdy/spdy_headers_handler_interface.h"
 #include "net/spdy/spdy_protocol.h"
 
-using std::string;
-
 namespace net {
 
 namespace {
@@ -940,8 +938,8 @@
   base::Optional<size_t> opt_pad_length_;
 
   // Temporary buffers for the AltSvc fields.
-  string alt_svc_origin_;
-  string alt_svc_value_;
+  SpdyString alt_svc_origin_;
+  SpdyString alt_svc_value_;
 
   // Listener used if we transition to an error state; the listener ignores all
   // the callbacks.
diff --git a/net/spdy/platform/api/spdy_string.h b/net/spdy/platform/api/spdy_string.h
new file mode 100644
index 0000000..59089a5
--- /dev/null
+++ b/net/spdy/platform/api/spdy_string.h
@@ -0,0 +1,16 @@
+// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_SPDY_PLATFORM_API_SPDY_STRING_H_
+#define NET_SPDY_PLATFORM_API_SPDY_STRING_H_
+
+#include "net/spdy/platform/impl/spdy_string_impl.h"
+
+namespace net {
+
+using SpdyString = SpdyStringImpl;
+
+}  // namespace net
+
+#endif  // NET_SPDY_PLATFORM_API_SPDY_STRING_H_
diff --git a/net/spdy/platform/api/spdy_string_utils.h b/net/spdy/platform/api/spdy_string_utils.h
index 3d7da08..4f986ff 100644
--- a/net/spdy/platform/api/spdy_string_utils.h
+++ b/net/spdy/platform/api/spdy_string_utils.h
@@ -5,25 +5,25 @@
 #ifndef NET_SPDY_PLATFORM_API_SPDY_STRING_UTILS_H_
 #define NET_SPDY_PLATFORM_API_SPDY_STRING_UTILS_H_
 
-#include <string>
 #include <utility>
 
+#include "net/spdy/platform/api/spdy_string.h"
 #include "net/spdy/platform/impl/spdy_string_utils_impl.h"
 
 namespace net {
 
 template <typename... Args>
-inline std::string SpdyStrCat(const Args&... args) {
+inline SpdyString SpdyStrCat(const Args&... args) {
   return SpdyStrCatImpl(std::forward<const Args&>(args)...);
 }
 
 template <typename... Args>
-inline void SpdyStrAppend(std::string* output, const Args&... args) {
+inline void SpdyStrAppend(SpdyString* output, const Args&... args) {
   SpdyStrAppendImpl(output, std::forward<const Args&>(args)...);
 }
 
 template <typename... Args>
-inline std::string SpdyStringPrintf(const Args&... args) {
+inline SpdyString SpdyStringPrintf(const Args&... args) {
   return SpdyStringPrintfImpl(std::forward<const Args&>(args)...);
 }
 
diff --git a/net/spdy/platform/api/spdy_string_utils_test.cc b/net/spdy/platform/api/spdy_string_utils_test.cc
index e7df3b6..20992f99 100644
--- a/net/spdy/platform/api/spdy_string_utils_test.cc
+++ b/net/spdy/platform/api/spdy_string_utils_test.cc
@@ -19,7 +19,7 @@
 
   // Single string-like argument.
   const char kFoo[] = "foo";
-  const std::string string_foo(kFoo);
+  const SpdyString string_foo(kFoo);
   const SpdyStringPiece stringpiece_foo(string_foo);
   EXPECT_EQ("foo", SpdyStrCat(kFoo));
   EXPECT_EQ("foo", SpdyStrCat(string_foo));
@@ -28,7 +28,7 @@
   // Two string-like arguments.
   const char kBar[] = "bar";
   const SpdyStringPiece stringpiece_bar(kBar);
-  const std::string string_bar(kBar);
+  const SpdyString string_bar(kBar);
   EXPECT_EQ("foobar", SpdyStrCat(kFoo, kBar));
   EXPECT_EQ("foobar", SpdyStrCat(kFoo, string_bar));
   EXPECT_EQ("foobar", SpdyStrCat(kFoo, stringpiece_bar));
@@ -72,13 +72,13 @@
 
 TEST(SpdyStringUtilsTest, SpdyStrAppend) {
   // No arguments on empty string.
-  std::string output;
+  SpdyString output;
   SpdyStrAppend(&output);
   EXPECT_TRUE(output.empty());
 
   // Single string-like argument.
   const char kFoo[] = "foo";
-  const std::string string_foo(kFoo);
+  const SpdyString string_foo(kFoo);
   const SpdyStringPiece stringpiece_foo(string_foo);
   SpdyStrAppend(&output, kFoo);
   EXPECT_EQ("foo", output);
@@ -96,7 +96,7 @@
   // Two string-like arguments.
   const char kBar[] = "bar";
   const SpdyStringPiece stringpiece_bar(kBar);
-  const std::string string_bar(kBar);
+  const SpdyString string_bar(kBar);
   SpdyStrAppend(&output, kFoo, kBar);
   EXPECT_EQ("foobar", output);
   SpdyStrAppend(&output, kFoo, string_bar);
@@ -173,7 +173,7 @@
 }
 
 TEST(SpdyStringUtilsTest, SpdyStringAppendF) {
-  std::string output;
+  SpdyString output;
 
   SpdyStringAppendF(&output, "%s", "");
   EXPECT_TRUE(output.empty());
diff --git a/net/spdy/platform/impl/spdy_string_impl.h b/net/spdy/platform/impl/spdy_string_impl.h
new file mode 100644
index 0000000..91a6336
--- /dev/null
+++ b/net/spdy/platform/impl/spdy_string_impl.h
@@ -0,0 +1,16 @@
+// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_SPDY_PLATFORM_IMPL_SPDY_STRING_IMPL_H_
+#define NET_SPDY_PLATFORM_IMPL_SPDY_STRING_IMPL_H_
+
+#include <string>
+
+namespace net {
+
+using SpdyStringImpl = std::string;
+
+}  // namespace net
+
+#endif  // NET_SPDY_PLATFORM_IMPL_SPDY_STRING_IMPL_H_
diff --git a/net/spdy/platform/impl/spdy_string_utils_impl.h b/net/spdy/platform/impl/spdy_string_utils_impl.h
index 4a5a0f3..76c6990 100644
--- a/net/spdy/platform/impl/spdy_string_utils_impl.h
+++ b/net/spdy/platform/impl/spdy_string_utils_impl.h
@@ -6,15 +6,15 @@
 #define NET_SPDY_PLATFORM_IMPL_SPDY_STRING_UTILS_IMPL_H_
 
 #include <sstream>
-#include <string>
 
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
+#include "net/spdy/platform/api/spdy_string.h"
 
 namespace net {
 
 template <typename... Args>
-inline std::string SpdyStrCatImpl(const Args&... args) {
+inline SpdyString SpdyStrCatImpl(const Args&... args) {
   std::ostringstream oss;
   int dummy[] = {1, (oss << args, 0)...};
   static_cast<void>(dummy);
@@ -22,12 +22,12 @@
 }
 
 template <typename... Args>
-inline void SpdyStrAppendImpl(std::string* output, Args... args) {
+inline void SpdyStrAppendImpl(SpdyString* output, Args... args) {
   output->append(SpdyStrCatImpl(args...));
 }
 
 template <typename... Args>
-inline std::string SpdyStringPrintfImpl(const Args&... args) {
+inline SpdyString SpdyStringPrintfImpl(const Args&... args) {
   return base::StringPrintf(std::forward<const Args&>(args)...);
 }
 
diff --git a/net/spdy/spdy_alt_svc_wire_format.cc b/net/spdy/spdy_alt_svc_wire_format.cc
index d1205dd7e..803fb7b 100644
--- a/net/spdy/spdy_alt_svc_wire_format.cc
+++ b/net/spdy/spdy_alt_svc_wire_format.cc
@@ -7,7 +7,6 @@
 #include <algorithm>
 #include <cctype>
 #include <limits>
-#include <string>
 
 #include "base/logging.h"
 #include "net/spdy/platform/api/spdy_string_utils.h"
@@ -39,8 +38,8 @@
 SpdyAltSvcWireFormat::AlternativeService::AlternativeService() {}
 
 SpdyAltSvcWireFormat::AlternativeService::AlternativeService(
-    const std::string& protocol_id,
-    const std::string& host,
+    const SpdyString& protocol_id,
+    const SpdyString& host,
     uint16_t port,
     uint32_t max_age,
     VersionVector version)
@@ -72,7 +71,7 @@
     // Parse protocol-id.
     SpdyStringPiece::const_iterator percent_encoded_protocol_id_end =
         std::find(c, value.end(), '=');
-    std::string protocol_id;
+    SpdyString protocol_id;
     if (percent_encoded_protocol_id_end == c ||
         !PercentDecode(c, percent_encoded_protocol_id_end, &protocol_id)) {
       return false;
@@ -103,7 +102,7 @@
       return false;
     }
     DCHECK_EQ('"', *c);
-    std::string host;
+    SpdyString host;
     uint16_t port;
     if (!ParseAltAuthority(alt_authority_begin, c, &host, &port)) {
       return false;
@@ -127,7 +126,7 @@
       if (c == parameters_end) {
         break;
       }
-      std::string parameter_name;
+      SpdyString parameter_name;
       for (; c != parameters_end && *c != '=' && *c != ' ' && *c != '\t'; ++c) {
         parameter_name.push_back(tolower(*c));
       }
@@ -188,13 +187,13 @@
 }
 
 // static
-std::string SpdyAltSvcWireFormat::SerializeHeaderFieldValue(
+SpdyString SpdyAltSvcWireFormat::SerializeHeaderFieldValue(
     const AlternativeServiceVector& altsvc_vector) {
   if (altsvc_vector.empty()) {
-    return std::string("clear");
+    return SpdyString("clear");
   }
   const char kNibbleToHex[] = "0123456789ABCDEF";
-  std::string value;
+  SpdyString value;
   for (const AlternativeService& altsvc : altsvc_vector) {
     if (!value.empty()) {
       value.push_back(',');
@@ -268,7 +267,7 @@
 // static
 bool SpdyAltSvcWireFormat::PercentDecode(SpdyStringPiece::const_iterator c,
                                          SpdyStringPiece::const_iterator end,
-                                         std::string* output) {
+                                         SpdyString* output) {
   output->clear();
   for (; c != end; ++c) {
     if (*c != '%') {
@@ -296,7 +295,7 @@
 bool SpdyAltSvcWireFormat::ParseAltAuthority(
     SpdyStringPiece::const_iterator c,
     SpdyStringPiece::const_iterator end,
-    std::string* host,
+    SpdyString* host,
     uint16_t* port) {
   host->clear();
   if (c == end) {
diff --git a/net/spdy/spdy_alt_svc_wire_format.h b/net/spdy/spdy_alt_svc_wire_format.h
index 856b08e..b6b8ee1 100644
--- a/net/spdy/spdy_alt_svc_wire_format.h
+++ b/net/spdy/spdy_alt_svc_wire_format.h
@@ -10,11 +10,11 @@
 #ifndef NET_SPDY_SPDY_ALT_SVC_WIRE_FORMAT_H_
 #define NET_SPDY_SPDY_ALT_SVC_WIRE_FORMAT_H_
 
-#include <stdint.h>
-
+#include <cstdint>
 #include <vector>
 
 #include "net/base/net_export.h"
+#include "net/spdy/platform/api/spdy_string.h"
 #include "net/spdy/platform/api/spdy_string_piece.h"
 
 namespace net {
@@ -28,8 +28,8 @@
   using VersionVector = std::vector<uint16_t>;
 
   struct NET_EXPORT_PRIVATE AlternativeService {
-    std::string protocol_id;
-    std::string host;
+    SpdyString protocol_id;
+    SpdyString host;
 
     // Default is 0: invalid port.
     uint16_t port = 0;
@@ -39,8 +39,8 @@
     VersionVector version;
 
     AlternativeService();
-    AlternativeService(const std::string& protocol_id,
-                       const std::string& host,
+    AlternativeService(const SpdyString& protocol_id,
+                       const SpdyString& host,
                        uint16_t port,
                        uint32_t max_age,
                        VersionVector version);
@@ -61,7 +61,7 @@
   friend class test::SpdyAltSvcWireFormatPeer;
   static bool ParseHeaderFieldValue(SpdyStringPiece value,
                                     AlternativeServiceVector* altsvc_vector);
-  static std::string SerializeHeaderFieldValue(
+  static SpdyString SerializeHeaderFieldValue(
       const AlternativeServiceVector& altsvc_vector);
 
  private:
@@ -69,10 +69,10 @@
                              SpdyStringPiece::const_iterator end);
   static bool PercentDecode(SpdyStringPiece::const_iterator c,
                             SpdyStringPiece::const_iterator end,
-                            std::string* output);
+                            SpdyString* output);
   static bool ParseAltAuthority(SpdyStringPiece::const_iterator c,
                                 SpdyStringPiece::const_iterator end,
-                                std::string* host,
+                                SpdyString* host,
                                 uint16_t* port);
   static bool ParsePositiveInteger16(SpdyStringPiece::const_iterator c,
                                      SpdyStringPiece::const_iterator end,
diff --git a/net/spdy/spdy_alt_svc_wire_format_test.cc b/net/spdy/spdy_alt_svc_wire_format_test.cc
index 0d5679e..1650c35 100644
--- a/net/spdy/spdy_alt_svc_wire_format_test.cc
+++ b/net/spdy/spdy_alt_svc_wire_format_test.cc
@@ -23,12 +23,12 @@
   }
   static bool PercentDecode(SpdyStringPiece::const_iterator c,
                             SpdyStringPiece::const_iterator end,
-                            std::string* output) {
+                            SpdyString* output) {
     return SpdyAltSvcWireFormat::PercentDecode(c, end, output);
   }
   static bool ParseAltAuthority(SpdyStringPiece::const_iterator c,
                                 SpdyStringPiece::const_iterator end,
-                                std::string* host,
+                                SpdyString* host,
                                 uint16_t* port) {
     return SpdyAltSvcWireFormat::ParseAltAuthority(c, end, host, port);
   }
@@ -52,7 +52,7 @@
 // random case, and corresponding AlternativeService entries.
 void FuzzHeaderFieldValue(
     int i,
-    std::string* header_field_value,
+    SpdyString* header_field_value,
     SpdyAltSvcWireFormat::AlternativeService* expected_altsvc) {
   if (!header_field_value->empty()) {
     header_field_value->push_back(',');
@@ -109,7 +109,7 @@
 // canonical form, that is, what SerializeHeaderFieldValue() should output.
 void FuzzAlternativeService(int i,
                             SpdyAltSvcWireFormat::AlternativeService* altsvc,
-                            std::string* expected_header_field_value) {
+                            SpdyString* expected_header_field_value) {
   if (!expected_header_field_value->empty()) {
     expected_header_field_value->push_back(',');
   }
@@ -162,7 +162,7 @@
 // separator, etc.  Single alternative service at a time.
 TEST(SpdyAltSvcWireFormatTest, ParseHeaderFieldValue) {
   for (int i = 0; i < 1 << 11; ++i) {
-    std::string header_field_value;
+    SpdyString header_field_value;
     SpdyAltSvcWireFormat::AlternativeService expected_altsvc;
     FuzzHeaderFieldValue(i, &header_field_value, &expected_altsvc);
     SpdyAltSvcWireFormat::AlternativeServiceVector altsvc_vector;
@@ -176,7 +176,7 @@
     EXPECT_EQ(expected_altsvc.version, altsvc_vector[0].version);
 
     // Roundtrip test starting with |altsvc_vector|.
-    std::string reserialized_header_field_value =
+    SpdyString reserialized_header_field_value =
         SpdyAltSvcWireFormat::SerializeHeaderFieldValue(altsvc_vector);
     SpdyAltSvcWireFormat::AlternativeServiceVector roundtrip_altsvc_vector;
     ASSERT_TRUE(SpdyAltSvcWireFormat::ParseHeaderFieldValue(
@@ -196,7 +196,7 @@
 // separator, etc.  Possibly multiple alternative service at a time.
 TEST(SpdyAltSvcWireFormatTest, ParseHeaderFieldValueMultiple) {
   for (int i = 0; i < 1 << 11;) {
-    std::string header_field_value;
+    SpdyString header_field_value;
     SpdyAltSvcWireFormat::AlternativeServiceVector expected_altsvc_vector;
     // This will generate almost two hundred header field values with two,
     // three, four, five, six, and seven alternative services each, and
@@ -221,7 +221,7 @@
     }
 
     // Roundtrip test starting with |altsvc_vector|.
-    std::string reserialized_header_field_value =
+    SpdyString reserialized_header_field_value =
         SpdyAltSvcWireFormat::SerializeHeaderFieldValue(altsvc_vector);
     SpdyAltSvcWireFormat::AlternativeServiceVector roundtrip_altsvc_vector;
     ASSERT_TRUE(SpdyAltSvcWireFormat::ParseHeaderFieldValue(
@@ -255,7 +255,7 @@
 TEST(SpdyAltSvcWireFormatTest, RoundTrip) {
   for (int i = 0; i < 1 << 3; ++i) {
     SpdyAltSvcWireFormat::AlternativeService altsvc;
-    std::string expected_header_field_value;
+    SpdyString expected_header_field_value;
     FuzzAlternativeService(i, &altsvc, &expected_header_field_value);
 
     // Test ParseHeaderFieldValue().
@@ -283,7 +283,7 @@
 // parameter.  Multiple alternative services at a time.
 TEST(SpdyAltSvcWireFormatTest, RoundTripMultiple) {
   SpdyAltSvcWireFormat::AlternativeServiceVector altsvc_vector;
-  std::string expected_header_field_value;
+  SpdyString expected_header_field_value;
   for (int i = 0; i < 1 << 3; ++i) {
     SpdyAltSvcWireFormat::AlternativeService altsvc;
     FuzzAlternativeService(i, &altsvc, &expected_header_field_value);
@@ -355,7 +355,7 @@
   SpdyAltSvcWireFormat::AlternativeServiceVector altsvc_vector;
   const char* field_value_array[] = {"a=\":137\"", "a=\"foo:137\"",
                                      "a%25=\"foo\\\"bar\\\\baz:137\""};
-  for (const std::string& field_value : field_value_array) {
+  for (const SpdyString& field_value : field_value_array) {
     for (size_t len = 1; len < field_value.size(); ++len) {
       EXPECT_FALSE(SpdyAltSvcWireFormat::ParseHeaderFieldValue(
           field_value.substr(0, len), &altsvc_vector))
@@ -383,7 +383,7 @@
 // Test PercentDecode() on valid input.
 TEST(SpdyAltSvcWireFormatTest, PercentDecodeValid) {
   SpdyStringPiece input("");
-  std::string output;
+  SpdyString output;
   ASSERT_TRUE(test::SpdyAltSvcWireFormatPeer::PercentDecode(
       input.begin(), input.end(), &output));
   EXPECT_EQ("", output);
@@ -406,7 +406,7 @@
   const char* invalid_input_array[] = {"a%", "a%x", "a%b", "%J22", "%9z"};
   for (const char* invalid_input : invalid_input_array) {
     SpdyStringPiece input(invalid_input);
-    std::string output;
+    SpdyString output;
     EXPECT_FALSE(test::SpdyAltSvcWireFormatPeer::PercentDecode(
         input.begin(), input.end(), &output))
         << input;
@@ -416,7 +416,7 @@
 // Test ParseAltAuthority() on valid input.
 TEST(SpdyAltSvcWireFormatTest, ParseAltAuthorityValid) {
   SpdyStringPiece input(":42");
-  std::string host;
+  SpdyString host;
   uint16_t port;
   ASSERT_TRUE(test::SpdyAltSvcWireFormatPeer::ParseAltAuthority(
       input.begin(), input.end(), &host, &port));
@@ -457,7 +457,7 @@
                                        "2003:8:0:16::509d:9615]:443"};
   for (const char* invalid_input : invalid_input_array) {
     SpdyStringPiece input(invalid_input);
-    std::string host;
+    SpdyString host;
     uint16_t port;
     EXPECT_FALSE(test::SpdyAltSvcWireFormatPeer::ParseAltAuthority(
         input.begin(), input.end(), &host, &port))
diff --git a/net/spdy/spdy_buffer_unittest.cc b/net/spdy/spdy_buffer_unittest.cc
index 3dd4942..f46f083 100644
--- a/net/spdy/spdy_buffer_unittest.cc
+++ b/net/spdy/spdy_buffer_unittest.cc
@@ -7,11 +7,11 @@
 #include <cstddef>
 #include <cstring>
 #include <memory>
-#include <string>
 
 #include "base/bind.h"
 #include "base/memory/ref_counted.h"
 #include "net/base/io_buffer.h"
+#include "net/spdy/platform/api/spdy_string.h"
 #include "net/spdy/spdy_protocol.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -25,8 +25,8 @@
 class SpdyBufferTest : public ::testing::Test {};
 
 // Make a string from the data remaining in |buffer|.
-std::string BufferToString(const SpdyBuffer& buffer) {
-  return std::string(buffer.GetRemainingData(), buffer.GetRemainingSize());
+SpdyString BufferToString(const SpdyBuffer& buffer) {
+  return SpdyString(buffer.GetRemainingData(), buffer.GetRemainingSize());
 }
 
 // Construct a SpdyBuffer from a SpdySerializedFrame and make sure its data
@@ -43,14 +43,14 @@
 // Construct a SpdyBuffer from a const char*/size_t pair and make sure
 // it makes a copy of the data.
 TEST_F(SpdyBufferTest, DataConstructor) {
-  std::string data(kData, kDataSize);
+  SpdyString data(kData, kDataSize);
   SpdyBuffer buffer(data.data(), data.size());
   // This mutation shouldn't affect |buffer|'s data.
   data[0] = 'H';
 
   EXPECT_NE(kData, buffer.GetRemainingData());
   EXPECT_EQ(kDataSize, buffer.GetRemainingSize());
-  EXPECT_EQ(std::string(kData, kDataSize), BufferToString(buffer));
+  EXPECT_EQ(SpdyString(kData, kDataSize), BufferToString(buffer));
 }
 
 void IncrementBy(size_t* x,
@@ -74,10 +74,10 @@
   buffer.AddConsumeCallback(
       base::Bind(&IncrementBy, &x2, SpdyBuffer::CONSUME));
 
-  EXPECT_EQ(std::string(kData, kDataSize), BufferToString(buffer));
+  EXPECT_EQ(SpdyString(kData, kDataSize), BufferToString(buffer));
 
   buffer.Consume(5);
-  EXPECT_EQ(std::string(kData + 5, kDataSize - 5), BufferToString(buffer));
+  EXPECT_EQ(SpdyString(kData + 5, kDataSize - 5), BufferToString(buffer));
   EXPECT_EQ(5u, x1);
   EXPECT_EQ(5u, x2);
 
@@ -110,11 +110,11 @@
   buffer.Consume(5);
   scoped_refptr<IOBuffer> io_buffer = buffer.GetIOBufferForRemainingData();
   size_t io_buffer_size = buffer.GetRemainingSize();
-  const std::string expectedData(kData + 5, kDataSize - 5);
-  EXPECT_EQ(expectedData, std::string(io_buffer->data(), io_buffer_size));
+  const SpdyString expectedData(kData + 5, kDataSize - 5);
+  EXPECT_EQ(expectedData, SpdyString(io_buffer->data(), io_buffer_size));
 
   buffer.Consume(kDataSize - 5);
-  EXPECT_EQ(expectedData, std::string(io_buffer->data(), io_buffer_size));
+  EXPECT_EQ(expectedData, SpdyString(io_buffer->data(), io_buffer_size));
 }
 
 // Make sure the IOBuffer returned by GetIOBufferForRemainingData()
diff --git a/net/spdy/spdy_deframer_visitor.cc b/net/spdy/spdy_deframer_visitor.cc
index 3af9f20..c6075c9 100644
--- a/net/spdy/spdy_deframer_visitor.cc
+++ b/net/spdy/spdy_deframer_visitor.cc
@@ -5,7 +5,6 @@
 #include "net/spdy/spdy_deframer_visitor.h"
 
 #include <stdlib.h>
-#include <string.h>
 
 #include <algorithm>
 #include <cstdint>
@@ -21,7 +20,6 @@
 #include "net/spdy/spdy_test_utils.h"
 
 using ::base::MakeUnique;
-using ::std::string;
 using ::testing::AssertionFailure;
 using ::testing::AssertionResult;
 using ::testing::AssertionSuccess;
@@ -212,7 +210,7 @@
   bool fin_ = false;
   bool got_hpack_end_ = false;
 
-  std::unique_ptr<string> data_;
+  std::unique_ptr<SpdyString> data_;
 
   // Total length of the data frame.
   size_t data_len_ = 0;
@@ -221,7 +219,7 @@
   // Length field).
   size_t padding_len_ = 0;
 
-  std::unique_ptr<string> goaway_description_;
+  std::unique_ptr<SpdyString> goaway_description_;
   std::unique_ptr<StringPairVector> headers_;
   std::unique_ptr<SettingVector> settings_;
   std::unique_ptr<TestHeadersHandler> headers_handler_;
@@ -418,7 +416,7 @@
                                << Http2FrameTypeToString(frame_type_);
   CHECK_GT(stream_id, 0u);
   auto ptr = MakeUnique<SpdyAltSvcIR>(stream_id);
-  ptr->set_origin(std::string(origin));
+  ptr->set_origin(SpdyString(origin));
   for (auto& altsvc : altsvc_vector) {
     ptr->add_altsvc(altsvc);
   }
@@ -457,7 +455,7 @@
   stream_id_ = stream_id;
   fin_ = fin;
   data_len_ = length;
-  data_.reset(new string());
+  data_.reset(new SpdyString());
 }
 
 // The SpdyFramer will not process any more data at this point.
@@ -481,7 +479,7 @@
                                << Http2FrameTypeToString(frame_type_);
   frame_type_ = GOAWAY;
   goaway_ir_ = MakeUnique<SpdyGoAwayIR>(last_good_stream_id, error_code, "");
-  goaway_description_.reset(new string());
+  goaway_description_.reset(new SpdyString());
 }
 
 // If len==0 then we've reached the end of the GOAWAY frame.
@@ -744,7 +742,7 @@
       << "   frame_type_=" << Http2FrameTypeToString(frame_type_);
   CHECK(!got_hpack_end_);
   CHECK(headers_);
-  headers_->emplace_back(std::string(key), std::string(value));
+  headers_->emplace_back(SpdyString(key), SpdyString(value));
   CHECK(headers_handler_);
   headers_handler_->OnHeader(key, value);
 }
diff --git a/net/spdy/spdy_deframer_visitor.h b/net/spdy/spdy_deframer_visitor.h
index 87188a4..9727d37 100644
--- a/net/spdy/spdy_deframer_visitor.h
+++ b/net/spdy/spdy_deframer_visitor.h
@@ -70,13 +70,13 @@
 #include <stdint.h>
 
 #include <memory>
-#include <string>
 #include <type_traits>
 #include <utility>
 #include <vector>
 
 #include "base/logging.h"
 #include "base/macros.h"
+#include "net/spdy/platform/api/spdy_string.h"
 #include "net/spdy/spdy_framer.h"
 #include "net/spdy/spdy_protocol.h"
 #include "net/spdy/spdy_protocol_test_utils.h"
@@ -92,7 +92,7 @@
 // particular the order of each header entry, though it doesn't expose the
 // inner details of the HPACK block, such as the type of encoding selected
 // for each header entry, nor dynamic table size changes.
-typedef std::pair<std::string, std::string> StringPair;
+typedef std::pair<SpdyString, SpdyString> StringPair;
 typedef std::vector<StringPair> StringPairVector;
 
 // Forward decl.
diff --git a/net/spdy/spdy_deframer_visitor_test.cc b/net/spdy/spdy_deframer_visitor_test.cc
index 06baf41..885062d 100644
--- a/net/spdy/spdy_deframer_visitor_test.cc
+++ b/net/spdy/spdy_deframer_visitor_test.cc
@@ -5,7 +5,6 @@
 #include "net/spdy/spdy_deframer_visitor.h"
 
 #include <stdlib.h>
-#include <string.h>
 
 #include <algorithm>
 #include <limits>
@@ -22,7 +21,6 @@
 #include "net/spdy/spdy_test_utils.h"
 
 using ::base::MakeUnique;
-using ::std::string;
 
 namespace net {
 namespace test {
@@ -66,9 +64,9 @@
     return encoder_.SerializeFrame(frame);
   }
 
-  string SerializeFrames(
+  SpdyString SerializeFrames(
       const std::vector<std::unique_ptr<SpdyFrameIR>>& frames) {
-    string result;
+    SpdyString result;
     for (const auto& frame_ptr : frames) {
       auto sf = SerializeFrame(*frame_ptr);
       result.append(sf.data(), sf.size());
diff --git a/net/spdy/spdy_frame_builder.h b/net/spdy/spdy_frame_builder.h
index 721934f..689fbcd 100644
--- a/net/spdy/spdy_frame_builder.h
+++ b/net/spdy/spdy_frame_builder.h
@@ -9,7 +9,6 @@
 #include <stdint.h>
 
 #include <memory>
-#include <string>
 
 #include "base/gtest_prod_util.h"
 #include "base/sys_byteorder.h"
diff --git a/net/spdy/spdy_framer.cc b/net/spdy/spdy_framer.cc
index a73f7de..c7411471 100644
--- a/net/spdy/spdy_framer.cc
+++ b/net/spdy/spdy_framer.cc
@@ -13,7 +13,6 @@
 #include <list>
 #include <memory>
 #include <new>
-#include <string>
 #include <vector>
 
 #include "base/lazy_instance.h"
@@ -35,7 +34,6 @@
 #include "net/spdy/spdy_frame_reader.h"
 #include "net/spdy/spdy_framer_decoder_adapter.h"
 
-using std::string;
 using std::vector;
 
 namespace net {
@@ -1637,7 +1635,7 @@
                << " contains upper-case characters.";
       return false;
     }
-    std::string name(temp);
+    SpdyString name(temp);
 
     // Read header value.
     if (!reader.ReadStringPiece32(&temp)) {
@@ -1645,7 +1643,7 @@
                << num_headers << ").";
       return false;
     }
-    std::string value(temp);
+    SpdyString value(temp);
 
     // Ensure no duplicates.
     if (block->find(name) != block->end()) {
@@ -1691,7 +1689,7 @@
   size_t size_without_block =
       is_first_frame_ ? framer_->GetHeaderFrameSizeSansBlock(*headers_ir_)
                       : framer_->GetContinuationMinimumSize();
-  auto encoding = base::MakeUnique<string>();
+  auto encoding = base::MakeUnique<SpdyString>();
   encoder_->Next(kMaxControlFrameSize - size_without_block, encoding.get());
   has_next_frame_ = encoder_->HasNext();
 
@@ -1755,7 +1753,7 @@
   }
   builder.WriteBytes(data_ir.data(), data_ir.data_len());
   if (data_ir.padding_payload_len() > 0) {
-    string padding(data_ir.padding_payload_len(), 0);
+    SpdyString padding(data_ir.padding_payload_len(), 0);
     builder.WriteBytes(padding.data(), padding.length());
   }
   DCHECK_EQ(size_with_padding, builder.length());
@@ -1903,7 +1901,7 @@
 void SpdyFramer::SerializeHeadersBuilderHelper(const SpdyHeadersIR& headers,
                                                uint8_t* flags,
                                                size_t* size,
-                                               string* hpack_encoding,
+                                               SpdyString* hpack_encoding,
                                                int* weight,
                                                size_t* length_field) {
   if (headers.fin()) {
@@ -1959,7 +1957,7 @@
   // The size of this frame, including padding (if there is any) and
   // variable-length header block.
   size_t size = 0;
-  string hpack_encoding;
+  SpdyString hpack_encoding;
   int weight = 0;
   size_t length_field = 0;
   SerializeHeadersBuilderHelper(headers, &flags, &size, &hpack_encoding,
@@ -2016,7 +2014,7 @@
 void SpdyFramer::SerializePushPromiseBuilderHelper(
     const SpdyPushPromiseIR& push_promise,
     uint8_t* flags,
-    string* hpack_encoding,
+    SpdyString* hpack_encoding,
     size_t* size) {
   *flags = 0;
   // This will get overwritten if we overflow into a CONTINUATION frame.
@@ -2044,7 +2042,7 @@
     const SpdyPushPromiseIR& push_promise) {
   uint8_t flags = 0;
   size_t size = 0;
-  string hpack_encoding;
+  SpdyString hpack_encoding;
   SerializePushPromiseBuilderHelper(push_promise, &flags, &hpack_encoding,
                                     &size);
 
@@ -2090,7 +2088,7 @@
 
 SpdySerializedFrame SpdyFramer::SerializeHeadersGivenEncoding(
     const SpdyHeadersIR& headers,
-    const string& encoding) const {
+    const SpdyString& encoding) const {
   size_t frame_size = GetHeaderFrameSizeSansBlock(headers) + encoding.size();
   SpdyFrameBuilder builder(frame_size);
   builder.BeginNewFrame(*this, SpdyFrameType::HEADERS,
@@ -2113,7 +2111,7 @@
   builder.WriteBytes(&encoding[0], encoding.size());
 
   if (headers.padding_payload_len() > 0) {
-    string padding(headers.padding_payload_len(), 0);
+    SpdyString padding(headers.padding_payload_len(), 0);
     builder.WriteBytes(padding.data(), padding.length());
   }
   return builder.take();
@@ -2121,7 +2119,7 @@
 
 SpdySerializedFrame SpdyFramer::SerializeContinuation(
     const SpdyContinuationIR& continuation) const {
-  const string& encoding = continuation.encoding();
+  const SpdyString& encoding = continuation.encoding();
   size_t frame_size = GetContinuationMinimumSize() + encoding.size();
   SpdyFrameBuilder builder(frame_size);
   uint8_t flags = continuation.end_headers() ? HEADERS_FLAG_END_HEADERS : 0;
@@ -2134,7 +2132,7 @@
 }
 
 void SpdyFramer::SerializeAltSvcBuilderHelper(const SpdyAltSvcIR& altsvc_ir,
-                                              string* value,
+                                              SpdyString* value,
                                               size_t* size) const {
   *size = GetAltSvcMinimumSize();
   *size = *size + altsvc_ir.origin().length();
@@ -2144,7 +2142,7 @@
 }
 
 SpdySerializedFrame SpdyFramer::SerializeAltSvc(const SpdyAltSvcIR& altsvc_ir) {
-  string value;
+  SpdyString value;
   size_t size = 0;
   SerializeAltSvcBuilderHelper(altsvc_ir, &value, &size);
   SpdyFrameBuilder builder(size);
@@ -2334,8 +2332,8 @@
 
   ok = ok && builder.WriteBytes(data_ir.data(), data_ir.data_len());
   if (data_ir.padding_payload_len() > 0) {
-    string padding;
-    padding = string(data_ir.padding_payload_len(), 0);
+    SpdyString padding;
+    padding = SpdyString(data_ir.padding_payload_len(), 0);
     ok = ok && builder.WriteBytes(padding.data(), padding.length());
   }
   DCHECK_EQ(size_with_padding, builder.length());
@@ -2458,7 +2456,7 @@
   // The size of this frame, including padding (if there is any) and
   // variable-length header block.
   size_t size = 0;
-  string hpack_encoding;
+  SpdyString hpack_encoding;
   int weight = 0;
   size_t length_field = 0;
   SerializeHeadersBuilderHelper(headers, &flags, &size, &hpack_encoding,
@@ -2518,7 +2516,7 @@
                                       ZeroCopyOutputBuffer* output) {
   uint8_t flags = 0;
   size_t size = 0;
-  string hpack_encoding;
+  SpdyString hpack_encoding;
   SerializePushPromiseBuilderHelper(push_promise, &flags, &hpack_encoding,
                                     &size);
 
@@ -2566,7 +2564,7 @@
 
 bool SpdyFramer::SerializeContinuation(const SpdyContinuationIR& continuation,
                                        ZeroCopyOutputBuffer* output) const {
-  const string& encoding = continuation.encoding();
+  const SpdyString& encoding = continuation.encoding();
   size_t frame_size = GetContinuationMinimumSize() + encoding.size();
   SpdyFrameBuilder builder(frame_size, output);
   uint8_t flags = continuation.end_headers() ? HEADERS_FLAG_END_HEADERS : 0;
@@ -2580,7 +2578,7 @@
 
 bool SpdyFramer::SerializeAltSvc(const SpdyAltSvcIR& altsvc_ir,
                                  ZeroCopyOutputBuffer* output) {
-  string value;
+  SpdyString value;
   size_t size = 0;
   SerializeAltSvcBuilderHelper(altsvc_ir, &value, &size);
   SpdyFrameBuilder builder(size, output);
@@ -2714,7 +2712,7 @@
 }
 
 bool SpdyFramer::WritePayloadWithContinuation(SpdyFrameBuilder* builder,
-                                              const string& hpack_encoding,
+                                              const SpdyString& hpack_encoding,
                                               SpdyStreamId stream_id,
                                               SpdyFrameType type,
                                               int padding_payload_len) {
@@ -2739,7 +2737,7 @@
   bool ret = builder->WriteBytes(&hpack_encoding[0],
                                  hpack_encoding.size() - bytes_remaining);
   if (padding_payload_len > 0) {
-    string padding = string(padding_payload_len, 0);
+    SpdyString padding = SpdyString(padding_payload_len, 0);
     ret &= builder->WriteBytes(padding.data(), padding.length());
   }
   if (bytes_remaining > 0 && !skip_rewritelength_) {
diff --git a/net/spdy/spdy_framer.h b/net/spdy/spdy_framer.h
index f7048ad..c08a57d 100644
--- a/net/spdy/spdy_framer.h
+++ b/net/spdy/spdy_framer.h
@@ -11,13 +11,13 @@
 #include <cstdint>
 #include <map>
 #include <memory>
-#include <string>
 #include <utility>
 
 #include "base/sys_byteorder.h"
 #include "net/base/net_export.h"
 #include "net/spdy/hpack/hpack_decoder_interface.h"
 #include "net/spdy/hpack/hpack_encoder.h"
+#include "net/spdy/platform/api/spdy_string.h"
 #include "net/spdy/platform/api/spdy_string_piece.h"
 #include "net/spdy/spdy_alt_svc_wire_format.h"
 #include "net/spdy/spdy_flags.h"
@@ -715,7 +715,7 @@
   size_t GetNumberRequiredContinuationFrames(size_t size);
 
   bool WritePayloadWithContinuation(SpdyFrameBuilder* builder,
-                                    const std::string& hpack_encoding,
+                                    const SpdyString& hpack_encoding,
                                     SpdyStreamId stream_id,
                                     SpdyFrameType type,
                                     int padding_payload_len);
@@ -733,7 +733,7 @@
   // block. Does not need or use the SpdyHeaderBlock inside SpdyHeadersIR.
   SpdySerializedFrame SerializeHeadersGivenEncoding(
       const SpdyHeadersIR& headers,
-      const std::string& encoding) const;
+      const SpdyString& encoding) const;
 
   // Calculates the number of bytes required to serialize a SpdyHeadersIR, not
   // including the bytes to be used for the encoded header set.
@@ -760,17 +760,17 @@
                                       const SettingsMap* values,
                                       size_t* size) const;
   void SerializeAltSvcBuilderHelper(const SpdyAltSvcIR& altsvc_ir,
-                                    std::string* value,
+                                    SpdyString* value,
                                     size_t* size) const;
   void SerializeHeadersBuilderHelper(const SpdyHeadersIR& headers,
                                      uint8_t* flags,
                                      size_t* size,
-                                     std::string* hpack_encoding,
+                                     SpdyString* hpack_encoding,
                                      int* weight,
                                      size_t* length_field);
   void SerializePushPromiseBuilderHelper(const SpdyPushPromiseIR& push_promise,
                                          uint8_t* flags,
-                                         std::string* hpack_encoding,
+                                         SpdyString* hpack_encoding,
                                          size_t* size);
 
   // The size of the control frame buffer.
diff --git a/net/spdy/spdy_framer_decoder_adapter.cc b/net/spdy/spdy_framer_decoder_adapter.cc
index 5bb0479..9b7feb5 100644
--- a/net/spdy/spdy_framer_decoder_adapter.cc
+++ b/net/spdy/spdy_framer_decoder_adapter.cc
@@ -5,7 +5,6 @@
 #include "net/spdy/spdy_framer_decoder_adapter.h"
 
 #include <memory>
-#include <string>
 #include <utility>
 
 #include "base/format_macros.h"
diff --git a/net/spdy/spdy_framer_test.cc b/net/spdy/spdy_framer_test.cc
index 3d30213..42b2cd22 100644
--- a/net/spdy/spdy_framer_test.cc
+++ b/net/spdy/spdy_framer_test.cc
@@ -11,7 +11,6 @@
 #include <cstdint>
 #include <limits>
 #include <memory>
-#include <string>
 #include <tuple>
 #include <vector>
 
@@ -33,7 +32,6 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
 
-using std::string;
 using testing::_;
 
 namespace net {
@@ -471,7 +469,7 @@
             << "\", altsvc_vector)";
     test_altsvc_ir_.set_stream_id(stream_id);
     if (origin.length() > 0) {
-      test_altsvc_ir_.set_origin(std::string(origin));
+      test_altsvc_ir_.set_origin(SpdyString(origin));
     }
     for (const SpdyAltSvcWireFormat::AlternativeService& altsvc :
          altsvc_vector) {
@@ -645,7 +643,7 @@
   size_t length_ = 0;
   uint8_t type_ = 0;
   uint8_t flags_ = 0;
-  string payload_;
+  SpdyString payload_;
 };
 
 // Retrieves serialized headers from a HEADERS frame.
@@ -712,7 +710,7 @@
     }
   }
 
-  void CompareFrame(const string& description,
+  void CompareFrame(const SpdyString& description,
                     const SpdySerializedFrame& actual_frame,
                     const unsigned char* expected,
                     const int expected_len) {
@@ -722,7 +720,7 @@
                                   expected, expected_len);
   }
 
-  void CompareFrames(const string& description,
+  void CompareFrames(const SpdyString& description,
                      const SpdySerializedFrame& expected_frame,
                      const SpdySerializedFrame& actual_frame) {
     CompareCharArraysWithHexError(
@@ -1174,7 +1172,7 @@
 
   SpdyContinuationIR continuation(0);
   auto some_nonsense_encoding =
-      base::MakeUnique<string>("some nonsense encoding");
+      base::MakeUnique<SpdyString>("some nonsense encoding");
   continuation.take_encoding(std::move(some_nonsense_encoding));
   continuation.set_end_headers(true);
   SpdySerializedFrame frame(framer.SerializeContinuation(continuation));
@@ -1268,11 +1266,11 @@
   frame.WriteUInt32(0);   // Priority exclusivity and dependent stream.
   frame.WriteUInt8(255);  // Priority weight.
 
-  string value("value1\0value2", 13);
+  SpdyString value("value1\0value2", 13);
   // TODO(jgraettinger): If this pattern appears again, move to test class.
   SpdyHeaderBlock header_set;
   header_set["name"] = value;
-  string buffer;
+  SpdyString buffer;
   HpackEncoder encoder(ObtainHpackHuffmanTable());
   encoder.DisableCompression();
   encoder.EncodeHeaderSet(header_set, &buffer);
@@ -2477,7 +2475,7 @@
   SpdyHeaderBlock header_block;
   header_block["bar"] = "foo";
   header_block["foo"] = "bar";
-  auto buffer = base::MakeUnique<string>();
+  auto buffer = base::MakeUnique<SpdyString>();
   HpackEncoder encoder(ObtainHpackHuffmanTable());
   encoder.DisableCompression();
   encoder.EncodeHeaderSet(header_block, buffer.get());
@@ -2592,7 +2590,7 @@
 
     SpdyPushPromiseIR push_promise(42, 57);
     push_promise.set_padding_len(1);
-    string big_value(TestSpdyVisitor::sent_control_frame_max_size(), 'x');
+    SpdyString big_value(TestSpdyVisitor::sent_control_frame_max_size(), 'x');
     push_promise.SetHeader("xxx", big_value);
     SpdySerializedFrame frame(framer.SerializePushPromise(push_promise));
     if (use_output_) {
@@ -2742,7 +2740,7 @@
   // Exact payload length will change with HPACK, but this should be long
   // enough to cause an overflow.
   const size_t kBigValueSize = TestSpdyVisitor::sent_control_frame_max_size();
-  string big_value(kBigValueSize, 'x');
+  SpdyString big_value(kBigValueSize, 'x');
   headers.SetHeader("aa", big_value);
   SpdySerializedFrame control_frame(SpdyFramerPeer::SerializeHeaders(
       &framer, headers, use_output_ ? &output_ : nullptr));
@@ -2768,9 +2766,9 @@
   // Exact payload length will change with HPACK, but this should be long
   // enough to cause an overflow.
   const size_t kBigValueSize = TestSpdyVisitor::sent_control_frame_max_size();
-  string big_valuex(kBigValueSize, 'x');
+  SpdyString big_valuex(kBigValueSize, 'x');
   headers->SetHeader("aa", big_valuex);
-  string big_valuez(kBigValueSize, 'z');
+  SpdyString big_valuez(kBigValueSize, 'z');
   headers->SetHeader("bb", big_valuez);
 
   SpdyFramer::SpdyHeaderFrameIterator frame_it(&framer, std::move(headers));
@@ -2829,7 +2827,7 @@
   // Exact payload length will change with HPACK, but this should be long
   // enough to cause an overflow.
   const size_t kBigValueSize = TestSpdyVisitor::sent_control_frame_max_size();
-  string big_value(kBigValueSize, 'x');
+  SpdyString big_value(kBigValueSize, 'x');
   push_promise.SetHeader("aa", big_value);
   SpdySerializedFrame control_frame(framer.SerializePushPromise(push_promise));
   if (use_output_) {
@@ -2858,7 +2856,7 @@
   const size_t kHeaderBufferSize =
       TestSpdyVisitor::header_data_chunk_max_size() * kHeaderBufferChunks;
   const size_t kBigValueSize = kHeaderBufferSize * 2;
-  string big_value(kBigValueSize, 'x');
+  SpdyString big_value(kBigValueSize, 'x');
   SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   SpdyHeadersIR headers(1);
   headers.set_fin(true);
@@ -2899,7 +2897,7 @@
       0x00, 0x00, 0x00,          // Truncated Status Field
   };
   const size_t pad_length = length + kFrameHeaderSize - sizeof(kH2FrameData);
-  string pad(pad_length, 'A');
+  SpdyString pad(pad_length, 'A');
   TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION);
 
   visitor.SimulateInFramer(kH2FrameData, sizeof(kH2FrameData));
@@ -3663,7 +3661,7 @@
   EXPECT_EQ(20u, extension.length_);
   EXPECT_EQ(255, extension.type_);
   EXPECT_EQ(0xff, extension.flags_);
-  EXPECT_EQ(string(20, '\xff'), extension.payload_);
+  EXPECT_EQ(SpdyString(20, '\xff'), extension.payload_);
 
   // Follow it up with a valid control frame to make sure we handle
   // subsequent frames correctly.
@@ -4607,7 +4605,7 @@
   VLOG(1) << "frame1_size = " << frame1_size;
   VLOG(1) << "frame2_size = " << frame2_size;
 
-  string input_buffer;
+  SpdyString input_buffer;
   input_buffer.append(frame1.data(), frame1_size);
   input_buffer.append(frame2.data(), frame2_size);
 
@@ -4656,7 +4654,7 @@
   VLOG(1) << "frame1_size = " << frame1_size;
   VLOG(1) << "frame2_size = " << frame2_size;
 
-  string input_buffer;
+  SpdyString input_buffer;
   input_buffer.append(frame1.data(), frame1_size);
   input_buffer.append(frame2.data(), frame2_size);
 
diff --git a/net/spdy/spdy_header_block.cc b/net/spdy/spdy_header_block.cc
index 9430ef0b..1cb7b13 100644
--- a/net/spdy/spdy_header_block.cc
+++ b/net/spdy/spdy_header_block.cc
@@ -18,8 +18,6 @@
 #include "net/spdy/platform/api/spdy_estimate_memory_usage.h"
 #include "net/spdy/platform/api/spdy_string_utils.h"
 
-using std::string;
-
 namespace net {
 namespace {
 
@@ -209,11 +207,11 @@
   return *this;
 }
 
-string SpdyHeaderBlock::ValueProxy::as_string() const {
+SpdyString SpdyHeaderBlock::ValueProxy::as_string() const {
   if (lookup_result_ == block_->end()) {
     return "";
   } else {
-    return std::string(lookup_result_->second.value());
+    return SpdyString(lookup_result_->second.value());
   }
 }
 
@@ -245,12 +243,12 @@
   return !(operator==(other));
 }
 
-string SpdyHeaderBlock::DebugString() const {
+SpdyString SpdyHeaderBlock::DebugString() const {
   if (empty()) {
     return "{}";
   }
 
-  string output = "\n{\n";
+  SpdyString output = "\n{\n";
   for (auto it = begin(); it != end(); ++it) {
     SpdyStrAppend(&output, "  ", it->first, " ", it->second, "\n");
   }
@@ -361,7 +359,7 @@
 
   for (base::DictionaryValue::Iterator it(*header_dict); !it.IsAtEnd();
        it.Advance()) {
-    string value;
+    SpdyString value;
     if (!it.value().GetAsString(&value)) {
       headers->clear();
       return false;
diff --git a/net/spdy/spdy_header_block.h b/net/spdy/spdy_header_block.h
index 2e2cd39..10702c8 100644
--- a/net/spdy/spdy_header_block.h
+++ b/net/spdy/spdy_header_block.h
@@ -10,13 +10,13 @@
 #include <list>
 #include <map>
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "base/macros.h"
 #include "net/base/linked_hash_map.h"
 #include "net/base/net_export.h"
 #include "net/log/net_log.h"
+#include "net/spdy/platform/api/spdy_string.h"
 #include "net/spdy/platform/api/spdy_string_piece.h"
 
 namespace base {
@@ -150,7 +150,7 @@
 
   // Provides a human readable multi-line representation of the stored header
   // keys and values.
-  std::string DebugString() const;
+  SpdyString DebugString() const;
 
   iterator begin() { return iterator(block_.begin()); }
   iterator end() { return iterator(block_.end()); }
@@ -184,8 +184,8 @@
   ValueProxy operator[](const SpdyStringPiece key);
 
   // This object provides automatic conversions that allow SpdyHeaderBlock to be
-  // nearly a drop-in replacement for linked_hash_map<string, string>. It reads
-  // data from or writes data to a SpdyHeaderBlock::Storage.
+  // nearly a drop-in replacement for linked_hash_map<SpdyString, SpdyString>.
+  // It reads data from or writes data to a SpdyHeaderBlock::Storage.
   class NET_EXPORT ValueProxy {
    public:
     ~ValueProxy();
@@ -201,7 +201,7 @@
     // Assignment modifies the underlying SpdyHeaderBlock.
     ValueProxy& operator=(const SpdyStringPiece other);
 
-    std::string as_string() const;
+    SpdyString as_string() const;
 
    private:
     friend class SpdyHeaderBlock;
diff --git a/net/spdy/spdy_header_block_test.cc b/net/spdy/spdy_header_block_test.cc
index 75aa371..0828847 100644
--- a/net/spdy/spdy_header_block_test.cc
+++ b/net/spdy/spdy_header_block_test.cc
@@ -13,7 +13,6 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using std::string;
 using ::testing::ElementsAre;
 
 namespace net {
@@ -73,15 +72,15 @@
 // This test verifies that headers can be set in a variety of ways.
 TEST(SpdyHeaderBlockTest, AddHeaders) {
   SpdyHeaderBlock block;
-  block["foo"] = string(300, 'x');
+  block["foo"] = SpdyString(300, 'x');
   block["bar"] = "baz";
   block["qux"] = "qux1";
   block["qux"] = "qux2";
   block.insert(std::make_pair("key", "value"));
 
-  EXPECT_EQ(Pair("foo", string(300, 'x')), *block.find("foo"));
+  EXPECT_EQ(Pair("foo", SpdyString(300, 'x')), *block.find("foo"));
   EXPECT_EQ("baz", block["bar"]);
-  string qux("qux");
+  SpdyString qux("qux");
   EXPECT_EQ("qux2", block[qux]);
   ASSERT_NE(block.end(), block.find("key"));
   EXPECT_EQ(Pair("key", "value"), *block.find("key"));
@@ -93,7 +92,7 @@
 // This test verifies that SpdyHeaderBlock can be copied using Clone().
 TEST(SpdyHeaderBlockTest, CopyBlocks) {
   SpdyHeaderBlock block1;
-  block1["foo"] = string(300, 'x');
+  block1["foo"] = SpdyString(300, 'x');
   block1["bar"] = "baz";
   block1.insert(std::make_pair("qux", "qux1"));
 
@@ -164,7 +163,7 @@
   SpdyHeaderBlock block;
   block["foo"] = "foo";
   block.AppendValueOrAddHeader("foo", "bar");
-  EXPECT_EQ(Pair("foo", string("foo\0bar", 7)), *block.find("foo"));
+  EXPECT_EQ(Pair("foo", SpdyString("foo\0bar", 7)), *block.find("foo"));
 
   block.insert(std::make_pair("foo", "baz"));
   EXPECT_EQ("baz", block["foo"]);
@@ -188,9 +187,9 @@
 
   EXPECT_EQ("key1=value1; key2=value2; key3=value3", block["cookie"]);
   EXPECT_EQ("baz", block["foo"]);
-  EXPECT_EQ(string("h1v1\0h1v2\0h1v3", 14), block["h1"]);
-  EXPECT_EQ(string("h2v1\0h2v2\0h2v3", 14), block["h2"]);
-  EXPECT_EQ(string("h3v2\0h3v3", 9), block["h3"]);
+  EXPECT_EQ(SpdyString("h1v1\0h1v2\0h1v3", 14), block["h1"]);
+  EXPECT_EQ(SpdyString("h2v1\0h2v2\0h2v3", 14), block["h2"]);
+  EXPECT_EQ(SpdyString("h3v2\0h3v3", 9), block["h3"]);
   EXPECT_EQ("singleton", block["h4"]);
 }
 
diff --git a/net/spdy/spdy_header_indexing.cc b/net/spdy/spdy_header_indexing.cc
index 948b5a8..881ba86 100644
--- a/net/spdy/spdy_header_indexing.cc
+++ b/net/spdy/spdy_header_indexing.cc
@@ -21,7 +21,7 @@
 HeaderIndexing::~HeaderIndexing() {}
 
 void HeaderIndexing::CreateInitIndexingHeaders() {
-  const std::string initial_fields[] = {
+  const SpdyString initial_fields[] = {
       // Estimated top 100 fields.
       "alt-svc",
       "date",
@@ -139,7 +139,7 @@
     return false;
   }
   // header is in indexing set.
-  std::string header_str(header.data(), header.size());
+  SpdyString header_str(header.data(), header.size());
   if (indexing_set_.find(header_str) != indexing_set_.end()) {
     return true;
   }
@@ -156,7 +156,7 @@
   return false;
 }
 
-void HeaderIndexing::TryInsertHeader(std::string&& header,
+void HeaderIndexing::TryInsertHeader(SpdyString&& header,
                                      HeaderSet* set,
                                      size_t bound) {
   std::pair<HeaderSet::iterator, bool> result = set->insert(std::move(header));
diff --git a/net/spdy/spdy_header_indexing.h b/net/spdy/spdy_header_indexing.h
index a7d5c20..69cc08d 100644
--- a/net/spdy/spdy_header_indexing.h
+++ b/net/spdy/spdy_header_indexing.h
@@ -7,11 +7,11 @@
 
 #include <stdint.h>
 #include <memory>
-#include <string>
 #include <unordered_set>
 #include <utility>
 
 #include "net/base/net_export.h"
+#include "net/spdy/platform/api/spdy_string.h"
 #include "net/spdy/platform/api/spdy_string_piece.h"
 
 namespace net {
@@ -29,7 +29,7 @@
 // UpdateSets to log the headers into both sets.
 class NET_EXPORT HeaderIndexing {
  public:
-  using HeaderSet = std::unordered_set<std::string>;
+  using HeaderSet = std::unordered_set<SpdyString>;
 
   HeaderIndexing();
   ~HeaderIndexing();
@@ -53,7 +53,7 @@
 
  private:
   friend class test::HeaderIndexingPeer;
-  void TryInsertHeader(std::string&& header, HeaderSet* set, size_t bound);
+  void TryInsertHeader(SpdyString&& header, HeaderSet* set, size_t bound);
   // Headers to index.
   HeaderSet indexing_set_;
   // Headers seen so far.
diff --git a/net/spdy/spdy_header_indexing_test.cc b/net/spdy/spdy_header_indexing_test.cc
index b471c93d..8797fef 100644
--- a/net/spdy/spdy_header_indexing_test.cc
+++ b/net/spdy/spdy_header_indexing_test.cc
@@ -17,7 +17,7 @@
   HeaderIndexingPeer() : hi_() {}
 
   void CreateTestInit() {
-    std::string input[] = {"key1", "key2", "key3"};
+    SpdyString input[] = {"key1", "key2", "key3"};
     hi_.indexing_set_ =
         HeaderIndexing::HeaderSet(input, input + arraysize(input));
     hi_.tracking_set_ =
@@ -30,12 +30,12 @@
 
   void CreateInitIndexingHeaders() { hi_.CreateInitIndexingHeaders(); }
 
-  void TryInsert(std::string&& header) {
+  void TryInsert(SpdyString&& header) {
     hi_.TryInsertHeader(std::move(header), &(hi_.indexing_set_),
                         hi_.indexing_set_bound_);
   }
 
-  bool InTrackingSet(const std::string& str) {
+  bool InTrackingSet(const SpdyString& str) {
     return hi_.tracking_set_.find(str) != hi_.tracking_set_.end();
   }
 
@@ -67,16 +67,16 @@
 };
 
 TEST_F(SpdyHeaderIndexingTest, TestTryInsertHeader) {
-  std::string key("key4");
+  SpdyString key("key4");
   hi_->TryInsert(std::move(key));
   EXPECT_EQ(3u, hi_->indexing_set_size());
   EXPECT_TRUE(hi_->ShouldIndex("key4"));
 }
 
 TEST_F(SpdyHeaderIndexingTest, TestShouldIndex) {
-  std::string key3 = "key3";
-  std::string key4 = "key4";
-  std::string key5 = "key5";
+  SpdyString key3 = "key3";
+  SpdyString key4 = "key4";
+  SpdyString key5 = "key5";
   // Cache hit.
   EXPECT_TRUE(hi_->ShouldIndex(key3));
   EXPECT_EQ(3u, hi_->indexing_set_size());
diff --git a/net/spdy/spdy_http_stream.cc b/net/spdy/spdy_http_stream.cc
index 3a70ae6..8bfc4b7 100644
--- a/net/spdy/spdy_http_stream.cc
+++ b/net/spdy/spdy_http_stream.cc
@@ -7,7 +7,6 @@
 #include <algorithm>
 #include <list>
 #include <memory>
-#include <string>
 #include <utility>
 
 #include "base/bind.h"
@@ -24,6 +23,7 @@
 #include "net/http/http_response_info.h"
 #include "net/log/net_log_event_type.h"
 #include "net/log/net_log_with_source.h"
+#include "net/spdy/platform/api/spdy_string.h"
 #include "net/spdy/spdy_header_block.h"
 #include "net/spdy/spdy_http_utils.h"
 #include "net/spdy/spdy_protocol.h"
@@ -445,7 +445,7 @@
 
 void SpdyHttpStream::ResetStreamInternal() {
   spdy_session_->ResetStream(stream()->stream_id(), ERROR_CODE_INTERNAL_ERROR,
-                             std::string());
+                             SpdyString());
 }
 
 void SpdyHttpStream::OnRequestBodyReadCompleted(int status) {
diff --git a/net/spdy/spdy_http_stream_unittest.cc b/net/spdy/spdy_http_stream_unittest.cc
index 1db2e91..f6b1462 100644
--- a/net/spdy/spdy_http_stream_unittest.cc
+++ b/net/spdy/spdy_http_stream_unittest.cc
@@ -7,7 +7,6 @@
 #include <stdint.h>
 
 #include <memory>
-#include <string>
 
 #include "base/run_loop.h"
 #include "base/stl_util.h"
@@ -26,6 +25,7 @@
 #include "net/log/net_log_with_source.h"
 #include "net/log/test_net_log.h"
 #include "net/socket/socket_test_util.h"
+#include "net/spdy/platform/api/spdy_string.h"
 #include "net/spdy/spdy_http_utils.h"
 #include "net/spdy/spdy_session.h"
 #include "net/spdy/spdy_test_util_common.h"
@@ -161,10 +161,9 @@
         CreateSecureSpdySession(http_session_.get(), key_, NetLogWithSource());
   }
 
-  void TestSendCredentials(
-    ChannelIDService* channel_id_service,
-    const std::string& cert,
-    const std::string& proof);
+  void TestSendCredentials(ChannelIDService* channel_id_service,
+                           const SpdyString& cert,
+                           const SpdyString& proof);
 
   SpdyTestUtil spdy_util_;
   TestNetLog net_log_;
@@ -591,21 +590,21 @@
   ASSERT_EQ(kUploadDataSize,
             http_stream->ReadResponseBody(
                 buf1.get(), kUploadDataSize, callback.callback()));
-  EXPECT_EQ(kUploadData, std::string(buf1->data(), kUploadDataSize));
+  EXPECT_EQ(kUploadData, SpdyString(buf1->data(), kUploadDataSize));
 
   // Check |chunk2| response.
   scoped_refptr<IOBuffer> buf2(new IOBuffer(kUploadData1Size));
   ASSERT_EQ(kUploadData1Size,
             http_stream->ReadResponseBody(
                 buf2.get(), kUploadData1Size, callback.callback()));
-  EXPECT_EQ(kUploadData1, std::string(buf2->data(), kUploadData1Size));
+  EXPECT_EQ(kUploadData1, SpdyString(buf2->data(), kUploadData1Size));
 
   // Check |chunk3| response.
   scoped_refptr<IOBuffer> buf3(new IOBuffer(kUploadDataSize));
   ASSERT_EQ(kUploadDataSize,
             http_stream->ReadResponseBody(
                 buf3.get(), kUploadDataSize, callback.callback()));
-  EXPECT_EQ(kUploadData, std::string(buf3->data(), kUploadDataSize));
+  EXPECT_EQ(kUploadData, SpdyString(buf3->data(), kUploadDataSize));
 
   ASSERT_TRUE(response.headers.get());
   ASSERT_EQ(200, response.headers->response_code());
@@ -687,7 +686,7 @@
   ASSERT_EQ(kUploadDataSize,
             http_stream->ReadResponseBody(
                 buf1.get(), kUploadDataSize, callback.callback()));
-  EXPECT_EQ(kUploadData, std::string(buf1->data(), kUploadDataSize));
+  EXPECT_EQ(kUploadData, SpdyString(buf1->data(), kUploadDataSize));
 
   // Check |chunk2| response.
   ASSERT_EQ(0,
@@ -902,7 +901,7 @@
   ASSERT_EQ(kUploadDataSize,
             http_stream->ReadResponseBody(
                 buf1.get(), kUploadDataSize, callback.callback()));
-  EXPECT_EQ(kUploadData, std::string(buf1->data(), kUploadDataSize));
+  EXPECT_EQ(kUploadData, SpdyString(buf1->data(), kUploadDataSize));
 
   ASSERT_TRUE(response.headers.get());
   ASSERT_EQ(200, response.headers->response_code());
diff --git a/net/spdy/spdy_http_utils.cc b/net/spdy/spdy_http_utils.cc
index 005b198..24cd83c 100644
--- a/net/spdy/spdy_http_utils.cc
+++ b/net/spdy/spdy_http_utils.cc
@@ -4,8 +4,6 @@
 
 #include "net/spdy/spdy_http_utils.h"
 
-#include <string>
-
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
@@ -18,19 +16,20 @@
 #include "net/http/http_response_headers.h"
 #include "net/http/http_response_info.h"
 #include "net/http/http_util.h"
+#include "net/spdy/platform/api/spdy_string.h"
 #include "net/spdy/platform/api/spdy_string_piece.h"
 
 namespace net {
 
 namespace {
 
-void AddSpdyHeader(const std::string& name,
-                   const std::string& value,
+void AddSpdyHeader(const SpdyString& name,
+                   const SpdyString& value,
                    SpdyHeaderBlock* headers) {
   if (headers->find(name) == headers->end()) {
     (*headers)[name] = value;
   } else {
-    std::string joint_value = (*headers)[name].as_string();
+    SpdyString joint_value = (*headers)[name].as_string();
     joint_value.append(1, '\0');
     joint_value.append(value);
     (*headers)[name] = joint_value;
@@ -45,8 +44,8 @@
   SpdyHeaderBlock::const_iterator it = headers.find(":status");
   if (it == headers.end())
     return false;
-  std::string status = it->second.as_string();
-  std::string raw_headers("HTTP/1.1 ");
+  SpdyString status = it->second.as_string();
+  SpdyString raw_headers("HTTP/1.1 ");
   raw_headers.append(status);
   raw_headers.push_back('\0');
   for (it = headers.begin(); it != headers.end(); ++it) {
@@ -58,12 +57,12 @@
     // becomes
     //    Set-Cookie: foo\0
     //    Set-Cookie: bar\0
-    std::string value = it->second.as_string();
+    SpdyString value = it->second.as_string();
     size_t start = 0;
     size_t end = 0;
     do {
       end = value.find('\0', start);
-      std::string tval;
+      SpdyString tval;
       if (end != value.npos)
         tval = value.substr(start, (end - start));
       else
@@ -99,7 +98,7 @@
 
   HttpRequestHeaders::Iterator it(request_headers);
   while (it.GetNext()) {
-    std::string name = base::ToLowerASCII(it.name());
+    SpdyString name = base::ToLowerASCII(it.name());
     if (name.empty() || name[0] == ':' || name == "connection" ||
         name == "proxy-connection" || name == "transfer-encoding" ||
         name == "host") {
@@ -150,7 +149,7 @@
   SpdyHeaderBlock::const_iterator it = headers.find(":scheme");
   if (it == headers.end())
     return GURL();
-  std::string url = it->second.as_string();
+  SpdyString url = it->second.as_string();
   url.append("://");
 
   it = headers.find(":authority");
diff --git a/net/spdy/spdy_log_util.cc b/net/spdy/spdy_log_util.cc
index 6f3d6910..ec56b4b 100644
--- a/net/spdy/spdy_log_util.cc
+++ b/net/spdy/spdy_log_util.cc
@@ -11,16 +11,16 @@
 
 namespace net {
 
-std::string ElideGoAwayDebugDataForNetLog(NetLogCaptureMode capture_mode,
-                                          base::StringPiece debug_data) {
+SpdyString ElideGoAwayDebugDataForNetLog(NetLogCaptureMode capture_mode,
+                                         base::StringPiece debug_data) {
   // Note: this logic should be kept in sync with stripGoAwayDebugData in
   // chrome/browser/resources/net_internals/log_view_painter.js.
   if (capture_mode.include_cookies_and_credentials()) {
     return debug_data.as_string();
   }
 
-  return std::string("[") + base::SizeTToString(debug_data.size()) +
-         std::string(" bytes were stripped]");
+  return SpdyString("[") + base::SizeTToString(debug_data.size()) +
+         SpdyString(" bytes were stripped]");
 }
 
 std::unique_ptr<base::ListValue> ElideSpdyHeaderBlockForNetLog(
diff --git a/net/spdy/spdy_log_util.h b/net/spdy/spdy_log_util.h
index 1c564919..e2f98e6 100644
--- a/net/spdy/spdy_log_util.h
+++ b/net/spdy/spdy_log_util.h
@@ -6,11 +6,11 @@
 #define NET_SPDY_SPDY_LOG_UTIL_H_
 
 #include <memory>
-#include <string>
 
 #include "base/strings/string_piece.h"
 #include "net/base/net_export.h"
 #include "net/log/net_log_capture_mode.h"
+#include "net/spdy/platform/api/spdy_string.h"
 #include "net/spdy/spdy_header_block.h"
 
 namespace base {
@@ -21,9 +21,9 @@
 
 // Given an HTTP/2 GOAWAY frame |debug_data|, returns the elided version
 // according to |capture_mode|.
-NET_EXPORT_PRIVATE std::string ElideGoAwayDebugDataForNetLog(
-    NetLogCaptureMode capture_mode,
-    base::StringPiece debug_data);
+NET_EXPORT_PRIVATE SpdyString
+ElideGoAwayDebugDataForNetLog(NetLogCaptureMode capture_mode,
+                              base::StringPiece debug_data);
 
 // Given a SpdyHeaderBlock, return its base::ListValue representation.
 NET_EXPORT_PRIVATE std::unique_ptr<base::ListValue>
diff --git a/net/spdy/spdy_log_util_unittest.cc b/net/spdy/spdy_log_util_unittest.cc
index 40ac1c91..7c1f6d7d 100644
--- a/net/spdy/spdy_log_util_unittest.cc
+++ b/net/spdy/spdy_log_util_unittest.cc
@@ -27,7 +27,7 @@
   std::unique_ptr<base::ListValue> list =
       ElideSpdyHeaderBlockForNetLog(headers, NetLogCaptureMode::Default());
   EXPECT_EQ(2u, list->GetSize());
-  std::string field;
+  SpdyString field;
   EXPECT_TRUE(list->GetString(0, &field));
   EXPECT_EQ("foo: bar", field);
   EXPECT_TRUE(list->GetString(1, &field));
diff --git a/net/spdy/spdy_network_transaction_unittest.cc b/net/spdy/spdy_network_transaction_unittest.cc
index 6e93f7f7..f00777d 100644
--- a/net/spdy/spdy_network_transaction_unittest.cc
+++ b/net/spdy/spdy_network_transaction_unittest.cc
@@ -4,7 +4,6 @@
 
 #include <cmath>
 #include <memory>
-#include <string>
 #include <utility>
 #include <vector>
 
@@ -39,6 +38,7 @@
 #include "net/socket/client_socket_pool_base.h"
 #include "net/socket/next_proto.h"
 #include "net/spdy/buffered_spdy_framer.h"
+#include "net/spdy/platform/api/spdy_string.h"
 #include "net/spdy/platform/api/spdy_string_piece.h"
 #include "net/spdy/spdy_http_stream.h"
 #include "net/spdy/spdy_http_utils.h"
@@ -93,8 +93,8 @@
 
   struct TransactionHelperResult {
     int rv;
-    std::string status_line;
-    std::string response_data;
+    SpdyString status_line;
+    SpdyString response_data;
     HttpResponseInfo response_info;
   };
 
@@ -407,8 +407,7 @@
   // multiple transactions in the read pipeline; so as we read, we may have
   // to skip over data destined for other transactions while we consume
   // the data for |trans|.
-  int ReadResult(HttpNetworkTransaction* trans,
-                 std::string* result) {
+  int ReadResult(HttpNetworkTransaction* trans, SpdyString* result) {
     const int kSize = 3000;
 
     int bytes_read = 0;
@@ -448,7 +447,7 @@
   void RunServerPushTest(SequencedSocketData* data,
                          HttpResponseInfo* response,
                          HttpResponseInfo* push_response,
-                         const std::string& expected) {
+                         const SpdyString& expected) {
     NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY,
                                        NetLogWithSource(), nullptr);
     helper.RunPreTestSetup();
@@ -477,10 +476,10 @@
     // the results into a single string.
 
     // Read the server push body.
-    std::string result2;
+    SpdyString result2;
     ReadResult(&trans2, &result2);
     // Read the response body.
-    std::string result;
+    SpdyString result;
     ReadResult(trans, &result);
 
     // Verify that we consumed all test data.
@@ -565,8 +564,8 @@
     return upload_chunked_data_stream_.get();
   }
 
-  std::string GetDefaultUrlWithPath(const char* path) {
-    return std::string(kDefaultUrl) + path;
+  SpdyString GetDefaultUrlWithPath(const char* path) {
+    return SpdyString(kDefaultUrl) + path;
   }
 
   const GURL default_url_;
@@ -1672,7 +1671,7 @@
   helper.FinishDefaultTest();
   helper.VerifyDataConsumed();
 
-  std::string expected_response;
+  SpdyString expected_response;
   expected_response += kUploadData;
   expected_response += kUploadData;
   expected_response += kUploadData;
@@ -1798,7 +1797,7 @@
   helper.WaitForCallbackToComplete();
   EXPECT_THAT(helper.output().rv, IsOk());
 
-  std::string response_body;
+  SpdyString response_body;
   EXPECT_THAT(ReadTransaction(helper.trans(), &response_body), IsOk());
   EXPECT_EQ(kUploadData, response_body);
 
@@ -1903,7 +1902,7 @@
   ASSERT_TRUE(response);
   EXPECT_TRUE(response->headers);
   EXPECT_TRUE(response->was_fetched_via_spdy);
-  std::string response_data;
+  SpdyString response_data;
   rv = ReadTransaction(trans, &response_data);
   EXPECT_THAT(rv, IsError(ERR_SPDY_PROTOCOL_ERROR));
 
@@ -2237,7 +2236,7 @@
       spdy_util_.ConstructSpdyDataFrame(1, "should not include", 18, true));
 
   SpdyHeaderBlock push_headers;
-  spdy_util_.AddUrlToHeaderBlock(std::string(kDefaultUrl) + "b.dat",
+  spdy_util_.AddUrlToHeaderBlock(SpdyString(kDefaultUrl) + "b.dat",
                                  &push_headers);
 
   SpdySerializedFrame push_init_frame(
@@ -2354,7 +2353,7 @@
     EXPECT_EQ(1, d.response_started_count());
     EXPECT_FALSE(d.received_data_before_response());
     EXPECT_EQ(OK, d.request_status());
-    std::string contents("hello!");
+    SpdyString contents("hello!");
     EXPECT_EQ(contents, d.data_received());
   }
   EXPECT_TRUE(data.AllReadDataConsumed());
@@ -2422,7 +2421,7 @@
     base::RunLoop().Run();
 
     EXPECT_EQ(0, d.received_redirect_count());
-    std::string contents("hello!");
+    SpdyString contents("hello!");
     EXPECT_EQ(contents, d.data_received());
 
     std::unique_ptr<URLRequest> r2(spdy_url_request_context.CreateRequest(
@@ -2440,7 +2439,7 @@
     EXPECT_EQ(1, d2.response_started_count());
     EXPECT_FALSE(d2.received_data_before_response());
     EXPECT_EQ(OK, d2.request_status());
-    std::string contents2("hello!");
+    SpdyString contents2("hello!");
     EXPECT_EQ(contents2, d2.data_received());
   }
   EXPECT_TRUE(data.AllReadDataConsumed());
@@ -2474,7 +2473,7 @@
 
   HttpResponseInfo response;
   HttpResponseInfo response2;
-  std::string expected_push_result("pushed");
+  SpdyString expected_push_result("pushed");
   SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes));
   RunServerPushTest(&data,
                     &response,
@@ -2517,7 +2516,7 @@
 
   HttpResponseInfo response;
   HttpResponseInfo response2;
-  std::string expected_push_result("pushed");
+  SpdyString expected_push_result("pushed");
   SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes));
   RunServerPushTest(&data,
                     &response,
@@ -2560,7 +2559,7 @@
 
   HttpResponseInfo response;
   HttpResponseInfo response2;
-  std::string expected_push_result("pushed");
+  SpdyString expected_push_result("pushed");
   SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes));
   RunServerPushTest(&data,
                     &response,
@@ -2773,7 +2772,7 @@
 
   HttpResponseInfo response;
   HttpResponseInfo response2;
-  std::string expected_push_result("pushed");
+  SpdyString expected_push_result("pushed");
   SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes));
   RunServerPushTest(&data,
                     &response,
@@ -2829,7 +2828,7 @@
 
   HttpResponseInfo response;
   HttpResponseInfo response2;
-  std::string expected_push_result("pushed my darling hello my baby");
+  SpdyString expected_push_result("pushed my darling hello my baby");
   SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes));
   RunServerPushTest(&data, &response, &response2, kPushedData);
 
@@ -3128,7 +3127,7 @@
   response = *trans2.GetResponseInfo();
   EXPECT_TRUE(response.headers);
   EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine());
-  std::string result;
+  SpdyString result;
   ReadResult(&trans2, &result);
   EXPECT_EQ(kPushedData, result);
 
@@ -3200,14 +3199,14 @@
     scoped_refptr<HttpResponseHeaders> headers = out.response_info.headers;
     EXPECT_TRUE(headers);
     size_t iter = 0;
-    std::string name, value;
+    SpdyString name, value;
     SpdyHeaderBlock header_block;
     while (headers->EnumerateHeaderLines(&iter, &name, &value)) {
       auto value_it = header_block.find(name);
       if (value_it == header_block.end() || value_it->second.empty()) {
         header_block[name] = value;
       } else {
-        std::string joint_value = value_it->second.as_string();
+        SpdyString joint_value = value_it->second.as_string();
         joint_value.append(1, '\0');
         joint_value.append(value);
         header_block[name] = joint_value;
@@ -3270,7 +3269,7 @@
                         test_cases[i].num_headers[1],
                         &reply_headers);
     // Construct the expected header reply string before moving |reply_headers|.
-    std::string expected_reply =
+    SpdyString expected_reply =
         spdy_test_util.ConstructSpdyReplyString(reply_headers);
 
     SpdySerializedFrame frame_reply(
@@ -3311,7 +3310,7 @@
     scoped_refptr<HttpResponseHeaders> headers = out.response_info.headers;
     ASSERT_TRUE(headers) << i;
     size_t iter = 0;
-    std::string name, value, lines;
+    SpdyString name, value, lines;
     while (headers->EnumerateHeaderLines(&iter, &name, &value)) {
       lines.append(name);
       lines.append(": ");
@@ -3568,18 +3567,16 @@
   ASSERT_TRUE(entries[pos].params.get());
   ASSERT_TRUE(entries[pos].params->GetList("headers", &header_list));
 
-  std::vector<std::string> expected;
-  expected.push_back(std::string(spdy_util_.GetHostKey()) +
-                     ": www.example.org");
-  expected.push_back(std::string(spdy_util_.GetPathKey()) + ": /");
-  expected.push_back(std::string(spdy_util_.GetSchemeKey()) + ": " +
+  std::vector<SpdyString> expected;
+  expected.push_back(SpdyString(spdy_util_.GetHostKey()) + ": www.example.org");
+  expected.push_back(SpdyString(spdy_util_.GetPathKey()) + ": /");
+  expected.push_back(SpdyString(spdy_util_.GetSchemeKey()) + ": " +
                      default_url_.scheme());
-  expected.push_back(std::string(spdy_util_.GetMethodKey()) + ": GET");
+  expected.push_back(SpdyString(spdy_util_.GetMethodKey()) + ": GET");
   expected.push_back("user-agent: Chrome");
   EXPECT_EQ(expected.size(), header_list->GetSize());
-  for (std::vector<std::string>::const_iterator it = expected.begin();
-       it != expected.end();
-       ++it) {
+  for (std::vector<SpdyString>::const_iterator it = expected.begin();
+       it != expected.end(); ++it) {
     base::Value header(*it);
     EXPECT_NE(header_list->end(), header_list->Find(header)) <<
         "Header not found: " << *it;
@@ -3646,7 +3643,7 @@
   // Read Data
   TestCompletionCallback read_callback;
 
-  std::string content;
+  SpdyString content;
   do {
     // Read small chunks at a time.
     const int kSmallReadSize = 3;
@@ -3731,7 +3728,7 @@
   // Read Data
   TestCompletionCallback read_callback;
 
-  std::string content;
+  SpdyString content;
   int reads_completed = 0;
   do {
     // Read small chunks at a time.
@@ -3817,7 +3814,7 @@
   // Read Data
   TestCompletionCallback read_callback;
 
-  std::string content;
+  SpdyString content;
   int reads_completed = 0;
   do {
     // Read small chunks at a time.
@@ -3900,7 +3897,7 @@
   // Read Data
   TestCompletionCallback read_callback;
 
-  std::string content;
+  SpdyString content;
   int reads_completed = 0;
   do {
     // Read small chunks at a time.
@@ -4085,7 +4082,7 @@
   EXPECT_TRUE(response->was_alpn_negotiated);
   EXPECT_EQ("127.0.0.1", response->socket_address.host());
   EXPECT_EQ(443, response->socket_address.port());
-  std::string response_data;
+  SpdyString response_data;
   rv = ReadTransaction(&trans2, &response_data);
   EXPECT_THAT(rv, IsOk());
   EXPECT_EQ("hello!", response_data);
@@ -4201,7 +4198,7 @@
   EXPECT_TRUE(request.url.SchemeIs("https"));
   EXPECT_EQ("127.0.0.1", response->socket_address.host());
   EXPECT_EQ(443, response->socket_address.port());
-  std::string response_data;
+  SpdyString response_data;
   ASSERT_THAT(ReadTransaction(helper.trans(), &response_data), IsOk());
   EXPECT_EQ("hello", response_data);
 }
@@ -4296,7 +4293,7 @@
   EXPECT_TRUE(request.url.SchemeIs("https"));
   EXPECT_EQ("127.0.0.1", response->socket_address.host());
   EXPECT_EQ(70, response->socket_address.port());
-  std::string response_data;
+  SpdyString response_data;
   ASSERT_THAT(ReadTransaction(helper.trans(), &response_data), IsOk());
   EXPECT_EQ("hello", response_data);
 }
@@ -4348,7 +4345,7 @@
   ASSERT_TRUE(response.headers);
   EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine());
 
-  std::string response_data;
+  SpdyString response_data;
   ASSERT_THAT(ReadTransaction(trans, &response_data), IsOk());
   EXPECT_EQ("hello!", response_data);
   helper.VerifyDataConsumed();
@@ -4471,7 +4468,7 @@
   ASSERT_TRUE(response_proxy.headers);
   EXPECT_EQ("HTTP/1.1 200", response_proxy.headers->GetStatusLine());
 
-  std::string response_data;
+  SpdyString response_data;
   ASSERT_THAT(ReadTransaction(trans_proxy, &response_data), IsOk());
   EXPECT_EQ("hello!", response_data);
 
@@ -4554,7 +4551,7 @@
       ASSERT_TRUE(response);
       EXPECT_TRUE(response->headers);
       EXPECT_TRUE(response->was_fetched_via_spdy);
-      std::string response_data;
+      SpdyString response_data;
       rv = ReadTransaction(&trans, &response_data);
       EXPECT_THAT(rv, IsOk());
       EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine());
@@ -4695,7 +4692,7 @@
 
   HttpResponseInfo response;
   HttpResponseInfo response2;
-  std::string expected_push_result("pushed");
+  SpdyString expected_push_result("pushed");
   SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes));
   RunServerPushTest(&data,
                     &response,
@@ -4747,7 +4744,7 @@
 
   HttpResponseInfo response;
   HttpResponseInfo response2;
-  std::string expected_push_result("pushed");
+  SpdyString expected_push_result("pushed");
   SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes));
 
   NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY,
@@ -4779,10 +4776,10 @@
   base::RunLoop().RunUntilIdle();
 
   // Read the server push body.
-  std::string result2;
+  SpdyString result2;
   ReadResult(&trans2, &result2);
   // Read the response body.
-  std::string result;
+  SpdyString result;
   ReadResult(trans, &result);
 
   // Verify that the received push data is same as the expected push data.
@@ -4970,7 +4967,7 @@
     base::RunLoop().RunUntilIdle();
 
     // Read the response body.
-    std::string result;
+    SpdyString result;
     ReadResult(trans, &result);
 
     // Verify that we consumed all test data.
@@ -5067,7 +5064,7 @@
   EXPECT_TRUE(response.headers);
   EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine());
 
-  std::string result0;
+  SpdyString result0;
   ReadResult(trans0, &result0);
   EXPECT_EQ("hello!", result0);
 
@@ -5075,7 +5072,7 @@
   EXPECT_TRUE(push_response.headers);
   EXPECT_EQ("HTTP/1.1 200", push_response.headers->GetStatusLine());
 
-  std::string result1;
+  SpdyString result1;
   ReadResult(&trans1, &result1);
   EXPECT_EQ(kPushedData, result1);
 
@@ -5231,7 +5228,7 @@
   EXPECT_TRUE(response0.headers);
   EXPECT_EQ("HTTP/1.1 200", response0.headers->GetStatusLine());
 
-  std::string result0;
+  SpdyString result0;
   ReadResult(trans0, &result0);
   EXPECT_EQ(kData0, result0);
 
@@ -5239,7 +5236,7 @@
   EXPECT_TRUE(response1.headers);
   EXPECT_EQ("HTTP/1.1 200", response1.headers->GetStatusLine());
 
-  std::string result1;
+  SpdyString result1;
   ReadResult(&trans1, &result1);
   EXPECT_EQ(kData1, result1);
 
@@ -5247,7 +5244,7 @@
   EXPECT_TRUE(push_response.headers);
   EXPECT_EQ("HTTP/1.1 200", push_response.headers->GetStatusLine());
 
-  std::string result2;
+  SpdyString result2;
   ReadResult(&trans2, &result2);
   EXPECT_EQ(kPushedData, result2);
 
@@ -5472,8 +5469,8 @@
 // fail under specific circumstances.
 TEST_F(SpdyNetworkTransactionTest, WindowUpdateReceived) {
   static int kFrameCount = 2;
-  std::unique_ptr<std::string> content(
-      new std::string(kMaxSpdyFrameChunkSize, 'a'));
+  std::unique_ptr<SpdyString> content(
+      new SpdyString(kMaxSpdyFrameChunkSize, 'a'));
   SpdySerializedFrame req(spdy_util_.ConstructSpdyPost(
       kDefaultUrl, 1, kMaxSpdyFrameChunkSize * kFrameCount, LOWEST, nullptr,
       0));
@@ -5616,7 +5613,7 @@
   reads.push_back(CreateMockRead(resp, writes.size() + reads.size()));
 
   std::vector<SpdySerializedFrame> body_frames;
-  const std::string body_data(kChunkSize, 'x');
+  const SpdyString body_data(kChunkSize, 'x');
   for (size_t remaining = kTargetSize; remaining != 0;) {
     size_t frame_size = std::min(remaining, body_data.size());
     body_frames.push_back(spdy_util_.ConstructSpdyDataFrame(1, body_data.data(),
@@ -5699,8 +5696,8 @@
   // set content-length header correctly)
   static int kFrameCount = 3;
 
-  std::unique_ptr<std::string> content(
-      new std::string(kMaxSpdyFrameChunkSize, 'a'));
+  std::unique_ptr<SpdyString> content(
+      new SpdyString(kMaxSpdyFrameChunkSize, 'a'));
   SpdySerializedFrame req(spdy_util_.ConstructSpdyPost(
       kDefaultUrl, 1, kMaxSpdyFrameChunkSize * kFrameCount, LOWEST, nullptr,
       0));
@@ -5784,7 +5781,7 @@
       ceil(static_cast<double>(kBufferSize) / kMaxSpdyFrameChunkSize);
 
   // Construct content for a data frame of maximum size.
-  std::string content(kMaxSpdyFrameChunkSize, 'a');
+  SpdyString content(kMaxSpdyFrameChunkSize, 'a');
 
   SpdySerializedFrame req(spdy_util_.ConstructSpdyPost(
       kDefaultUrl, 1,
@@ -5808,8 +5805,8 @@
 
   // If kBufferSize * num_upload_buffers > initial_window_size,
   // we need one additional frame to send the rest of 'a'.
-  std::string last_body(kBufferSize * num_upload_buffers - initial_window_size,
-                        'a');
+  SpdyString last_body(kBufferSize * num_upload_buffers - initial_window_size,
+                       'a');
   SpdySerializedFrame body4(spdy_util_.ConstructSpdyDataFrame(
       1, last_body.c_str(), last_body.size(), false));
 
@@ -5866,7 +5863,7 @@
                            writes.size());
 
   std::vector<std::unique_ptr<UploadElementReader>> element_readers;
-  std::string upload_data_string(kBufferSize * num_upload_buffers, 'a');
+  SpdyString upload_data_string(kBufferSize * num_upload_buffers, 'a');
   upload_data_string.append(kUploadData, kUploadDataSize);
   element_readers.push_back(base::WrapUnique(new UploadBytesElementReader(
       upload_data_string.c_str(), upload_data_string.size())));
@@ -5934,7 +5931,7 @@
       ceil(static_cast<double>(kBufferSize) / kMaxSpdyFrameChunkSize);
 
   // Construct content for a data frame of maximum size.
-  std::string content(kMaxSpdyFrameChunkSize, 'a');
+  SpdyString content(kMaxSpdyFrameChunkSize, 'a');
 
   SpdySerializedFrame req(spdy_util_.ConstructSpdyPost(
       kDefaultUrl, 1,
@@ -5958,8 +5955,8 @@
 
   // If kBufferSize * num_upload_buffers > initial_window_size,
   // we need one additional frame to send the rest of 'a'.
-  std::string last_body(kBufferSize * num_upload_buffers - initial_window_size,
-                        'a');
+  SpdyString last_body(kBufferSize * num_upload_buffers - initial_window_size,
+                       'a');
   SpdySerializedFrame body4(spdy_util_.ConstructSpdyDataFrame(
       1, last_body.c_str(), last_body.size(), false));
 
@@ -6025,7 +6022,7 @@
                            writes.size());
 
   std::vector<std::unique_ptr<UploadElementReader>> element_readers;
-  std::string upload_data_string(kBufferSize * num_upload_buffers, 'a');
+  SpdyString upload_data_string(kBufferSize * num_upload_buffers, 'a');
   upload_data_string.append(kUploadData, kUploadDataSize);
   element_readers.push_back(base::WrapUnique(new UploadBytesElementReader(
       upload_data_string.c_str(), upload_data_string.size())));
@@ -6096,7 +6093,7 @@
       ceil(static_cast<double>(kBufferSize) / kMaxSpdyFrameChunkSize);
 
   // Construct content for a data frame of maximum size.
-  std::string content(kMaxSpdyFrameChunkSize, 'a');
+  SpdyString content(kMaxSpdyFrameChunkSize, 'a');
 
   SpdySerializedFrame req(spdy_util_.ConstructSpdyPost(
       kDefaultUrl, 1,
@@ -6120,8 +6117,8 @@
 
   // If kBufferSize * num_upload_buffers > initial_window_size,
   // we need one additional frame to send the rest of 'a'.
-  std::string last_body(kBufferSize * num_upload_buffers - initial_window_size,
-                        'a');
+  SpdyString last_body(kBufferSize * num_upload_buffers - initial_window_size,
+                       'a');
   SpdySerializedFrame body4(spdy_util_.ConstructSpdyDataFrame(
       1, last_body.c_str(), last_body.size(), false));
 
@@ -6189,7 +6186,7 @@
                            writes.size());
 
   std::vector<std::unique_ptr<UploadElementReader>> element_readers;
-  std::string upload_data_string(kBufferSize * num_upload_buffers, 'a');
+  SpdyString upload_data_string(kBufferSize * num_upload_buffers, 'a');
   upload_data_string.append(kUploadData, kUploadDataSize);
   element_readers.push_back(base::WrapUnique(new UploadBytesElementReader(
       upload_data_string.c_str(), upload_data_string.size())));
@@ -6297,8 +6294,8 @@
 // Regression test for https://crbug.com/493348: request header exceeds 16 kB
 // and thus sent in multiple frames when using HTTP/2.
 TEST_F(SpdyNetworkTransactionTest, LargeRequest) {
-  const std::string kKey("foo");
-  const std::string kValue(1 << 15, 'z');
+  const SpdyString kKey("foo");
+  const SpdyString kValue(1 << 15, 'z');
 
   HttpRequestInfo request;
   request.method = "GET";
@@ -6342,9 +6339,9 @@
 
   // HPACK decoder implementation limits string literal length to 16 kB.
   const char* response_headers[2];
-  const std::string kKey(16 * 1024, 'a');
+  const SpdyString kKey(16 * 1024, 'a');
   response_headers[0] = kKey.data();
-  const std::string kValue(16 * 1024, 'b');
+  const SpdyString kValue(16 * 1024, 'b');
   response_headers[1] = kValue.data();
 
   SpdySerializedFrame resp(
diff --git a/net/spdy/spdy_pinnable_buffer_piece_test.cc b/net/spdy/spdy_pinnable_buffer_piece_test.cc
index ab117a2e..e4687eee9 100644
--- a/net/spdy/spdy_pinnable_buffer_piece_test.cc
+++ b/net/spdy/spdy_pinnable_buffer_piece_test.cc
@@ -4,6 +4,7 @@
 
 #include "net/spdy/spdy_pinnable_buffer_piece.h"
 
+#include "net/spdy/platform/api/spdy_string.h"
 #include "net/spdy/spdy_prefixed_buffer_reader.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -13,14 +14,14 @@
 
 class SpdyPinnableBufferPieceTest : public ::testing::Test {
  protected:
-  SpdyPrefixedBufferReader Build(const std::string& prefix,
-                                 const std::string& suffix) {
+  SpdyPrefixedBufferReader Build(const SpdyString& prefix,
+                                 const SpdyString& suffix) {
     prefix_ = prefix;
     suffix_ = suffix;
     return SpdyPrefixedBufferReader(prefix_.data(), prefix_.length(),
                                     suffix_.data(), suffix_.length());
   }
-  std::string prefix_, suffix_;
+  SpdyString prefix_, suffix_;
 };
 
 TEST_F(SpdyPinnableBufferPieceTest, Pin) {
diff --git a/net/spdy/spdy_prefixed_buffer_reader_test.cc b/net/spdy/spdy_prefixed_buffer_reader_test.cc
index f14d3454..9e08b42e 100644
--- a/net/spdy/spdy_prefixed_buffer_reader_test.cc
+++ b/net/spdy/spdy_prefixed_buffer_reader_test.cc
@@ -4,6 +4,7 @@
 
 #include "net/spdy/spdy_prefixed_buffer_reader.h"
 
+#include "net/spdy/platform/api/spdy_string.h"
 #include "net/spdy/platform/api/spdy_string_piece.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -16,14 +17,14 @@
 
 class SpdyPrefixedBufferReaderTest : public ::testing::Test {
  protected:
-  SpdyPrefixedBufferReader Build(const std::string& prefix,
-                                 const std::string& suffix) {
+  SpdyPrefixedBufferReader Build(const SpdyString& prefix,
+                                 const SpdyString& suffix) {
     prefix_ = prefix;
     suffix_ = suffix;
     return SpdyPrefixedBufferReader(prefix_.data(), prefix_.length(),
                                     suffix_.data(), suffix_.length());
   }
-  std::string prefix_, suffix_;
+  SpdyString prefix_, suffix_;
 };
 
 TEST_F(SpdyPrefixedBufferReaderTest, ReadRawFromPrefix) {
diff --git a/net/spdy/spdy_protocol.cc b/net/spdy/spdy_protocol.cc
index 435c8417..7d287ca 100644
--- a/net/spdy/spdy_protocol.cc
+++ b/net/spdy/spdy_protocol.cc
@@ -228,9 +228,9 @@
 SpdyDataIR::SpdyDataIR(SpdyStreamId stream_id, const char* data)
     : SpdyDataIR(stream_id, SpdyStringPiece(data)) {}
 
-SpdyDataIR::SpdyDataIR(SpdyStreamId stream_id, std::string data)
+SpdyDataIR::SpdyDataIR(SpdyStreamId stream_id, SpdyString data)
     : SpdyFrameWithFinIR(stream_id),
-      data_store_(base::MakeUnique<std::string>(std::move(data))),
+      data_store_(base::MakeUnique<SpdyString>(std::move(data))),
       data_(data_store_->data()),
       data_len_(data_store_->size()),
       padded_(false),
@@ -306,7 +306,7 @@
 
 SpdyGoAwayIR::SpdyGoAwayIR(SpdyStreamId last_good_stream_id,
                            SpdyErrorCode error_code,
-                           std::string description)
+                           SpdyString description)
     : description_store_(std::move(description)),
       description_(description_store_) {
   set_last_good_stream_id(last_good_stream_id);
@@ -325,7 +325,7 @@
 
 SpdyContinuationIR::SpdyContinuationIR(SpdyStreamId stream_id)
     : SpdyFrameWithStreamIdIR(stream_id), end_headers_(false) {
-  encoding_ = base::MakeUnique<std::string>();
+  encoding_ = base::MakeUnique<SpdyString>();
 }
 
 SpdyContinuationIR::~SpdyContinuationIR() {}
diff --git a/net/spdy/spdy_protocol.h b/net/spdy/spdy_protocol.h
index a10d9da..a81539c 100644
--- a/net/spdy/spdy_protocol.h
+++ b/net/spdy/spdy_protocol.h
@@ -16,7 +16,6 @@
 #include <limits>
 #include <map>
 #include <memory>
-#include <string>
 #include <utility>
 
 #include "base/compiler_specific.h"
@@ -24,6 +23,7 @@
 #include "base/macros.h"
 #include "base/sys_byteorder.h"
 #include "net/base/net_export.h"
+#include "net/spdy/platform/api/spdy_string.h"
 #include "net/spdy/platform/api/spdy_string_piece.h"
 #include "net/spdy/spdy_alt_svc_wire_format.h"
 #include "net/spdy/spdy_bitmasks.h"
@@ -464,7 +464,7 @@
   SpdyDataIR(SpdyStreamId stream_id, const char* data);
 
   // Moves data into data_store_. Makes a copy if passed a non-movable string.
-  SpdyDataIR(SpdyStreamId stream_id, std::string data);
+  SpdyDataIR(SpdyStreamId stream_id, SpdyString data);
 
   // Use in conjunction with SetDataShallow() for shallow-copy on data.
   explicit SpdyDataIR(SpdyStreamId stream_id);
@@ -488,7 +488,7 @@
 
   // Deep-copy of data (keep private copy).
   void SetDataDeep(SpdyStringPiece data) {
-    data_store_.reset(new std::string(data.data(), data.size()));
+    data_store_.reset(new SpdyString(data.data(), data.size()));
     data_ = data_store_->data();
     data_len_ = data.size();
   }
@@ -514,7 +514,7 @@
 
  private:
   // Used to store data that this SpdyDataIR should own.
-  std::unique_ptr<std::string> data_store_;
+  std::unique_ptr<SpdyString> data_store_;
   const char* data_;
   size_t data_len_;
 
@@ -606,7 +606,7 @@
   // keep description live after constructing this SpdyGoAwayIR.
   SpdyGoAwayIR(SpdyStreamId last_good_stream_id,
                SpdyErrorCode error_code,
-               std::string description);
+               SpdyString description);
 
   ~SpdyGoAwayIR() override;
   SpdyStreamId last_good_stream_id() const { return last_good_stream_id_; }
@@ -629,7 +629,7 @@
  private:
   SpdyStreamId last_good_stream_id_;
   SpdyErrorCode error_code_;
-  const std::string description_store_;
+  const SpdyString description_store_;
   const SpdyStringPiece description_;
 
   DISALLOW_COPY_AND_ASSIGN(SpdyGoAwayIR);
@@ -748,13 +748,13 @@
 
   bool end_headers() const { return end_headers_; }
   void set_end_headers(bool end_headers) {end_headers_ = end_headers;}
-  const std::string& encoding() const { return *encoding_; }
-  void take_encoding(std::unique_ptr<std::string> encoding) {
+  const SpdyString& encoding() const { return *encoding_; }
+  void take_encoding(std::unique_ptr<SpdyString> encoding) {
     encoding_ = std::move(encoding);
   }
 
  private:
-  std::unique_ptr<std::string> encoding_;
+  std::unique_ptr<SpdyString> encoding_;
   bool end_headers_;
   DISALLOW_COPY_AND_ASSIGN(SpdyContinuationIR);
 };
@@ -764,12 +764,12 @@
   explicit SpdyAltSvcIR(SpdyStreamId stream_id);
   ~SpdyAltSvcIR() override;
 
-  std::string origin() const { return origin_; }
+  SpdyString origin() const { return origin_; }
   const SpdyAltSvcWireFormat::AlternativeServiceVector& altsvc_vector() const {
     return altsvc_vector_;
   }
 
-  void set_origin(std::string origin) { origin_ = std::move(origin); }
+  void set_origin(SpdyString origin) { origin_ = std::move(origin); }
   void add_altsvc(const SpdyAltSvcWireFormat::AlternativeService& altsvc) {
     altsvc_vector_.push_back(altsvc);
   }
@@ -779,7 +779,7 @@
   SpdyFrameType frame_type() const override;
 
  private:
-  std::string origin_;
+  SpdyString origin_;
   SpdyAltSvcWireFormat::AlternativeServiceVector altsvc_vector_;
   DISALLOW_COPY_AND_ASSIGN(SpdyAltSvcIR);
 };
diff --git a/net/spdy/spdy_protocol_test.cc b/net/spdy/spdy_protocol_test.cc
index cc12a62..7dffc98 100644
--- a/net/spdy/spdy_protocol_test.cc
+++ b/net/spdy/spdy_protocol_test.cc
@@ -14,8 +14,6 @@
 #include "net/test/gtest_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using std::string;
-
 namespace net {
 
 std::ostream& operator<<(std::ostream& os,
@@ -128,7 +126,7 @@
   struct {
     SpdySettingsIds setting_id;
     bool expected_bool;
-    const string expected_string;
+    const SpdyString expected_string;
   } test_cases[] = {
       {static_cast<SpdySettingsIds>(0), false, "SETTINGS_UNKNOWN"},
       {SETTINGS_HEADER_TABLE_SIZE, true, "SETTINGS_HEADER_TABLE_SIZE"},
@@ -234,19 +232,19 @@
   EXPECT_NE(SpdyStringPiece(d1.data(), d1.data_len()), s2);
 
   // Confirm copies a const string.
-  const string foo = "foo";
+  const SpdyString foo = "foo";
   SpdyDataIR d3(3, foo);
   EXPECT_EQ(foo, d3.data());
 
   // Confirm copies a non-const string.
-  string bar = "bar";
+  SpdyString bar = "bar";
   SpdyDataIR d4(4, bar);
   EXPECT_EQ("bar", bar);
   EXPECT_EQ("bar", SpdyStringPiece(d4.data(), d4.data_len()));
 
   // Confirm moves an rvalue reference. Note that the test string "baz" is too
   // short to trigger the move optimization, and instead a copy occurs.
-  string baz = "the quick brown fox";
+  SpdyString baz = "the quick brown fox";
   SpdyDataIR d5(5, std::move(baz));
   EXPECT_EQ("", baz);
   EXPECT_EQ(SpdyStringPiece(d5.data(), d5.data_len()), "the quick brown fox");
diff --git a/net/spdy/spdy_proxy_client_socket.cc b/net/spdy/spdy_proxy_client_socket.cc
index 9b9ce55..24805e1a 100644
--- a/net/spdy/spdy_proxy_client_socket.cc
+++ b/net/spdy/spdy_proxy_client_socket.cc
@@ -33,7 +33,7 @@
 
 SpdyProxyClientSocket::SpdyProxyClientSocket(
     const base::WeakPtr<SpdyStream>& spdy_stream,
-    const std::string& user_agent,
+    const SpdyString& user_agent,
     const HostPortPair& endpoint,
     const HostPortPair& proxy_server,
     const NetLogWithSource& source_net_log,
@@ -351,7 +351,7 @@
     auth_->AddAuthorizationHeader(&authorization_headers);
   }
 
-  std::string request_line;
+  SpdyString request_line;
   BuildTunnelRequest(endpoint_, authorization_headers, user_agent_,
                      &request_line, &request_.extra_headers);
 
diff --git a/net/spdy/spdy_proxy_client_socket.h b/net/spdy/spdy_proxy_client_socket.h
index 80e1953..dc0fd9c 100644
--- a/net/spdy/spdy_proxy_client_socket.h
+++ b/net/spdy/spdy_proxy_client_socket.h
@@ -9,7 +9,6 @@
 #include <stdint.h>
 
 #include <list>
-#include <string>
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
@@ -25,6 +24,7 @@
 #include "net/http/proxy_client_socket.h"
 #include "net/log/net_log_source.h"
 #include "net/log/net_log_with_source.h"
+#include "net/spdy/platform/api/spdy_string.h"
 #include "net/spdy/spdy_http_stream.h"
 #include "net/spdy/spdy_protocol.h"
 #include "net/spdy/spdy_read_queue.h"
@@ -45,7 +45,7 @@
   // data read/written to the socket will be transferred in data frames. This
   // object will set itself as |spdy_stream|'s delegate.
   SpdyProxyClientSocket(const base::WeakPtr<SpdyStream>& spdy_stream,
-                        const std::string& user_agent,
+                        const SpdyString& user_agent,
                         const HostPortPair& endpoint,
                         const HostPortPair& proxy_server,
                         const NetLogWithSource& source_net_log,
@@ -151,7 +151,7 @@
   const HostPortPair endpoint_;
   scoped_refptr<HttpAuthController> auth_;
 
-  std::string user_agent_;
+  SpdyString user_agent_;
 
   // We buffer the response body as it arrives asynchronously from the stream.
   SpdyReadQueue read_buffer_queue_;
diff --git a/net/spdy/spdy_proxy_client_socket_unittest.cc b/net/spdy/spdy_proxy_client_socket_unittest.cc
index 5ab6550e..e32e435 100644
--- a/net/spdy/spdy_proxy_client_socket_unittest.cc
+++ b/net/spdy/spdy_proxy_client_socket_unittest.cc
@@ -122,7 +122,7 @@
     base::RunLoop().RunUntilIdle();
   }
 
-  void CloseSpdySession(Error error, const std::string& description) {
+  void CloseSpdySession(Error error, const SpdyString& description) {
     spdy_session_->CloseSessionOnError(error, description);
   }
 
@@ -139,7 +139,7 @@
   SpdySessionDependencies session_deps_;
   MockConnect connect_data_;
   base::WeakPtr<SpdySession> spdy_session_;
-  std::string user_agent_;
+  SpdyString user_agent_;
   GURL url_;
   HostPortPair proxy_host_port_;
   HostPortPair endpoint_host_port_pair_;
@@ -242,7 +242,7 @@
                                                      int len) {
   scoped_refptr<IOBuffer> buf(new IOBuffer(len));
   ASSERT_EQ(len, sock_->Read(buf.get(), len, CompletionCallback()));
-  ASSERT_EQ(std::string(data, len), std::string(buf->data(), len));
+  ASSERT_EQ(SpdyString(data, len), SpdyString(buf->data(), len));
   ASSERT_TRUE(sock_->IsConnected());
 }
 
@@ -258,7 +258,7 @@
 
   EXPECT_EQ(len, read_callback_.WaitForResult());
   EXPECT_TRUE(sock_->IsConnected());
-  ASSERT_EQ(std::string(data, len), std::string(buf->data(), len));
+  ASSERT_EQ(SpdyString(data, len), SpdyString(buf->data(), len));
 }
 
 void SpdyProxyClientSocketTest::AssertReadStarts(const char* data, int len) {
@@ -274,7 +274,7 @@
 
   // Now the read will return
   EXPECT_EQ(len, read_callback_.WaitForResult());
-  ASSERT_EQ(std::string(data, len), std::string(read_buf_->data(), len));
+  ASSERT_EQ(SpdyString(data, len), SpdyString(read_buf_->data(), len));
 }
 
 void SpdyProxyClientSocketTest::AssertAsyncWriteSucceeds(const char* data,
@@ -454,7 +454,7 @@
   ASSERT_FALSE(headers->HasHeader("set-cookie"));
   ASSERT_TRUE(headers->HasHeaderValue("content-length", "0"));
 
-  std::string location;
+  SpdyString location;
   ASSERT_TRUE(headers->IsRedirect(&location));
   ASSERT_EQ(location, kRedirectUrl);
 
@@ -568,7 +568,7 @@
 }
 
 TEST_F(SpdyProxyClientSocketTest, WriteSplitsLargeDataIntoMultipleFrames) {
-  std::string chunk_data(kMaxSpdyFrameChunkSize, 'x');
+  SpdyString chunk_data(kMaxSpdyFrameChunkSize, 'x');
   SpdySerializedFrame conn(ConstructConnectRequestFrame());
   SpdySerializedFrame chunk(
       ConstructBodyFrame(chunk_data.data(), chunk_data.length()));
@@ -586,7 +586,7 @@
 
   AssertConnectSucceeds();
 
-  std::string big_data(kMaxSpdyFrameChunkSize * 3, 'x');
+  SpdyString big_data(kMaxSpdyFrameChunkSize * 3, 'x');
   scoped_refptr<IOBufferWithSize> buf(CreateBuffer(big_data.data(),
                                                    big_data.length()));
 
@@ -796,7 +796,7 @@
   // Now attempt to do a read of more data than remains buffered
   scoped_refptr<IOBuffer> buf(new IOBuffer(kLen33));
   ASSERT_EQ(kLen3, sock_->Read(buf.get(), kLen33, read_callback_.callback()));
-  ASSERT_EQ(std::string(kMsg3, kLen3), std::string(buf->data(), kLen3));
+  ASSERT_EQ(SpdyString(kMsg3, kLen3), SpdyString(buf->data(), kLen3));
   ASSERT_TRUE(sock_->IsConnected());
 }
 
@@ -1025,7 +1025,7 @@
   ASSERT_FALSE(sock_->IsConnected());
   scoped_refptr<IOBuffer> buf(new IOBuffer(kLen1));
   ASSERT_EQ(kLen1, sock_->Read(buf.get(), kLen1, CompletionCallback()));
-  ASSERT_EQ(std::string(kMsg1, kLen1), std::string(buf->data(), kLen1));
+  ASSERT_EQ(SpdyString(kMsg1, kLen1), SpdyString(buf->data(), kLen1));
 
   ASSERT_EQ(0, sock_->Read(NULL, 1, CompletionCallback()));
   ASSERT_EQ(0, sock_->Read(NULL, 1, CompletionCallback()));
@@ -1114,7 +1114,7 @@
   // Make sure the write actually starts.
   base::RunLoop().RunUntilIdle();
 
-  CloseSpdySession(ERR_ABORTED, std::string());
+  CloseSpdySession(ERR_ABORTED, SpdyString());
 
   EXPECT_THAT(write_callback_.WaitForResult(), IsError(ERR_CONNECTION_CLOSED));
 }
diff --git a/net/spdy/spdy_read_queue_unittest.cc b/net/spdy/spdy_read_queue_unittest.cc
index a211af1..935694b7 100644
--- a/net/spdy/spdy_read_queue_unittest.cc
+++ b/net/spdy/spdy_read_queue_unittest.cc
@@ -7,11 +7,11 @@
 #include <algorithm>
 #include <cstddef>
 #include <memory>
-#include <string>
 
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/stl_util.h"
+#include "net/spdy/platform/api/spdy_string.h"
 #include "net/spdy/spdy_buffer.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -24,7 +24,7 @@
 
 // Enqueues |data| onto |queue| in chunks of at most |max_buffer_size|
 // bytes.
-void EnqueueString(const std::string& data,
+void EnqueueString(const SpdyString& data,
                    size_t max_buffer_size,
                    SpdyReadQueue* queue) {
   ASSERT_GT(data.size(), 0u);
@@ -42,8 +42,8 @@
 
 // Dequeues all bytes in |queue| in chunks of at most
 // |max_buffer_size| bytes and returns the data as a string.
-std::string DrainToString(size_t max_buffer_size, SpdyReadQueue* queue) {
-  std::string data;
+SpdyString DrainToString(size_t max_buffer_size, SpdyReadQueue* queue) {
+  SpdyString data;
 
   // Pad the buffer so we can detect out-of-bound writes.
   size_t padding = std::max(static_cast<size_t>(4096), queue->GetTotalSize());
@@ -78,10 +78,10 @@
 // sizes.
 void RunEnqueueDequeueTest(size_t enqueue_max_buffer_size,
                            size_t dequeue_max_buffer_size) {
-  std::string data(kData, kDataSize);
+  SpdyString data(kData, kDataSize);
   SpdyReadQueue read_queue;
   EnqueueString(data, enqueue_max_buffer_size, &read_queue);
-  const std::string& drained_data =
+  const SpdyString& drained_data =
       DrainToString(dequeue_max_buffer_size, &read_queue);
   EXPECT_EQ(data, drained_data);
 }
diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc
index 4a61c77..da91d6ce 100644
--- a/net/spdy/spdy_session.cc
+++ b/net/spdy/spdy_session.cc
@@ -138,7 +138,7 @@
 
 std::unique_ptr<base::Value> NetLogSpdySessionCloseCallback(
     int net_error,
-    const std::string* description,
+    const SpdyString* description,
     NetLogCaptureMode /* capture_mode */) {
   auto dict = base::MakeUnique<base::DictionaryValue>();
   dict->SetInteger("net_error", net_error);
@@ -251,7 +251,7 @@
 std::unique_ptr<base::Value> NetLogSpdySendRstStreamCallback(
     SpdyStreamId stream_id,
     SpdyErrorCode error_code,
-    const std::string* description,
+    const SpdyString* description,
     NetLogCaptureMode /* capture_mode */) {
   auto dict = base::MakeUnique<base::DictionaryValue>();
   dict->SetInteger("stream_id", static_cast<int>(stream_id));
@@ -320,7 +320,7 @@
     size_t num_created_streams,
     size_t num_pushed_streams,
     size_t max_concurrent_streams,
-    const std::string& url,
+    const SpdyString& url,
     NetLogCaptureMode capture_mode) {
   auto dict = base::MakeUnique<base::DictionaryValue>();
   dict->SetInteger("num_active_streams", num_active_streams);
@@ -678,8 +678,8 @@
 // static
 bool SpdySession::CanPool(TransportSecurityState* transport_security_state,
                           const SSLInfo& ssl_info,
-                          const std::string& old_hostname,
-                          const std::string& new_hostname) {
+                          const SpdyString& old_hostname,
+                          const SpdyString& new_hostname) {
   // Pooling is prohibited if the server cert is not valid for the new domain,
   // and for connections on which client certs were sent. It is also prohibited
   // when channel ID was sent if the hosts are from different eTLDs+1.
@@ -698,7 +698,7 @@
   if (!ssl_info.cert->VerifyNameMatch(new_hostname, false))
     return false;
 
-  std::string pinning_failure_log;
+  SpdyString pinning_failure_log;
   // DISABLE_PIN_REPORTS is set here because this check can fail in
   // normal operation without being indicative of a misconfiguration or
   // attack. Port is left at 0 as it is never used.
@@ -910,7 +910,7 @@
                  READ_STATE_DO_READ, OK));
 }
 
-bool SpdySession::VerifyDomainAuthentication(const std::string& domain) {
+bool SpdySession::VerifyDomainAuthentication(const SpdyString& domain) {
   if (availability_state_ == STATE_DRAINING)
     return false;
 
@@ -1115,7 +1115,7 @@
 
 void SpdySession::ResetStream(SpdyStreamId stream_id,
                               SpdyErrorCode error_code,
-                              const std::string& description) {
+                              const SpdyString& description) {
   DCHECK_NE(stream_id, 0u);
 
   ActiveStreamMap::iterator it = active_streams_.find(stream_id);
@@ -1179,7 +1179,7 @@
 }
 
 void SpdySession::CloseSessionOnError(Error err,
-                                      const std::string& description) {
+                                      const SpdyString& description) {
   DCHECK_LT(err, ERR_IO_PENDING);
   DoDrainSession(err, description);
 }
@@ -1574,7 +1574,7 @@
   if (associated_stream_id == 0) {
     // In HTTP/2 0 stream id in PUSH_PROMISE frame leads to framer error and
     // session going away. We should never get here.
-    std::string description = SpdyStringPrintf(
+    SpdyString description = SpdyStringPrintf(
         "Received invalid associated stream id %d for pushed stream %d",
         associated_stream_id, stream_id);
     EnqueueResetStreamFrame(stream_id, request_priority,
@@ -1754,7 +1754,7 @@
 
 void SpdySession::ResetStreamIterator(ActiveStreamMap::iterator it,
                                       SpdyErrorCode error_code,
-                                      const std::string& description) {
+                                      const SpdyString& description) {
   // Send the RST_STREAM frame first as CloseActiveStreamIterator()
   // may close us.
   SpdyStreamId stream_id = it->first;
@@ -1769,7 +1769,7 @@
 void SpdySession::EnqueueResetStreamFrame(SpdyStreamId stream_id,
                                           RequestPriority priority,
                                           SpdyErrorCode error_code,
-                                          const std::string& description) {
+                                          const SpdyString& description) {
   DCHECK_NE(stream_id, 0u);
 
   net_log().AddEvent(NetLogEventType::HTTP2_SESSION_SEND_RST_STREAM,
@@ -2453,7 +2453,7 @@
   DCHECK(unclaimed_pushed_streams_.empty());
 }
 
-void SpdySession::DoDrainSession(Error err, const std::string& description) {
+void SpdySession::DoDrainSession(Error err, const SpdyString& description) {
   if (availability_state_ == STATE_DRAINING) {
     return;
   }
@@ -2504,7 +2504,7 @@
 
 void SpdySession::LogAbandonedStream(SpdyStream* stream, Error status) {
   DCHECK(stream);
-  std::string description =
+  SpdyString description =
       SpdyStringPrintf("ABANDONED (stream_id=%d): ", stream->stream_id()) +
       stream->url().spec();
   stream->LogStreamError(status, description);
@@ -2587,14 +2587,14 @@
 
   RecordProtocolErrorHistogram(
       MapFramerErrorToProtocolError(spdy_framer_error));
-  std::string description =
+  SpdyString description =
       SpdyStringPrintf("Framer error: %d (%s).", spdy_framer_error,
                        SpdyFramer::SpdyFramerErrorToString(spdy_framer_error));
   DoDrainSession(MapFramerErrorToNetError(spdy_framer_error), description);
 }
 
 void SpdySession::OnStreamError(SpdyStreamId stream_id,
-                                const std::string& description) {
+                                const SpdyString& description) {
   CHECK(in_io_loop_);
 
   ActiveStreamMap::iterator it = active_streams_.find(stream_id);
diff --git a/net/spdy/spdy_session.h b/net/spdy/spdy_session.h
index 6b23cbf..58ea684 100644
--- a/net/spdy/spdy_session.h
+++ b/net/spdy/spdy_session.h
@@ -12,7 +12,6 @@
 #include <map>
 #include <memory>
 #include <set>
-#include <string>
 #include <vector>
 
 #include "base/gtest_prod_util.h"
@@ -35,6 +34,7 @@
 #include "net/spdy/buffered_spdy_framer.h"
 #include "net/spdy/http2_priority_dependencies.h"
 #include "net/spdy/multiplexed_session.h"
+#include "net/spdy/platform/api/spdy_string.h"
 #include "net/spdy/platform/api/spdy_string_piece.h"
 #include "net/spdy/server_push_delegate.h"
 #include "net/spdy/spdy_alt_svc_wire_format.h"
@@ -292,8 +292,8 @@
   // |old_hostname| associated with |ssl_info|.
   static bool CanPool(TransportSecurityState* transport_security_state,
                       const SSLInfo& ssl_info,
-                      const std::string& old_hostname,
-                      const std::string& new_hostname);
+                      const SpdyString& old_hostname,
+                      const SpdyString& new_hostname);
 
   // Create a new SpdySession.
   // |spdy_session_key| is the host/port that this session connects to, privacy
@@ -368,7 +368,7 @@
   // TODO(wtc): rename this function and the Net.SpdyIPPoolDomainMatch
   // histogram because this function does more than verifying domain
   // authentication now.
-  bool VerifyDomainAuthentication(const std::string& domain);
+  bool VerifyDomainAuthentication(const SpdyString& domain);
 
   // Pushes the given producer into the write queue for
   // |stream|. |stream| is guaranteed to be activated before the
@@ -407,7 +407,7 @@
   // that that stream may hold the last reference to the session.
   void ResetStream(SpdyStreamId stream_id,
                    SpdyErrorCode error_code,
-                   const std::string& description);
+                   const SpdyString& description);
 
   // Check if a stream is active.
   bool IsStreamActive(SpdyStreamId stream_id) const;
@@ -451,7 +451,7 @@
   // |err| should be < ERR_IO_PENDING; this function is intended to be
   // called on error.
   // |description| indicates the reason for the error.
-  void CloseSessionOnError(Error err, const std::string& description);
+  void CloseSessionOnError(Error err, const SpdyString& description);
 
   // Mark this session as unavailable, meaning that it will not be used to
   // service new streams. Unlike when a GOAWAY frame is received, this function
@@ -706,7 +706,7 @@
   // CloseActiveStreamIterator().
   void ResetStreamIterator(ActiveStreamMap::iterator it,
                            SpdyErrorCode error_code,
-                           const std::string& description);
+                           const SpdyString& description);
 
   // Send a RST_STREAM frame with the given parameters. There should
   // either be no active stream with the given ID, or that active
@@ -714,7 +714,7 @@
   void EnqueueResetStreamFrame(SpdyStreamId stream_id,
                                RequestPriority priority,
                                SpdyErrorCode error_code,
-                               const std::string& description);
+                               const SpdyString& description);
 
   // Send a PRIORITY frame with the given parameters.
   void EnqueuePriorityFrame(SpdyStreamId stream_id,
@@ -852,7 +852,7 @@
 
   // If the session is already draining, does nothing. Otherwise, moves
   // the session to the draining state.
-  void DoDrainSession(Error err, const std::string& description);
+  void DoDrainSession(Error err, const SpdyString& description);
 
   // Called right before closing a (possibly-inactive) stream for a
   // reason other than being requested to by the stream.
@@ -874,7 +874,7 @@
   // BufferedSpdyFramerVisitorInterface:
   void OnError(SpdyFramer::SpdyFramerError spdy_framer_error) override;
   void OnStreamError(SpdyStreamId stream_id,
-                     const std::string& description) override;
+                     const SpdyString& description) override;
   void OnPing(SpdyPingId unique_id, bool is_ack) override;
   void OnRstStream(SpdyStreamId stream_id, SpdyErrorCode error_code) override;
   void OnGoAway(SpdyStreamId last_accepted_stream_id,
diff --git a/net/spdy/spdy_session_pool.cc b/net/spdy/spdy_session_pool.cc
index 5e40386..d20b365 100644
--- a/net/spdy/spdy_session_pool.cc
+++ b/net/spdy/spdy_session_pool.cc
@@ -398,7 +398,7 @@
 
 void SpdySessionPool::DumpMemoryStats(
     base::trace_event::ProcessMemoryDump* pmd,
-    const std::string& parent_dump_absolute_name) const {
+    const SpdyString& parent_dump_absolute_name) const {
   if (sessions_.empty())
     return;
   size_t total_size = 0;
@@ -495,10 +495,9 @@
   return current_sessions;
 }
 
-void SpdySessionPool::CloseCurrentSessionsHelper(
-    Error error,
-    const std::string& description,
-    bool idle_only) {
+void SpdySessionPool::CloseCurrentSessionsHelper(Error error,
+                                                 const SpdyString& description,
+                                                 bool idle_only) {
   WeakSessionList current_sessions = GetCurrentSessions();
   for (WeakSessionList::const_iterator it = current_sessions.begin();
        it != current_sessions.end(); ++it) {
diff --git a/net/spdy/spdy_session_pool.h b/net/spdy/spdy_session_pool.h
index 9da2fb7a..7d0b294 100644
--- a/net/spdy/spdy_session_pool.h
+++ b/net/spdy/spdy_session_pool.h
@@ -10,7 +10,6 @@
 #include <map>
 #include <memory>
 #include <set>
-#include <string>
 #include <vector>
 
 #include "base/macros.h"
@@ -24,6 +23,7 @@
 #include "net/cert/cert_database.h"
 #include "net/proxy/proxy_config.h"
 #include "net/proxy/proxy_server.h"
+#include "net/spdy/platform/api/spdy_string.h"
 #include "net/spdy/server_push_delegate.h"
 #include "net/spdy/spdy_protocol.h"
 #include "net/spdy/spdy_session_key.h"
@@ -164,7 +164,7 @@
   void OnCertDBChanged() override;
 
   void DumpMemoryStats(base::trace_event::ProcessMemoryDump* pmd,
-                       const std::string& parent_dump_absolute_name) const;
+                       const SpdyString& parent_dump_absolute_name) const;
 
  private:
   friend class SpdySessionPoolPeer;  // For testing.
@@ -202,10 +202,9 @@
   // Close only the currently existing SpdySessions with |error|.  Let
   // any new ones created while this method is running continue to
   // live. If |idle_only| is true only idle sessions are closed.
-  void CloseCurrentSessionsHelper(
-      Error error,
-      const std::string& description,
-      bool idle_only);
+  void CloseCurrentSessionsHelper(Error error,
+                                  const SpdyString& description,
+                                  bool idle_only);
 
   HttpServerProperties* http_server_properties_;
 
diff --git a/net/spdy/spdy_session_pool_unittest.cc b/net/spdy/spdy_session_pool_unittest.cc
index 58596db..5a19906 100644
--- a/net/spdy/spdy_session_pool_unittest.cc
+++ b/net/spdy/spdy_session_pool_unittest.cc
@@ -6,7 +6,6 @@
 
 #include <cstddef>
 #include <memory>
-#include <string>
 #include <utility>
 
 #include "base/memory/ptr_util.h"
@@ -170,7 +169,7 @@
   CreateNetworkSession();
 
   // Set up session 1
-  const std::string kTestHost1("www.example.org");
+  const SpdyString kTestHost1("www.example.org");
   HostPortPair test_host_port_pair1(kTestHost1, 80);
   SpdySessionKey key1(test_host_port_pair1, ProxyServer::Direct(),
                       PRIVACY_MODE_DISABLED);
@@ -185,7 +184,7 @@
   StaticSocketDataProvider data2(reads, arraysize(reads), nullptr, 0);
   data2.set_connect_data(connect_data);
   session_deps_.socket_factory->AddSocketDataProvider(&data2);
-  const std::string kTestHost2("mail.example.org");
+  const SpdyString kTestHost2("mail.example.org");
   HostPortPair test_host_port_pair2(kTestHost2, 80);
   SpdySessionKey key2(test_host_port_pair2, ProxyServer::Direct(),
                       PRIVACY_MODE_DISABLED);
@@ -200,7 +199,7 @@
   StaticSocketDataProvider data3(reads, arraysize(reads), nullptr, 0);
   data3.set_connect_data(connect_data);
   session_deps_.socket_factory->AddSocketDataProvider(&data3);
-  const std::string kTestHost3("mail.example.com");
+  const SpdyString kTestHost3("mail.example.com");
   HostPortPair test_host_port_pair3(kTestHost3, 80);
   SpdySessionKey key3(test_host_port_pair3, ProxyServer::Direct(),
                       PRIVACY_MODE_DISABLED);
@@ -334,9 +333,9 @@
     SpdyPoolCloseSessionsType close_sessions_type) {
   const int kTestPort = 80;
   struct TestHosts {
-    std::string url;
-    std::string name;
-    std::string iplist;
+    SpdyString url;
+    SpdyString name;
+    SpdyString iplist;
     SpdySessionKey key;
     AddressList addresses;
   } test_hosts[] = {
@@ -352,7 +351,7 @@
   std::unique_ptr<HostResolver::Request> request[arraysize(test_hosts)];
   for (size_t i = 0; i < arraysize(test_hosts); i++) {
     session_deps_.host_resolver->rules()->AddIPLiteralRule(
-        test_hosts[i].name, test_hosts[i].iplist, std::string());
+        test_hosts[i].name, test_hosts[i].iplist, SpdyString());
 
     // This test requires that the HostResolver cache be populated.  Normal
     // code would have done this already, but we do it manually.
@@ -446,8 +445,8 @@
   // Cleanup the sessions.
   switch (close_sessions_type) {
     case SPDY_POOL_CLOSE_SESSIONS_MANUALLY:
-      session->CloseSessionOnError(ERR_ABORTED, std::string());
-      session2->CloseSessionOnError(ERR_ABORTED, std::string());
+      session->CloseSessionOnError(ERR_ABORTED, SpdyString());
+      session2->CloseSessionOnError(ERR_ABORTED, SpdyString());
       base::RunLoop().RunUntilIdle();
       EXPECT_FALSE(session);
       EXPECT_FALSE(session2);
@@ -498,7 +497,7 @@
       EXPECT_FALSE(spdy_stream1);
       EXPECT_FALSE(spdy_stream2);
 
-      session2->CloseSessionOnError(ERR_ABORTED, std::string());
+      session2->CloseSessionOnError(ERR_ABORTED, SpdyString());
       base::RunLoop().RunUntilIdle();
       EXPECT_FALSE(session2);
       break;
@@ -527,8 +526,8 @@
   // Define two hosts with identical IP address.
   const int kTestPort = 443;
   struct TestHosts {
-    std::string name;
-    std::string iplist;
+    SpdyString name;
+    SpdyString iplist;
     SpdySessionKey key;
     AddressList addresses;
     std::unique_ptr<HostResolver::Request> request;
@@ -540,7 +539,7 @@
   session_deps_.host_resolver->set_synchronous_mode(true);
   for (size_t i = 0; i < arraysize(test_hosts); i++) {
     session_deps_.host_resolver->rules()->AddIPLiteralRule(
-        test_hosts[i].name, test_hosts[i].iplist, std::string());
+        test_hosts[i].name, test_hosts[i].iplist, SpdyString());
 
     HostResolver::RequestInfo info(HostPortPair(test_hosts[i].name, kTestPort));
     session_deps_.host_resolver->Resolve(
@@ -607,8 +606,8 @@
   // Define two hosts with identical IP address.
   const int kTestPort = 443;
   struct TestHosts {
-    std::string name;
-    std::string iplist;
+    SpdyString name;
+    SpdyString iplist;
     SpdySessionKey key;
     AddressList addresses;
     std::unique_ptr<HostResolver::Request> request;
@@ -620,7 +619,7 @@
   session_deps_.host_resolver->set_synchronous_mode(true);
   for (size_t i = 0; i < arraysize(test_hosts); i++) {
     session_deps_.host_resolver->rules()->AddIPLiteralRule(
-        test_hosts[i].name, test_hosts[i].iplist, std::string());
+        test_hosts[i].name, test_hosts[i].iplist, SpdyString());
 
     HostResolver::RequestInfo info(HostPortPair(test_hosts[i].name, kTestPort));
     session_deps_.host_resolver->Resolve(
@@ -705,7 +704,7 @@
   CreateNetworkSession();
 
   // Set up session A: Going away, but with an active stream.
-  const std::string kTestHostA("www.example.org");
+  const SpdyString kTestHostA("www.example.org");
   HostPortPair test_host_port_pairA(kTestHostA, 80);
   SpdySessionKey keyA(
       test_host_port_pairA, ProxyServer::Direct(), PRIVACY_MODE_DISABLED);
@@ -736,7 +735,7 @@
 
   AddSSLSocketData();
 
-  const std::string kTestHostB("mail.example.org");
+  const SpdyString kTestHostB("mail.example.org");
   HostPortPair test_host_port_pairB(kTestHostB, 80);
   SpdySessionKey keyB(
       test_host_port_pairB, ProxyServer::Direct(), PRIVACY_MODE_DISABLED);
@@ -758,7 +757,7 @@
 
   AddSSLSocketData();
 
-  const std::string kTestHostC("mail.example.com");
+  const SpdyString kTestHostC("mail.example.com");
   HostPortPair test_host_port_pairC(kTestHostC, 80);
   SpdySessionKey keyC(
       test_host_port_pairC, ProxyServer::Direct(), PRIVACY_MODE_DISABLED);
@@ -886,8 +885,8 @@
   const base::trace_event::ProcessMemoryDump::AllocatorDumpsMap&
       allocator_dumps = process_memory_dump->allocator_dumps();
   for (const auto& pair : allocator_dumps) {
-    const std::string& dump_name = pair.first;
-    if (dump_name.find("spdy_session_pool") == std::string::npos)
+    const SpdyString& dump_name = pair.first;
+    if (dump_name.find("spdy_session_pool") == SpdyString::npos)
       continue;
     std::unique_ptr<base::Value> raw_attrs =
         pair.second->attributes_for_testing()->ToBaseValue();
@@ -896,7 +895,7 @@
     base::DictionaryValue* active_session_count_attr;
     ASSERT_TRUE(attrs->GetDictionary("active_session_count",
                                      &active_session_count_attr));
-    std::string active_session_count;
+    SpdyString active_session_count;
     ASSERT_TRUE(
         active_session_count_attr->GetString("value", &active_session_count));
     // No created stream so the session should be idle.
diff --git a/net/spdy/spdy_session_test_util.cc b/net/spdy/spdy_session_test_util.cc
index 36e4c57f..86939c4 100644
--- a/net/spdy/spdy_session_test_util.cc
+++ b/net/spdy/spdy_session_test_util.cc
@@ -10,11 +10,9 @@
 namespace net {
 
 SpdySessionTestTaskObserver::SpdySessionTestTaskObserver(
-    const std::string& file_name,
-    const std::string& function_name)
-    : executed_count_(0),
-      file_name_(file_name),
-      function_name_(function_name) {
+    const SpdyString& file_name,
+    const SpdyString& function_name)
+    : executed_count_(0), file_name_(file_name), function_name_(function_name) {
   base::MessageLoop::current()->AddTaskObserver(this);
 }
 
diff --git a/net/spdy/spdy_session_test_util.h b/net/spdy/spdy_session_test_util.h
index 819b15c8..ceb385d4 100644
--- a/net/spdy/spdy_session_test_util.h
+++ b/net/spdy/spdy_session_test_util.h
@@ -7,10 +7,9 @@
 
 #include <stdint.h>
 
-#include <string>
-
 #include "base/message_loop/message_loop.h"
 #include "base/pending_task.h"
+#include "net/spdy/platform/api/spdy_string.h"
 
 namespace net {
 
@@ -25,8 +24,8 @@
   // Example:
   //  file_name = "foo.cc"
   //  function = "DoFoo"
-  SpdySessionTestTaskObserver(const std::string& file_name,
-                              const std::string& function_name);
+  SpdySessionTestTaskObserver(const SpdyString& file_name,
+                              const SpdyString& function_name);
   ~SpdySessionTestTaskObserver() override;
 
   // Implements MessageLoop::TaskObserver.
@@ -38,8 +37,8 @@
 
  private:
   uint16_t executed_count_;
-  std::string file_name_;
-  std::string function_name_;
+  SpdyString file_name_;
+  SpdyString function_name_;
 };
 
 }  // namespace net
diff --git a/net/spdy/spdy_session_unittest.cc b/net/spdy/spdy_session_unittest.cc
index f7e82a50..ecc0ad65 100644
--- a/net/spdy/spdy_session_unittest.cc
+++ b/net/spdy/spdy_session_unittest.cc
@@ -77,8 +77,7 @@
 
 class MockRequireCTDelegate : public TransportSecurityState::RequireCTDelegate {
  public:
-  MOCK_METHOD1(IsCTRequiredForHost,
-               CTRequirementLevel(const std::string& host));
+  MOCK_METHOD1(IsCTRequiredForHost, CTRequirementLevel(const SpdyString& host));
 };
 
 }  // namespace
@@ -2005,10 +2004,10 @@
   int unclaimed_streams;
   ASSERT_TRUE(entry.GetIntegerValue("unclaimed_streams", &unclaimed_streams));
   EXPECT_EQ(0, unclaimed_streams);
-  std::string error_code;
+  SpdyString error_code;
   ASSERT_TRUE(entry.GetStringValue("error_code", &error_code));
   EXPECT_EQ("11 (ENHANCE_YOUR_CALM)", error_code);
-  std::string debug_data;
+  SpdyString debug_data;
   ASSERT_TRUE(entry.GetStringValue("debug_data", &debug_data));
   EXPECT_EQ("foo", debug_data);
 
@@ -2290,7 +2289,7 @@
   EXPECT_EQ(0u, spdy_stream2->stream_id());
 
   // Ensure we don't crash while closing the session.
-  session_->CloseSessionOnError(ERR_ABORTED, std::string());
+  session_->CloseSessionOnError(ERR_ABORTED, SpdyString());
 
   EXPECT_FALSE(spdy_stream1);
   EXPECT_FALSE(spdy_stream2);
@@ -2346,7 +2345,7 @@
   EXPECT_EQ(0u, spdy_stream2->stream_id());
 
   // Ensure we don't crash while closing the session.
-  session_->CloseSessionOnError(ERR_ABORTED, std::string());
+  session_->CloseSessionOnError(ERR_ABORTED, SpdyString());
 
   EXPECT_FALSE(spdy_stream1);
   EXPECT_FALSE(spdy_stream2);
@@ -2417,7 +2416,7 @@
   EXPECT_EQ(3u, spdy_stream2->stream_id());
 
   // Ensure we don't crash while closing the session.
-  session_->CloseSessionOnError(ERR_ABORTED, std::string());
+  session_->CloseSessionOnError(ERR_ABORTED, SpdyString());
 
   EXPECT_FALSE(spdy_stream1);
   EXPECT_FALSE(spdy_stream2);
@@ -2492,7 +2491,7 @@
   EXPECT_EQ(3u, spdy_stream2->stream_id());
 
   // Ensure we don't crash while closing the session.
-  session_->CloseSessionOnError(ERR_ABORTED, std::string());
+  session_->CloseSessionOnError(ERR_ABORTED, SpdyString());
 
   EXPECT_FALSE(spdy_stream1);
   EXPECT_FALSE(spdy_stream2);
@@ -3395,12 +3394,12 @@
 
   session_deps_.host_resolver->set_synchronous_mode(true);
   session_deps_.host_resolver->rules()->AddIPLiteralRule(
-      "www.example.org", "192.168.0.2", std::string());
+      "www.example.org", "192.168.0.2", SpdyString());
   session_deps_.host_resolver->rules()->AddIPLiteralRule(
-      "mail.example.org", "192.168.0.2", std::string());
+      "mail.example.org", "192.168.0.2", SpdyString());
   // Not strictly needed.
-  session_deps_.host_resolver->rules()->AddIPLiteralRule(
-      "3.com", "192.168.0.3", std::string());
+  session_deps_.host_resolver->rules()->AddIPLiteralRule("3.com", "192.168.0.3",
+                                                         SpdyString());
 
   CreateNetworkSession();
 
@@ -3571,11 +3570,11 @@
   EXPECT_TRUE(HasSpdySession(spdy_session_pool_, key_privacy_enabled));
   EXPECT_TRUE(HasSpdySession(spdy_session_pool_, key_privacy_disabled));
 
-  session_privacy_enabled->CloseSessionOnError(ERR_ABORTED, std::string());
+  session_privacy_enabled->CloseSessionOnError(ERR_ABORTED, SpdyString());
   EXPECT_FALSE(HasSpdySession(spdy_session_pool_, key_privacy_enabled));
   EXPECT_TRUE(HasSpdySession(spdy_session_pool_, key_privacy_disabled));
 
-  session_privacy_disabled->CloseSessionOnError(ERR_ABORTED, std::string());
+  session_privacy_disabled->CloseSessionOnError(ERR_ABORTED, SpdyString());
   EXPECT_FALSE(HasSpdySession(spdy_session_pool_, key_privacy_enabled));
   EXPECT_FALSE(HasSpdySession(spdy_session_pool_, key_privacy_disabled));
 }
@@ -3885,7 +3884,7 @@
   };
 
   SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
-  const std::string payload(data_frame_size, 'a');
+  const SpdyString payload(data_frame_size, 'a');
   SpdySerializedFrame data_frame(spdy_util_.ConstructSpdyDataFrame(
       1, payload.data(), data_frame_size, false));
   MockRead reads[] = {
@@ -3959,10 +3958,10 @@
       CreateMockWrite(goaway, 4),
   };
 
-  const std::string first_data_frame(first_data_frame_size, 'a');
+  const SpdyString first_data_frame(first_data_frame_size, 'a');
   SpdySerializedFrame first(spdy_util_.ConstructSpdyDataFrame(
       1, first_data_frame.data(), first_data_frame_size, false));
-  const std::string second_data_frame(second_data_frame_size, 'b');
+  const SpdyString second_data_frame(second_data_frame_size, 'b');
   SpdySerializedFrame second(spdy_util_.ConstructSpdyDataFrame(
       1, second_data_frame.data(), second_data_frame_size, false));
   MockRead reads[] = {
@@ -4019,10 +4018,10 @@
   };
 
   SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
-  const std::string first_data_frame(first_data_frame_size, 'a');
+  const SpdyString first_data_frame(first_data_frame_size, 'a');
   SpdySerializedFrame first(spdy_util_.ConstructSpdyDataFrame(
       1, first_data_frame.data(), first_data_frame_size, false));
-  const std::string second_data_frame(second_data_frame_size, 'b');
+  const SpdyString second_data_frame(second_data_frame_size, 'b');
   SpdySerializedFrame second(spdy_util_.ConstructSpdyDataFrame(
       1, second_data_frame.data(), second_data_frame_size, false));
   MockRead reads[] = {
@@ -4066,7 +4065,7 @@
             spdy_stream->recv_window_size());
 
   // Consume first data frame.  This does not trigger a WINDOW_UPDATE.
-  std::string received_data = delegate.TakeReceivedData();
+  SpdyString received_data = delegate.TakeReceivedData();
   EXPECT_EQ(static_cast<size_t>(first_data_frame_size), received_data.size());
   EXPECT_EQ(stream_max_recv_window_size, spdy_stream->recv_window_size());
 
@@ -4100,7 +4099,7 @@
 // value, i.e. we shouldn't "leak" receive window bytes.
 TEST_F(SpdySessionTest, SessionFlowControlNoReceiveLeaks) {
   const int32_t kMsgDataSize = 100;
-  const std::string msg_data(kMsgDataSize, 'a');
+  const SpdyString msg_data(kMsgDataSize, 'a');
 
   SpdySerializedFrame req(spdy_util_.ConstructSpdyPost(
       kDefaultUrl, 1, kMsgDataSize, MEDIUM, nullptr, 0));
@@ -4171,7 +4170,7 @@
 // to its original value, i.e. we shouldn't "leak" send window bytes.
 TEST_F(SpdySessionTest, SessionFlowControlNoSendLeaks) {
   const int32_t kMsgDataSize = 100;
-  const std::string msg_data(kMsgDataSize, 'a');
+  const SpdyString msg_data(kMsgDataSize, 'a');
 
   SpdySerializedFrame req(spdy_util_.ConstructSpdyPost(
       kDefaultUrl, 1, kMsgDataSize, MEDIUM, nullptr, 0));
@@ -4243,7 +4242,7 @@
 // change appropriately.
 TEST_F(SpdySessionTest, SessionFlowControlEndToEnd) {
   const int32_t kMsgDataSize = 100;
-  const std::string msg_data(kMsgDataSize, 'a');
+  const SpdyString msg_data(kMsgDataSize, 'a');
 
   SpdySerializedFrame req(spdy_util_.ConstructSpdyPost(
       kDefaultUrl, 1, kMsgDataSize, MEDIUM, nullptr, 0));
@@ -4406,7 +4405,7 @@
 
   EXPECT_TRUE(delegate.send_headers_completed());
   EXPECT_EQ("200", delegate.GetResponseHeaderValue(":status"));
-  EXPECT_EQ(std::string(), delegate.TakeReceivedData());
+  EXPECT_EQ(SpdyString(), delegate.TakeReceivedData());
 
   // Run SpdySession::PumpWriteLoop which destroys |session_|.
   base::RunLoop().RunUntilIdle();
@@ -4567,11 +4566,11 @@
 
   EXPECT_TRUE(delegate1.send_headers_completed());
   EXPECT_EQ("200", delegate1.GetResponseHeaderValue(":status"));
-  EXPECT_EQ(std::string(), delegate1.TakeReceivedData());
+  EXPECT_EQ(SpdyString(), delegate1.TakeReceivedData());
 
   EXPECT_TRUE(delegate2.send_headers_completed());
   EXPECT_EQ("200", delegate2.GetResponseHeaderValue(":status"));
-  EXPECT_EQ(std::string(), delegate2.TakeReceivedData());
+  EXPECT_EQ(SpdyString(), delegate2.TakeReceivedData());
 
   EXPECT_FALSE(session_);
   EXPECT_TRUE(data.AllWriteDataConsumed());
@@ -4729,14 +4728,14 @@
   EXPECT_THAT(delegate3.WaitForClose(), IsOk());
 
   EXPECT_TRUE(delegate1.send_headers_completed());
-  EXPECT_EQ(std::string(), delegate1.TakeReceivedData());
+  EXPECT_EQ(SpdyString(), delegate1.TakeReceivedData());
 
   EXPECT_TRUE(delegate2.send_headers_completed());
   EXPECT_EQ("200", delegate2.GetResponseHeaderValue(":status"));
-  EXPECT_EQ(std::string(), delegate2.TakeReceivedData());
+  EXPECT_EQ(SpdyString(), delegate2.TakeReceivedData());
 
   EXPECT_TRUE(delegate3.send_headers_completed());
-  EXPECT_EQ(std::string(), delegate3.TakeReceivedData());
+  EXPECT_EQ(SpdyString(), delegate3.TakeReceivedData());
 
   EXPECT_TRUE(data.AllWriteDataConsumed());
 }
@@ -4828,10 +4827,10 @@
   EXPECT_THAT(delegate2.WaitForClose(), IsError(ERR_CONNECTION_CLOSED));
 
   EXPECT_TRUE(delegate1.send_headers_completed());
-  EXPECT_EQ(std::string(), delegate1.TakeReceivedData());
+  EXPECT_EQ(SpdyString(), delegate1.TakeReceivedData());
 
   EXPECT_TRUE(delegate2.send_headers_completed());
-  EXPECT_EQ(std::string(), delegate2.TakeReceivedData());
+  EXPECT_EQ(SpdyString(), delegate2.TakeReceivedData());
 
   EXPECT_TRUE(data.AllWriteDataConsumed());
 }
diff --git a/net/spdy/spdy_stream.cc b/net/spdy/spdy_stream.cc
index bcc891861..829bb2b 100644
--- a/net/spdy/spdy_stream.cc
+++ b/net/spdy/spdy_stream.cc
@@ -35,7 +35,7 @@
 std::unique_ptr<base::Value> NetLogSpdyStreamErrorCallback(
     SpdyStreamId stream_id,
     int status,
-    const std::string* description,
+    const SpdyString* description,
     NetLogCaptureMode /* capture_mode */) {
   std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
   dict->SetInteger("stream_id", static_cast<int>(stream_id));
@@ -257,7 +257,7 @@
     int32_t max_delta_window_size =
         std::numeric_limits<int32_t>::max() - send_window_size_;
     if (delta_window_size > max_delta_window_size) {
-      std::string desc = SpdyStringPrintf(
+      SpdyString desc = SpdyStringPrintf(
           "Received WINDOW_UPDATE [delta: %d] for stream %d overflows "
           "send_window_size_ [current: %d]",
           delta_window_size, stream_id_, send_window_size_);
@@ -387,7 +387,7 @@
       {
         SpdyHeaderBlock::const_iterator it = response_headers.find(":status");
         if (it == response_headers.end()) {
-          const std::string error("Response headers do not include :status.");
+          const SpdyString error("Response headers do not include :status.");
           LogStreamError(ERR_SPDY_PROTOCOL_ERROR, error);
           session_->ResetStream(stream_id_, ERROR_CODE_PROTOCOL_ERROR, error);
           return;
@@ -395,7 +395,7 @@
 
         int status;
         if (!StringToInt(it->second, &status)) {
-          const std::string error("Cannot parse :status.");
+          const SpdyString error("Cannot parse :status.");
           LogStreamError(ERR_SPDY_PROTOCOL_ERROR, error);
           session_->ResetStream(stream_id_, ERROR_CODE_PROTOCOL_ERROR, error);
           return;
@@ -416,7 +416,7 @@
           // A bidirectional stream or a request/response stream is ready for
           // the response headers only after request headers are sent.
           if (io_state_ == STATE_IDLE) {
-            const std::string error("Response received before request sent.");
+            const SpdyString error("Response received before request sent.");
             LogStreamError(ERR_SPDY_PROTOCOL_ERROR, error);
             session_->ResetStream(stream_id_, ERROR_CODE_PROTOCOL_ERROR, error);
             return;
@@ -447,7 +447,7 @@
     case READY_FOR_DATA_OR_TRAILERS:
       // Second header block is trailers.
       if (type_ == SPDY_PUSH_STREAM) {
-        const std::string error("Trailers not supported for push stream.");
+        const SpdyString error("Trailers not supported for push stream.");
         LogStreamError(ERR_SPDY_PROTOCOL_ERROR, error);
         session_->ResetStream(stream_id_, ERROR_CODE_PROTOCOL_ERROR, error);
         return;
@@ -459,7 +459,7 @@
 
     case TRAILERS_RECEIVED:
       // No further header blocks are allowed after trailers.
-      const std::string error("Header block received after trailers.");
+      const SpdyString error("Header block received after trailers.");
       LogStreamError(ERR_SPDY_PROTOCOL_ERROR, error);
       session_->ResetStream(stream_id_, ERROR_CODE_PROTOCOL_ERROR, error);
       break;
@@ -482,14 +482,14 @@
   DCHECK(session_->IsStreamActive(stream_id_));
 
   if (response_state_ == READY_FOR_HEADERS) {
-    const std::string error("DATA received before headers.");
+    const SpdyString error("DATA received before headers.");
     LogStreamError(ERR_SPDY_PROTOCOL_ERROR, error);
     session_->ResetStream(stream_id_, ERROR_CODE_PROTOCOL_ERROR, error);
     return;
   }
 
   if (response_state_ == TRAILERS_RECEIVED && buffer) {
-    const std::string error("DATA received after trailers.");
+    const SpdyString error("DATA received after trailers.");
     LogStreamError(ERR_SPDY_PROTOCOL_ERROR, error);
     session_->ResetStream(stream_id_, ERROR_CODE_PROTOCOL_ERROR, error);
     return;
@@ -636,7 +636,7 @@
   }
 }
 
-void SpdyStream::LogStreamError(int status, const std::string& description) {
+void SpdyStream::LogStreamError(int status, const SpdyString& description) {
   net_log_.AddEvent(NetLogEventType::HTTP2_STREAM_ERROR,
                     base::Bind(&NetLogSpdyStreamErrorCallback, stream_id_,
                                status, &description));
@@ -668,7 +668,7 @@
     return;
 
   if (stream_id_ != 0) {
-    session_->ResetStream(stream_id_, ERROR_CODE_CANCEL, std::string());
+    session_->ResetStream(stream_id_, ERROR_CODE_CANCEL, SpdyString());
   } else {
     session_->CloseCreatedStream(GetWeakPtr(), ERROR_CODE_CANCEL);
   }
@@ -910,8 +910,8 @@
     description = SpdyStringPrintf("%s (0x%08X)", #s, s); \
     break
 
-std::string SpdyStream::DescribeState(State state) {
-  std::string description;
+SpdyString SpdyStream::DescribeState(State state) {
+  SpdyString description;
   switch (state) {
     STATE_CASE(STATE_IDLE);
     STATE_CASE(STATE_OPEN);
diff --git a/net/spdy/spdy_stream.h b/net/spdy/spdy_stream.h
index b3d3245..a18a7fd 100644
--- a/net/spdy/spdy_stream.h
+++ b/net/spdy/spdy_stream.h
@@ -10,7 +10,6 @@
 
 #include <deque>
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "base/macros.h"
@@ -23,6 +22,7 @@
 #include "net/log/net_log_with_source.h"
 #include "net/socket/next_proto.h"
 #include "net/socket/ssl_client_socket.h"
+#include "net/spdy/platform/api/spdy_string.h"
 #include "net/spdy/spdy_buffer.h"
 #include "net/spdy/spdy_framer.h"
 #include "net/spdy/spdy_header_block.h"
@@ -289,7 +289,7 @@
   void OnClose(int status);
 
   // Called by the SpdySession to log stream related errors.
-  void LogStreamError(int status, const std::string& description);
+  void LogStreamError(int status, const SpdyString& description);
 
   // If this stream is active, reset it, and close it otherwise. In
   // either case the stream is deleted.
@@ -435,7 +435,7 @@
   // OnHeadersReceived() on the delegate if attached.
   void SaveResponseHeaders(const SpdyHeaderBlock& response_headers);
 
-  static std::string DescribeState(State state);
+  static SpdyString DescribeState(State state);
 
   const SpdyStreamType type_;
 
diff --git a/net/spdy/spdy_stream_test_util.cc b/net/spdy/spdy_stream_test_util.cc
index 35c7f10..f55c373 100644
--- a/net/spdy/spdy_stream_test_util.cc
+++ b/net/spdy/spdy_stream_test_util.cc
@@ -93,9 +93,9 @@
   return result;
 }
 
-std::string StreamDelegateBase::TakeReceivedData() {
+SpdyString StreamDelegateBase::TakeReceivedData() {
   size_t len = received_data_queue_.GetTotalSize();
-  std::string received_data(len, '\0');
+  SpdyString received_data(len, '\0');
   if (len > 0) {
     EXPECT_EQ(len, received_data_queue_.Dequeue(
                        base::string_as_array(&received_data), len));
@@ -103,10 +103,10 @@
   return received_data;
 }
 
-std::string StreamDelegateBase::GetResponseHeaderValue(
-    const std::string& name) const {
+SpdyString StreamDelegateBase::GetResponseHeaderValue(
+    const SpdyString& name) const {
   SpdyHeaderBlock::const_iterator it = response_headers_.find(name);
-  return (it == response_headers_.end()) ? std::string()
+  return (it == response_headers_.end()) ? SpdyString()
                                          : it->second.as_string();
 }
 
diff --git a/net/spdy/spdy_stream_test_util.h b/net/spdy/spdy_stream_test_util.h
index 8683863..4fcc99b 100644
--- a/net/spdy/spdy_stream_test_util.h
+++ b/net/spdy/spdy_stream_test_util.h
@@ -64,7 +64,7 @@
 
   // Drains all data from the underlying read queue and returns it as
   // a string.
-  std::string TakeReceivedData();
+  SpdyString TakeReceivedData();
 
   // Returns whether or not the stream is closed.
   bool StreamIsClosed() const { return !stream_.get(); }
@@ -73,7 +73,7 @@
   // returns the stream's ID when it was open.
   SpdyStreamId stream_id() const { return stream_id_; }
 
-  std::string GetResponseHeaderValue(const std::string& name) const;
+  SpdyString GetResponseHeaderValue(const SpdyString& name) const;
   bool send_headers_completed() const { return send_headers_completed_; }
 
  protected:
diff --git a/net/spdy/spdy_stream_unittest.cc b/net/spdy/spdy_stream_unittest.cc
index f8400dda..50b2a1b 100644
--- a/net/spdy/spdy_stream_unittest.cc
+++ b/net/spdy/spdy_stream_unittest.cc
@@ -10,7 +10,6 @@
 #include <cstddef>
 #include <limits>
 #include <memory>
-#include <string>
 #include <utility>
 #include <vector>
 
@@ -196,7 +195,7 @@
 
   EXPECT_TRUE(delegate.send_headers_completed());
   EXPECT_EQ("200", delegate.GetResponseHeaderValue(spdy_util_.GetStatusKey()));
-  EXPECT_EQ(std::string(kPostBody, kPostBodyLength),
+  EXPECT_EQ(SpdyString(kPostBody, kPostBodyLength),
             delegate.TakeReceivedData());
   EXPECT_TRUE(data.AllWriteDataConsumed());
 }
@@ -277,7 +276,7 @@
   const SpdyHeaderBlock& received_trailers = delegate.trailers();
   SpdyHeaderBlock::const_iterator it = received_trailers.find("foo");
   EXPECT_EQ("bar", it->second);
-  EXPECT_EQ(std::string(kPostBody, kPostBodyLength),
+  EXPECT_EQ(SpdyString(kPostBody, kPostBodyLength),
             delegate.TakeReceivedData());
   EXPECT_TRUE(data.AllWriteDataConsumed());
 }
@@ -424,7 +423,7 @@
 
   EXPECT_TRUE(delegate.send_headers_completed());
   EXPECT_EQ("200", delegate.GetResponseHeaderValue(spdy_util_.GetStatusKey()));
-  EXPECT_EQ(std::string(kPostBody, kPostBodyLength),
+  EXPECT_EQ(SpdyString(kPostBody, kPostBodyLength),
             delegate.TakeReceivedData());
   EXPECT_TRUE(data.AllWriteDataConsumed());
 
@@ -449,7 +448,7 @@
       kDefaultUrl, 1, kPostBodyLength, LOWEST, nullptr, 0));
   AddWrite(req);
 
-  std::string chunk_data(kMaxSpdyFrameChunkSize, 'x');
+  SpdyString chunk_data(kMaxSpdyFrameChunkSize, 'x');
   SpdySerializedFrame chunk(spdy_util_.ConstructSpdyDataFrame(
       1, chunk_data.data(), chunk_data.length(), false));
   AddWrite(chunk);
@@ -478,7 +477,7 @@
       SPDY_REQUEST_RESPONSE_STREAM, session, url_, LOWEST, NetLogWithSource());
   ASSERT_TRUE(stream);
 
-  std::string body_data(3 * kMaxSpdyFrameChunkSize, 'x');
+  SpdyString body_data(3 * kMaxSpdyFrameChunkSize, 'x');
   StreamDelegateWithBody delegate(stream, body_data);
   stream->SetDelegate(&delegate);
 
@@ -494,7 +493,7 @@
 
   EXPECT_TRUE(delegate.send_headers_completed());
   EXPECT_EQ("200", delegate.GetResponseHeaderValue(spdy_util_.GetStatusKey()));
-  EXPECT_EQ(std::string(), delegate.TakeReceivedData());
+  EXPECT_EQ(SpdyString(), delegate.TakeReceivedData());
   EXPECT_TRUE(data.AllWriteDataConsumed());
 }
 
@@ -508,7 +507,7 @@
   SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0));
   AddRead(resp);
 
-  std::string chunk_data(kMaxSpdyFrameChunkSize, 'x');
+  SpdyString chunk_data(kMaxSpdyFrameChunkSize, 'x');
   SpdySerializedFrame chunk(spdy_util_.ConstructSpdyDataFrame(
       1, chunk_data.data(), chunk_data.length(), false));
   AddWrite(chunk);
@@ -531,7 +530,7 @@
       SPDY_BIDIRECTIONAL_STREAM, session, url_, LOWEST, NetLogWithSource());
   ASSERT_TRUE(stream);
 
-  std::string body_data(3 * kMaxSpdyFrameChunkSize, 'x');
+  SpdyString body_data(3 * kMaxSpdyFrameChunkSize, 'x');
   StreamDelegateSendImmediate delegate(stream, body_data);
   stream->SetDelegate(&delegate);
 
@@ -547,7 +546,7 @@
 
   EXPECT_TRUE(delegate.send_headers_completed());
   EXPECT_EQ("200", delegate.GetResponseHeaderValue(spdy_util_.GetStatusKey()));
-  EXPECT_EQ(std::string(), delegate.TakeReceivedData());
+  EXPECT_EQ(SpdyString(), delegate.TakeReceivedData());
   EXPECT_TRUE(data.AllWriteDataConsumed());
 }
 
@@ -787,7 +786,7 @@
 
   EXPECT_THAT(delegate.WaitForClose(), IsOk());
   EXPECT_EQ("200", delegate.GetResponseHeaderValue(spdy_util_.GetStatusKey()));
-  EXPECT_EQ(std::string(kPostBody, kPostBodyLength),
+  EXPECT_EQ(SpdyString(kPostBody, kPostBodyLength),
             delegate.TakeReceivedData());
 
   // Finish async network reads and writes.
@@ -897,7 +896,7 @@
 
   EXPECT_THAT(delegate.WaitForClose(), IsOk());
   EXPECT_EQ("200", delegate.GetResponseHeaderValue(spdy_util_.GetStatusKey()));
-  EXPECT_EQ(std::string(kPostBody, kPostBodyLength),
+  EXPECT_EQ(SpdyString(kPostBody, kPostBodyLength),
             delegate.TakeReceivedData());
 
   // Finish async network reads and writes.
@@ -1076,7 +1075,7 @@
 
   EXPECT_THAT(delegate.WaitForClose(), IsOk());
   EXPECT_EQ("200", delegate.GetResponseHeaderValue(spdy_util_.GetStatusKey()));
-  EXPECT_EQ(std::string(kPostBody, kPostBodyLength),
+  EXPECT_EQ(SpdyString(kPostBody, kPostBodyLength),
             delegate.TakeReceivedData());
 
   // Finish async network reads and writes.
@@ -1388,7 +1387,7 @@
 
   EXPECT_TRUE(delegate.send_headers_completed());
   EXPECT_EQ("200", delegate.GetResponseHeaderValue(":status"));
-  EXPECT_EQ(std::string(), delegate.TakeReceivedData());
+  EXPECT_EQ(SpdyString(), delegate.TakeReceivedData());
   EXPECT_TRUE(data.AllWriteDataConsumed());
 }
 
@@ -1469,7 +1468,7 @@
 
   EXPECT_TRUE(delegate.send_headers_completed());
   EXPECT_EQ("200", delegate.GetResponseHeaderValue(":status"));
-  EXPECT_EQ(std::string(kPostBody, kPostBodyLength),
+  EXPECT_EQ(SpdyString(kPostBody, kPostBodyLength),
             delegate.TakeReceivedData());
   EXPECT_TRUE(data.AllWriteDataConsumed());
 }
diff --git a/net/spdy/spdy_test_util_common.cc b/net/spdy/spdy_test_util_common.cc
index 8d5786d..a7df840 100644
--- a/net/spdy/spdy_test_util_common.cc
+++ b/net/spdy/spdy_test_util_common.cc
@@ -50,9 +50,9 @@
 // Parses a URL into the scheme, host, and path components required for a
 // SPDY request.
 void ParseUrl(SpdyStringPiece url,
-              std::string* scheme,
-              std::string* host,
-              std::string* path) {
+              SpdyString* scheme,
+              SpdyString* host,
+              SpdyString* path) {
   GURL gurl(url);
   path->assign(gurl.PathForRequest());
   scheme->assign(gurl.scheme());
@@ -89,8 +89,8 @@
 void AppendToHeaderBlock(const char* const extra_headers[],
                          int extra_header_count,
                          SpdyHeaderBlock* headers) {
-  std::string this_header;
-  std::string this_value;
+  SpdyString this_header;
+  SpdyString this_value;
 
   if (!extra_header_count)
     return;
@@ -104,11 +104,11 @@
     // Sanity check: Non-empty header.
     DCHECK_NE('\0', *extra_headers[i * 2]) << "Empty header value pair";
     this_header = extra_headers[i * 2];
-    std::string::size_type header_len = this_header.length();
+    SpdyString::size_type header_len = this_header.length();
     if (!header_len)
       continue;
     this_value = extra_headers[1 + (i * 2)];
-    std::string new_value;
+    SpdyString new_value;
     if (headers->find(this_header) != headers->end()) {
       // More than one entry in the header.
       // Don't add the header again, just the append to the value,
@@ -193,7 +193,7 @@
 
   void OnError(SpdyFramer::SpdyFramerError spdy_framer_error) override {}
   void OnStreamError(SpdyStreamId stream_id,
-                     const std::string& description) override {}
+                     const SpdyString& description) override {}
   void OnHeaders(SpdyStreamId stream_id,
                  bool has_priority,
                  int weight,
@@ -291,8 +291,8 @@
   std::vector<uint8_t> private_key;
   if (!key_->ExportPrivateKey(&private_key))
     return false;
-  std::string head = "fakesignature";
-  std::string tail = "/fakesignature";
+  SpdyString head = "fakesignature";
+  SpdyString tail = "/fakesignature";
 
   signature->clear();
   signature->insert(signature->end(), head.begin(), head.end());
@@ -703,7 +703,7 @@
 
 void SpdyTestUtil::AddUrlToHeaderBlock(SpdyStringPiece url,
                                        SpdyHeaderBlock* headers) const {
-  std::string scheme, host, path;
+  SpdyString scheme, host, path;
   ParseUrl(url, &scheme, &host, &path);
   (*headers)[GetHostKey()] = host;
   (*headers)[GetSchemeKey()] = scheme;
@@ -739,16 +739,16 @@
   return ConstructHeaderBlock("PUT", url, &content_length);
 }
 
-std::string SpdyTestUtil::ConstructSpdyReplyString(
+SpdyString SpdyTestUtil::ConstructSpdyReplyString(
     const SpdyHeaderBlock& headers) const {
-  std::string reply_string;
+  SpdyString reply_string;
   for (SpdyHeaderBlock::const_iterator it = headers.begin();
        it != headers.end(); ++it) {
-    std::string key = it->first.as_string();
+    SpdyString key = it->first.as_string();
     // Remove leading colon from pseudo headers.
     if (key[0] == ':')
       key = key.substr(1);
-    for (const std::string& value :
+    for (const SpdyString& value :
          base::SplitString(it->second, SpdyStringPiece("\0", 1),
                            base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
       reply_string += key + ": " + value + "\n";
@@ -797,7 +797,7 @@
 SpdySerializedFrame SpdyTestUtil::ConstructSpdyGoAway(
     SpdyStreamId last_good_stream_id,
     SpdyErrorCode error_code,
-    const std::string& desc) {
+    const SpdyString& desc) {
   SpdyGoAwayIR go_ir(last_good_stream_id, error_code, desc);
   return SpdySerializedFrame(headerless_spdy_framer_.SerializeFrame(go_ir));
 }
@@ -1150,7 +1150,7 @@
 SpdyHeaderBlock SpdyTestUtil::ConstructHeaderBlock(SpdyStringPiece method,
                                                    SpdyStringPiece url,
                                                    int64_t* content_length) {
-  std::string scheme, host, path;
+  SpdyString scheme, host, path;
   ParseUrl(url, &scheme, &host, &path);
   SpdyHeaderBlock headers;
   headers[GetMethodKey()] = method.as_string();
@@ -1158,7 +1158,7 @@
   headers[GetSchemeKey()] = scheme.c_str();
   headers[GetPathKey()] = path.c_str();
   if (content_length) {
-    std::string length_str = base::Int64ToString(*content_length);
+    SpdyString length_str = base::Int64ToString(*content_length);
     headers["content-length"] = length_str;
   }
   return headers;
diff --git a/net/spdy/spdy_test_util_common.h b/net/spdy/spdy_test_util_common.h
index af88246..d2712f8 100644
--- a/net/spdy/spdy_test_util_common.h
+++ b/net/spdy/spdy_test_util_common.h
@@ -10,7 +10,6 @@
 
 #include <map>
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "base/macros.h"
@@ -31,6 +30,7 @@
 #include "net/proxy/proxy_server.h"
 #include "net/proxy/proxy_service.h"
 #include "net/socket/socket_test_util.h"
+#include "net/spdy/platform/api/spdy_string.h"
 #include "net/spdy/platform/api/spdy_string_piece.h"
 #include "net/spdy/spdy_protocol.h"
 #include "net/ssl/ssl_config_service_defaults.h"
@@ -313,7 +313,7 @@
                                                  int64_t content_length);
 
   // Construct an expected SPDY reply string from the given headers.
-  std::string ConstructSpdyReplyString(const SpdyHeaderBlock& headers) const;
+  SpdyString ConstructSpdyReplyString(const SpdyHeaderBlock& headers) const;
 
   // Construct an expected SPDY SETTINGS frame.
   // |settings| are the settings to set.
@@ -340,7 +340,7 @@
   // ownership of the frame.
   SpdySerializedFrame ConstructSpdyGoAway(SpdyStreamId last_good_stream_id,
                                           SpdyErrorCode error_code,
-                                          const std::string& desc);
+                                          const SpdyString& desc);
 
   // Construct a SPDY WINDOW_UPDATE frame.
   // Returns the constructed frame.  The caller takes ownership of the frame.
diff --git a/net/spdy/spdy_test_utils.cc b/net/spdy/spdy_test_utils.cc
index f03e286..100d84f0 100644
--- a/net/spdy/spdy_test_utils.cc
+++ b/net/spdy/spdy_test_utils.cc
@@ -22,12 +22,10 @@
 namespace net {
 namespace test {
 
-using std::string;
-
-string HexDumpWithMarks(const unsigned char* data,
-                        int length,
-                        const bool* marks,
-                        int mark_length) {
+SpdyString HexDumpWithMarks(const unsigned char* data,
+                            int length,
+                            const bool* marks,
+                            int mark_length) {
   static const char kHexChars[] = "0123456789abcdef";
   static const int kColumns = 4;
 
@@ -38,7 +36,7 @@
     mark_length = std::min(mark_length, kSizeLimit);
   }
 
-  string hex;
+  SpdyString hex;
   for (const unsigned char* row = data; length > 0;
        row += kColumns, length -= kColumns) {
     for (const unsigned char *p = row; p < row + 4; ++p) {
@@ -64,7 +62,7 @@
   return hex;
 }
 
-void CompareCharArraysWithHexError(const string& description,
+void CompareCharArraysWithHexError(const SpdyString& description,
                                    const unsigned char* actual,
                                    const int actual_len,
                                    const unsigned char* expected,
@@ -106,9 +104,9 @@
   }
 }
 
-string a2b_hex(const char* hex_data) {
+SpdyString a2b_hex(const char* hex_data) {
   std::vector<uint8_t> output;
-  string result;
+  SpdyString result;
   if (base::HexStringToBytes(hex_data, &output))
     result.assign(reinterpret_cast<const char*>(&output[0]), output.size());
   return result;
@@ -120,23 +118,23 @@
   return hash_value;
 }
 
-string GetTestPin(uint8_t label) {
+SpdyString GetTestPin(uint8_t label) {
   HashValue hash_value = GetTestHashValue(label);
-  string base64;
+  SpdyString base64;
   base::Base64Encode(SpdyStringPiece(reinterpret_cast<char*>(hash_value.data()),
                                      hash_value.size()),
                      &base64);
 
-  return string("pin-sha256=\"") + base64 + "\"";
+  return SpdyString("pin-sha256=\"") + base64 + "\"";
 }
 
 void AddPin(TransportSecurityState* state,
-            const string& host,
+            const SpdyString& host,
             uint8_t primary_label,
             uint8_t backup_label) {
-  string primary_pin = GetTestPin(primary_label);
-  string backup_pin = GetTestPin(backup_label);
-  string header = "max-age = 10000; " + primary_pin + "; " + backup_pin;
+  SpdyString primary_pin = GetTestPin(primary_label);
+  SpdyString backup_pin = GetTestPin(backup_label);
+  SpdyString header = "max-age = 10000; " + primary_pin + "; " + backup_pin;
 
   // Construct a fake SSLInfo that will pass AddHPKPHeader's checks.
   SSLInfo ssl_info;
diff --git a/net/spdy/spdy_test_utils.h b/net/spdy/spdy_test_utils.h
index feb307d..2bd2049 100644
--- a/net/spdy/spdy_test_utils.h
+++ b/net/spdy/spdy_test_utils.h
@@ -8,8 +8,7 @@
 #include <stddef.h>
 #include <stdint.h>
 
-#include <string>
-
+#include "net/spdy/platform/api/spdy_string.h"
 #include "net/spdy/platform/api/spdy_string_piece.h"
 #include "net/spdy/server_push_delegate.h"
 #include "net/spdy/spdy_bug_tracker.h"
@@ -32,32 +31,33 @@
 
 namespace test {
 
-std::string HexDumpWithMarks(const unsigned char* data, int length,
-                             const bool* marks, int mark_length);
+SpdyString HexDumpWithMarks(const unsigned char* data,
+                            int length,
+                            const bool* marks,
+                            int mark_length);
 
-void CompareCharArraysWithHexError(
-    const std::string& description,
-    const unsigned char* actual,
-    const int actual_len,
-    const unsigned char* expected,
-    const int expected_len);
+void CompareCharArraysWithHexError(const SpdyString& description,
+                                   const unsigned char* actual,
+                                   const int actual_len,
+                                   const unsigned char* expected,
+                                   const int expected_len);
 
 void SetFrameFlags(SpdySerializedFrame* frame, uint8_t flags);
 
 void SetFrameLength(SpdySerializedFrame* frame, size_t length);
 
-std::string a2b_hex(const char* hex_data);
+SpdyString a2b_hex(const char* hex_data);
 
 // Returns a SHA1 HashValue in which each byte has the value |label|.
 HashValue GetTestHashValue(uint8_t label);
 
 // Returns SHA1 pinning header for the of the base64 encoding of
 // GetTestHashValue(|label|).
-std::string GetTestPin(uint8_t label);
+SpdyString GetTestPin(uint8_t label);
 
 // Adds a pin for |host| to |state|.
 void AddPin(TransportSecurityState* state,
-            const std::string& host,
+            const SpdyString& host,
             uint8_t primary_label,
             uint8_t backup_label);
 
diff --git a/net/spdy/spdy_write_queue_unittest.cc b/net/spdy/spdy_write_queue_unittest.cc
index 4bd13df..87f7d8b 100644
--- a/net/spdy/spdy_write_queue_unittest.cc
+++ b/net/spdy/spdy_write_queue_unittest.cc
@@ -7,7 +7,6 @@
 #include <cstddef>
 #include <cstring>
 #include <memory>
-#include <string>
 #include <utility>
 
 #include "base/logging.h"
@@ -15,6 +14,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "net/base/request_priority.h"
 #include "net/log/net_log_with_source.h"
+#include "net/spdy/platform/api/spdy_string.h"
 #include "net/spdy/spdy_buffer_producer.h"
 #include "net/spdy/spdy_stream.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -24,8 +24,6 @@
 
 namespace {
 
-using std::string;
-
 const char kOriginal[] = "original";
 const char kRequeued[] = "requeued";
 
@@ -33,7 +31,7 @@
 
 // Makes a SpdyFrameProducer producing a frame with the data in the
 // given string.
-std::unique_ptr<SpdyBufferProducer> StringToProducer(const std::string& s) {
+std::unique_ptr<SpdyBufferProducer> StringToProducer(const SpdyString& s) {
   std::unique_ptr<char[]> data(new char[s.size()]);
   std::memcpy(data.get(), s.data(), s.size());
   return std::unique_ptr<SpdyBufferProducer>(
@@ -84,9 +82,9 @@
 
 // Produces a frame with the given producer and returns a copy of its
 // data as a string.
-std::string ProducerToString(std::unique_ptr<SpdyBufferProducer> producer) {
+SpdyString ProducerToString(std::unique_ptr<SpdyBufferProducer> producer) {
   std::unique_ptr<SpdyBuffer> buffer = producer->ProduceBuffer();
-  return std::string(buffer->GetRemainingData(), buffer->GetRemainingSize());
+  return SpdyString(buffer->GetRemainingData(), buffer->GetRemainingSize());
 }
 
 // Produces a frame with the given producer and returns a copy of its
@@ -300,7 +298,8 @@
 
     EXPECT_TRUE(queue.Dequeue(&frame_type, &producer, &stream));
     EXPECT_TRUE(queue.IsEmpty());
-    EXPECT_EQ(string(kOriginal), producer->ProduceBuffer()->GetRemainingData());
+    EXPECT_EQ(SpdyString(kOriginal),
+              producer->ProduceBuffer()->GetRemainingData());
   }
   // |producer| was destroyed, and a buffer is re-queued.
   EXPECT_FALSE(queue.IsEmpty());
@@ -310,7 +309,8 @@
   base::WeakPtr<SpdyStream> stream;
 
   EXPECT_TRUE(queue.Dequeue(&frame_type, &producer, &stream));
-  EXPECT_EQ(string(kRequeued), producer->ProduceBuffer()->GetRemainingData());
+  EXPECT_EQ(SpdyString(kRequeued),
+            producer->ProduceBuffer()->GetRemainingData());
 }
 
 TEST_F(SpdyWriteQueueTest, ReentranceOnClear) {
@@ -328,7 +328,8 @@
   base::WeakPtr<SpdyStream> stream;
 
   EXPECT_TRUE(queue.Dequeue(&frame_type, &producer, &stream));
-  EXPECT_EQ(string(kRequeued), producer->ProduceBuffer()->GetRemainingData());
+  EXPECT_EQ(SpdyString(kRequeued),
+            producer->ProduceBuffer()->GetRemainingData());
 }
 
 TEST_F(SpdyWriteQueueTest, ReentranceOnRemovePendingWritesAfter) {
@@ -349,7 +350,8 @@
   base::WeakPtr<SpdyStream> weak_stream;
 
   EXPECT_TRUE(queue.Dequeue(&frame_type, &producer, &weak_stream));
-  EXPECT_EQ(string(kRequeued), producer->ProduceBuffer()->GetRemainingData());
+  EXPECT_EQ(SpdyString(kRequeued),
+            producer->ProduceBuffer()->GetRemainingData());
 }
 
 TEST_F(SpdyWriteQueueTest, ReentranceOnRemovePendingWritesForStream) {
@@ -370,7 +372,8 @@
   base::WeakPtr<SpdyStream> weak_stream;
 
   EXPECT_TRUE(queue.Dequeue(&frame_type, &producer, &weak_stream));
-  EXPECT_EQ(string(kRequeued), producer->ProduceBuffer()->GetRemainingData());
+  EXPECT_EQ(SpdyString(kRequeued),
+            producer->ProduceBuffer()->GetRemainingData());
 }
 
 }  // namespace
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json
index 6315690..67cbbd9 100644
--- a/testing/buildbot/chromium.gpu.fyi.json
+++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -5797,6 +5797,19 @@
         },
         "test": "service_unittests",
         "use_xvfb": false
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0a2e",
+              "os": "Mac-10.12"
+            }
+          ]
+        },
+        "test": "swiftshader_unittests",
+        "use_xvfb": false
       }
     ],
     "isolated_scripts": [
@@ -6192,6 +6205,20 @@
         },
         "test": "service_unittests",
         "use_xvfb": false
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": false,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac"
+            }
+          ]
+        },
+        "test": "swiftshader_unittests",
+        "use_xvfb": false
       }
     ],
     "isolated_scripts": [
@@ -6624,6 +6651,20 @@
         },
         "test": "service_unittests",
         "use_xvfb": false
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": false,
+          "dimension_sets": [
+            {
+              "gpu": "10de:0fe9",
+              "hidpi": "1",
+              "os": "Mac"
+            }
+          ]
+        },
+        "test": "swiftshader_unittests",
+        "use_xvfb": false
       }
     ],
     "isolated_scripts": [
@@ -7073,6 +7114,24 @@
         "use_xvfb": false
       },
       {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0a2e",
+              "os": "Mac-10.12"
+            },
+            {
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac"
+            }
+          ]
+        },
+        "test": "swiftshader_unittests",
+        "use_xvfb": false
+      },
+      {
         "override_compile_targets": [
           "tab_capture_end2end_tests_run"
         ],
@@ -7501,6 +7560,19 @@
         },
         "test": "gles2_conform_test",
         "use_xvfb": false
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": false,
+          "dimension_sets": [
+            {
+              "gpu": "1002:679e",
+              "os": "Mac-10.10"
+            }
+          ]
+        },
+        "test": "swiftshader_unittests",
+        "use_xvfb": false
       }
     ],
     "isolated_scripts": [
@@ -7872,6 +7944,19 @@
         },
         "test": "gles2_conform_test",
         "use_xvfb": false
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": false,
+          "dimension_sets": [
+            {
+              "gpu": "1002:679e",
+              "os": "Mac-10.10"
+            }
+          ]
+        },
+        "test": "swiftshader_unittests",
+        "use_xvfb": false
       }
     ],
     "isolated_scripts": [
@@ -8275,6 +8360,19 @@
         "use_xvfb": false
       },
       {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0a2e",
+              "os": "Mac-10.12"
+            }
+          ]
+        },
+        "test": "swiftshader_unittests",
+        "use_xvfb": false
+      },
+      {
         "override_compile_targets": [
           "tab_capture_end2end_tests_run"
         ],
@@ -8697,6 +8795,20 @@
         },
         "test": "service_unittests",
         "use_xvfb": false
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac"
+            }
+          ]
+        },
+        "test": "swiftshader_unittests",
+        "use_xvfb": false
       }
     ],
     "isolated_scripts": [
@@ -9089,6 +9201,20 @@
         },
         "test": "service_unittests",
         "use_xvfb": false
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:0fe9",
+              "hidpi": "1",
+              "os": "Mac"
+            }
+          ]
+        },
+        "test": "swiftshader_unittests",
+        "use_xvfb": false
       }
     ],
     "isolated_scripts": [
@@ -9483,6 +9609,20 @@
         "use_xvfb": false
       },
       {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac"
+            }
+          ]
+        },
+        "test": "swiftshader_unittests",
+        "use_xvfb": false
+      },
+      {
         "override_compile_targets": [
           "tab_capture_end2end_tests_run"
         ],
@@ -9919,6 +10059,20 @@
         "use_xvfb": false
       },
       {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:0fe9",
+              "hidpi": "1",
+              "os": "Mac"
+            }
+          ]
+        },
+        "test": "swiftshader_unittests",
+        "use_xvfb": false
+      },
+      {
         "override_compile_targets": [
           "tab_capture_end2end_tests_run"
         ],
@@ -10467,6 +10621,19 @@
         },
         "test": "service_unittests",
         "use_xvfb": false
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0a2e",
+              "os": "Mac-10.12"
+            }
+          ]
+        },
+        "test": "swiftshader_unittests",
+        "use_xvfb": false
       }
     ],
     "isolated_scripts": [
@@ -10579,6 +10746,20 @@
         },
         "test": "service_unittests",
         "use_xvfb": false
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac"
+            }
+          ]
+        },
+        "test": "swiftshader_unittests",
+        "use_xvfb": false
       }
     ],
     "isolated_scripts": [
@@ -10690,6 +10871,19 @@
         },
         "test": "service_unittests",
         "use_xvfb": false
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0a2e",
+              "os": "Mac-10.12"
+            }
+          ]
+        },
+        "test": "swiftshader_unittests",
+        "use_xvfb": false
       }
     ],
     "isolated_scripts": [
@@ -10802,6 +10996,20 @@
         },
         "test": "service_unittests",
         "use_xvfb": false
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:0fe9",
+              "hidpi": "1",
+              "os": "Mac"
+            }
+          ]
+        },
+        "test": "swiftshader_unittests",
+        "use_xvfb": false
       }
     ],
     "isolated_scripts": [
@@ -10916,6 +11124,20 @@
         },
         "test": "service_unittests",
         "use_xvfb": false
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac"
+            }
+          ]
+        },
+        "test": "swiftshader_unittests",
+        "use_xvfb": false
       }
     ],
     "isolated_scripts": [
@@ -11030,6 +11252,20 @@
         },
         "test": "service_unittests",
         "use_xvfb": false
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:0fe9",
+              "hidpi": "1",
+              "os": "Mac"
+            }
+          ]
+        },
+        "test": "swiftshader_unittests",
+        "use_xvfb": false
       }
     ],
     "isolated_scripts": [
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2 b/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
index fed22d1..d6edb1e 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
@@ -110,6 +110,7 @@
 
 Bug(none) compositing/3d-cube.html [ Failure ]
 Bug(none) compositing/absolute-inside-out-of-view-fixed.html [ Failure ]
+Bug(none) compositing/always-composite-fixed-position-when-descendants-composite.html [ Failure ]
 Bug(none) compositing/animation/hidden-composited.html [ Failure ]
 Bug(none) compositing/backface-visibility/backface-visibility-image.html [ Failure ]
 Bug(none) compositing/backface-visibility/backface-visibility-webgl.html [ Failure ]
@@ -1366,6 +1367,11 @@
 crbug.com/692310 virtual/threaded/transitions/opacity-transition-zindex.html [ Timeout ]
 crbug.com/692310 virtual/threaded/transitions/unprefixed-transform.html [ Timeout ]
 
+Bug(none) virtual/threaded/animations/3d/change-transform-in-end-event.html [ Failure ]
+Bug(none) virtual/threaded/animations/composited-pseudo-element-animation.html [ Failure ]
+Bug(none) virtual/threaded/animations/css-composite-animation-affects-use-elements.html [ Failure ]
+Bug(none) virtual/threaded/transitions/transition-end-event-rendering.html [ Failure ]
+
 # Compositor Worker logic has not yet been fully ported to SPv2.
 crbug.com/686897 virtual/threaded/fast/compositorworker/compositor-attribute-change-worker.html [ Timeout ]
 crbug.com/686897 virtual/threaded/fast/compositorworker/compositor-proxy-disconnect-worker.html [ Timeout ]
diff --git a/third_party/WebKit/LayoutTests/imagecapture/MediaStreamTrack-applyConstraints-getSettings.html b/third_party/WebKit/LayoutTests/imagecapture/MediaStreamTrack-applyConstraints-getSettings.html
index 3929d27..cfbb985 100644
--- a/third_party/WebKit/LayoutTests/imagecapture/MediaStreamTrack-applyConstraints-getSettings.html
+++ b/third_party/WebKit/LayoutTests/imagecapture/MediaStreamTrack-applyConstraints-getSettings.html
@@ -94,8 +94,7 @@
 
       assert_equals(constraints.advanced[0].zoom, settings.zoom, 'zoom');
 
-      // TODO(mcasas): do |torch| when the mojom interface is updated,
-      // https://crbug.com/700607.
+      assert_equals(constraints.advanced[0].torch, settings.torch, 'torch');
 
       t.done();
     })
diff --git a/third_party/WebKit/LayoutTests/imagecapture/MediaStreamTrack-getCapabilities.html b/third_party/WebKit/LayoutTests/imagecapture/MediaStreamTrack-getCapabilities.html
index 59d9794..08d4dadd 100644
--- a/third_party/WebKit/LayoutTests/imagecapture/MediaStreamTrack-getCapabilities.html
+++ b/third_party/WebKit/LayoutTests/imagecapture/MediaStreamTrack-getCapabilities.html
@@ -109,7 +109,7 @@
         assert_equals(capabilities.zoom.min, mock_capabilities.zoom.min);
         assert_equals(capabilities.zoom.step, mock_capabilities.zoom.step);
 
-        assert_equals(capabilities.torch, mock_capabilities.torch, 'torch');
+        assert_equals(capabilities.torch, mock_capabilities.supports_torch, 'torch');
 
         t.done();
       }, 100);
diff --git a/third_party/WebKit/LayoutTests/imagecapture/MediaStreamTrack-getSettings.html b/third_party/WebKit/LayoutTests/imagecapture/MediaStreamTrack-getSettings.html
index 3c1ed9d..37406069 100644
--- a/third_party/WebKit/LayoutTests/imagecapture/MediaStreamTrack-getSettings.html
+++ b/third_party/WebKit/LayoutTests/imagecapture/MediaStreamTrack-getSettings.html
@@ -70,8 +70,7 @@
 
         assert_equals(settings.zoom, mock_settings.zoom.current);
 
-        // TODO(mcasas): do |torch| when the mojom interface is updated,
-        // https://crbug.com/700607.
+        assert_equals(settings.torch, mock_settings.torch, 'torch');
 
         t.done();
       }, 100);
diff --git a/third_party/WebKit/LayoutTests/imagecapture/resources/mock-imagecapture.js b/third_party/WebKit/LayoutTests/imagecapture/resources/mock-imagecapture.js
index d89fed5..0afe582c 100644
--- a/third_party/WebKit/LayoutTests/imagecapture/resources/mock-imagecapture.js
+++ b/third_party/WebKit/LayoutTests/imagecapture/resources/mock-imagecapture.js
@@ -26,6 +26,7 @@
           fill_light_mode : [ imageCapture.FillLightMode.AUTO,
                               imageCapture.FillLightMode.FLASH],
           red_eye_reduction : imageCapture.RedEyeReduction.CONTROLLABLE,
+          supports_torch : true,
           torch : false,
           color_temperature :
               { min : 2500.0, max : 6500.0, current : 6000.0, step : 1000.0 },
diff --git a/third_party/WebKit/Source/bindings/scripts/v8_utilities.py b/third_party/WebKit/Source/bindings/scripts/v8_utilities.py
index 09ad202..dec531f 100644
--- a/third_party/WebKit/Source/bindings/scripts/v8_utilities.py
+++ b/third_party/WebKit/Source/bindings/scripts/v8_utilities.py
@@ -423,6 +423,12 @@
                         'not be specified on the same definition: %s'
                         % definition_or_member.name)
 
+    if is_feature_policy_enabled and 'SecureContext' in extended_attributes:
+        raise Exception('[FeaturePolicy] and [SecureContext] must '
+                        'not be specified on the same definition '
+                        '(see https://crbug.com/695123 for workaround): %s'
+                        % definition_or_member.name)
+
     if is_feature_policy_enabled:
         includes.add('bindings/core/v8/ScriptState.h')
         includes.add('platform/feature_policy/FeaturePolicy.h')
diff --git a/third_party/WebKit/Source/core/css/CSSCounterValue.cpp b/third_party/WebKit/Source/core/css/CSSCounterValue.cpp
index 7773db20..c4cba1c 100644
--- a/third_party/WebKit/Source/core/css/CSSCounterValue.cpp
+++ b/third_party/WebKit/Source/core/css/CSSCounterValue.cpp
@@ -9,6 +9,8 @@
 
 namespace blink {
 
+namespace cssvalue {
+
 String CSSCounterValue::customCSSText() const {
   StringBuilder result;
   if (separator().isEmpty())
@@ -38,4 +40,6 @@
   CSSValue::traceAfterDispatch(visitor);
 }
 
+}  // namespace cssvalue
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/css/CSSCounterValue.h b/third_party/WebKit/Source/core/css/CSSCounterValue.h
index 2d8b0255..ca2eb898 100644
--- a/third_party/WebKit/Source/core/css/CSSCounterValue.h
+++ b/third_party/WebKit/Source/core/css/CSSCounterValue.h
@@ -28,6 +28,8 @@
 
 namespace blink {
 
+namespace cssvalue {
+
 class CSSCounterValue : public CSSValue {
  public:
   static CSSCounterValue* create(CSSCustomIdentValue* identifier,
@@ -65,6 +67,8 @@
 
 DEFINE_CSS_VALUE_TYPE_CASTS(CSSCounterValue, isCounterValue());
 
+}  // namespace cssvalue
+
 }  // namespace blink
 
 #endif  // CSSCounterValue_h
diff --git a/third_party/WebKit/Source/core/css/CSSCursorImageValue.cpp b/third_party/WebKit/Source/core/css/CSSCursorImageValue.cpp
index a3af03c5..17c585a 100644
--- a/third_party/WebKit/Source/core/css/CSSCursorImageValue.cpp
+++ b/third_party/WebKit/Source/core/css/CSSCursorImageValue.cpp
@@ -26,6 +26,8 @@
 
 namespace blink {
 
+namespace cssvalue {
+
 CSSCursorImageValue::CSSCursorImageValue(const CSSValue& imageValue,
                                          bool hotSpotSpecified,
                                          const IntPoint& hotSpot)
@@ -62,4 +64,6 @@
   CSSValue::traceAfterDispatch(visitor);
 }
 
+}  // namespace cssvalue
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/css/CSSCursorImageValue.h b/third_party/WebKit/Source/core/css/CSSCursorImageValue.h
index 644c712..547c4305 100644
--- a/third_party/WebKit/Source/core/css/CSSCursorImageValue.h
+++ b/third_party/WebKit/Source/core/css/CSSCursorImageValue.h
@@ -26,6 +26,8 @@
 
 namespace blink {
 
+namespace cssvalue {
+
 class CSSCursorImageValue : public CSSValue {
  public:
   static const CSSCursorImageValue* create(const CSSValue& imageValue,
@@ -58,6 +60,8 @@
 
 DEFINE_CSS_VALUE_TYPE_CASTS(CSSCursorImageValue, isCursorImageValue());
 
+}  // namespace cssvalue
+
 }  // namespace blink
 
 #endif  // CSSCursorImageValue_h
diff --git a/third_party/WebKit/Source/core/css/properties/CSSPropertyAPIContent.cpp b/third_party/WebKit/Source/core/css/properties/CSSPropertyAPIContent.cpp
index b922d517..6b01f2c 100644
--- a/third_party/WebKit/Source/core/css/properties/CSSPropertyAPIContent.cpp
+++ b/third_party/WebKit/Source/core/css/properties/CSSPropertyAPIContent.cpp
@@ -14,6 +14,8 @@
 
 namespace blink {
 
+using CSSCounterValue = cssvalue::CSSCounterValue;
+
 namespace {
 
 CSSValue* consumeAttr(CSSParserTokenRange args,
diff --git a/third_party/WebKit/Source/core/css/properties/CSSPropertyAPICursor.cpp b/third_party/WebKit/Source/core/css/properties/CSSPropertyAPICursor.cpp
index c1b7250..47ead98 100644
--- a/third_party/WebKit/Source/core/css/properties/CSSPropertyAPICursor.cpp
+++ b/third_party/WebKit/Source/core/css/properties/CSSPropertyAPICursor.cpp
@@ -13,6 +13,8 @@
 
 namespace blink {
 
+using CSSCursorImageValue = cssvalue::CSSCursorImageValue;
+
 const CSSValue* CSSPropertyAPICursor::parseSingleValue(
     CSSParserTokenRange& range,
     const CSSParserContext& context) {
diff --git a/third_party/WebKit/Source/core/css/resolver/StyleBuilderCustom.cpp b/third_party/WebKit/Source/core/css/resolver/StyleBuilderCustom.cpp
index 2eb7a94..d6dd498 100644
--- a/third_party/WebKit/Source/core/css/resolver/StyleBuilderCustom.cpp
+++ b/third_party/WebKit/Source/core/css/resolver/StyleBuilderCustom.cpp
@@ -217,7 +217,8 @@
     state.style()->setCursor(ECursor::kAuto);
     for (const auto& item : toCSSValueList(value)) {
       if (item->isCursorImageValue()) {
-        const CSSCursorImageValue& cursor = toCSSCursorImageValue(*item);
+        const cssvalue::CSSCursorImageValue& cursor =
+            cssvalue::toCSSCursorImageValue(*item);
         const CSSValue& image = cursor.imageValue();
         state.style()->addCursor(state.styleImage(CSSPropertyCursor, image),
                                  cursor.hotSpotSpecified(), cursor.hotSpot());
@@ -723,7 +724,8 @@
       nextContent =
           ContentData::create(state.styleImage(CSSPropertyContent, *item));
     } else if (item->isCounterValue()) {
-      const CSSCounterValue* counterValue = toCSSCounterValue(item.get());
+      const cssvalue::CSSCounterValue* counterValue =
+          cssvalue::toCSSCounterValue(item.get());
       const auto listStyleType =
           cssValueIDToPlatformEnum<EListStyleType>(counterValue->listStyle());
       std::unique_ptr<CounterContent> counter =
diff --git a/third_party/WebKit/Source/core/paint/PaintControllerPaintTest.cpp b/third_party/WebKit/Source/core/paint/PaintControllerPaintTest.cpp
index 7f1813a..bb56f43 100644
--- a/third_party/WebKit/Source/core/paint/PaintControllerPaintTest.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintControllerPaintTest.cpp
@@ -36,12 +36,9 @@
       *toLayoutText(div.firstChild()->layoutObject())->firstTextBox();
 
   if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) {
-    EXPECT_DISPLAY_LIST(
-        rootPaintController().getDisplayItemList(), 4,
-        TestDisplayItem(*layoutView().layer(), DisplayItem::kSubsequence),
-        TestDisplayItem(layoutView(), documentBackgroundType),
-        TestDisplayItem(textInlineBox, foregroundType),
-        TestDisplayItem(*layoutView().layer(), DisplayItem::kEndSubsequence));
+    EXPECT_DISPLAY_LIST(rootPaintController().getDisplayItemList(), 2,
+                        TestDisplayItem(layoutView(), documentBackgroundType),
+                        TestDisplayItem(textInlineBox, foregroundType));
   } else {
     EXPECT_DISPLAY_LIST(rootPaintController().getDisplayItemList(), 2,
                         TestDisplayItem(layoutView(), documentBackgroundType),
@@ -53,14 +50,12 @@
 
   if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) {
     EXPECT_DISPLAY_LIST(
-        rootPaintController().getDisplayItemList(), 5,
-        TestDisplayItem(*layoutView().layer(), DisplayItem::kSubsequence),
+        rootPaintController().getDisplayItemList(), 3,
         TestDisplayItem(layoutView(), documentBackgroundType),
         TestDisplayItem(textInlineBox, foregroundType),
         TestDisplayItem(
             document().frame()->selection().caretDisplayItemClientForTesting(),
-            DisplayItem::kCaret),  // New!
-        TestDisplayItem(*layoutView().layer(), DisplayItem::kEndSubsequence));
+            DisplayItem::kCaret));  // New!
   } else {
     EXPECT_DISPLAY_LIST(
         rootPaintController().getDisplayItemList(), 3,
@@ -83,12 +78,9 @@
   InlineTextBox& firstTextBox = *text.firstTextBox();
 
   if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) {
-    EXPECT_DISPLAY_LIST(
-        rootPaintController().getDisplayItemList(), 4,
-        TestDisplayItem(*layoutView().layer(), DisplayItem::kSubsequence),
-        TestDisplayItem(layoutView(), documentBackgroundType),
-        TestDisplayItem(firstTextBox, foregroundType),
-        TestDisplayItem(*layoutView().layer(), DisplayItem::kEndSubsequence));
+    EXPECT_DISPLAY_LIST(rootPaintController().getDisplayItemList(), 2,
+                        TestDisplayItem(layoutView(), documentBackgroundType),
+                        TestDisplayItem(firstTextBox, foregroundType));
   } else {
     EXPECT_DISPLAY_LIST(rootPaintController().getDisplayItemList(), 2,
                         TestDisplayItem(layoutView(), documentBackgroundType),
@@ -103,13 +95,10 @@
   InlineTextBox& secondTextBox = *newText.firstTextBox()->nextTextBox();
 
   if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) {
-    EXPECT_DISPLAY_LIST(
-        rootPaintController().getDisplayItemList(), 5,
-        TestDisplayItem(*layoutView().layer(), DisplayItem::kSubsequence),
-        TestDisplayItem(layoutView(), documentBackgroundType),
-        TestDisplayItem(newFirstTextBox, foregroundType),
-        TestDisplayItem(secondTextBox, foregroundType),
-        TestDisplayItem(*layoutView().layer(), DisplayItem::kEndSubsequence));
+    EXPECT_DISPLAY_LIST(rootPaintController().getDisplayItemList(), 3,
+                        TestDisplayItem(layoutView(), documentBackgroundType),
+                        TestDisplayItem(newFirstTextBox, foregroundType),
+                        TestDisplayItem(secondTextBox, foregroundType));
   } else {
     EXPECT_DISPLAY_LIST(rootPaintController().getDisplayItemList(), 3,
                         TestDisplayItem(layoutView(), documentBackgroundType),
@@ -126,21 +115,13 @@
       "  <div style='width: 100px; height: 100px; background-color: "
       "blue'></div>"
       "</div>");
-  PaintLayer& htmlLayer =
-      *toLayoutBoxModelObject(document().documentElement()->layoutObject())
-           ->layer();
   LayoutBlock& div = *toLayoutBlock(getLayoutObjectByElementId("div"));
   LayoutObject& subDiv = *div.firstChild();
   LayoutObject& subDiv2 = *subDiv.nextSibling();
-  EXPECT_DISPLAY_LIST(
-      rootPaintController().getDisplayItemList(), 7,
-      TestDisplayItem(*layoutView().layer(), DisplayItem::kSubsequence),
-      TestDisplayItem(layoutView(), documentBackgroundType),
-      TestDisplayItem(htmlLayer, DisplayItem::kSubsequence),
-      TestDisplayItem(subDiv, backgroundType),
-      TestDisplayItem(subDiv2, backgroundType),
-      TestDisplayItem(htmlLayer, DisplayItem::kEndSubsequence),
-      TestDisplayItem(*layoutView().layer(), DisplayItem::kEndSubsequence));
+  EXPECT_DISPLAY_LIST(rootPaintController().getDisplayItemList(), 3,
+                      TestDisplayItem(layoutView(), documentBackgroundType),
+                      TestDisplayItem(subDiv, backgroundType),
+                      TestDisplayItem(subDiv2, backgroundType));
 
   // Verify that the background does not scroll.
   const PaintChunk& backgroundChunk = rootPaintController().paintChunks()[0];
@@ -168,20 +149,12 @@
       "  <div style='width: 100px; height: 100px; background-color: "
       "blue'></div>"
       "</div>");
-  PaintLayer& htmlLayer =
-      *toLayoutBoxModelObject(document().documentElement()->layoutObject())
-           ->layer();
   LayoutBlock& div = *toLayoutBlock(getLayoutObjectByElementId("div"));
   LayoutObject& subDiv = *div.firstChild();
 
-  EXPECT_DISPLAY_LIST(
-      rootPaintController().getDisplayItemList(), 6,
-      TestDisplayItem(*layoutView().layer(), DisplayItem::kSubsequence),
-      TestDisplayItem(layoutView(), documentBackgroundType),
-      TestDisplayItem(htmlLayer, DisplayItem::kSubsequence),
-      TestDisplayItem(subDiv, backgroundType),
-      TestDisplayItem(htmlLayer, DisplayItem::kEndSubsequence),
-      TestDisplayItem(*layoutView().layer(), DisplayItem::kEndSubsequence));
+  EXPECT_DISPLAY_LIST(rootPaintController().getDisplayItemList(), 2,
+                      TestDisplayItem(layoutView(), documentBackgroundType),
+                      TestDisplayItem(subDiv, backgroundType));
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerPainterTest.cpp b/third_party/WebKit/Source/core/paint/PaintLayerPainterTest.cpp
index b5df9d81..7a605b2 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerPainterTest.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintLayerPainterTest.cpp
@@ -69,50 +69,29 @@
       "</div>");
   document().view()->updateAllLifecyclePhases();
 
-  PaintLayer& htmlLayer =
-      *toLayoutBoxModelObject(document().documentElement()->layoutObject())
-           ->layer();
   LayoutObject& container1 =
       *document().getElementById("container1")->layoutObject();
-  PaintLayer& container1Layer = *toLayoutBoxModelObject(container1).layer();
   LayoutObject& content1 =
       *document().getElementById("content1")->layoutObject();
   LayoutObject& container2 =
       *document().getElementById("container2")->layoutObject();
-  PaintLayer& container2Layer = *toLayoutBoxModelObject(container2).layer();
   LayoutObject& content2 =
       *document().getElementById("content2")->layoutObject();
 
   if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) {
-    EXPECT_DISPLAY_LIST(
-        rootPaintController().getDisplayItemList(), 13,
-        TestDisplayItem(*layoutView().layer(), DisplayItem::kSubsequence),
-        TestDisplayItem(layoutView(), documentBackgroundType),
-        TestDisplayItem(htmlLayer, DisplayItem::kSubsequence),
-        TestDisplayItem(container1Layer, DisplayItem::kSubsequence),
-        TestDisplayItem(container1, backgroundType),
-        TestDisplayItem(content1, backgroundType),
-        TestDisplayItem(container1Layer, DisplayItem::kEndSubsequence),
-        TestDisplayItem(container2Layer, DisplayItem::kSubsequence),
-        TestDisplayItem(container2, backgroundType),
-        TestDisplayItem(content2, backgroundType),
-        TestDisplayItem(container2Layer, DisplayItem::kEndSubsequence),
-        TestDisplayItem(htmlLayer, DisplayItem::kEndSubsequence),
-        TestDisplayItem(*layoutView().layer(), DisplayItem::kEndSubsequence));
+    EXPECT_DISPLAY_LIST(rootPaintController().getDisplayItemList(), 5,
+                        TestDisplayItem(layoutView(), documentBackgroundType),
+                        TestDisplayItem(container1, backgroundType),
+                        TestDisplayItem(content1, backgroundType),
+                        TestDisplayItem(container2, backgroundType),
+                        TestDisplayItem(content2, backgroundType));
   } else {
-    EXPECT_DISPLAY_LIST(
-        rootPaintController().getDisplayItemList(), 11,
-        TestDisplayItem(layoutView(), documentBackgroundType),
-        TestDisplayItem(htmlLayer, DisplayItem::kSubsequence),
-        TestDisplayItem(container1Layer, DisplayItem::kSubsequence),
-        TestDisplayItem(container1, backgroundType),
-        TestDisplayItem(content1, backgroundType),
-        TestDisplayItem(container1Layer, DisplayItem::kEndSubsequence),
-        TestDisplayItem(container2Layer, DisplayItem::kSubsequence),
-        TestDisplayItem(container2, backgroundType),
-        TestDisplayItem(content2, backgroundType),
-        TestDisplayItem(container2Layer, DisplayItem::kEndSubsequence),
-        TestDisplayItem(htmlLayer, DisplayItem::kEndSubsequence));
+    EXPECT_DISPLAY_LIST(rootPaintController().getDisplayItemList(), 5,
+                        TestDisplayItem(layoutView(), documentBackgroundType),
+                        TestDisplayItem(container1, backgroundType),
+                        TestDisplayItem(content1, backgroundType),
+                        TestDisplayItem(container2, backgroundType),
+                        TestDisplayItem(content2, backgroundType));
   }
 
   toHTMLElement(content1.node())
@@ -122,40 +101,24 @@
   document().view()->updateAllLifecyclePhasesExceptPaint();
   EXPECT_TRUE(paintWithoutCommit());
 
-  EXPECT_EQ(6, numCachedNewItems());
+  EXPECT_EQ(4, numCachedNewItems());
 
   commit();
 
   if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) {
-    EXPECT_DISPLAY_LIST(
-        rootPaintController().getDisplayItemList(), 13,
-        TestDisplayItem(*layoutView().layer(), DisplayItem::kSubsequence),
-        TestDisplayItem(layoutView(), documentBackgroundType),
-        TestDisplayItem(htmlLayer, DisplayItem::kSubsequence),
-        TestDisplayItem(container1Layer, DisplayItem::kSubsequence),
-        TestDisplayItem(container1, backgroundType),
-        TestDisplayItem(content1, backgroundType),
-        TestDisplayItem(container1Layer, DisplayItem::kEndSubsequence),
-        TestDisplayItem(container2Layer, DisplayItem::kSubsequence),
-        TestDisplayItem(container2, backgroundType),
-        TestDisplayItem(content2, backgroundType),
-        TestDisplayItem(container2Layer, DisplayItem::kEndSubsequence),
-        TestDisplayItem(htmlLayer, DisplayItem::kEndSubsequence),
-        TestDisplayItem(*layoutView().layer(), DisplayItem::kEndSubsequence));
+    EXPECT_DISPLAY_LIST(rootPaintController().getDisplayItemList(), 5,
+                        TestDisplayItem(layoutView(), documentBackgroundType),
+                        TestDisplayItem(container1, backgroundType),
+                        TestDisplayItem(content1, backgroundType),
+                        TestDisplayItem(container2, backgroundType),
+                        TestDisplayItem(content2, backgroundType));
   } else {
-    EXPECT_DISPLAY_LIST(
-        rootPaintController().getDisplayItemList(), 11,
-        TestDisplayItem(layoutView(), documentBackgroundType),
-        TestDisplayItem(htmlLayer, DisplayItem::kSubsequence),
-        TestDisplayItem(container1Layer, DisplayItem::kSubsequence),
-        TestDisplayItem(container1, backgroundType),
-        TestDisplayItem(content1, backgroundType),
-        TestDisplayItem(container1Layer, DisplayItem::kEndSubsequence),
-        TestDisplayItem(container2Layer, DisplayItem::kSubsequence),
-        TestDisplayItem(container2, backgroundType),
-        TestDisplayItem(content2, backgroundType),
-        TestDisplayItem(container2Layer, DisplayItem::kEndSubsequence),
-        TestDisplayItem(htmlLayer, DisplayItem::kEndSubsequence));
+    EXPECT_DISPLAY_LIST(rootPaintController().getDisplayItemList(), 5,
+                        TestDisplayItem(layoutView(), documentBackgroundType),
+                        TestDisplayItem(container1, backgroundType),
+                        TestDisplayItem(content1, backgroundType),
+                        TestDisplayItem(container2, backgroundType),
+                        TestDisplayItem(content2, backgroundType));
   }
 }
 
@@ -169,40 +132,25 @@
       "50px'></div>");
   document().view()->updateAllLifecyclePhases();
 
-  PaintLayer& htmlLayer =
-      *toLayoutBoxModelObject(document().documentElement()->layoutObject())
-           ->layer();
   LayoutObject& svg = *document().getElementById("svg")->layoutObject();
-  PaintLayer& svgLayer = *toLayoutBoxModelObject(svg).layer();
   LayoutObject& rect = *document().getElementById("rect")->layoutObject();
   LayoutObject& div = *document().getElementById("div")->layoutObject();
 
   if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) {
     // SPv2 slips the clip box (see BoxClipper).
-    EXPECT_DISPLAY_LIST(
-        rootPaintController().getDisplayItemList(), 8,
-        TestDisplayItem(*layoutView().layer(), DisplayItem::kSubsequence),
-        TestDisplayItem(layoutView(), documentBackgroundType),
-        TestDisplayItem(htmlLayer, DisplayItem::kSubsequence),
-        TestDisplayItem(svgLayer, DisplayItem::kSubsequence),
-        TestDisplayItem(rect, foregroundType),
-        TestDisplayItem(svgLayer, DisplayItem::kEndSubsequence),
-        TestDisplayItem(htmlLayer, DisplayItem::kEndSubsequence),
-        TestDisplayItem(*layoutView().layer(), DisplayItem::kEndSubsequence));
+    EXPECT_DISPLAY_LIST(rootPaintController().getDisplayItemList(), 2,
+                        TestDisplayItem(layoutView(), documentBackgroundType),
+                        TestDisplayItem(rect, foregroundType));
   } else {
     EXPECT_DISPLAY_LIST(
-        rootPaintController().getDisplayItemList(), 10,
+        rootPaintController().getDisplayItemList(), 6,
         TestDisplayItem(layoutView(), documentBackgroundType),
-        TestDisplayItem(htmlLayer, DisplayItem::kSubsequence),
-        TestDisplayItem(svgLayer, DisplayItem::kSubsequence),
         TestDisplayItem(svg, DisplayItem::kClipLayerForeground),
         TestDisplayItem(svg, DisplayItem::kBeginTransform),
         TestDisplayItem(rect, foregroundType),
         TestDisplayItem(svg, DisplayItem::kEndTransform),
         TestDisplayItem(svg, DisplayItem::clipTypeToEndClipType(
-                                 DisplayItem::kClipLayerForeground)),
-        TestDisplayItem(svgLayer, DisplayItem::kEndSubsequence),
-        TestDisplayItem(htmlLayer, DisplayItem::kEndSubsequence));
+                                 DisplayItem::kClipLayerForeground)));
   }
 
   // Change the color of the div. This should not invalidate the subsequence
@@ -217,39 +165,28 @@
   // Reuse of SVG and document background. 2 fewer with SPv2 enabled because
   // clip display items don't appear in SPv2 display lists.
   if (RuntimeEnabledFeatures::slimmingPaintV2Enabled())
-    EXPECT_EQ(4, numCachedNewItems());
+    EXPECT_EQ(2, numCachedNewItems());
   else
-    EXPECT_EQ(8, numCachedNewItems());
+    EXPECT_EQ(6, numCachedNewItems());
 
   commit();
 
   if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) {
-    EXPECT_DISPLAY_LIST(
-        rootPaintController().getDisplayItemList(), 9,
-        TestDisplayItem(*layoutView().layer(), DisplayItem::kSubsequence),
-        TestDisplayItem(layoutView(), documentBackgroundType),
-        TestDisplayItem(htmlLayer, DisplayItem::kSubsequence),
-        TestDisplayItem(svgLayer, DisplayItem::kSubsequence),
-        TestDisplayItem(rect, foregroundType),
-        TestDisplayItem(svgLayer, DisplayItem::kEndSubsequence),
-        TestDisplayItem(div, backgroundType),
-        TestDisplayItem(htmlLayer, DisplayItem::kEndSubsequence),
-        TestDisplayItem(*layoutView().layer(), DisplayItem::kEndSubsequence));
+    EXPECT_DISPLAY_LIST(rootPaintController().getDisplayItemList(), 3,
+                        TestDisplayItem(layoutView(), documentBackgroundType),
+                        TestDisplayItem(rect, foregroundType),
+                        TestDisplayItem(div, backgroundType));
   } else {
     EXPECT_DISPLAY_LIST(
-        rootPaintController().getDisplayItemList(), 11,
+        rootPaintController().getDisplayItemList(), 7,
         TestDisplayItem(layoutView(), documentBackgroundType),
-        TestDisplayItem(htmlLayer, DisplayItem::kSubsequence),
-        TestDisplayItem(svgLayer, DisplayItem::kSubsequence),
         TestDisplayItem(svg, DisplayItem::kClipLayerForeground),
         TestDisplayItem(svg, DisplayItem::kBeginTransform),
         TestDisplayItem(rect, foregroundType),
         TestDisplayItem(svg, DisplayItem::kEndTransform),
         TestDisplayItem(svg, DisplayItem::clipTypeToEndClipType(
                                  DisplayItem::kClipLayerForeground)),
-        TestDisplayItem(svgLayer, DisplayItem::kEndSubsequence),
         TestDisplayItem(div, backgroundType),
-        TestDisplayItem(htmlLayer, DisplayItem::kEndSubsequence),
         TestDisplayItem(layoutView(),
                         DisplayItem::clipTypeToEndClipType(
                             DisplayItem::kClipFrameToVisibleContentRect)));
@@ -282,24 +219,18 @@
       "</div>");
   rootPaintController().invalidateAll();
 
-  PaintLayer& htmlLayer =
-      *toLayoutBoxModelObject(document().documentElement()->layoutObject())
-           ->layer();
   LayoutObject& container1 =
       *document().getElementById("container1")->layoutObject();
-  PaintLayer& container1Layer = *toLayoutBoxModelObject(container1).layer();
   LayoutObject& content1 =
       *document().getElementById("content1")->layoutObject();
   LayoutObject& container2 =
       *document().getElementById("container2")->layoutObject();
-  PaintLayer& container2Layer = *toLayoutBoxModelObject(container2).layer();
   LayoutObject& content2a =
       *document().getElementById("content2a")->layoutObject();
   LayoutObject& content2b =
       *document().getElementById("content2b")->layoutObject();
   LayoutObject& container3 =
       *document().getElementById("container3")->layoutObject();
-  PaintLayer& container3Layer = *toLayoutBoxModelObject(container3).layer();
   LayoutObject& content3 =
       *document().getElementById("content3")->layoutObject();
 
@@ -311,23 +242,14 @@
   // Container2 is partly (including its stacking chidren) in the interest rect;
   // Content2b is out of the interest rect and output nothing;
   // Container3 is partly in the interest rect.
-  EXPECT_DISPLAY_LIST(
-      rootPaintController().getDisplayItemList(), 15,
-      TestDisplayItem(layoutView(), documentBackgroundType),
-      TestDisplayItem(htmlLayer, DisplayItem::kSubsequence),
-      TestDisplayItem(container1Layer, DisplayItem::kSubsequence),
-      TestDisplayItem(container1, backgroundType),
-      TestDisplayItem(content1, backgroundType),
-      TestDisplayItem(container1Layer, DisplayItem::kEndSubsequence),
-      TestDisplayItem(container2Layer, DisplayItem::kSubsequence),
-      TestDisplayItem(container2, backgroundType),
-      TestDisplayItem(content2a, backgroundType),
-      TestDisplayItem(container2Layer, DisplayItem::kEndSubsequence),
-      TestDisplayItem(container3Layer, DisplayItem::kSubsequence),
-      TestDisplayItem(container3, backgroundType),
-      TestDisplayItem(content3, backgroundType),
-      TestDisplayItem(container3Layer, DisplayItem::kEndSubsequence),
-      TestDisplayItem(htmlLayer, DisplayItem::kEndSubsequence));
+  EXPECT_DISPLAY_LIST(rootPaintController().getDisplayItemList(), 7,
+                      TestDisplayItem(layoutView(), documentBackgroundType),
+                      TestDisplayItem(container1, backgroundType),
+                      TestDisplayItem(content1, backgroundType),
+                      TestDisplayItem(container2, backgroundType),
+                      TestDisplayItem(content2a, backgroundType),
+                      TestDisplayItem(container3, backgroundType),
+                      TestDisplayItem(content3, backgroundType));
 
   document().view()->updateAllLifecyclePhasesExceptPaint();
   IntRect newInterestRect(0, 100, 300, 1000);
@@ -339,26 +261,17 @@
   // Content2b is out of the interest rect and outputs nothing;
   // Container3 becomes out of the interest rect and outputs empty subsequence
   // pair.
-  EXPECT_EQ(7, numCachedNewItems());
+  EXPECT_EQ(5, numCachedNewItems());
 
   commit();
 
-  EXPECT_DISPLAY_LIST(
-      rootPaintController().getDisplayItemList(), 14,
-      TestDisplayItem(layoutView(), documentBackgroundType),
-      TestDisplayItem(htmlLayer, DisplayItem::kSubsequence),
-      TestDisplayItem(container1Layer, DisplayItem::kSubsequence),
-      TestDisplayItem(container1, backgroundType),
-      TestDisplayItem(content1, backgroundType),
-      TestDisplayItem(container1Layer, DisplayItem::kEndSubsequence),
-      TestDisplayItem(container2Layer, DisplayItem::kSubsequence),
-      TestDisplayItem(container2, backgroundType),
-      TestDisplayItem(content2a, backgroundType),
-      TestDisplayItem(content2b, backgroundType),
-      TestDisplayItem(container2Layer, DisplayItem::kEndSubsequence),
-      TestDisplayItem(container3Layer, DisplayItem::kSubsequence),
-      TestDisplayItem(container3Layer, DisplayItem::kEndSubsequence),
-      TestDisplayItem(htmlLayer, DisplayItem::kEndSubsequence));
+  EXPECT_DISPLAY_LIST(rootPaintController().getDisplayItemList(), 6,
+                      TestDisplayItem(layoutView(), documentBackgroundType),
+                      TestDisplayItem(container1, backgroundType),
+                      TestDisplayItem(content1, backgroundType),
+                      TestDisplayItem(container2, backgroundType),
+                      TestDisplayItem(content2a, backgroundType),
+                      TestDisplayItem(content2b, backgroundType));
 }
 
 TEST_P(PaintLayerPainterTest,
@@ -379,50 +292,29 @@
   IntRect interestRect(0, 0, 50, 300);
   paint(&interestRect);
 
-  PaintLayer& htmlLayer =
-      *toLayoutBoxModelObject(document().documentElement()->layoutObject())
-           ->layer();
   LayoutObject& container1 =
       *document().getElementById("container1")->layoutObject();
-  PaintLayer& container1Layer = *toLayoutBoxModelObject(container1).layer();
   LayoutObject& content1 =
       *document().getElementById("content1")->layoutObject();
   LayoutObject& container2 =
       *document().getElementById("container2")->layoutObject();
-  PaintLayer& container2Layer = *toLayoutBoxModelObject(container2).layer();
   LayoutObject& content2 =
       *document().getElementById("content2")->layoutObject();
 
   if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) {
-    EXPECT_DISPLAY_LIST(
-        rootPaintController().getDisplayItemList(), 13,
-        TestDisplayItem(*layoutView().layer(), DisplayItem::kSubsequence),
-        TestDisplayItem(layoutView(), documentBackgroundType),
-        TestDisplayItem(htmlLayer, DisplayItem::kSubsequence),
-        TestDisplayItem(container1Layer, DisplayItem::kSubsequence),
-        TestDisplayItem(container1, backgroundType),
-        TestDisplayItem(content1, backgroundType),
-        TestDisplayItem(container1Layer, DisplayItem::kEndSubsequence),
-        TestDisplayItem(container2Layer, DisplayItem::kSubsequence),
-        TestDisplayItem(container2, backgroundType),
-        TestDisplayItem(content2, backgroundType),
-        TestDisplayItem(container2Layer, DisplayItem::kEndSubsequence),
-        TestDisplayItem(htmlLayer, DisplayItem::kEndSubsequence),
-        TestDisplayItem(*layoutView().layer(), DisplayItem::kEndSubsequence));
+    EXPECT_DISPLAY_LIST(rootPaintController().getDisplayItemList(), 5,
+                        TestDisplayItem(layoutView(), documentBackgroundType),
+                        TestDisplayItem(container1, backgroundType),
+                        TestDisplayItem(content1, backgroundType),
+                        TestDisplayItem(container2, backgroundType),
+                        TestDisplayItem(content2, backgroundType));
   } else {
-    EXPECT_DISPLAY_LIST(
-        rootPaintController().getDisplayItemList(), 11,
-        TestDisplayItem(layoutView(), documentBackgroundType),
-        TestDisplayItem(htmlLayer, DisplayItem::kSubsequence),
-        TestDisplayItem(container1Layer, DisplayItem::kSubsequence),
-        TestDisplayItem(container1, backgroundType),
-        TestDisplayItem(content1, backgroundType),
-        TestDisplayItem(container1Layer, DisplayItem::kEndSubsequence),
-        TestDisplayItem(container2Layer, DisplayItem::kSubsequence),
-        TestDisplayItem(container2, backgroundType),
-        TestDisplayItem(content2, backgroundType),
-        TestDisplayItem(container2Layer, DisplayItem::kEndSubsequence),
-        TestDisplayItem(htmlLayer, DisplayItem::kEndSubsequence));
+    EXPECT_DISPLAY_LIST(rootPaintController().getDisplayItemList(), 5,
+                        TestDisplayItem(layoutView(), documentBackgroundType),
+                        TestDisplayItem(container1, backgroundType),
+                        TestDisplayItem(content1, backgroundType),
+                        TestDisplayItem(container2, backgroundType),
+                        TestDisplayItem(content2, backgroundType));
   }
 
   toHTMLElement(content1.node())
@@ -432,40 +324,24 @@
   document().view()->updateAllLifecyclePhasesExceptPaint();
   EXPECT_TRUE(paintWithoutCommit(&interestRect));
 
-  EXPECT_EQ(6, numCachedNewItems());
+  EXPECT_EQ(4, numCachedNewItems());
 
   commit();
 
   if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) {
-    EXPECT_DISPLAY_LIST(
-        rootPaintController().getDisplayItemList(), 13,
-        TestDisplayItem(*layoutView().layer(), DisplayItem::kSubsequence),
-        TestDisplayItem(layoutView(), documentBackgroundType),
-        TestDisplayItem(htmlLayer, DisplayItem::kSubsequence),
-        TestDisplayItem(container1Layer, DisplayItem::kSubsequence),
-        TestDisplayItem(container1, backgroundType),
-        TestDisplayItem(content1, backgroundType),
-        TestDisplayItem(container1Layer, DisplayItem::kEndSubsequence),
-        TestDisplayItem(container2Layer, DisplayItem::kSubsequence),
-        TestDisplayItem(container2, backgroundType),
-        TestDisplayItem(content2, backgroundType),
-        TestDisplayItem(container2Layer, DisplayItem::kEndSubsequence),
-        TestDisplayItem(htmlLayer, DisplayItem::kEndSubsequence),
-        TestDisplayItem(*layoutView().layer(), DisplayItem::kEndSubsequence));
+    EXPECT_DISPLAY_LIST(rootPaintController().getDisplayItemList(), 5,
+                        TestDisplayItem(layoutView(), documentBackgroundType),
+                        TestDisplayItem(container1, backgroundType),
+                        TestDisplayItem(content1, backgroundType),
+                        TestDisplayItem(container2, backgroundType),
+                        TestDisplayItem(content2, backgroundType));
   } else {
-    EXPECT_DISPLAY_LIST(
-        rootPaintController().getDisplayItemList(), 11,
-        TestDisplayItem(layoutView(), documentBackgroundType),
-        TestDisplayItem(htmlLayer, DisplayItem::kSubsequence),
-        TestDisplayItem(container1Layer, DisplayItem::kSubsequence),
-        TestDisplayItem(container1, backgroundType),
-        TestDisplayItem(content1, backgroundType),
-        TestDisplayItem(container1Layer, DisplayItem::kEndSubsequence),
-        TestDisplayItem(container2Layer, DisplayItem::kSubsequence),
-        TestDisplayItem(container2, backgroundType),
-        TestDisplayItem(content2, backgroundType),
-        TestDisplayItem(container2Layer, DisplayItem::kEndSubsequence),
-        TestDisplayItem(htmlLayer, DisplayItem::kEndSubsequence));
+    EXPECT_DISPLAY_LIST(rootPaintController().getDisplayItemList(), 5,
+                        TestDisplayItem(layoutView(), documentBackgroundType),
+                        TestDisplayItem(container1, backgroundType),
+                        TestDisplayItem(content1, backgroundType),
+                        TestDisplayItem(container2, backgroundType),
+                        TestDisplayItem(content2, backgroundType));
   }
 }
 
diff --git a/third_party/WebKit/Source/core/paint/PaintPropertyTreeUpdateTests.cpp b/third_party/WebKit/Source/core/paint/PaintPropertyTreeUpdateTests.cpp
index 24a7855..1fa7b73 100644
--- a/third_party/WebKit/Source/core/paint/PaintPropertyTreeUpdateTests.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintPropertyTreeUpdateTests.cpp
@@ -146,7 +146,6 @@
       "<style>body { margin: 0; }</style>"
       "<div id='forceScroll' style='height: 8888px;'></div>");
   document().view()->updateAllLifecyclePhases();
-
   FrameView* parent = document().view();
   EXPECT_TRUE(frameScroll(parent)->hasBackgroundAttachmentFixedDescendants());
   FrameView* child = childDocument().view();
diff --git a/third_party/WebKit/Source/core/paint/TablePainterTest.cpp b/third_party/WebKit/Source/core/paint/TablePainterTest.cpp
index 775d04c..0457359 100644
--- a/third_party/WebKit/Source/core/paint/TablePainterTest.cpp
+++ b/third_party/WebKit/Source/core/paint/TablePainterTest.cpp
@@ -122,9 +122,6 @@
   LayoutObject& cell1 = *getLayoutObjectByElementId("cell1");
   LayoutObject& cell2 = *getLayoutObjectByElementId("cell2");
   LayoutObject& row = *getLayoutObjectByElementId("row");
-  PaintLayer& htmlLayer =
-      *toLayoutBoxModelObject(document().documentElement()->layoutObject())
-           ->layer();
 
   rootPaintController().invalidateAll();
   document().view()->updateAllLifecyclePhasesExceptPaint();
@@ -133,14 +130,12 @@
   paint(&interestRect);
 
   EXPECT_DISPLAY_LIST(
-      rootPaintController().getDisplayItemList(), 7,
+      rootPaintController().getDisplayItemList(), 5,
       TestDisplayItem(layoutView, DisplayItem::kDocumentBackground),
-      TestDisplayItem(htmlLayer, DisplayItem::kSubsequence),
       TestDisplayItem(row, DisplayItem::kBeginCompositing),
       TestDisplayItem(row, DisplayItem::kBoxDecorationBackground),
       TestDisplayItem(cell1, DisplayItem::kBoxDecorationBackground),
-      TestDisplayItem(row, DisplayItem::kEndCompositing),
-      TestDisplayItem(htmlLayer, DisplayItem::kEndSubsequence));
+      TestDisplayItem(row, DisplayItem::kEndCompositing));
 
   document().view()->updateAllLifecyclePhasesExceptPaint();
   // Intersects the spacing only.
@@ -148,10 +143,8 @@
   paint(&interestRect);
 
   EXPECT_DISPLAY_LIST(
-      rootPaintController().getDisplayItemList(), 3,
-      TestDisplayItem(layoutView, DisplayItem::kDocumentBackground),
-      TestDisplayItem(htmlLayer, DisplayItem::kSubsequence),
-      TestDisplayItem(htmlLayer, DisplayItem::kEndSubsequence));
+      rootPaintController().getDisplayItemList(), 1,
+      TestDisplayItem(layoutView, DisplayItem::kDocumentBackground));
 
   document().view()->updateAllLifecyclePhasesExceptPaint();
   // Intersects cell2 only.
@@ -159,14 +152,12 @@
   paint(&interestRect);
 
   EXPECT_DISPLAY_LIST(
-      rootPaintController().getDisplayItemList(), 7,
+      rootPaintController().getDisplayItemList(), 5,
       TestDisplayItem(layoutView, DisplayItem::kDocumentBackground),
-      TestDisplayItem(htmlLayer, DisplayItem::kSubsequence),
       TestDisplayItem(row, DisplayItem::kBeginCompositing),
       TestDisplayItem(row, DisplayItem::kBoxDecorationBackground),
       TestDisplayItem(cell2, DisplayItem::kBoxDecorationBackground),
-      TestDisplayItem(row, DisplayItem::kEndCompositing),
-      TestDisplayItem(htmlLayer, DisplayItem::kEndSubsequence));
+      TestDisplayItem(row, DisplayItem::kEndCompositing));
 }
 
 TEST_F(TablePainterTest, CollapsedBorderAndOverflow) {
diff --git a/third_party/WebKit/Source/modules/gamepad/OWNERS b/third_party/WebKit/Source/modules/gamepad/OWNERS
index 1ed1f65..25d98580 100644
--- a/third_party/WebKit/Source/modules/gamepad/OWNERS
+++ b/third_party/WebKit/Source/modules/gamepad/OWNERS
@@ -1,4 +1,6 @@
 b.kelemen@samsung.com
 bajones@chromium.org
+mattreynolds@chromium.org
 
+# TEAM: device-dev@chromium.org
 # COMPONENT: IO>Gamepad
diff --git a/third_party/WebKit/Source/modules/imagecapture/ImageCapture.cpp b/third_party/WebKit/Source/modules/imagecapture/ImageCapture.cpp
index e0d50dc..d38b64c3 100644
--- a/third_party/WebKit/Source/modules/imagecapture/ImageCapture.cpp
+++ b/third_party/WebKit/Source/modules/imagecapture/ImageCapture.cpp
@@ -600,10 +600,9 @@
     m_settings.setZoom(capabilities.zoom->current);
   }
 
-  m_capabilities.setTorch(capabilities.torch);
-
-  // TODO(mcasas): do |torch| when the mojom interface is updated,
-  // https://crbug.com/700607.
+  m_capabilities.setTorch(capabilities.supports_torch);
+  if (capabilities.supports_torch)
+    m_settings.setTorch(capabilities.torch);
 }
 
 void ImageCapture::onServiceConnectionError() {
diff --git a/third_party/WebKit/Source/platform/BUILD.gn b/third_party/WebKit/Source/platform/BUILD.gn
index 7f0e2a39..f62a9d4 100644
--- a/third_party/WebKit/Source/platform/BUILD.gn
+++ b/third_party/WebKit/Source/platform/BUILD.gn
@@ -1050,7 +1050,6 @@
     "graphics/paint/ScrollDisplayItem.h",
     "graphics/paint/ScrollPaintPropertyNode.cpp",
     "graphics/paint/ScrollPaintPropertyNode.h",
-    "graphics/paint/SubsequenceDisplayItem.h",
     "graphics/paint/SubsequenceRecorder.cpp",
     "graphics/paint/SubsequenceRecorder.h",
     "graphics/paint/Transform3DDisplayItem.cpp",
diff --git a/third_party/WebKit/Source/platform/graphics/compositing/PaintChunksToCcLayer.cpp b/third_party/WebKit/Source/platform/graphics/compositing/PaintChunksToCcLayer.cpp
index 8b0b073..bceb3f0 100644
--- a/third_party/WebKit/Source/platform/graphics/compositing/PaintChunksToCcLayer.cpp
+++ b/third_party/WebKit/Source/platform/graphics/compositing/PaintChunksToCcLayer.cpp
@@ -180,9 +180,7 @@
 static gfx::Rect largeRect(-200000, -200000, 400000, 400000);
 static void appendDisplayItemToCcDisplayItemList(const DisplayItem& displayItem,
                                                  cc::DisplayItemList* list) {
-  DCHECK(DisplayItem::isDrawingType(displayItem.getType()) ||
-         displayItem.getType() == DisplayItem::kSubsequence ||
-         displayItem.getType() == DisplayItem::kEndSubsequence);
+  DCHECK(DisplayItem::isDrawingType(displayItem.getType()));
   if (DisplayItem::isDrawingType(displayItem.getType())) {
     sk_sp<const PaintRecord> record =
         static_cast<const DrawingDisplayItem&>(displayItem).GetPaintRecord();
diff --git a/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.cpp b/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.cpp
index 4fa9514..228e1e7 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.cpp
@@ -217,8 +217,6 @@
     DEBUG_STRING_CASE(EndTransform);
     DEBUG_STRING_CASE(BeginClipPath);
     DEBUG_STRING_CASE(EndClipPath);
-    DEBUG_STRING_CASE(Subsequence);
-    DEBUG_STRING_CASE(EndSubsequence);
     DEBUG_STRING_CASE(UninitializedType);
     DEFAULT_CASE;
   }
diff --git a/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.h b/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.h
index 066928e4..d46c218 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.h
@@ -182,10 +182,6 @@
     kEndTransform,
     kBeginClipPath,
     kEndClipPath,
-
-    kSubsequence,
-    kEndSubsequence,
-
     kUninitializedType,
     kTypeLast = kUninitializedType
   };
@@ -314,9 +310,7 @@
 
   DEFINE_PAIRED_CATEGORY_METHODS(Transform3D, transform3D)
 
-  static bool isCacheableType(Type type) {
-    return isDrawingType(type) || type == kSubsequence;
-  }
+  static bool isCacheableType(Type type) { return isDrawingType(type); }
   bool isCacheable() const {
     return !skippedCache() && isCacheableType(m_type);
   }
diff --git a/third_party/WebKit/Source/platform/graphics/paint/DisplayItemListTest.cpp b/third_party/WebKit/Source/platform/graphics/paint/DisplayItemListTest.cpp
index 8b31873..c765760c 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/DisplayItemListTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/DisplayItemListTest.cpp
@@ -8,7 +8,6 @@
 #include "platform/graphics/paint/DrawingDisplayItem.h"
 #include "platform/graphics/paint/PaintFlags.h"
 #include "platform/graphics/paint/PaintRecorder.h"
-#include "platform/graphics/paint/SubsequenceDisplayItem.h"
 #include "platform/graphics/skia/SkiaUtils.h"
 #include "platform/testing/FakeDisplayItemClient.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -62,24 +61,14 @@
   // represent the union of all drawing display item visual rects between the
   // pair. We should consider revising Blink's display item list in some form
   // so as to only store visual rects for drawing display items.
-
-  IntRect subsequenceBounds(5, 6, 7, 8);
-  m_list.allocateAndConstruct<BeginSubsequenceDisplayItem>(m_client);
-  m_list.appendVisualRect(subsequenceBounds);
-
   IntRect drawingBounds(5, 6, 1, 1);
   m_list.allocateAndConstruct<DrawingDisplayItem>(
       m_client, DisplayItem::Type::kDocumentBackground,
       createRectRecord(drawingBounds), true);
   m_list.appendVisualRect(drawingBounds);
 
-  m_list.allocateAndConstruct<EndSubsequenceDisplayItem>(m_client);
-  m_list.appendVisualRect(subsequenceBounds);
-
-  EXPECT_EQ(static_cast<size_t>(3), m_list.size());
-  EXPECT_RECT_EQ(subsequenceBounds, m_list.visualRect(0));
-  EXPECT_RECT_EQ(drawingBounds, m_list.visualRect(1));
-  EXPECT_RECT_EQ(subsequenceBounds, m_list.visualRect(2));
+  EXPECT_EQ(static_cast<size_t>(1), m_list.size());
+  EXPECT_RECT_EQ(drawingBounds, m_list.visualRect(0));
 }
 }  // namespace
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/graphics/paint/PaintChunkTest.cpp b/third_party/WebKit/Source/platform/graphics/paint/PaintChunkTest.cpp
index 7afd52c..a212dfb 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/PaintChunkTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/PaintChunkTest.cpp
@@ -36,20 +36,13 @@
   PaintChunkProperties properties;
   FakeDisplayItemClient client1;
   client1.updateCacheGeneration();
-  DisplayItem::Id id1a(client1, DisplayItem::kDrawingFirst);
-  DisplayItem::Id id1b(client1, DisplayItem::kSubsequence);
-  EXPECT_FALSE(PaintChunk(0, 1, &id1a, properties)
-                   .matches(PaintChunk(0, 1, &id1b, properties)));
-  EXPECT_FALSE(PaintChunk(0, 1, &id1b, properties)
-                   .matches(PaintChunk(0, 1, &id1a, properties)));
+  DisplayItem::Id id1(client1, DisplayItem::kDrawingFirst);
 
   FakeDisplayItemClient client2;
   client2.updateCacheGeneration();
   DisplayItem::Id id2(client2, DisplayItem::kDrawingFirst);
-  EXPECT_FALSE(PaintChunk(0, 1, &id1a, properties)
-                   .matches(PaintChunk(0, 1, &id2, properties)));
   EXPECT_FALSE(PaintChunk(0, 1, &id2, properties)
-                   .matches(PaintChunk(0, 1, &id1a, properties)));
+                   .matches(PaintChunk(0, 1, &id1, properties)));
 }
 
 TEST(PaintChunkTest, IdNotMatchesNull) {
diff --git a/third_party/WebKit/Source/platform/graphics/paint/PaintController.cpp b/third_party/WebKit/Source/platform/graphics/paint/PaintController.cpp
index fa5209e7..ac49b8a 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/PaintController.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/PaintController.cpp
@@ -99,22 +99,28 @@
     return false;
   }
 
-  size_t cachedItem =
-      findCachedItem(DisplayItem::Id(client, DisplayItem::kSubsequence));
-  if (cachedItem == kNotFound) {
-    NOTREACHED();
+  SubsequenceMarkers* markers = getSubsequenceMarkers(client);
+  if (!markers) {
     return false;
   }
 
   // |cachedItem| will point to the first item after the subsequence or end of
   // the current list.
   ensureNewDisplayItemListInitialCapacity();
-  copyCachedSubsequence(cachedItem);
 
-  m_nextItemToMatch = cachedItem;
-  // Items before |cachedItem| have been copied so we don't need to index them.
-  if (cachedItem > m_nextItemToIndex)
-    m_nextItemToIndex = cachedItem;
+  size_t sizeBeforeCopy = m_newDisplayItemList.size();
+  copyCachedSubsequence(markers->start, markers->end);
+
+  if (!RuntimeEnabledFeatures::paintUnderInvalidationCheckingEnabled()) {
+    addCachedSubsequence(client, sizeBeforeCopy,
+                         m_newDisplayItemList.size() - 1);
+  }
+
+  m_nextItemToMatch = markers->end + 1;
+  // Items before |m_nextItemToMatch| have been copied so we don't need to index
+  // them.
+  if (m_nextItemToMatch > m_nextItemToIndex)
+    m_nextItemToIndex = m_nextItemToMatch;
 
   if (RuntimeEnabledFeatures::paintUnderInvalidationCheckingEnabled()) {
     // Return false to let the painter actually paint. We will check if the new
@@ -125,6 +131,40 @@
   return true;
 }
 
+PaintController::SubsequenceMarkers* PaintController::getSubsequenceMarkers(
+    const DisplayItemClient& client) {
+  auto result = m_currentCachedSubsequences.find(&client);
+  if (result == m_currentCachedSubsequences.end())
+    return nullptr;
+  return &result->value;
+}
+
+void PaintController::addCachedSubsequence(const DisplayItemClient& client,
+                                           unsigned start,
+                                           unsigned end) {
+  DCHECK(start <= end);
+  DCHECK(end < m_newDisplayItemList.size());
+  if (isCheckingUnderInvalidation()) {
+    SubsequenceMarkers* markers = getSubsequenceMarkers(client);
+    if (!markers) {
+      showSequenceUnderInvalidationError(
+          "under-invalidation : unexpected subsequence", client, start, end);
+      DCHECK(false);
+    }
+    if (markers->end - markers->start != end - start) {
+      showSequenceUnderInvalidationError(
+          "under-invalidation: new subsequence wrong length", client, start,
+          end);
+      DCHECK(false);
+    }
+  }
+
+  DCHECK(m_newCachedSubsequences.find(&client) ==
+         m_newCachedSubsequences.end());
+
+  m_newCachedSubsequences.insert(&client, SubsequenceMarkers(start, end));
+}
+
 bool PaintController::lastDisplayItemIsNoopBegin() const {
   if (m_newDisplayItemList.isEmpty())
     return false;
@@ -170,31 +210,29 @@
   return nullptr;
 }
 
+#if CHECK_DISPLAY_ITEM_CLIENT_ALIVENESS
+void PaintController::beginShouldKeepAlive(const DisplayItemClient& client) {
+  if (!isSkippingCache()) {
+    // Mark the client shouldKeepAlive under this PaintController.
+    // The status will end after the new display items are committed.
+    client.beginShouldKeepAlive(this);
+
+    if (!m_currentSubsequenceClients.isEmpty()) {
+      // Mark the client shouldKeepAlive under the current subsequence.
+      // The status will end when the subsequence owner is invalidated or
+      // deleted.
+      client.beginShouldKeepAlive(m_currentSubsequenceClients.back());
+    }
+  }
+}
+#endif
+
 void PaintController::processNewItem(DisplayItem& displayItem) {
   DCHECK(!m_constructionDisabled);
 
 #if CHECK_DISPLAY_ITEM_CLIENT_ALIVENESS
-  if (!isSkippingCache()) {
-    if (displayItem.isCacheable()) {
-      // Mark the client shouldKeepAlive under this PaintController.
-      // The status will end after the new display items are committed.
-      displayItem.client().beginShouldKeepAlive(this);
-
-      if (!m_currentSubsequenceClients.isEmpty()) {
-        // Mark the client shouldKeepAlive under the current subsequence.
-        // The status will end when the subsequence owner is invalidated or
-        // deleted.
-        displayItem.client().beginShouldKeepAlive(
-            m_currentSubsequenceClients.back());
-      }
-    }
-
-    if (displayItem.getType() == DisplayItem::kSubsequence) {
-      m_currentSubsequenceClients.push_back(&displayItem.client());
-    } else if (displayItem.getType() == DisplayItem::kEndSubsequence) {
-      CHECK(m_currentSubsequenceClients.back() == &displayItem.client());
-      m_currentSubsequenceClients.pop_back();
-    }
+  if (displayItem.isCacheable()) {
+    beginShouldKeepAlive(displayItem.client());
   }
 #endif
 
@@ -217,7 +255,6 @@
     const auto& beginDisplayItem =
         m_newDisplayItemList[m_newDisplayItemList.size() - 2];
     if (beginDisplayItem.isBegin() &&
-        beginDisplayItem.getType() != DisplayItem::kSubsequence &&
         !beginDisplayItem.drawsContent())
       DCHECK(!displayItem.isEndAndPairedWith(beginDisplayItem.getType()));
   }
@@ -393,32 +430,29 @@
   return kNotFound;
 }
 
-// Copies a cached subsequence from current list to the new list. On return,
-// |cachedItemIndex| points to the item after the EndSubsequence item of the
-// subsequence. When paintUnderInvaldiationCheckingEnabled() we'll not actually
+// Copies a cached subsequence from current list to the new list.
+// When paintUnderInvaldiationCheckingEnabled() we'll not actually
 // copy the subsequence, but mark the begin and end of the subsequence for
 // under-invalidation checking.
-void PaintController::copyCachedSubsequence(size_t& cachedItemIndex) {
+void PaintController::copyCachedSubsequence(size_t beginIndex,
+                                            size_t endIndex) {
   AutoReset<size_t> subsequenceBeginIndex(
       &m_currentCachedSubsequenceBeginIndexInNewList,
       m_newDisplayItemList.size());
   DisplayItem* cachedItem =
-      &m_currentPaintArtifact.getDisplayItemList()[cachedItemIndex];
-  DCHECK(cachedItem->getType() == DisplayItem::kSubsequence);
+      &m_currentPaintArtifact.getDisplayItemList()[beginIndex];
 
   if (RuntimeEnabledFeatures::paintUnderInvalidationCheckingEnabled()) {
     DCHECK(!isCheckingUnderInvalidation());
-    m_underInvalidationCheckingBegin = cachedItemIndex;
+    m_underInvalidationCheckingBegin = beginIndex;
     m_underInvalidationMessagePrefix =
         "(In cached subsequence of " + cachedItem->client().debugName() + ")";
   }
 
-  DisplayItem::Id endSubsequenceId(cachedItem->client(),
-                                   DisplayItem::kEndSubsequence);
   Vector<PaintChunk>::const_iterator cachedChunk;
   if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) {
     cachedChunk =
-        m_currentPaintArtifact.findChunkByDisplayItemIndex(cachedItemIndex);
+        m_currentPaintArtifact.findChunkByDisplayItemIndex(beginIndex);
     DCHECK(cachedChunk != m_currentPaintArtifact.paintChunks().end());
     updateCurrentPaintChunkProperties(
         cachedChunk->id ? &*cachedChunk->id : nullptr, cachedChunk->properties);
@@ -427,40 +461,32 @@
     cachedChunk = m_currentPaintArtifact.paintChunks().begin();
   }
 
-  while (true) {
+  for (size_t currentIndex = beginIndex; currentIndex <= endIndex;
+       ++currentIndex) {
+    cachedItem = &m_currentPaintArtifact.getDisplayItemList()[currentIndex];
     DCHECK(cachedItem->hasValidClient());
 #if CHECK_DISPLAY_ITEM_CLIENT_ALIVENESS
     CHECK(cachedItem->client().isAlive());
 #endif
     ++m_numCachedNewItems;
-    bool metEndSubsequence = cachedItem->getId() == endSubsequenceId;
     if (!RuntimeEnabledFeatures::paintUnderInvalidationCheckingEnabled()) {
       if (RuntimeEnabledFeatures::slimmingPaintV2Enabled() &&
-          cachedItemIndex == cachedChunk->endIndex) {
+          currentIndex == cachedChunk->endIndex) {
         ++cachedChunk;
         DCHECK(cachedChunk != m_currentPaintArtifact.paintChunks().end());
         updateCurrentPaintChunkProperties(
             cachedChunk->id ? &*cachedChunk->id : nullptr,
             cachedChunk->properties);
       }
-      processNewItem(moveItemFromCurrentListToNewList(cachedItemIndex));
+      processNewItem(moveItemFromCurrentListToNewList(currentIndex));
       if (RuntimeEnabledFeatures::slimmingPaintV2Enabled())
         DCHECK((!m_newPaintChunks.lastChunk().id && !cachedChunk->id) ||
                m_newPaintChunks.lastChunk().matches(*cachedChunk));
     }
-
-    ++cachedItemIndex;
-    if (metEndSubsequence)
-      break;
-
-    // We should always be able to find the EndSubsequence display item.
-    DCHECK(cachedItemIndex <
-           m_currentPaintArtifact.getDisplayItemList().size());
-    cachedItem = &m_currentPaintArtifact.getDisplayItemList()[cachedItemIndex];
   }
 
   if (RuntimeEnabledFeatures::paintUnderInvalidationCheckingEnabled()) {
-    m_underInvalidationCheckingEnd = cachedItemIndex;
+    m_underInvalidationCheckingEnd = endIndex + 1;
     DCHECK(isCheckingUnderInvalidation());
   }
 }
@@ -492,7 +518,6 @@
                "num_non_cached_new_items",
                (int)m_newDisplayItemList.size() - m_numCachedNewItems);
   m_numCachedNewItems = 0;
-
   // These data structures are used during painting only.
   DCHECK(!isSkippingCache());
 #if DCHECK_IS_ON()
@@ -507,6 +532,16 @@
 
   m_currentCacheGeneration =
       DisplayItemClient::CacheGenerationOrInvalidationReason::next();
+
+  m_newCachedSubsequences.swap(m_currentCachedSubsequences);
+  m_newCachedSubsequences.clear();
+  for (auto& item : m_currentCachedSubsequences) {
+    item.key->setDisplayItemsCached(m_currentCacheGeneration);
+#if CHECK_DISPLAY_ITEM_CLIENT_ALIVENESS
+    DisplayItemClient::endShouldKeepAliveAllClients(item.key);
+#endif
+  }
+
   Vector<const DisplayItemClient*> skippedCacheClients;
   for (const auto& item : m_newDisplayItemList) {
     // No reason to continue the analysis once we have a veto.
@@ -566,7 +601,6 @@
   m_newDisplayItemList = DisplayItemList(0);
 
 #if CHECK_DISPLAY_ITEM_CLIENT_ALIVENESS
-  CHECK(m_currentSubsequenceClients.isEmpty());
   DisplayItemClient::endShouldKeepAliveAllClients(this);
 #endif
 
@@ -785,6 +819,21 @@
 #endif  // NDEBUG
 }
 
+void PaintController::showSequenceUnderInvalidationError(
+    const char* reason,
+    const DisplayItemClient& client,
+    int start,
+    int end) {
+  LOG(ERROR) << m_underInvalidationMessagePrefix << " " << reason;
+  LOG(ERROR) << "Subsequence client: " << client.debugName();
+#ifndef NDEBUG
+//  showDebugData();
+#else
+  LOG(ERROR) << "Run debug build to get more details.";
+#endif
+  LOG(ERROR) << "See http://crbug.com/619103.";
+}
+
 void PaintController::checkUnderInvalidation() {
   DCHECK(RuntimeEnabledFeatures::paintUnderInvalidationCheckingEnabled());
 
diff --git a/third_party/WebKit/Source/platform/graphics/paint/PaintController.h b/third_party/WebKit/Source/platform/graphics/paint/PaintController.h
index c4a237b..2fdedcade 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/PaintController.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/PaintController.h
@@ -109,8 +109,11 @@
   // true. Otherwise returns false.
   bool useCachedSubsequenceIfPossible(const DisplayItemClient&);
 
+  void addCachedSubsequence(const DisplayItemClient&,
+                            unsigned start,
+                            unsigned end);
+
   // True if the last display item is a begin that doesn't draw content.
-  bool lastDisplayItemIsNoopBegin() const;
   void removeLastDisplayItem();
   const DisplayItem* lastDisplayItem(unsigned offset);
 
@@ -194,6 +197,17 @@
     return m_paintChunksRasterInvalidationTrackingMap.get();
   }
 
+#if CHECK_DISPLAY_ITEM_CLIENT_ALIVENESS
+  void beginShouldKeepAlive(const DisplayItemClient&);
+
+  void beginSubsequence(const DisplayItemClient& client) {
+    m_currentSubsequenceClients.push_back(&client);
+    beginShouldKeepAlive(client);
+  }
+
+  void endSubsequence() { m_currentSubsequenceClients.pop_back(); }
+#endif
+
  protected:
   PaintController()
       : m_newDisplayItemList(0),
@@ -204,14 +218,14 @@
         m_imagePainted(false),
         m_skippingCacheCount(0),
         m_numCachedNewItems(0),
-        m_currentCachedSubsequenceBeginIndexInNewList(kNotFound)
+        m_currentCachedSubsequenceBeginIndexInNewList(kNotFound),
 #ifndef NDEBUG
-        ,
         m_numSequentialMatches(0),
         m_numOutOfOrderMatches(0),
-        m_numIndexedItems(0)
+        m_numIndexedItems(0),
 #endif
-  {
+        m_underInvalidationCheckingBegin(0),
+        m_underInvalidationCheckingEnd(0) {
     resetCurrentListIndices();
     setTracksRasterInvalidations(
         RuntimeEnabledFeatures::paintUnderInvalidationCheckingEnabled());
@@ -221,6 +235,8 @@
   friend class PaintControllerTestBase;
   friend class PaintControllerPaintTestBase;
 
+  bool lastDisplayItemIsNoopBegin() const;
+
   void ensureNewDisplayItemListInitialCapacity() {
     if (m_newDisplayItemList.isEmpty()) {
       // TODO(wangxianzhu): Consider revisiting this heuristic.
@@ -252,7 +268,7 @@
 
   size_t findCachedItem(const DisplayItem::Id&);
   size_t findOutOfOrderCachedItemForward(const DisplayItem::Id&);
-  void copyCachedSubsequence(size_t&);
+  void copyCachedSubsequence(size_t beginIndex, size_t endIndex);
 
   // Resets the indices (e.g. m_nextItemToMatch) of
   // m_currentPaintArtifact.getDisplayItemList() to their initial values. This
@@ -273,12 +289,30 @@
   void showUnderInvalidationError(const char* reason,
                                   const DisplayItem& newItem,
                                   const DisplayItem* oldItem) const;
+
+  void showSequenceUnderInvalidationError(const char* reason,
+                                          const DisplayItemClient&,
+                                          int start,
+                                          int end);
+
   void checkUnderInvalidation();
   bool isCheckingUnderInvalidation() const {
     return m_underInvalidationCheckingEnd - m_underInvalidationCheckingBegin >
            0;
   }
 
+  struct SubsequenceMarkers {
+    SubsequenceMarkers() : start(0), end(0) {}
+    SubsequenceMarkers(size_t startArg, size_t endArg)
+        : start(startArg), end(endArg) {}
+    // The start and end index within m_currentPaintArtifact of this
+    // subsequence.
+    size_t start;
+    size_t end;
+  };
+
+  SubsequenceMarkers* getSubsequenceMarkers(const DisplayItemClient&);
+
   // The last complete paint artifact.
   // In SPv2, this includes paint chunks as well as display items.
   PaintArtifact m_currentPaintArtifact;
@@ -378,6 +412,14 @@
   // A stack recording subsequence clients that are currently painting.
   Vector<const DisplayItemClient*> m_currentSubsequenceClients;
 #endif
+
+  typedef HashMap<const DisplayItemClient*, SubsequenceMarkers>
+      CachedSubsequenceMap;
+  CachedSubsequenceMap m_currentCachedSubsequences;
+  CachedSubsequenceMap m_newCachedSubsequences;
+
+  FRIEND_TEST_ALL_PREFIXES(PaintControllerTest, CachedSubsequenceSwapOrder);
+  FRIEND_TEST_ALL_PREFIXES(PaintControllerTest, CachedNestedSubsequenceUpdate);
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/graphics/paint/PaintControllerTest.cpp b/third_party/WebKit/Source/platform/graphics/paint/PaintControllerTest.cpp
index 869bd75..7a78d7f 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/PaintControllerTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/PaintControllerTest.cpp
@@ -1083,21 +1083,27 @@
   }
   getPaintController().commitNewDisplayItems();
 
-  EXPECT_DISPLAY_LIST(
-      getPaintController().getDisplayItemList(), 12,
-      TestDisplayItem(container1, DisplayItem::kSubsequence),
-      TestDisplayItem(container1, backgroundDrawingType),
-      TestDisplayItem(content1, backgroundDrawingType),
-      TestDisplayItem(content1, foregroundDrawingType),
-      TestDisplayItem(container1, foregroundDrawingType),
-      TestDisplayItem(container1, DisplayItem::kEndSubsequence),
+  EXPECT_DISPLAY_LIST(getPaintController().getDisplayItemList(), 8,
+                      TestDisplayItem(container1, backgroundDrawingType),
+                      TestDisplayItem(content1, backgroundDrawingType),
+                      TestDisplayItem(content1, foregroundDrawingType),
+                      TestDisplayItem(container1, foregroundDrawingType),
 
-      TestDisplayItem(container2, DisplayItem::kSubsequence),
-      TestDisplayItem(container2, backgroundDrawingType),
-      TestDisplayItem(content2, backgroundDrawingType),
-      TestDisplayItem(content2, foregroundDrawingType),
-      TestDisplayItem(container2, foregroundDrawingType),
-      TestDisplayItem(container2, DisplayItem::kEndSubsequence));
+                      TestDisplayItem(container2, backgroundDrawingType),
+                      TestDisplayItem(content2, backgroundDrawingType),
+                      TestDisplayItem(content2, foregroundDrawingType),
+                      TestDisplayItem(container2, foregroundDrawingType));
+
+  PaintController::SubsequenceMarkers* markers =
+      getPaintController().getSubsequenceMarkers(container1);
+  CHECK(markers);
+  EXPECT_EQ(0u, markers->start);
+  EXPECT_EQ(3u, markers->end);
+
+  markers = getPaintController().getSubsequenceMarkers(container2);
+  CHECK(markers);
+  EXPECT_EQ(4u, markers->start);
+  EXPECT_EQ(7u, markers->end);
 
   if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) {
     EXPECT_EQ(2u, getPaintController().paintChunks().size());
@@ -1160,30 +1166,34 @@
         context, container1));
   }
 
-  EXPECT_EQ(12, numCachedNewItems());
+  EXPECT_EQ(8, numCachedNewItems());
 #ifndef NDEBUG
-  EXPECT_EQ(1, numSequentialMatches());
-  EXPECT_EQ(1, numOutOfOrderMatches());
-  EXPECT_EQ(5, numIndexedItems());
+  EXPECT_EQ(0, numSequentialMatches());
+  EXPECT_EQ(0, numOutOfOrderMatches());
+  EXPECT_EQ(0, numIndexedItems());
 #endif
 
   getPaintController().commitNewDisplayItems();
 
-  EXPECT_DISPLAY_LIST(
-      getPaintController().getDisplayItemList(), 12,
-      TestDisplayItem(container2, DisplayItem::kSubsequence),
-      TestDisplayItem(container2, backgroundDrawingType),
-      TestDisplayItem(content2, backgroundDrawingType),
-      TestDisplayItem(content2, foregroundDrawingType),
-      TestDisplayItem(container2, foregroundDrawingType),
-      TestDisplayItem(container2, DisplayItem::kEndSubsequence),
+  EXPECT_DISPLAY_LIST(getPaintController().getDisplayItemList(), 8,
+                      TestDisplayItem(container2, backgroundDrawingType),
+                      TestDisplayItem(content2, backgroundDrawingType),
+                      TestDisplayItem(content2, foregroundDrawingType),
+                      TestDisplayItem(container2, foregroundDrawingType),
+                      TestDisplayItem(container1, backgroundDrawingType),
+                      TestDisplayItem(content1, backgroundDrawingType),
+                      TestDisplayItem(content1, foregroundDrawingType),
+                      TestDisplayItem(container1, foregroundDrawingType));
 
-      TestDisplayItem(container1, DisplayItem::kSubsequence),
-      TestDisplayItem(container1, backgroundDrawingType),
-      TestDisplayItem(content1, backgroundDrawingType),
-      TestDisplayItem(content1, foregroundDrawingType),
-      TestDisplayItem(container1, foregroundDrawingType),
-      TestDisplayItem(container1, DisplayItem::kEndSubsequence));
+  markers = getPaintController().getSubsequenceMarkers(container2);
+  CHECK(markers);
+  EXPECT_EQ(0u, markers->start);
+  EXPECT_EQ(3u, markers->end);
+
+  markers = getPaintController().getSubsequenceMarkers(container1);
+  CHECK(markers);
+  EXPECT_EQ(4u, markers->start);
+  EXPECT_EQ(7u, markers->end);
 
 #if CHECK_DISPLAY_ITEM_CLIENT_ALIVENESS
   DisplayItemClient::endShouldKeepAliveAllClients();
@@ -1426,23 +1436,34 @@
   }
   getPaintController().commitNewDisplayItems();
 
-  EXPECT_DISPLAY_LIST(
-      getPaintController().getDisplayItemList(), 14,
-      TestDisplayItem(container1, DisplayItem::kSubsequence),
-      TestDisplayItem(container1, backgroundDrawingType),
-      TestDisplayItem(content1, DisplayItem::kSubsequence),
-      TestDisplayItem(content1, backgroundDrawingType),
-      TestDisplayItem(content1, foregroundDrawingType),
-      TestDisplayItem(content1, DisplayItem::kEndSubsequence),
-      TestDisplayItem(container1, foregroundDrawingType),
-      TestDisplayItem(container1, DisplayItem::kEndSubsequence),
+  EXPECT_DISPLAY_LIST(getPaintController().getDisplayItemList(), 6,
+                      TestDisplayItem(container1, backgroundDrawingType),
+                      TestDisplayItem(content1, backgroundDrawingType),
+                      TestDisplayItem(content1, foregroundDrawingType),
+                      TestDisplayItem(container1, foregroundDrawingType),
+                      TestDisplayItem(container2, backgroundDrawingType),
+                      TestDisplayItem(content2, backgroundDrawingType));
 
-      TestDisplayItem(container2, DisplayItem::kSubsequence),
-      TestDisplayItem(container2, backgroundDrawingType),
-      TestDisplayItem(content2, DisplayItem::kSubsequence),
-      TestDisplayItem(content2, backgroundDrawingType),
-      TestDisplayItem(content2, DisplayItem::kEndSubsequence),
-      TestDisplayItem(container2, DisplayItem::kEndSubsequence));
+  PaintController::SubsequenceMarkers* markers =
+      getPaintController().getSubsequenceMarkers(container1);
+  CHECK(markers);
+  EXPECT_EQ(0u, markers->start);
+  EXPECT_EQ(3u, markers->end);
+
+  markers = getPaintController().getSubsequenceMarkers(content1);
+  CHECK(markers);
+  EXPECT_EQ(1u, markers->start);
+  EXPECT_EQ(2u, markers->end);
+
+  markers = getPaintController().getSubsequenceMarkers(container2);
+  CHECK(markers);
+  EXPECT_EQ(4u, markers->start);
+  EXPECT_EQ(5u, markers->end);
+
+  markers = getPaintController().getSubsequenceMarkers(content2);
+  CHECK(markers);
+  EXPECT_EQ(5u, markers->start);
+  EXPECT_EQ(5u, markers->end);
 
   if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) {
     EXPECT_EQ(5u, getPaintController().paintChunks().size());
@@ -1526,28 +1547,35 @@
              FloatRect(100, 100, 100, 100));
   }
 
-  EXPECT_EQ(4, numCachedNewItems());
+  EXPECT_EQ(2, numCachedNewItems());
 #ifndef NDEBUG
-  EXPECT_EQ(1, numSequentialMatches());
+  EXPECT_EQ(0, numSequentialMatches());
   EXPECT_EQ(0, numOutOfOrderMatches());
-  EXPECT_EQ(2, numIndexedItems());
+  EXPECT_EQ(0, numIndexedItems());
 #endif
 
   getPaintController().commitNewDisplayItems();
 
-  EXPECT_DISPLAY_LIST(
-      getPaintController().getDisplayItemList(), 10,
-      TestDisplayItem(content2, DisplayItem::kSubsequence),
-      TestDisplayItem(content2, foregroundDrawingType),
-      TestDisplayItem(content2, DisplayItem::kEndSubsequence),
+  EXPECT_DISPLAY_LIST(getPaintController().getDisplayItemList(), 4,
+                      TestDisplayItem(content2, foregroundDrawingType),
+                      TestDisplayItem(content1, backgroundDrawingType),
+                      TestDisplayItem(content1, foregroundDrawingType),
+                      TestDisplayItem(container1, foregroundDrawingType));
 
-      TestDisplayItem(container1, DisplayItem::kSubsequence),
-      TestDisplayItem(content1, DisplayItem::kSubsequence),
-      TestDisplayItem(content1, backgroundDrawingType),
-      TestDisplayItem(content1, foregroundDrawingType),
-      TestDisplayItem(content1, DisplayItem::kEndSubsequence),
-      TestDisplayItem(container1, foregroundDrawingType),
-      TestDisplayItem(container1, DisplayItem::kEndSubsequence));
+  markers = getPaintController().getSubsequenceMarkers(content2);
+  CHECK(markers);
+  EXPECT_EQ(0u, markers->start);
+  EXPECT_EQ(0u, markers->end);
+
+  markers = getPaintController().getSubsequenceMarkers(container1);
+  CHECK(markers);
+  EXPECT_EQ(1u, markers->start);
+  EXPECT_EQ(3u, markers->end);
+
+  markers = getPaintController().getSubsequenceMarkers(content1);
+  CHECK(markers);
+  EXPECT_EQ(1u, markers->start);
+  EXPECT_EQ(2u, markers->end);
 
   if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) {
     EXPECT_EQ(3u, getPaintController().paintChunks().size());
@@ -2241,7 +2269,7 @@
                FloatRect(100, 100, 300, 300));
     }
     getPaintController().commitNewDisplayItems();
-    EXPECT_EQ(3u,
+    EXPECT_EQ(1u,
               getPaintController().paintArtifact().getDisplayItemList().size());
 
     {
@@ -2253,7 +2281,7 @@
                FloatRect(100, 100, 300, 300));
     }
     getPaintController().commitNewDisplayItems();
-    EXPECT_EQ(3u,
+    EXPECT_EQ(1u,
               getPaintController().paintArtifact().getDisplayItemList().size());
 
 #if CHECK_DISPLAY_ITEM_CLIENT_ALIVENESS
@@ -2291,14 +2319,16 @@
 
 TEST_F(PaintControllerUnderInvalidationTest, MoreDrawingInSubsequence) {
   EXPECT_DEATH(testMoreDrawingInSubsequence(),
-               "\"\\(In cached subsequence of first\\)\" under-invalidation: "
-               "display item changed");
+               "Check failed: false. Can't find cached display item");
 }
 
 TEST_F(PaintControllerUnderInvalidationTest, LessDrawingInSubsequence) {
+  // We allow invalidated display item clients as long as they would produce the
+  // same display items. The cases of changed display items are tested by other
+  // test cases.
   EXPECT_DEATH(testLessDrawingInSubsequence(),
                "\"\\(In cached subsequence of first\\)\" under-invalidation: "
-               "display item changed");
+               "new subsequence wrong length");
 }
 
 TEST_F(PaintControllerUnderInvalidationTest, ChangeNonCacheableInSubsequence) {
diff --git a/third_party/WebKit/Source/platform/graphics/paint/SubsequenceDisplayItem.h b/third_party/WebKit/Source/platform/graphics/paint/SubsequenceDisplayItem.h
deleted file mode 100644
index 611ca09..0000000
--- a/third_party/WebKit/Source/platform/graphics/paint/SubsequenceDisplayItem.h
+++ /dev/null
@@ -1,34 +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 SubsequenceDisplayItem_h
-#define SubsequenceDisplayItem_h
-
-#include "platform/geometry/FloatRect.h"
-#include "platform/graphics/paint/DisplayItem.h"
-#include "platform/wtf/Assertions.h"
-
-namespace blink {
-
-class BeginSubsequenceDisplayItem final : public PairedBeginDisplayItem {
- public:
-  BeginSubsequenceDisplayItem(const DisplayItemClient& client)
-      : PairedBeginDisplayItem(client, kSubsequence, sizeof(*this)) {}
-};
-
-class EndSubsequenceDisplayItem final : public PairedEndDisplayItem {
- public:
-  EndSubsequenceDisplayItem(const DisplayItemClient& client)
-      : PairedEndDisplayItem(client, kEndSubsequence, sizeof(*this)) {}
-
-#if DCHECK_IS_ON()
-  bool isEndAndPairedWith(DisplayItem::Type otherType) const final {
-    return getType() == kEndSubsequence && otherType == kSubsequence;
-  }
-#endif
-};
-
-}  // namespace blink
-
-#endif  // SubsequenceDisplayItem_h
diff --git a/third_party/WebKit/Source/platform/graphics/paint/SubsequenceRecorder.cpp b/third_party/WebKit/Source/platform/graphics/paint/SubsequenceRecorder.cpp
index 371baf0..0a67e06f 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/SubsequenceRecorder.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/SubsequenceRecorder.cpp
@@ -7,7 +7,6 @@
 #include "platform/RuntimeEnabledFeatures.h"
 #include "platform/graphics/GraphicsContext.h"
 #include "platform/graphics/paint/PaintController.h"
-#include "platform/graphics/paint/SubsequenceDisplayItem.h"
 
 namespace blink {
 
@@ -20,26 +19,27 @@
     return;
 
   m_beginSubsequenceIndex = m_paintController.newDisplayItemList().size();
-  m_paintController.createAndAppend<BeginSubsequenceDisplayItem>(m_client);
+
+#if CHECK_DISPLAY_ITEM_CLIENT_ALIVENESS
+  m_paintController.beginSubsequence(m_client);
+#endif
 }
 
 SubsequenceRecorder::~SubsequenceRecorder() {
+#if CHECK_DISPLAY_ITEM_CLIENT_ALIVENESS
+  m_paintController.endSubsequence();
+#endif
+
   if (m_paintController.displayItemConstructionIsDisabled())
     return;
 
-  if (m_paintController.lastDisplayItemIsNoopBegin()) {
-    ASSERT(m_beginSubsequenceIndex ==
-           m_paintController.newDisplayItemList().size() - 1);
-    // Remove uncacheable no-op BeginSubsequence/EndSubsequence pairs.
-    // Don't remove cacheable no-op pairs because we need to match them later
-    // with CachedSubsequences.
-    if (m_paintController.newDisplayItemList().last().skippedCache()) {
-      m_paintController.removeLastDisplayItem();
-      return;
-    }
-  }
+  // Skip empty subsequences.
+  if (m_paintController.newDisplayItemList().size() == m_beginSubsequenceIndex)
+    return;
 
-  m_paintController.createAndAppend<EndSubsequenceDisplayItem>(m_client);
+  m_paintController.addCachedSubsequence(
+      m_client, m_beginSubsequenceIndex,
+      m_paintController.newDisplayItemList().size() - 1);
 }
 
 }  // namespace blink
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 0fddafe..286baa84 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -21871,6 +21871,11 @@
   <summary>Whether or not a frame displays an overlay.</summary>
 </histogram>
 
+<histogram name="GPU.DirectComposition.SwapchainFormat" enum="SwapchainFormat">
+  <owner>jbauman@chromium.org</owner>
+  <summary>What type of swapchain was actually created for an overlay.</summary>
+</histogram>
+
 <histogram name="GPU.DoLinkProgramTime" units="ms">
   <owner>jmadill@chromium.org</owner>
   <summary>
@@ -113019,6 +113024,11 @@
   <int value="3" label="Attempted"/>
 </enum>
 
+<enum name="SwapchainFormat" type="int">
+  <int value="0" label="B8G8R8A8"/>
+  <int value="1" label="YUY2"/>
+</enum>
+
 <enum name="SwReporterRunningTimeRegistryError" type="int">
   <int value="0" label="No error"/>
   <int value="1" label="Registry key invalid"/>
diff --git a/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.js b/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.js
index c68aece..9a94cfa 100644
--- a/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.js
+++ b/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.js
@@ -131,6 +131,10 @@
     var focusedIndex =
         Array.prototype.indexOf.call(this.options_, this.root.activeElement);
 
+    // Handle case where nothing is focused and up is pressed.
+    if (focusedIndex === -1 && step === -1)
+      focusedIndex = 0;
+
     do {
       focusedIndex = (numOptions + focusedIndex + step) % numOptions;
       nextOption = this.options_[focusedIndex];