diff --git a/DEPS b/DEPS
index 1cff200..b1dc93c1 100644
--- a/DEPS
+++ b/DEPS
@@ -44,7 +44,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': 'acd456c628a9163f4525a4e518a3408623572ebf',
+  'v8_revision': '22d24f2acb48276a581196082d5572c558da232e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 6a95507..32ad6dab 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -4136,6 +4136,8 @@
     "browsing_data/mock_browsing_data_service_worker_helper.h",
     "download/download_test_file_activity_observer.cc",
     "download/download_test_file_activity_observer.h",
+    "history/history_test_utils.cc",
+    "history/history_test_utils.h",
     "media/webrtc/fake_desktop_media_list.cc",
     "media/webrtc/fake_desktop_media_list.h",
     "net/dns_probe_test_util.cc",
diff --git a/chrome/browser/history/history_browsertest.cc b/chrome/browser/history/history_browsertest.cc
index 8a12511..13af0c3 100644
--- a/chrome/browser/history/history_browsertest.cc
+++ b/chrome/browser/history/history_browsertest.cc
@@ -14,6 +14,7 @@
 #include "build/build_config.h"
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/history/history_service_factory.h"
+#include "chrome/browser/history/history_test_utils.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
@@ -40,33 +41,6 @@
 const base::FilePath::CharType kDocRoot[] =
     FILE_PATH_LITERAL("chrome/test/data");
 
-// Note: WaitableEvent is not used for synchronization between the main thread
-// and history backend thread because the history subsystem posts tasks back
-// to the main thread. Had we tried to Signal an event in such a task
-// and Wait for it on the main thread, the task would not run at all because
-// the main thread would be blocked on the Wait call, resulting in a deadlock.
-
-// A task to be scheduled on the history backend thread.
-// Notifies the main thread after all history backend thread tasks have run.
-class WaitForHistoryTask : public history::HistoryDBTask {
- public:
-  WaitForHistoryTask() {}
-
-  bool RunOnDBThread(history::HistoryBackend* backend,
-                     history::HistoryDatabase* db) override {
-    return true;
-  }
-
-  void DoneRunOnMainThread() override {
-    base::MessageLoop::current()->QuitWhenIdle();
-  }
-
- private:
-  ~WaitForHistoryTask() override {}
-
-  DISALLOW_COPY_AND_ASSIGN(WaitForHistoryTask);
-};
-
 }  // namespace
 
 class HistoryBrowserTest : public InProcessBrowserTest {
@@ -99,15 +73,6 @@
         base::FilePath(FILE_PATH_LITERAL("title2.html")));
   }
 
-  void WaitForHistoryBackendToRun() {
-    base::CancelableTaskTracker task_tracker;
-    std::unique_ptr<history::HistoryDBTask> task(new WaitForHistoryTask());
-    history::HistoryService* history = HistoryServiceFactory::GetForProfile(
-        GetProfile(), ServiceAccessType::EXPLICIT_ACCESS);
-    history->ScheduleDBTask(std::move(task), &task_tracker);
-    content::RunMessageLoop();
-  }
-
   void ExpectEmptyHistory() {
     std::vector<GURL> urls(GetHistoryContents());
     EXPECT_EQ(0U, urls.size());
@@ -144,7 +109,7 @@
   ExpectEmptyHistory();
 
   ui_test_utils::NavigateToURL(browser(), GetTestUrl());
-  WaitForHistoryBackendToRun();
+  WaitForHistoryBackendToRun(GetProfile());
 
   {
     std::vector<GURL> urls(GetHistoryContents());
@@ -167,7 +132,7 @@
   ExpectEmptyHistory();
 
   ui_test_utils::NavigateToURL(browser(), GetTestUrl());
-  WaitForHistoryBackendToRun();
+  WaitForHistoryBackendToRun(GetProfile());
   ExpectEmptyHistory();
 }
 
@@ -180,7 +145,7 @@
       browser()->profile(), ServiceAccessType::EXPLICIT_ACCESS));
 
   ui_test_utils::NavigateToURL(browser(), GetTestUrl());
-  WaitForHistoryBackendToRun();
+  WaitForHistoryBackendToRun(GetProfile());
 
   {
     std::vector<GURL> urls(GetHistoryContents());
@@ -191,7 +156,7 @@
   GetPrefs()->SetBoolean(prefs::kSavingBrowserHistoryDisabled, true);
 
   ui_test_utils::NavigateToURL(browser(), GetTestUrl());
-  WaitForHistoryBackendToRun();
+  WaitForHistoryBackendToRun(GetProfile());
 
   {
     // No additional entries should be present in the history.
@@ -211,13 +176,13 @@
   ExpectEmptyHistory();
 
   ui_test_utils::NavigateToURL(browser(), GetTestUrl());
-  WaitForHistoryBackendToRun();
+  WaitForHistoryBackendToRun(GetProfile());
   ExpectEmptyHistory();
 
   GetPrefs()->SetBoolean(prefs::kSavingBrowserHistoryDisabled, false);
 
   ui_test_utils::NavigateToURL(browser(), GetTestUrl());
-  WaitForHistoryBackendToRun();
+  WaitForHistoryBackendToRun(GetProfile());
 
   {
     std::vector<GURL> urls(GetHistoryContents());
diff --git a/chrome/browser/history/history_test_utils.cc b/chrome/browser/history/history_test_utils.cc
new file mode 100644
index 0000000..1f13927
--- /dev/null
+++ b/chrome/browser/history/history_test_utils.cc
@@ -0,0 +1,54 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+#include <utility>
+
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/task/cancelable_task_tracker.h"
+#include "chrome/browser/history/history_service_factory.h"
+#include "components/history/core/browser/history_db_task.h"
+#include "components/history/core/browser/history_service.h"
+#include "content/public/test/test_utils.h"
+
+namespace {
+
+// Note: WaitableEvent is not used for synchronization between the main thread
+// and history backend thread because the history subsystem posts tasks back
+// to the main thread. Had we tried to Signal an event in such a task
+// and Wait for it on the main thread, the task would not run at all because
+// the main thread would be blocked on the Wait call, resulting in a deadlock.
+
+// A task to be scheduled on the history backend thread.
+// Notifies the main thread after all history backend thread tasks have run.
+class WaitForHistoryTask : public history::HistoryDBTask {
+ public:
+  WaitForHistoryTask() {}
+
+  bool RunOnDBThread(history::HistoryBackend* backend,
+                     history::HistoryDatabase* db) override {
+    return true;
+  }
+
+  void DoneRunOnMainThread() override {
+    base::MessageLoop::current()->QuitWhenIdle();
+  }
+
+ private:
+  ~WaitForHistoryTask() override {}
+
+  DISALLOW_COPY_AND_ASSIGN(WaitForHistoryTask);
+};
+
+}  // namespace
+
+void WaitForHistoryBackendToRun(Profile* profile) {
+  base::CancelableTaskTracker task_tracker;
+  std::unique_ptr<history::HistoryDBTask> task(new WaitForHistoryTask());
+  history::HistoryService* history = HistoryServiceFactory::GetForProfile(
+      profile, ServiceAccessType::EXPLICIT_ACCESS);
+  history->ScheduleDBTask(std::move(task), &task_tracker);
+  content::RunMessageLoop();
+}
diff --git a/chrome/browser/history/history_test_utils.h b/chrome/browser/history/history_test_utils.h
new file mode 100644
index 0000000..7704a4c
--- /dev/null
+++ b/chrome/browser/history/history_test_utils.h
@@ -0,0 +1,14 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_HISTORY_HISTORY_TEST_UTILS_H_
+#define CHROME_BROWSER_HISTORY_HISTORY_TEST_UTILS_H_
+
+class Profile;
+
+// Spins the current message loop until all pending messages on the history DB
+// thread complete.
+void WaitForHistoryBackendToRun(Profile* profile);
+
+#endif  // CHROME_BROWSER_HISTORY_HISTORY_TEST_UTILS_H_
diff --git a/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc b/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc
index 710a3714..eca68b7 100644
--- a/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc
+++ b/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/content_settings/tab_specific_content_settings.h"
+#include "chrome/browser/history/history_test_utils.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "chrome/browser/ui/blocked_content/popup_blocker_tab_helper.h"
@@ -337,16 +338,8 @@
 }
 
 // Verify that when you unblock popup, the popup shows in history and omnibox.
-// TODO(crbug.com/663333) Flaky on Linux.
-#if defined(OS_LINUX)
-#define MAYBE_UnblockedPopupShowsInHistoryAndOmnibox \
-  DISABLED_UnblockedPopupShowsInHistoryAndOmnibox
-#else
-#define MAYBE_UnblockedPopupShowsInHistoryAndOmnibox \
-  UnblockedPopupShowsInHistoryAndOmnibox
-#endif
 IN_PROC_BROWSER_TEST_F(PopupBlockerBrowserTest,
-                       MAYBE_UnblockedPopupShowsInHistoryAndOmnibox) {
+                       UnblockedPopupShowsInHistoryAndOmnibox) {
   base::CommandLine::ForCurrentProcess()->AppendSwitch(
       switches::kDisablePopupBlocking);
   GURL url(embedded_test_server()->GetURL(
@@ -357,6 +350,7 @@
       "data:text/html,<title>Popup Success!</title>you should not see this "
       "message if popup blocker is enabled";
 
+  WaitForHistoryBackendToRun(browser()->profile());
   ui_test_utils::HistoryEnumerator history(browser()->profile());
   std::vector<GURL>& history_urls = history.urls();
   ASSERT_EQ(2u, history_urls.size());
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
index 3e4e68b..87067102 100644
--- a/chrome/renderer/chrome_content_renderer_client.cc
+++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -75,7 +75,6 @@
 #include "components/network_hints/renderer/prescient_networking_dispatcher.h"
 #include "components/password_manager/content/renderer/credential_manager_client.h"
 #include "components/pdf/renderer/pepper_pdf_host.h"
-#include "components/plugins/renderer/mobile_youtube_plugin.h"
 #include "components/signin/core/common/profile_management_switches.h"
 #include "components/startup_metric_utils/common/startup_metric.mojom.h"
 #include "components/subresource_filter/content/renderer/ruleset_dealer.h"
@@ -138,6 +137,8 @@
 #include "chrome/common/plugin_utils.h"
 #include "chrome/renderer/plugins/chrome_plugin_placeholder.h"
 #include "chrome/renderer/plugins/power_saver_info.h"
+#else
+#include "components/plugins/renderer/plugin_placeholder.h"
 #endif
 
 #if BUILDFLAG(ENABLE_PRINTING)
@@ -572,18 +573,6 @@
       orig_mime_type, &output));
   *plugin = CreatePlugin(render_frame, frame, params, output);
 #else  // !defined(ENABLE_PLUGINS)
-
-#if defined(OS_ANDROID)
-  if (plugins::MobileYouTubePlugin::IsYouTubeURL(url, orig_mime_type)) {
-    base::StringPiece template_html(
-        ResourceBundle::GetSharedInstance().GetRawDataResource(
-            IDR_MOBILE_YOUTUBE_PLUGIN_HTML));
-    *plugin = (new plugins::MobileYouTubePlugin(render_frame, frame, params,
-                                                template_html))->plugin();
-    return true;
-  }
-#endif  // defined(OS_ANDROID)
-
   PluginUMAReporter::GetInstance()->ReportPluginMissing(orig_mime_type, url);
   *plugin = NonLoadablePluginPlaceholder::CreateNotSupportedPlugin(
                 render_frame, frame, params)->plugin();
diff --git a/chrome/renderer/resources/plugins/mobile_youtube_plugin.html b/chrome/renderer/resources/plugins/mobile_youtube_plugin.html
deleted file mode 100644
index 69e8efa7..0000000
--- a/chrome/renderer/resources/plugins/mobile_youtube_plugin.html
+++ /dev/null
@@ -1,46 +0,0 @@
-<!doctype html>
-<html>
-<head>
-<meta name="viewport" content="width=device-width, user-scalable=no">
-<script>
-function insertBackground() {
-  var video = loadTimeData.getValue("video_id");
-  document.body.style.backgroundImage = "url('http://img.youtube.com/vi/" + video + "/0.jpg')";
-}
-</script>
-<style type="text/css">
-body {
-  background-color: black;
-  background-repeat: no-repeat;
-  background-size: cover;
-  overflow: hidden;
-}
-
-#inner {
-  position: absolute;
-  left: 50%;
-  top: 50%;
-  margin-left: -33px;
-  margin-top: -23px;
-}
-
-#logo {
-  position: absolute;
-  right: 5px;
-  bottom: 5px;
-}
-
-#play {
-  opacity: .7;
-}
-
-#youtube {
-  opacity: .7;
-}
-</style>
-</head>
-<body id="body" onLoad="insertBackground()">
-<div id="logo"><img id="youtube" src="youtube.png"/></div>
-<div id="inner"><img id="play" src="play.png" onclick="plugin.openYoutubeURL();"/></div>
-</body>
-</html>
diff --git a/chrome/renderer/resources/renderer_resources.grd b/chrome/renderer/resources/renderer_resources.grd
index 24f2c39..889ff84b 100644
--- a/chrome/renderer/resources/renderer_resources.grd
+++ b/chrome/renderer/resources/renderer_resources.grd
@@ -14,9 +14,6 @@
       <structure type="chrome_scaled_image" name="IDR_SAD_PLUGIN" file="common\sadplugin.png" />
     </structures>
     <includes>
-      <if expr="is_android">
-        <include name="IDR_MOBILE_YOUTUBE_PLUGIN_HTML" file="plugins/mobile_youtube_plugin.html" flattenhtml="true" type="BINDATA" />
-      </if>
       <include name="IDR_BLOCKED_PLUGIN_HTML" file="plugins/blocked_plugin.html" flattenhtml="true" type="BINDATA" />
       <include name="IDR_DISABLED_PLUGIN_HTML" file="plugins/disabled_plugin.html" flattenhtml="true" type="BINDATA" />
       <include name="IDR_PREFER_HTML_PLUGIN_HTML" file="plugins/prefer_html_plugin.html" flattenhtml="true" type="BINDATA" />
diff --git a/components/plugins/renderer/BUILD.gn b/components/plugins/renderer/BUILD.gn
index 1f35c4c..6c0081f 100644
--- a/components/plugins/renderer/BUILD.gn
+++ b/components/plugins/renderer/BUILD.gn
@@ -17,12 +17,6 @@
       "loadable_plugin_placeholder.h",
     ]
   }
-  if (is_android) {
-    sources += [
-      "mobile_youtube_plugin.cc",
-      "mobile_youtube_plugin.h",
-    ]
-  }
 
   deps = [
     "//content/public/child",
diff --git a/components/plugins/renderer/mobile_youtube_plugin.cc b/components/plugins/renderer/mobile_youtube_plugin.cc
deleted file mode 100644
index 455640b..0000000
--- a/components/plugins/renderer/mobile_youtube_plugin.cc
+++ /dev/null
@@ -1,125 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/plugins/renderer/mobile_youtube_plugin.h"
-
-#include <stddef.h>
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/strings/string_piece.h"
-#include "base/strings/string_util.h"
-#include "base/values.h"
-#include "content/public/common/content_constants.h"
-#include "content/public/renderer/render_frame.h"
-#include "gin/object_template_builder.h"
-#include "third_party/WebKit/public/web/WebFrame.h"
-#include "ui/base/webui/jstemplate_builder.h"
-
-using blink::WebFrame;
-using blink::WebPlugin;
-using blink::WebURLRequest;
-
-const char* const kSlashVSlash = "/v/";
-const char* const kSlashESlash = "/e/";
-
-namespace {
-
-std::string GetYoutubeVideoId(const blink::WebPluginParams& params) {
-  GURL url(params.url);
-  std::string video_id = url.path().substr(strlen(kSlashVSlash));
-
-  // Extract just the video id
-  size_t video_id_end = video_id.find('&');
-  if (video_id_end != std::string::npos)
-    video_id = video_id.substr(0, video_id_end);
-  return video_id;
-}
-
-std::string HtmlData(const blink::WebPluginParams& params,
-                     base::StringPiece template_html) {
-  base::DictionaryValue values;
-  values.SetString("video_id", GetYoutubeVideoId(params));
-  return webui::GetI18nTemplateHtml(template_html, &values);
-}
-
-bool IsValidYouTubeVideo(const std::string& path) {
-  unsigned len = strlen(kSlashVSlash);
-
-  // check for more than just /v/ or /e/.
-  if (path.length() <= len)
-    return false;
-
-  // Youtube flash url can start with /v/ or /e/.
-  if (!base::StartsWith(path, kSlashVSlash,
-                        base::CompareCase::INSENSITIVE_ASCII) &&
-      !base::StartsWith(path, kSlashESlash,
-                        base::CompareCase::INSENSITIVE_ASCII))
-    return false;
-
-  // Start after /v/
-  for (unsigned i = len; i < path.length(); i++) {
-    char c = path[i];
-    if (isalpha(c) || isdigit(c) || c == '_' || c == '-')
-      continue;
-    // The url can have more parameters such as &hl=en after the video id.
-    // Once we start seeing extra parameters we can return true.
-    return c == '&' && i > len;
-  }
-  return true;
-}
-
-}  // namespace
-
-namespace plugins {
-
-gin::WrapperInfo MobileYouTubePlugin::kWrapperInfo = {gin::kEmbedderNativeGin};
-
-MobileYouTubePlugin::MobileYouTubePlugin(content::RenderFrame* render_frame,
-                                         blink::WebLocalFrame* frame,
-                                         const blink::WebPluginParams& params,
-                                         base::StringPiece& template_html)
-    : PluginPlaceholderBase(render_frame,
-                            frame,
-                            params,
-                            HtmlData(params, template_html)) {
-}
-
-MobileYouTubePlugin::~MobileYouTubePlugin() {}
-
-// static
-bool MobileYouTubePlugin::IsYouTubeURL(const GURL& url,
-                                       const std::string& mime_type) {
-  std::string host = url.host();
-  bool is_youtube =
-      base::EndsWith(host, "youtube.com", base::CompareCase::SENSITIVE) ||
-      base::EndsWith(host, "youtube-nocookie.com",
-                     base::CompareCase::SENSITIVE);
-
-  return is_youtube && IsValidYouTubeVideo(url.path()) &&
-         base::LowerCaseEqualsASCII(mime_type,
-                                    content::kFlashPluginSwfMimeType);
-}
-
-void MobileYouTubePlugin::OpenYoutubeUrlCallback() {
-  std::string youtube("vnd.youtube:");
-  GURL url(youtube.append(GetYoutubeVideoId(GetPluginParams())));
-  WebURLRequest request;
-  request.setURL(url);
-  render_frame()->LoadURLExternally(request,
-                                    blink::WebNavigationPolicyNewForegroundTab);
-}
-
-v8::Local<v8::Value> MobileYouTubePlugin::GetV8Handle(v8::Isolate* isolate) {
-  return gin::CreateHandle(isolate, this).ToV8();
-}
-
-gin::ObjectTemplateBuilder MobileYouTubePlugin::GetObjectTemplateBuilder(
-    v8::Isolate* isolate) {
-  return gin::Wrappable<MobileYouTubePlugin>::GetObjectTemplateBuilder(isolate)
-      .SetMethod("openYoutubeURL",
-                 &MobileYouTubePlugin::OpenYoutubeUrlCallback);
-}
-
-}  // namespace plugins
diff --git a/components/plugins/renderer/mobile_youtube_plugin.h b/components/plugins/renderer/mobile_youtube_plugin.h
deleted file mode 100644
index 7def319a..0000000
--- a/components/plugins/renderer/mobile_youtube_plugin.h
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_PLUGINS_RENDERER_MOBILE_YOUTUBE_PLUGIN_H_
-#define COMPONENTS_PLUGINS_RENDERER_MOBILE_YOUTUBE_PLUGIN_H_
-
-#include "base/macros.h"
-#include "components/plugins/renderer/plugin_placeholder.h"
-
-namespace plugins {
-
-// Class representing placeholders for old style embedded youtube video on
-// mobile device. For old style embedded youtube video, it has a url in the form
-// of http://www.youtube.com/v/VIDEO_ID. This placeholder replaces the url with
-// a simple html page and clicking the play image redirects the user to the
-// mobile youtube app.
-class MobileYouTubePlugin final : public PluginPlaceholderBase,
-                                  public gin::Wrappable<MobileYouTubePlugin> {
- public:
-  static gin::WrapperInfo kWrapperInfo;
-
-  MobileYouTubePlugin(content::RenderFrame* render_frame,
-                      blink::WebLocalFrame* frame,
-                      const blink::WebPluginParams& params,
-                      base::StringPiece& template_html);
-
-  // Whether this is a youtube url.
-  static bool IsYouTubeURL(const GURL& url, const std::string& mime_type);
-
- private:
-  ~MobileYouTubePlugin() override;
-
-  // Opens a youtube app in the current tab.
-  void OpenYoutubeUrlCallback();
-
-  // WebViewPlugin::Delegate methods:
-  v8::Local<v8::Value> GetV8Handle(v8::Isolate* isolate) override;
-
-  // gin::Wrappable (via PluginPlaceholder) method
-  gin::ObjectTemplateBuilder GetObjectTemplateBuilder(
-      v8::Isolate* isolate) final;
-
-  DISALLOW_COPY_AND_ASSIGN(MobileYouTubePlugin);
-};
-
-}  // namespace plugins
-
-#endif  // COMPONENTS_PLUGINS_RENDERER_MOBILE_YOUTUBE_PLUGIN_H_
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index f2cf5e1e..ca84a1a 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -1417,6 +1417,28 @@
 crbug.com/660580 [ Precise ] virtual/threaded/printing/fixed-positioned-headers-and-footers.html [ Skip ]
 
 # Tests added from W3C auto import bot
+crbug.com/626703 imported/wpt/imagebitmap-renderingcontext/bitmaprenderer-as-imagesource.html [ Crash Timeout ]
+crbug.com/626703 imported/wpt/dom/events/EventTarget-dispatchEvent.html [ Failure ]
+crbug.com/626703 imported/wpt/html/browsers/history/the-history-interface/non-automated/traverse_the_history_unload_prompt_1-manual.html [ Failure ]
+crbug.com/626703 imported/wpt/html/browsers/history/the-history-interface/non-automated/traverse_the_history_unload_prompt_2-manual.html [ Failure ]
+crbug.com/626703 imported/wpt/html/browsers/history/the-history-interface/non-automated/traverse_the_session_history_unload_prompt_1-manual.html [ Failure ]
+crbug.com/626703 imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-menu.html [ Failure ]
+crbug.com/626703 imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-skip-no-boxes.html [ Failure ]
+crbug.com/626703 imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-no-list-owner.html [ Failure ]
+crbug.com/626703 imported/wpt/streams/byte-length-queuing-strategy.https.html [ Timeout Failure ]
+crbug.com/626703 imported/wpt/streams/count-queuing-strategy.https.html [ Timeout Failure ]
+crbug.com/626703 imported/wpt/streams/readable-streams/bad-strategies.https.html [ Timeout Failure ]
+crbug.com/626703 imported/wpt/streams/readable-streams/bad-underlying-sources.https.html [ Timeout Failure ]
+crbug.com/626703 imported/wpt/streams/readable-streams/brand-checks.https.html [ Timeout Failure ]
+crbug.com/626703 imported/wpt/streams/readable-streams/cancel.https.html [ Timeout Failure ]
+crbug.com/626703 imported/wpt/streams/readable-streams/count-queuing-strategy-integration.https.html [ Timeout Failure ]
+crbug.com/626703 imported/wpt/streams/readable-streams/garbage-collection.https.html [ Timeout Failure ]
+crbug.com/626703 imported/wpt/streams/readable-streams/general.https.html [ Timeout Failure ]
+crbug.com/626703 imported/wpt/streams/readable-streams/pipe-through.https.html [ Timeout Failure ]
+crbug.com/626703 imported/wpt/streams/readable-streams/readable-stream-reader.https.html [ Timeout Failure ]
+crbug.com/626703 imported/wpt/streams/readable-streams/tee.https.html [ Timeout Failure ]
+crbug.com/626703 imported/wpt/streams/readable-streams/templated.https.html [ Timeout Failure ]
+crbug.com/626703 [ Win10 ] imported/wpt/html/semantics/embedded-content/the-img-element/sizes/parse-a-sizes-attribute.html [ Failure ]
 crbug.com/626703 imported/wpt/html/webappapis/idle-callbacks/callback-timeout.html [ Timeout Pass Failure ]
 crbug.com/626703 imported/wpt/html/webappapis/idle-callbacks/callback-multiple-calls.html [ Timeout Pass Failure ]
 crbug.com/626703 imported/wpt/html/webappapis/idle-callbacks/callback-exception.html [ Timeout Pass Failure ]
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/MANIFEST.json b/third_party/WebKit/LayoutTests/imported/wpt/MANIFEST.json
index 0352c7e..452ff34 100644
--- a/third_party/WebKit/LayoutTests/imported/wpt/MANIFEST.json
+++ b/third_party/WebKit/LayoutTests/imported/wpt/MANIFEST.json
@@ -125,34 +125,22 @@
             "url": "/fullscreen/model/remove-single-manual.html"
           }
         ],
-        "html/browsers/history/the-location-interface/non-automated/manual_click_assign_during_load-manual.html": [
+        "html/browsers/history/the-history-interface/non-automated/traverse_the_history_unload_prompt_1-manual.html": [
           {
-            "path": "html/browsers/history/the-location-interface/non-automated/manual_click_assign_during_load-manual.html",
-            "url": "/html/browsers/history/the-location-interface/non-automated/manual_click_assign_during_load-manual.html"
+            "path": "html/browsers/history/the-history-interface/non-automated/traverse_the_history_unload_prompt_1-manual.html",
+            "url": "/html/browsers/history/the-history-interface/non-automated/traverse_the_history_unload_prompt_1-manual.html"
           }
         ],
-        "html/browsers/history/the-location-interface/non-automated/manual_click_location_replace_during_load-manual.html": [
+        "html/browsers/history/the-history-interface/non-automated/traverse_the_history_unload_prompt_2-manual.html": [
           {
-            "path": "html/browsers/history/the-location-interface/non-automated/manual_click_location_replace_during_load-manual.html",
-            "url": "/html/browsers/history/the-location-interface/non-automated/manual_click_location_replace_during_load-manual.html"
+            "path": "html/browsers/history/the-history-interface/non-automated/traverse_the_history_unload_prompt_2-manual.html",
+            "url": "/html/browsers/history/the-history-interface/non-automated/traverse_the_history_unload_prompt_2-manual.html"
           }
         ],
-        "html/browsers/history/the-location-interface/non-automated/manual_click_replace_during_load-manual.html": [
+        "html/browsers/history/the-history-interface/non-automated/traverse_the_session_history_unload_prompt_1-manual.html": [
           {
-            "path": "html/browsers/history/the-location-interface/non-automated/manual_click_replace_during_load-manual.html",
-            "url": "/html/browsers/history/the-location-interface/non-automated/manual_click_replace_during_load-manual.html"
-          }
-        ],
-        "html/browsers/history/the-location-interface/non-automated/manual_form_submit_assign_during_load-manual.html": [
-          {
-            "path": "html/browsers/history/the-location-interface/non-automated/manual_form_submit_assign_during_load-manual.html",
-            "url": "/html/browsers/history/the-location-interface/non-automated/manual_form_submit_assign_during_load-manual.html"
-          }
-        ],
-        "html/browsers/history/the-location-interface/non-automated/reload_in_resize-manual.html": [
-          {
-            "path": "html/browsers/history/the-location-interface/non-automated/reload_in_resize-manual.html",
-            "url": "/html/browsers/history/the-location-interface/non-automated/reload_in_resize-manual.html"
+            "path": "html/browsers/history/the-history-interface/non-automated/traverse_the_session_history_unload_prompt_1-manual.html",
+            "url": "/html/browsers/history/the-history-interface/non-automated/traverse_the_session_history_unload_prompt_1-manual.html"
           }
         ],
         "html/browsers/the-window-object/the-windowproxy-object/test-window-proxy-locationbar-manual.html": [
@@ -2551,6 +2539,126 @@
             "url": "/html/semantics/grouping-content/the-li-element/grouping-li-reftest-002.html"
           }
         ],
+        "html/semantics/grouping-content/the-li-element/grouping-li-reftest-display-list-item.html": [
+          {
+            "path": "html/semantics/grouping-content/the-li-element/grouping-li-reftest-display-list-item.html",
+            "references": [
+              [
+                "/html/semantics/grouping-content/the-li-element/grouping-li-reftest-display-list-item-ref.html",
+                "=="
+              ]
+            ],
+            "url": "/html/semantics/grouping-content/the-li-element/grouping-li-reftest-display-list-item.html"
+          }
+        ],
+        "html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-menu.html": [
+          {
+            "path": "html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-menu.html",
+            "references": [
+              [
+                "/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-menu-ref.html",
+                "=="
+              ]
+            ],
+            "url": "/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-menu.html"
+          }
+        ],
+        "html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-mixed.html": [
+          {
+            "path": "html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-mixed.html",
+            "references": [
+              [
+                "/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-mixed-ref.html",
+                "=="
+              ]
+            ],
+            "url": "/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-mixed.html"
+          }
+        ],
+        "html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-not-dir.html": [
+          {
+            "path": "html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-not-dir.html",
+            "references": [
+              [
+                "/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-not-dir-ref.html",
+                "=="
+              ]
+            ],
+            "url": "/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-not-dir.html"
+          }
+        ],
+        "html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ol.html": [
+          {
+            "path": "html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ol.html",
+            "references": [
+              [
+                "/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ol-ref.html",
+                "=="
+              ]
+            ],
+            "url": "/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ol.html"
+          }
+        ],
+        "html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-parent.html": [
+          {
+            "path": "html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-parent.html",
+            "references": [
+              [
+                "/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-parent-ref.html",
+                "=="
+              ]
+            ],
+            "url": "/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-parent.html"
+          }
+        ],
+        "html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-skip-no-boxes.html": [
+          {
+            "path": "html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-skip-no-boxes.html",
+            "references": [
+              [
+                "/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-skip-no-boxes-ref.html",
+                "=="
+              ]
+            ],
+            "url": "/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-skip-no-boxes.html"
+          }
+        ],
+        "html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ul.html": [
+          {
+            "path": "html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ul.html",
+            "references": [
+              [
+                "/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ul-ref.html",
+                "=="
+              ]
+            ],
+            "url": "/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ul.html"
+          }
+        ],
+        "html/semantics/grouping-content/the-li-element/grouping-li-reftest-no-list-owner.html": [
+          {
+            "path": "html/semantics/grouping-content/the-li-element/grouping-li-reftest-no-list-owner.html",
+            "references": [
+              [
+                "/html/semantics/grouping-content/the-li-element/grouping-li-reftest-no-list-owner-ref.html",
+                "=="
+              ]
+            ],
+            "url": "/html/semantics/grouping-content/the-li-element/grouping-li-reftest-no-list-owner.html"
+          }
+        ],
+        "html/semantics/grouping-content/the-li-element/grouping-li-reftest-not-being-rendered.html": [
+          {
+            "path": "html/semantics/grouping-content/the-li-element/grouping-li-reftest-not-being-rendered.html",
+            "references": [
+              [
+                "/html/semantics/grouping-content/the-li-element/grouping-li-reftest-not-being-rendered-ref.html",
+                "=="
+              ]
+            ],
+            "url": "/html/semantics/grouping-content/the-li-element/grouping-li-reftest-not-being-rendered.html"
+          }
+        ],
         "html/semantics/grouping-content/the-ol-element/grouping-ol-rev-reftest-001.html": [
           {
             "path": "html/semantics/grouping-content/the-ol-element/grouping-ol-rev-reftest-001.html",
@@ -5362,11 +5470,11 @@
         "console/console-is-a-namespace.any.js": [
           {
             "path": "console/console-is-a-namespace.any.js",
-            "url": "/console/console-is-a-namespace.any.worker.html"
+            "url": "/console/console-is-a-namespace.any.html"
           },
           {
             "path": "console/console-is-a-namespace.any.js",
-            "url": "/console/console-is-a-namespace.any.html"
+            "url": "/console/console-is-a-namespace.any.worker.html"
           }
         ],
         "custom-elements/CustomElementRegistry.html": [
@@ -8302,6 +8410,12 @@
             "url": "/html/browsers/history/the-history-interface/history_go_plus.html"
           }
         ],
+        "html/browsers/history/the-history-interface/history_go_to_uri.html": [
+          {
+            "path": "html/browsers/history/the-history-interface/history_go_to_uri.html",
+            "url": "/html/browsers/history/the-history-interface/history_go_to_uri.html"
+          }
+        ],
         "html/browsers/history/the-history-interface/history_go_undefined.html": [
           {
             "path": "html/browsers/history/the-history-interface/history_go_undefined.html",
@@ -13458,10 +13572,10 @@
             "url": "/html/webappapis/scripting/events/invalid-uncompiled-raw-handler-compiled-once.html"
           }
         ],
-        "html/webappapis/scripting/events/messageevent-constructor.html": [
+        "html/webappapis/scripting/events/messageevent-constructor.https.html": [
           {
-            "path": "html/webappapis/scripting/events/messageevent-constructor.html",
-            "url": "/html/webappapis/scripting/events/messageevent-constructor.html"
+            "path": "html/webappapis/scripting/events/messageevent-constructor.https.html",
+            "url": "/html/webappapis/scripting/events/messageevent-constructor.https.html"
           }
         ],
         "html/webappapis/scripting/events/onerroreventhandler.html": [
@@ -13776,6 +13890,42 @@
             "url": "/html/webappapis/timers/evil-spec-example.html"
           }
         ],
+        "imagebitmap-renderingcontext/bitmaprenderer-as-imagesource.html": [
+          {
+            "path": "imagebitmap-renderingcontext/bitmaprenderer-as-imagesource.html",
+            "url": "/imagebitmap-renderingcontext/bitmaprenderer-as-imagesource.html"
+          }
+        ],
+        "imagebitmap-renderingcontext/context-creation-with-alpha.html": [
+          {
+            "path": "imagebitmap-renderingcontext/context-creation-with-alpha.html",
+            "url": "/imagebitmap-renderingcontext/context-creation-with-alpha.html"
+          }
+        ],
+        "imagebitmap-renderingcontext/context-creation.html": [
+          {
+            "path": "imagebitmap-renderingcontext/context-creation.html",
+            "url": "/imagebitmap-renderingcontext/context-creation.html"
+          }
+        ],
+        "imagebitmap-renderingcontext/context-preserves-canvas.html": [
+          {
+            "path": "imagebitmap-renderingcontext/context-preserves-canvas.html",
+            "url": "/imagebitmap-renderingcontext/context-preserves-canvas.html"
+          }
+        ],
+        "imagebitmap-renderingcontext/tranferFromImageBitmap-null.html": [
+          {
+            "path": "imagebitmap-renderingcontext/tranferFromImageBitmap-null.html",
+            "url": "/imagebitmap-renderingcontext/tranferFromImageBitmap-null.html"
+          }
+        ],
+        "imagebitmap-renderingcontext/transferFromImageBitmap-detached.html": [
+          {
+            "path": "imagebitmap-renderingcontext/transferFromImageBitmap-detached.html",
+            "url": "/imagebitmap-renderingcontext/transferFromImageBitmap-detached.html"
+          }
+        ],
         "mediacapture-record/BlobEvent-constructor.html": [
           {
             "path": "mediacapture-record/BlobEvent-constructor.html",
@@ -14474,6 +14624,12 @@
             "url": "/service-workers/service-worker/service-worker-csp-script.https.html"
           }
         ],
+        "service-workers/service-worker/serviceworker-message-event-historical.https.html": [
+          {
+            "path": "service-workers/service-worker/serviceworker-message-event-historical.https.html",
+            "url": "/service-workers/service-worker/serviceworker-message-event-historical.https.html"
+          }
+        ],
         "service-workers/service-worker/serviceworkerobject-scripturl.https.html": [
           {
             "path": "service-workers/service-worker/serviceworkerobject-scripturl.https.html",
@@ -15086,6 +15242,84 @@
             "url": "/shadow-dom/untriaged/user-interaction/ranges-and-selections/test-002.html"
           }
         ],
+        "streams/byte-length-queuing-strategy.https.html": [
+          {
+            "path": "streams/byte-length-queuing-strategy.https.html",
+            "url": "/streams/byte-length-queuing-strategy.https.html"
+          }
+        ],
+        "streams/count-queuing-strategy.https.html": [
+          {
+            "path": "streams/count-queuing-strategy.https.html",
+            "url": "/streams/count-queuing-strategy.https.html"
+          }
+        ],
+        "streams/readable-streams/bad-strategies.https.html": [
+          {
+            "path": "streams/readable-streams/bad-strategies.https.html",
+            "url": "/streams/readable-streams/bad-strategies.https.html"
+          }
+        ],
+        "streams/readable-streams/bad-underlying-sources.https.html": [
+          {
+            "path": "streams/readable-streams/bad-underlying-sources.https.html",
+            "url": "/streams/readable-streams/bad-underlying-sources.https.html"
+          }
+        ],
+        "streams/readable-streams/brand-checks.https.html": [
+          {
+            "path": "streams/readable-streams/brand-checks.https.html",
+            "url": "/streams/readable-streams/brand-checks.https.html"
+          }
+        ],
+        "streams/readable-streams/cancel.https.html": [
+          {
+            "path": "streams/readable-streams/cancel.https.html",
+            "url": "/streams/readable-streams/cancel.https.html"
+          }
+        ],
+        "streams/readable-streams/count-queuing-strategy-integration.https.html": [
+          {
+            "path": "streams/readable-streams/count-queuing-strategy-integration.https.html",
+            "url": "/streams/readable-streams/count-queuing-strategy-integration.https.html"
+          }
+        ],
+        "streams/readable-streams/garbage-collection.https.html": [
+          {
+            "path": "streams/readable-streams/garbage-collection.https.html",
+            "url": "/streams/readable-streams/garbage-collection.https.html"
+          }
+        ],
+        "streams/readable-streams/general.https.html": [
+          {
+            "path": "streams/readable-streams/general.https.html",
+            "url": "/streams/readable-streams/general.https.html"
+          }
+        ],
+        "streams/readable-streams/pipe-through.https.html": [
+          {
+            "path": "streams/readable-streams/pipe-through.https.html",
+            "url": "/streams/readable-streams/pipe-through.https.html"
+          }
+        ],
+        "streams/readable-streams/readable-stream-reader.https.html": [
+          {
+            "path": "streams/readable-streams/readable-stream-reader.https.html",
+            "url": "/streams/readable-streams/readable-stream-reader.https.html"
+          }
+        ],
+        "streams/readable-streams/tee.https.html": [
+          {
+            "path": "streams/readable-streams/tee.https.html",
+            "url": "/streams/readable-streams/tee.https.html"
+          }
+        ],
+        "streams/readable-streams/templated.https.html": [
+          {
+            "path": "streams/readable-streams/templated.https.html",
+            "url": "/streams/readable-streams/templated.https.html"
+          }
+        ],
         "svg/historical.html": [
           {
             "path": "svg/historical.html",
@@ -18869,6 +19103,126 @@
           "url": "/html/semantics/grouping-content/the-li-element/grouping-li-reftest-002.html"
         }
       ],
+      "html/semantics/grouping-content/the-li-element/grouping-li-reftest-display-list-item.html": [
+        {
+          "path": "html/semantics/grouping-content/the-li-element/grouping-li-reftest-display-list-item.html",
+          "references": [
+            [
+              "/html/semantics/grouping-content/the-li-element/grouping-li-reftest-display-list-item-ref.html",
+              "=="
+            ]
+          ],
+          "url": "/html/semantics/grouping-content/the-li-element/grouping-li-reftest-display-list-item.html"
+        }
+      ],
+      "html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-menu.html": [
+        {
+          "path": "html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-menu.html",
+          "references": [
+            [
+              "/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-menu-ref.html",
+              "=="
+            ]
+          ],
+          "url": "/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-menu.html"
+        }
+      ],
+      "html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-mixed.html": [
+        {
+          "path": "html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-mixed.html",
+          "references": [
+            [
+              "/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-mixed-ref.html",
+              "=="
+            ]
+          ],
+          "url": "/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-mixed.html"
+        }
+      ],
+      "html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-not-dir.html": [
+        {
+          "path": "html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-not-dir.html",
+          "references": [
+            [
+              "/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-not-dir-ref.html",
+              "=="
+            ]
+          ],
+          "url": "/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-not-dir.html"
+        }
+      ],
+      "html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ol.html": [
+        {
+          "path": "html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ol.html",
+          "references": [
+            [
+              "/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ol-ref.html",
+              "=="
+            ]
+          ],
+          "url": "/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ol.html"
+        }
+      ],
+      "html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-parent.html": [
+        {
+          "path": "html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-parent.html",
+          "references": [
+            [
+              "/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-parent-ref.html",
+              "=="
+            ]
+          ],
+          "url": "/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-parent.html"
+        }
+      ],
+      "html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-skip-no-boxes.html": [
+        {
+          "path": "html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-skip-no-boxes.html",
+          "references": [
+            [
+              "/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-skip-no-boxes-ref.html",
+              "=="
+            ]
+          ],
+          "url": "/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-skip-no-boxes.html"
+        }
+      ],
+      "html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ul.html": [
+        {
+          "path": "html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ul.html",
+          "references": [
+            [
+              "/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ul-ref.html",
+              "=="
+            ]
+          ],
+          "url": "/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ul.html"
+        }
+      ],
+      "html/semantics/grouping-content/the-li-element/grouping-li-reftest-no-list-owner.html": [
+        {
+          "path": "html/semantics/grouping-content/the-li-element/grouping-li-reftest-no-list-owner.html",
+          "references": [
+            [
+              "/html/semantics/grouping-content/the-li-element/grouping-li-reftest-no-list-owner-ref.html",
+              "=="
+            ]
+          ],
+          "url": "/html/semantics/grouping-content/the-li-element/grouping-li-reftest-no-list-owner.html"
+        }
+      ],
+      "html/semantics/grouping-content/the-li-element/grouping-li-reftest-not-being-rendered.html": [
+        {
+          "path": "html/semantics/grouping-content/the-li-element/grouping-li-reftest-not-being-rendered.html",
+          "references": [
+            [
+              "/html/semantics/grouping-content/the-li-element/grouping-li-reftest-not-being-rendered-ref.html",
+              "=="
+            ]
+          ],
+          "url": "/html/semantics/grouping-content/the-li-element/grouping-li-reftest-not-being-rendered.html"
+        }
+      ],
       "html/semantics/grouping-content/the-ol-element/grouping-ol-rev-reftest-001.html": [
         {
           "path": "html/semantics/grouping-content/the-ol-element/grouping-ol-rev-reftest-001.html",
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/dom/nodes/Document-createEvent-expected.txt b/third_party/WebKit/LayoutTests/imported/wpt/dom/nodes/Document-createEvent-expected.txt
index a8bca32..9e83ac85 100644
--- a/third_party/WebKit/LayoutTests/imported/wpt/dom/nodes/Document-createEvent-expected.txt
+++ b/third_party/WebKit/LayoutTests/imported/wpt/dom/nodes/Document-createEvent-expected.txt
@@ -163,18 +163,6 @@
 FAIL createEvent('svgevents') should be initialized correctly. Cannot read property 'type' of undefined
 FAIL SVGEVENTS should be an alias for Event. Failed to execute 'createEvent' on 'Document': The provided event type ('SVGEVENTS') is invalid.
 FAIL createEvent('SVGEVENTS') should be initialized correctly. Cannot read property 'type' of undefined
-FAIL SVGZoomEvent should be an alias for SVGZoomEvent. Failed to execute 'createEvent' on 'Document': The provided event type ('SVGZoomEvent') is invalid.
-FAIL createEvent('SVGZoomEvent') should be initialized correctly. Cannot read property 'type' of undefined
-FAIL svgzoomevent should be an alias for SVGZoomEvent. Failed to execute 'createEvent' on 'Document': The provided event type ('svgzoomevent') is invalid.
-FAIL createEvent('svgzoomevent') should be initialized correctly. Cannot read property 'type' of undefined
-FAIL SVGZOOMEVENT should be an alias for SVGZoomEvent. Failed to execute 'createEvent' on 'Document': The provided event type ('SVGZOOMEVENT') is invalid.
-FAIL createEvent('SVGZOOMEVENT') should be initialized correctly. Cannot read property 'type' of undefined
-FAIL SVGZoomEvents should be an alias for SVGZoomEvent. Failed to execute 'createEvent' on 'Document': The provided event type ('SVGZoomEvents') is invalid.
-FAIL createEvent('SVGZoomEvents') should be initialized correctly. Cannot read property 'type' of undefined
-FAIL svgzoomevents should be an alias for SVGZoomEvent. Failed to execute 'createEvent' on 'Document': The provided event type ('svgzoomevents') is invalid.
-FAIL createEvent('svgzoomevents') should be initialized correctly. Cannot read property 'type' of undefined
-FAIL SVGZOOMEVENTS should be an alias for SVGZoomEvent. Failed to execute 'createEvent' on 'Document': The provided event type ('SVGZOOMEVENTS') is invalid.
-FAIL createEvent('SVGZOOMEVENTS') should be initialized correctly. Cannot read property 'type' of undefined
 FAIL TextEvent should be an alias for CompositionEvent. assert_equals: expected object "[object CompositionEvent]" but got object "[object TextEvent]"
 PASS createEvent('TextEvent') should be initialized correctly. 
 FAIL textevent should be an alias for CompositionEvent. Failed to execute 'createEvent' on 'Document': The provided event type ('textevent') is invalid.
@@ -325,6 +313,8 @@
 PASS Should throw NOT_SUPPORTED_ERR for non-legacy event interface "ResourceProgressEvent" 
 PASS Should throw NOT_SUPPORTED_ERR for pluralized non-legacy event interface "ResourceProgressEvents" 
 PASS Should throw NOT_SUPPORTED_ERR for non-legacy event interface "SVGEvent" 
+PASS Should throw NOT_SUPPORTED_ERR for non-legacy event interface "SVGZoomEvent" 
+PASS Should throw NOT_SUPPORTED_ERR for pluralized non-legacy event interface "SVGZoomEvents" 
 PASS Should throw NOT_SUPPORTED_ERR for non-legacy event interface "ScrollAreaEvent" 
 PASS Should throw NOT_SUPPORTED_ERR for pluralized non-legacy event interface "ScrollAreaEvents" 
 PASS Should throw NOT_SUPPORTED_ERR for non-legacy event interface "SecurityPolicyViolationEvent" 
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/dom/nodes/Document-createEvent.html b/third_party/WebKit/LayoutTests/imported/wpt/dom/nodes/Document-createEvent.html
index f1d57c4..e440061 100644
--- a/third_party/WebKit/LayoutTests/imported/wpt/dom/nodes/Document-createEvent.html
+++ b/third_party/WebKit/LayoutTests/imported/wpt/dom/nodes/Document-createEvent.html
@@ -117,6 +117,7 @@
   "RelatedEvent",
   "ResourceProgressEvent",
   "SVGEvent",
+  "SVGZoomEvent",
   "ScrollAreaEvent",
   "SecurityPolicyViolationEvent",
   "ServicePortConnectEvent",
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/dom/nodes/Document-createEvent.js b/third_party/WebKit/LayoutTests/imported/wpt/dom/nodes/Document-createEvent.js
index e55487a..56f9bf9 100644
--- a/third_party/WebKit/LayoutTests/imported/wpt/dom/nodes/Document-createEvent.js
+++ b/third_party/WebKit/LayoutTests/imported/wpt/dom/nodes/Document-createEvent.js
@@ -23,8 +23,6 @@
   "ProgressEvent": "ProgressEvent",
   "StorageEvent": "StorageEvent",
   "SVGEvents": "Event",
-  "SVGZoomEvent": "SVGZoomEvent",
-  "SVGZoomEvents": "SVGZoomEvent",
   "TextEvent": "CompositionEvent",
   "TouchEvent": "TouchEvent",
   "TrackEvent": "TrackEvent",
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/browsers/history/the-history-interface/history_go_to_uri-1.html b/third_party/WebKit/LayoutTests/imported/wpt/html/browsers/history/the-history-interface/history_go_to_uri-1.html
new file mode 100644
index 0000000..46c744e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/browsers/history/the-history-interface/history_go_to_uri-1.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<script src="history.js"></script>
+<script>
+  onunload = function() {}
+
+  onload = function() {
+    if (!opener.started) {
+      queue_next();
+    } else {
+      opener.pages.push(id);
+      opener.start_test_wait();
+      if (!opener.gone) {
+        // This is meant to test that passing a string is not supported.
+        // According to the spec, the value passed to 'go' must be an int.
+        // Internet Explorer supports passing a string and will navigate
+        // to that Url. This test will protect against regressing in
+        // this area and reverting back to IE's incorrect behavior.
+          history.go("history_entry.html");
+          opener.gone = true;
+      }
+    }
+  };
+</script>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/browsers/history/the-history-interface/history_go_to_uri.html b/third_party/WebKit/LayoutTests/imported/wpt/html/browsers/history/the-history-interface/history_go_to_uri.html
new file mode 100644
index 0000000..6b5ebf1
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/browsers/history/the-history-interface/history_go_to_uri.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<title>history.go() negative tests</title>
+<link rel="author" title="John Jansen" href="mailto:johnjan@microsoft.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/browsers.html#dom-history-go">
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+  var t = async_test(undefined, {timeout:5000});
+  started = false;
+  gone = false;
+  pages = []
+  timer = null;
+  start_test_wait = t.step_func(
+    function() {
+      clearTimeout(timer);
+      timer = setTimeout(t.step_func(
+        function() {
+          try {
+            assert_array_equals(pages, [3, 2, 2], "Pages opened during history navigation");
+            t.done();
+          } finally {
+            win.close();
+          }
+        }
+      ), 500);
+    }
+  );
+  t.step(function() {win = window.open("history_entry.html?urls=history_go_to_uri-1.html,history_forward-2.html");
+  });
+</script>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/infrastructure/common-dom-interfaces/collections/htmlallcollection-expected.txt b/third_party/WebKit/LayoutTests/imported/wpt/html/infrastructure/common-dom-interfaces/collections/htmlallcollection-expected.txt
new file mode 100644
index 0000000..54b035a1
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/infrastructure/common-dom-interfaces/collections/htmlallcollection-expected.txt
@@ -0,0 +1,34 @@
+This is a testharness.js-based test.
+PASS document.all is an HTMLAllCollection 
+PASS length attribute 
+PASS indexed property getter 
+PASS indexed property getter out of range 
+PASS named property getter 
+PASS named property getter with dot syntax 
+PASS named property getter with invalid name 
+PASS named property getter returning collection 
+PASS named property getter with "array index property name" 
+PASS named property getter with invalid "array index property name" 
+PASS namedItem method 
+PASS namedItem method with invalid name 
+PASS namedItem method returning collection 
+PASS namedItem method with "array index property name" 
+PASS namedItem method with invalid "array index property name" 
+PASS namedItem method with no argument 
+PASS legacy caller 
+FAIL legacy caller with invalid name assert_equals: expected (object) null but got (undefined) undefined
+PASS legacy caller returning collection 
+PASS legacy caller with "array index property name" 
+PASS legacy caller with "array index property name" as number 
+FAIL legacy caller with invalid "array index property name" assert_equals: expected (object) null but got (undefined) undefined
+FAIL legacy caller with no argument assert_equals: expected (object) null but got (undefined) undefined
+PASS item method 
+FAIL item method with invalid name assert_equals: expected (object) null but got (undefined) undefined
+PASS item method returning collection 
+PASS item method with "array index property name" 
+PASS item method with "array index property name" as number 
+FAIL item method with invalid "array index property name" assert_equals: expected (object) null but got (undefined) undefined
+FAIL item method with no argument assert_equals: expected (object) null but got (undefined) undefined
+FAIL collections are new live HTMLCollection instances assert_true: expected true got false
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/infrastructure/common-dom-interfaces/collections/htmlallcollection.html b/third_party/WebKit/LayoutTests/imported/wpt/html/infrastructure/common-dom-interfaces/collections/htmlallcollection.html
index 9ce8ec35..fd25042 100644
--- a/third_party/WebKit/LayoutTests/imported/wpt/html/infrastructure/common-dom-interfaces/collections/htmlallcollection.html
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/infrastructure/common-dom-interfaces/collections/htmlallcollection.html
@@ -3,7 +3,8 @@
 <head>
 <title>HTMLAllCollection Tests</title>
 <link rel="author" title="Dan Druta" href="mailto:dan.druta@att.com"/>
-<link rel="help" href="2.7.2.1 - Common Infrastructure/Common DOM Interfaces/Collections/HTMLAllCollection"/>
+<link rel="author" title="Philip Jägenstedt" href="mailto:philip@foolip.org"/>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/infrastructure.html#the-htmlallcollection-interface"/>
 <meta name="flags" content="TOKENS" />
 <meta name="assert" content="TEST ASSERTION"/>
 <script src="/resources/testharness.js"></script>
@@ -13,34 +14,249 @@
 <img src="../../../../images/green.png" name="picture">
 <a name="foo"></a>
 <a name="foo"></a>
+<span id="42"></span>
+<span id="043"></span>
+<div id="4294967294"></div>
+<div id="4294967295"></div>
+<div id="4294967296"></div>
 <script>
-test(function(){ assert_equals(document.all.length,14)}, "Test for HTMLAllCollection size");
+var anchors = document.querySelectorAll("a");
+var divs = document.querySelectorAll("div");
+var scripts = document.querySelectorAll("script");
+var spans = document.querySelectorAll("span");
 
-test(function(){ assert_equals(document.all.item(0).tagName,"HTML")}, "Test lookup by index using ()");
+test(function() {
+  assert_true(document.all instanceof HTMLAllCollection);
+}, "document.all is an HTMLAllCollection");
 
-test(function(){ assert_equals(document.all[0].tagName,"HTML")}, "Test lookup by index using []");
+test(function() {
+  assert_equals(document.all.length, 20);
+}, "length attribute");
 
-test(function(){ assert_equals(document.all.item("picture").nodeName,"IMG")}, "Test lookup IMG by name");
+// indexed property getter
 
-test(function(){ assert_equals(document.all.namedItem("picture").nodeName,"IMG")}, "Test lookup IMG by namedItem ");
+test(function() {
+  assert_equals(document.all[0], document.documentElement);
+  assert_equals(document.all[19], scripts[2]);
+}, "indexed property getter");
 
-test(function(){ assert_equals(document.all("picture").nodeName,"IMG")}, "Test lookup IMG in collection using ()");
+test(function() {
+  assert_equals(document.all[-1], undefined);
+  assert_equals(document.all[20], undefined);
+  assert_equals(document.all[42], undefined);
+  assert_equals(document.all[43], undefined);
+  assert_equals(document.all[4294967294], undefined);
+  assert_equals(document.all[4294967295], divs[1]);
+  assert_equals(document.all[4294967296], divs[2]);
+}, "indexed property getter out of range");
 
-test(function(){ assert_equals(document.all["picture"].nodeName,"IMG")}, "Test lookup IMG in collection using []");
-
-test(function(){ assert_equals(document.all.picture.nodeName,"IMG")}, "Test lookup IMG in collection using .");
-
-test(function(){ assert_equals(document.all.tags.id,"tags")}, "Test lookup tags in collection using .");
+// named property getter
 
 test(function() {
   assert_equals(document.all["root"], document.documentElement);
-}, "Should find root element too");
+  assert_equals(document.all["flags"].content, "TOKENS");
+  assert_equals(document.all["picture"].tagName, "IMG");
+}, "named property getter");
 
 test(function() {
-  assert_equals(document.all["foo"].length, 2);
-}, "Should find both anchors and produce a list");
+  assert_equals(document.all.root, document.documentElement);
+  assert_equals(document.all.flags.content, "TOKENS");
+  assert_equals(document.all.picture.tagName, "IMG");
+}, "named property getter with dot syntax");
 
-test
+test(function() {
+  assert_equals(document.all["noname"], undefined);
+  assert_equals(document.all.noname, undefined);
+}, "named property getter with invalid name");
+
+test(function() {
+  var collection = document.all["foo"];
+  assert_equals(collection.length, 2);
+  assert_equals(collection[0], anchors[0]);
+  assert_equals(collection[1], anchors[1]);
+}, "named property getter returning collection");
+
+test(function() {
+  assert_equals(document.all["0"], document.documentElement);
+  assert_equals(document.all["19"], document.scripts[2]);
+  assert_equals(document.all["20"], undefined);
+  assert_equals(document.all["42"], undefined);
+  assert_equals(document.all["43"], undefined);
+}, "named property getter with \"array index property name\"");
+
+test(function() {
+  assert_equals(document.all["00"], undefined);
+  assert_equals(document.all["042"], undefined);
+  assert_equals(document.all["043"], spans[1]);
+  assert_equals(document.all["4294967294"], undefined);
+  assert_equals(document.all["4294967295"], divs[1]);
+  assert_equals(document.all["4294967296"], divs[2]);
+}, "named property getter with invalid \"array index property name\"");
+
+// namedItem method
+
+test(function() {
+  assert_equals(document.all.namedItem("root"), document.documentElement);
+  assert_equals(document.all.namedItem("flags").content, "TOKENS");
+  assert_equals(document.all.namedItem("picture").tagName, "IMG");
+}, "namedItem method");
+
+test(function() {
+  assert_equals(document.all.namedItem("noname"), null);
+}, "namedItem method with invalid name");
+
+test(function() {
+  var collection = document.all.namedItem("foo");
+  assert_equals(collection.length, 2);
+  assert_equals(collection[0], anchors[0]);
+  assert_equals(collection[1], anchors[1]);
+}, "namedItem method returning collection");
+
+test(function() {
+  assert_equals(document.all.namedItem("0"), null);
+  assert_equals(document.all.namedItem("19"), null);
+  assert_equals(document.all.namedItem("20"), null);
+  assert_equals(document.all.namedItem("42"), spans[0]);
+  assert_equals(document.all.namedItem("43"), null);
+}, "namedItem method with \"array index property name\"");
+
+test(function() {
+  assert_equals(document.all.namedItem("00"), null);
+  assert_equals(document.all.namedItem("042"), null);
+  assert_equals(document.all.namedItem("043"), spans[1]);
+  assert_equals(document.all.namedItem("4294967294"), divs[0]);
+  assert_equals(document.all.namedItem("4294967295"), divs[1]);
+  assert_equals(document.all.namedItem("4294967296"), divs[2]);
+}, "namedItem method with invalid \"array index property name\"");
+
+test(function() {
+  assert_equals(document.all.namedItem.length, 1);
+  assert_throws(new TypeError, function() {
+    document.all.namedItem();
+  });
+}, "namedItem method with no argument");
+
+// legacy caller
+
+test(function() {
+  assert_equals(document.all("root"), document.documentElement);
+  assert_equals(document.all("flags").content, "TOKENS");
+  assert_equals(document.all("picture").tagName, "IMG");
+}, "legacy caller");
+
+test(function() {
+  assert_equals(document.all("noname"), null);
+}, "legacy caller with invalid name");
+
+test(function() {
+  var collection = document.all("foo");
+  assert_equals(collection.length, 2);
+  assert_equals(collection[0], anchors[0]);
+  assert_equals(collection[1], anchors[1]);
+}, "legacy caller returning collection");
+
+test(function() {
+  assert_equals(document.all("0"), document.documentElement);
+  assert_equals(document.all("19"), document.scripts[2]);
+  assert_equals(document.all("20"), null);
+  assert_equals(document.all("42"), null);
+  assert_equals(document.all("43"), null);
+}, "legacy caller with \"array index property name\"");
+
+test(function() {
+  assert_equals(document.all(0), document.documentElement);
+  assert_equals(document.all(19), document.scripts[2]);
+  assert_equals(document.all(20), null);
+  assert_equals(document.all(42), null);
+  assert_equals(document.all(43), null);
+}, "legacy caller with \"array index property name\" as number");
+
+test(function() {
+  assert_equals(document.all("00"), null);
+  assert_equals(document.all("042"), null);
+  assert_equals(document.all("043"), spans[1]);
+  assert_equals(document.all("4294967294"), null);
+  assert_equals(document.all("4294967295"), divs[1]);
+  assert_equals(document.all("4294967296"), divs[2]);
+}, "legacy caller with invalid \"array index property name\"");
+
+test(function() {
+  assert_equals(document.all(), null);
+}, "legacy caller with no argument");
+
+// item method
+
+test(function() {
+  assert_equals(document.all.item("root"), document.documentElement);
+  assert_equals(document.all.item("flags").content, "TOKENS");
+  assert_equals(document.all.item("picture").tagName, "IMG");
+}, "item method");
+
+test(function() {
+  assert_equals(document.all.item("noname"), null);
+}, "item method with invalid name");
+
+test(function() {
+  var collection = document.all.item("foo");
+  assert_equals(collection.length, 2);
+  assert_equals(collection[0], anchors[0]);
+  assert_equals(collection[1], anchors[1]);
+}, "item method returning collection");
+
+test(function() {
+  assert_equals(document.all.item("0"), document.documentElement);
+  assert_equals(document.all.item("19"), document.scripts[2]);
+  assert_equals(document.all.item("20"), null);
+  assert_equals(document.all.item("42"), null);
+  assert_equals(document.all.item("43"), null);
+}, "item method with \"array index property name\"");
+
+test(function() {
+  assert_equals(document.all.item(0), document.documentElement);
+  assert_equals(document.all.item(19), document.scripts[2]);
+  assert_equals(document.all.item(20), null);
+  assert_equals(document.all.item(42), null);
+  assert_equals(document.all.item(43), null);
+}, "item method with \"array index property name\" as number");
+
+test(function() {
+  assert_equals(document.all.item("00"), null);
+  assert_equals(document.all.item("042"), null);
+  assert_equals(document.all.item("043"), spans[1]);
+  assert_equals(document.all.item("4294967294"), null);
+  assert_equals(document.all.item("4294967295"), divs[1]);
+  assert_equals(document.all.item("4294967296"), divs[2]);
+}, "item method with invalid \"array index property name\"");
+
+test(function() {
+  assert_equals(document.all.item.length, 0);
+  assert_equals(document.all.item(), null);
+}, "item method with no argument");
+
+// live HTMLCollection
+
+test(function() {
+  var collections = [
+    document.all["foo"],
+    document.all.namedItem("foo"),
+    document.all("foo"),
+    document.all.item("foo"),
+  ];
+  // a new HTMLCollection is created for each call
+  for (var i = 0; i < collections.length; i++) {
+    assert_true(collections[i] instanceof HTMLCollection);
+    for (var j = i + 1; j < collections.length; j++) {
+      assert_not_equals(collections[i], collections[j]);
+    }
+  }
+  for (var c of collections) {
+    assert_equals(c.length, 2);
+  }
+  anchors[0].name = "bar";
+  for (var c of collections) {
+    assert_equals(c.length, 1);
+  }
+}, "collections are new live HTMLCollection instances");
 </script>
 <div id="log"></div>
 </body>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/forms/the-input-element/radio.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/forms/the-input-element/radio.html
index e76f611..6681b350 100644
--- a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/forms/the-input-element/radio.html
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/forms/the-input-element/radio.html
@@ -23,6 +23,7 @@
 <input type=radio name=group4 id=radio10>
 <input type=radio name=group4 id=radio11 checked>
 
+
 <script>
   var radio1 = document.getElementById('radio1'),
       radio2 = document.getElementById('radio2'),
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-refetest-no-list-owner-ref.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-refetest-no-list-owner-ref.html
new file mode 100644
index 0000000..e3ed2a51
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-refetest-no-list-owner-ref.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>If no ancestors generate CSS boxes, the list item has no owner, and thus gets numbered as 0</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<style>
+  html, body {
+    display: contents;
+  }
+
+  li {
+    list-style-type: decimal;
+    margin-left: 50px;
+  }
+</style>
+
+<p>This test matches if the list displays similar to the following</p>
+
+<pre>0. A
+0. B
+0. C</pre>
+
+<hr>
+
+<li value="0">A</li>
+<li value="0">B</li>
+<li value="0">C</li>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-001-expected.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-001-expected.html
index 5ff5296e..23b6cda 100644
--- a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-001-expected.html
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-001-expected.html
@@ -11,9 +11,7 @@
     <h1>Description</h1>
     <p>This test continues to validate the li element.</p>
 
-    <p>The spec states:</p>
-    <blockquote>"If the parent element is an ol element, then the li element has an ordinal value. The value attribute is processed relative to the element's parent ol element (q.v.), if there is one. If there is not, the attribute has no effect."</blockquote>
-    <p>This reftest verifies that the value attribute has no effect when applied to a list item NOT having an ol parent.</p>
+    <p>This reftest verifies that the value attribute has no effect when applied to a list item NOT having an ol parent and not marked as display: list-item.</p>
     <p>A reftest is necessary because the values of li elements as calculated by the user agent are NOT available programatically. Only explicitly-set values are then available programatically.</p>
     <p>This reftest passes if you see NO sequencing information on any of the items below.</p>
 
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-001-ref.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-001-ref.html
index 5ff5296e..23b6cda 100644
--- a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-001-ref.html
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-001-ref.html
@@ -11,9 +11,7 @@
     <h1>Description</h1>
     <p>This test continues to validate the li element.</p>
 
-    <p>The spec states:</p>
-    <blockquote>"If the parent element is an ol element, then the li element has an ordinal value. The value attribute is processed relative to the element's parent ol element (q.v.), if there is one. If there is not, the attribute has no effect."</blockquote>
-    <p>This reftest verifies that the value attribute has no effect when applied to a list item NOT having an ol parent.</p>
+    <p>This reftest verifies that the value attribute has no effect when applied to a list item NOT having an ol parent and not marked as display: list-item.</p>
     <p>A reftest is necessary because the values of li elements as calculated by the user agent are NOT available programatically. Only explicitly-set values are then available programatically.</p>
     <p>This reftest passes if you see NO sequencing information on any of the items below.</p>
 
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-001.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-001.html
index 71b72c2c..86200eda 100644
--- a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-001.html
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-001.html
@@ -12,9 +12,7 @@
     <h1>Description</h1>
     <p>This test continues to validate the li element.</p>
 
-    <p>The spec states:</p>
-    <blockquote>"If the parent element is an ol element, then the li element has an ordinal value. The value attribute is processed relative to the element's parent ol element (q.v.), if there is one. If there is not, the attribute has no effect."</blockquote>
-    <p>This reftest verifies that the value attribute has no effect when applied to a list item NOT having an ol parent.</p>
+    <p>This reftest verifies that the value attribute has no effect when applied to a list item NOT having an ol parent and not marked as display: list-item.</p>
     <p>A reftest is necessary because the values of li elements as calculated by the user agent are NOT available programatically.  Only explicitly-set values are then available programatically.</p>
     <p>This reftest passes if you see NO sequencing information on any of the items below.</p>
 
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-002-expected.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-002-expected.html
index bdb728f..1e453e1a 100644
--- a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-002-expected.html
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-002-expected.html
@@ -15,8 +15,6 @@
     <h1>Description</h1>
     <p>This test continues to validate the li element.</p>
 
-    <p>The spec states:</p>
-    <blockquote>"If the parent element is an ol element, then the li element has an ordinal value. The value attribute is processed relative to the element's parent ol element (q.v.), if there is one. If there is not, the attribute has no effect."</blockquote>
     <p>This reftest verifies that the value attribute has an effect when applied to a list item with an ol parent.</p>
     <p>A reftest is necessary because the values of li elements as calculated by the user agent are NOT available programatically.  Only explicitly-set values are then available programatically.</p>
     <p>This reftest passes if you see the numbers 1. 2. 3. below the words "Ordered List"</p>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-002-ref.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-002-ref.html
index bdb728f..1e453e1a 100644
--- a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-002-ref.html
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-002-ref.html
@@ -15,8 +15,6 @@
     <h1>Description</h1>
     <p>This test continues to validate the li element.</p>
 
-    <p>The spec states:</p>
-    <blockquote>"If the parent element is an ol element, then the li element has an ordinal value. The value attribute is processed relative to the element's parent ol element (q.v.), if there is one. If there is not, the attribute has no effect."</blockquote>
     <p>This reftest verifies that the value attribute has an effect when applied to a list item with an ol parent.</p>
     <p>A reftest is necessary because the values of li elements as calculated by the user agent are NOT available programatically.  Only explicitly-set values are then available programatically.</p>
     <p>This reftest passes if you see the numbers 1. 2. 3. below the words "Ordered List"</p>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-002.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-002.html
index ae4eebe..3c0a464 100644
--- a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-002.html
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-002.html
@@ -18,8 +18,6 @@
     <h1>Description</h1>
     <p>This test continues to validate the li element.</p>
 
-    <p>The spec states:</p>
-    <blockquote>"If the parent element is an ol element, then the li element has an ordinal value. The value attribute is processed relative to the element's parent ol element (q.v.), if there is one. If there is not, the attribute has no effect."</blockquote>
     <p>This reftest verifies that the value attribute has an effect when applied to a list item with an ol parent.</p>
     <p>A reftest is necessary because the values of li elements as calculated by the user agent are NOT available programatically.  Only explicitly-set values are then available programatically.</p>
     <p>This reftest passes if you see the numbers 1. 2. 3. below the words "Ordered List"</p>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-display-list-item-expected.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-display-list-item-expected.html
new file mode 100644
index 0000000..fb142f84
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-display-list-item-expected.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>display: list-item on non-&lt;li> elements</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<style>
+  .list-item {
+    display: list-item;
+    list-style-type: decimal;
+  }
+
+  .list-item[hidden] {
+    display: none;
+  }
+</style>
+
+<p>This test matches if both lists display similar to the following:</p>
+
+<pre>1. A
+2. B
+   D
+3. E</pre>
+
+<hr>
+
+<ol>
+  <li value="1">A</li>
+  <li value="2">B</li>
+  <span>D</span>
+  <li value="3">E</li>
+</ol>
+
+<ol>
+  <li value="1">A</li>
+  <li value="2">B</li>
+  <span>D</span>
+  <li value="3">E</li>
+</ol>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-display-list-item-ref.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-display-list-item-ref.html
new file mode 100644
index 0000000..fb142f84
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-display-list-item-ref.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>display: list-item on non-&lt;li> elements</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<style>
+  .list-item {
+    display: list-item;
+    list-style-type: decimal;
+  }
+
+  .list-item[hidden] {
+    display: none;
+  }
+</style>
+
+<p>This test matches if both lists display similar to the following:</p>
+
+<pre>1. A
+2. B
+   D
+3. E</pre>
+
+<hr>
+
+<ol>
+  <li value="1">A</li>
+  <li value="2">B</li>
+  <span>D</span>
+  <li value="3">E</li>
+</ol>
+
+<ol>
+  <li value="1">A</li>
+  <li value="2">B</li>
+  <span>D</span>
+  <li value="3">E</li>
+</ol>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-display-list-item.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-display-list-item.html
new file mode 100644
index 0000000..ce63cf7c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-display-list-item.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>display: list-item on non-&lt;li> elements</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<link rel="match" href="grouping-li-reftest-display-list-item-ref.html">
+
+<style>
+  .list-item {
+    display: list-item;
+    list-style-type: decimal;
+  }
+
+  .list-item[hidden] {
+    display: none;
+  }
+</style>
+
+<p>This test matches if both lists display similar to the following:</p>
+
+<pre>1. A
+2. B
+   D
+3. E</pre>
+
+<hr>
+
+<ul>
+  <span class="list-item">A</span>
+  <span class="list-item">B</span>
+  <span class="list-item" hidden>C</span>
+  <span>D</span>
+  <span class="list-item">E</span>
+</ul>
+
+<ol>
+  <div class="list-item">A</div>
+  <div class="list-item">B</div>
+  <div class="list-item" hidden>C</div>
+  <div>D</div>
+  <div class="list-item">E</div>
+</ol>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-menu-expected.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-menu-expected.html
new file mode 100644
index 0000000..2b1ea76
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-menu-expected.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>list owner is calculated to be narest ancestor menu if it exists</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<p>This test matches if the list displays similar to the following</p>
+
+<pre>1. A
+2. B
+3. C
+4. D
+5. E
+     1. F
+     2. G
+6. H
+     1. I
+     2. J
+          1. K
+          2. L</pre>
+
+<hr>
+
+<ol>
+  <li value="1">A</li>
+  <li value="2">B</li>
+  <li value="3">C</li>
+  <li value="4">D</li>
+  <li value="5">E</li>
+  <ol>
+    <li value="1">F</li>
+    <li value="2">G</li>
+  </ol>
+  <li value="6">H</li>
+  <ol>
+    <li value="1">I</li>
+    <li value="2">
+      J
+      <ol>
+        <li value="1">K</li>
+        <li value="2">L</li>
+      </ol>
+    </li>
+  </ol>
+</ol>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-menu-ref.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-menu-ref.html
new file mode 100644
index 0000000..2b1ea76
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-menu-ref.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>list owner is calculated to be narest ancestor menu if it exists</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<p>This test matches if the list displays similar to the following</p>
+
+<pre>1. A
+2. B
+3. C
+4. D
+5. E
+     1. F
+     2. G
+6. H
+     1. I
+     2. J
+          1. K
+          2. L</pre>
+
+<hr>
+
+<ol>
+  <li value="1">A</li>
+  <li value="2">B</li>
+  <li value="3">C</li>
+  <li value="4">D</li>
+  <li value="5">E</li>
+  <ol>
+    <li value="1">F</li>
+    <li value="2">G</li>
+  </ol>
+  <li value="6">H</li>
+  <ol>
+    <li value="1">I</li>
+    <li value="2">
+      J
+      <ol>
+        <li value="1">K</li>
+        <li value="2">L</li>
+      </ol>
+    </li>
+  </ol>
+</ol>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-menu.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-menu.html
new file mode 100644
index 0000000..86afa5d2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-menu.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>list owner is calculated to be narest ancestor menu if it exists</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<link rel="match" href="grouping-li-reftest-list-owner-menu-ref.html">
+
+<style>
+  li {
+    list-style-type: decimal;
+  }
+</style>
+
+<p>This test matches if the list displays similar to the following</p>
+
+<pre>1. A
+2. B
+3. C
+4. D
+5. E
+     1. F
+     2. G
+6. H
+     1. I
+     2. J
+          1. K
+          2. L</pre>
+
+<hr>
+
+<menu>
+  <li>A</li>
+  <li>B</li>
+  <div>
+    <li>C</li>
+    <span>
+      <li>D</li>
+      <li>E</li>
+    </span>
+    <menu>
+      <li>F</li>
+      <li>G</li>
+    </menu>
+  </div>
+  <li>H</li>
+  <menu>
+    <li>I</li>
+    <li>
+      J
+      <menu>
+        <li>K</li>
+        <li>L</li>
+      </menu>
+    </li>
+  </menu>
+</menu>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-mixed-expected.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-mixed-expected.html
new file mode 100644
index 0000000..b72768a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-mixed-expected.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>list owner is calculated to be nearest ancestor ul or ul (but not dir) if it exists</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<style>
+  li {
+    list-style-type: decimal;
+  }
+</style>
+
+<p>This test matches if the list displays similar to the following</p>
+
+<pre>1. A
+2. B
+3. C
+4. D
+5. E
+     1. F
+     2. G
+6. H
+     1. I
+     2. J
+          3. K
+          4. L</pre>
+
+<hr>
+
+<ol>
+  <li value="1">A</li>
+  <li value="2">B</li>
+  <li value="3">C</li>
+  <li value="4">D</li>
+  <li value="5">E</li>
+  <ol>
+    <li value="1">F</li>
+    <li value="2">G</li>
+  </ol>
+  <li value="6">H</li>
+  <ol>
+    <li value="1">I</li>
+    <li value="2">
+      J
+      <dir>
+        <li value="3">K</li>
+        <li value="4">L</li>
+      </dir>
+    </li>
+  </ol>
+</ol>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-mixed-ref.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-mixed-ref.html
new file mode 100644
index 0000000..b72768a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-mixed-ref.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>list owner is calculated to be nearest ancestor ul or ul (but not dir) if it exists</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<style>
+  li {
+    list-style-type: decimal;
+  }
+</style>
+
+<p>This test matches if the list displays similar to the following</p>
+
+<pre>1. A
+2. B
+3. C
+4. D
+5. E
+     1. F
+     2. G
+6. H
+     1. I
+     2. J
+          3. K
+          4. L</pre>
+
+<hr>
+
+<ol>
+  <li value="1">A</li>
+  <li value="2">B</li>
+  <li value="3">C</li>
+  <li value="4">D</li>
+  <li value="5">E</li>
+  <ol>
+    <li value="1">F</li>
+    <li value="2">G</li>
+  </ol>
+  <li value="6">H</li>
+  <ol>
+    <li value="1">I</li>
+    <li value="2">
+      J
+      <dir>
+        <li value="3">K</li>
+        <li value="4">L</li>
+      </dir>
+    </li>
+  </ol>
+</ol>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-mixed.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-mixed.html
new file mode 100644
index 0000000..82e39f9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-mixed.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>list owner is calculated to be nearest ancestor ul or ul (but not dir) if it exists</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<link rel="match" href="grouping-li-reftest-list-owner-mixed-ref.html">
+
+<style>
+  li {
+    list-style-type: decimal;
+  }
+
+  .list-item {
+    display: list-item;
+    list-style-type: decimal;
+  }
+
+</style>
+
+<p>This test matches if the list displays similar to the following</p>
+
+<pre>1. A
+2. B
+3. C
+4. D
+5. E
+     1. F
+     2. G
+6. H
+     1. I
+     2. J
+          3. K
+          4. L</pre>
+
+<hr>
+
+<ul>
+  <li>A</li>
+  <li>B</li>
+  <div>
+    <li>C</li>
+    <span>
+      <li>D</li>
+      <li>E</li>
+    </span>
+    <ol>
+      <li>F</li>
+      <span class="list-item">G</span>
+    </ol>
+  </div>
+  <li>H</li>
+  <ol>
+    <li>I</li>
+    <li>
+      J
+      <dir>
+        <li>K</li>
+        <li>L</li>
+      </dir>
+    </li>
+  </ol>
+</ul>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-not-dir-expected.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-not-dir-expected.html
new file mode 100644
index 0000000..fad00fa3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-not-dir-expected.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>The dir element is not treated specially when calculating list owners</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<style>
+  li {
+    list-style-type: decimal;
+  }
+</style>
+
+<p>This test matches if the list displays similar to the following</p>
+
+<pre>1. A
+2. B
+3. C
+4. D
+5. E
+     6. F
+     7. G
+8. H
+     9. I
+     10. J
+          11. K
+          12. L</pre>
+
+<hr>
+
+<ol>
+  <li value="1">A</li>
+  <li value="2">B</li>
+  <li value="3">C</li>
+  <li value="4">D</li>
+  <li value="5">E</li>
+  <dir>
+    <li value="6">F</li>
+    <li value="7">G</li>
+  </dir>
+  <li value="8">H</li>
+  <dir>
+    <li value="9">I</li>
+    <li value="10">
+      J
+      <dir>
+        <li value="11">K</li>
+        <li value="12">L</li>
+      </dir>
+    </li>
+  </dir>
+</ol>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-not-dir-ref.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-not-dir-ref.html
new file mode 100644
index 0000000..fad00fa3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-not-dir-ref.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>The dir element is not treated specially when calculating list owners</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<style>
+  li {
+    list-style-type: decimal;
+  }
+</style>
+
+<p>This test matches if the list displays similar to the following</p>
+
+<pre>1. A
+2. B
+3. C
+4. D
+5. E
+     6. F
+     7. G
+8. H
+     9. I
+     10. J
+          11. K
+          12. L</pre>
+
+<hr>
+
+<ol>
+  <li value="1">A</li>
+  <li value="2">B</li>
+  <li value="3">C</li>
+  <li value="4">D</li>
+  <li value="5">E</li>
+  <dir>
+    <li value="6">F</li>
+    <li value="7">G</li>
+  </dir>
+  <li value="8">H</li>
+  <dir>
+    <li value="9">I</li>
+    <li value="10">
+      J
+      <dir>
+        <li value="11">K</li>
+        <li value="12">L</li>
+      </dir>
+    </li>
+  </dir>
+</ol>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-not-dir.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-not-dir.html
new file mode 100644
index 0000000..747d907
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-not-dir.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>The dir element is not treated specially when calculating list owners</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<link rel="match" href="grouping-li-reftest-list-owner-not-dir-ref.html">
+
+<style>
+  li {
+    list-style-type: decimal;
+  }
+</style>
+
+<p>This test matches if the list displays similar to the following</p>
+
+<pre>1. A
+2. B
+3. C
+4. D
+5. E
+     6. F
+     7. G
+8. H
+     9. I
+     10. J
+          11. K
+          12. L</pre>
+
+<hr>
+
+<ol>
+  <li>A</li>
+  <li>B</li>
+  <div>
+    <li>C</li>
+    <span>
+      <li>D</li>
+      <li>E</li>
+    </span>
+    <dir>
+      <li>F</li>
+      <li>G</li>
+    </dir>
+  </div>
+  <li>H</li>
+  <dir>
+    <li>I</li>
+    <li>
+      J
+      <dir>
+        <li>K</li>
+        <li>L</li>
+      </dir>
+    </li>
+  </dir>
+</ol>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ol-expected.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ol-expected.html
new file mode 100644
index 0000000..96cc9c3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ol-expected.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>list owner is calculated to be narest ancestor ol if it exists</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<p>This test matches if the list displays similar to the following</p>
+
+<pre>1. A
+2. B
+3. C
+4. D
+5. E
+     1. F
+     2. G
+6. H
+     1. I
+     2. J
+          1. K
+          2. L</pre>
+
+<hr>
+
+<ol>
+  <li value="1">A</li>
+  <li value="2">B</li>
+  <li value="3">C</li>
+  <li value="4">D</li>
+  <li value="5">E</li>
+  <ol>
+    <li value="1">F</li>
+    <li value="2">G</li>
+  </ol>
+  <li value="6">H</li>
+  <ol>
+    <li value="1">I</li>
+    <li value="2">
+      J
+      <ol>
+        <li value="1">K</li>
+        <li value="2">L</li>
+      </ol>
+    </li>
+  </ol>
+</ol>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ol-ref.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ol-ref.html
new file mode 100644
index 0000000..96cc9c3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ol-ref.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>list owner is calculated to be narest ancestor ol if it exists</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<p>This test matches if the list displays similar to the following</p>
+
+<pre>1. A
+2. B
+3. C
+4. D
+5. E
+     1. F
+     2. G
+6. H
+     1. I
+     2. J
+          1. K
+          2. L</pre>
+
+<hr>
+
+<ol>
+  <li value="1">A</li>
+  <li value="2">B</li>
+  <li value="3">C</li>
+  <li value="4">D</li>
+  <li value="5">E</li>
+  <ol>
+    <li value="1">F</li>
+    <li value="2">G</li>
+  </ol>
+  <li value="6">H</li>
+  <ol>
+    <li value="1">I</li>
+    <li value="2">
+      J
+      <ol>
+        <li value="1">K</li>
+        <li value="2">L</li>
+      </ol>
+    </li>
+  </ol>
+</ol>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ol.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ol.html
new file mode 100644
index 0000000..fcb93cf
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ol.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>list owner is calculated to be narest ancestor ol if it exists</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<link rel="match" href="grouping-li-reftest-list-owner-ol-ref.html">
+
+<p>This test matches if the list displays similar to the following</p>
+
+<pre>1. A
+2. B
+3. C
+4. D
+5. E
+     1. F
+     2. G
+6. H
+     1. I
+     2. J
+          1. K
+          2. L</pre>
+
+<hr>
+
+<ol>
+  <li>A</li>
+  <li>B</li>
+  <div>
+    <li>C</li>
+    <span>
+      <li>D</li>
+      <li>E</li>
+    </span>
+    <ol>
+      <li>F</li>
+      <li>G</li>
+    </ol>
+  </div>
+  <li>H</li>
+  <ol>
+    <li>I</li>
+    <li>
+      J
+      <ol>
+        <li>K</li>
+        <li>L</li>
+      </ol>
+    </li>
+  </ol>
+</ol>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-parent-expected.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-parent-expected.html
new file mode 100644
index 0000000..03a0570
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-parent-expected.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>list owner is calculated to be the parent if there is no ancestor ul/ol/menu</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<style>
+  li {
+    list-style-type: decimal;
+    list-style-position: inside;
+  }
+
+  ol {
+    padding: 50px;
+    margin: 0;
+  }
+</style>
+
+<p>This test matches if the list displays similar to the following</p>
+
+<pre>1. A
+2. B
+1. C
+1. D
+       1. E
+3. F</pre>
+
+<hr>
+
+<ol>
+  <li value="1">A</li>
+  <li value="2">B</li>
+  <li value="1">C</li>
+  <li value="1">D</li>
+  <blockquote>
+    <li value="1">E</li>
+  </blockquote>
+  <li value="3">F</li>
+</ol>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-parent-ref.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-parent-ref.html
new file mode 100644
index 0000000..03a0570
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-parent-ref.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>list owner is calculated to be the parent if there is no ancestor ul/ol/menu</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<style>
+  li {
+    list-style-type: decimal;
+    list-style-position: inside;
+  }
+
+  ol {
+    padding: 50px;
+    margin: 0;
+  }
+</style>
+
+<p>This test matches if the list displays similar to the following</p>
+
+<pre>1. A
+2. B
+1. C
+1. D
+       1. E
+3. F</pre>
+
+<hr>
+
+<ol>
+  <li value="1">A</li>
+  <li value="2">B</li>
+  <li value="1">C</li>
+  <li value="1">D</li>
+  <blockquote>
+    <li value="1">E</li>
+  </blockquote>
+  <li value="3">F</li>
+</ol>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-parent.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-parent.html
new file mode 100644
index 0000000..0345add
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-parent.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>list owner is calculated to be the parent if there is no ancestor ul/ol/menu</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<link rel="match" href="grouping-li-reftest-list-owner-parent-ref.html">
+
+<style>
+  li {
+    list-style-type: decimal;
+    list-style-position: inside;
+  }
+
+  .container {
+    padding: 50px;
+  }
+</style>
+
+<p>This test matches if the list displays similar to the following</p>
+
+<pre>1. A
+2. B
+1. C
+1. D
+       1. E
+3. F</pre>
+
+<hr>
+
+<div class="container">
+  <li>A</li>
+  <li>B</li>
+  <div>
+    <li>C</li>
+    <span>
+      <li>D</li>
+    </span>
+  </div>
+  <blockquote>
+    <li>E</li>
+  </blockquote>
+  <li>F</li>
+</div>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-skip-no-boxes-expected.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-skip-no-boxes-expected.html
new file mode 100644
index 0000000..e758f52
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-skip-no-boxes-expected.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>list owner calculation skips elements that do not generate layout boxes</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<p>This test matches if the list displays similar to the following</p>
+
+<pre>1. A
+2. B
+3. C
+4. D
+5. E
+6. F
+7. G
+8. H
+9. I
+10. J
+     1. K
+     2. L</pre>
+
+<hr>
+
+<ol>
+  <li value="1">A</li>
+  <li value="2">B</li>
+  <li value="3">C</li>
+  <li value="4">D</li>
+  <li value="5">E</li>
+  <li value="6">F</li>
+  <li value="7">G</li>
+  <li value="8">H</li>
+  <li value="9">I</li>
+  <li value="10">
+    J
+    <ol>
+      <li value="1">K</li>
+      <li value="2">L</li>
+    </ol>
+  </li>
+</ol>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-skip-no-boxes-ref.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-skip-no-boxes-ref.html
new file mode 100644
index 0000000..e758f52
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-skip-no-boxes-ref.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>list owner calculation skips elements that do not generate layout boxes</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<p>This test matches if the list displays similar to the following</p>
+
+<pre>1. A
+2. B
+3. C
+4. D
+5. E
+6. F
+7. G
+8. H
+9. I
+10. J
+     1. K
+     2. L</pre>
+
+<hr>
+
+<ol>
+  <li value="1">A</li>
+  <li value="2">B</li>
+  <li value="3">C</li>
+  <li value="4">D</li>
+  <li value="5">E</li>
+  <li value="6">F</li>
+  <li value="7">G</li>
+  <li value="8">H</li>
+  <li value="9">I</li>
+  <li value="10">
+    J
+    <ol>
+      <li value="1">K</li>
+      <li value="2">L</li>
+    </ol>
+  </li>
+</ol>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-skip-no-boxes.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-skip-no-boxes.html
new file mode 100644
index 0000000..defdcb7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-skip-no-boxes.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>list owner calculation skips elements that do not generate layout boxes</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<link rel="match" href="grouping-li-reftest-list-owner-skip-no-boxes-ref.html">
+
+<p>This test matches if the list displays similar to the following</p>
+
+<pre>1. A
+2. B
+3. C
+4. D
+5. E
+6. F
+7. G
+8. H
+9. I
+10. J
+     1. K
+     2. L</pre>
+
+<hr>
+
+<ol>
+  <li>A</li>
+  <li>B</li>
+  <div>
+    <li>C</li>
+    <span>
+      <li>D</li>
+      <li>E</li>
+    </span>
+    <ol style="display: contents;">
+      <li>F</li>
+      <li>G</li>
+    </ol>
+  </div>
+  <li>H</li>
+  <ol style="display: contents;">
+    <li>I</li>
+    <li>
+      J
+      <ol>
+        <li>K</li>
+        <li>L</li>
+      </ol>
+    </li>
+  </ol>
+</ol>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ul-expected.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ul-expected.html
new file mode 100644
index 0000000..22ee9f4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ul-expected.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>list owner is calculated to be nearest ancestor ul if it exists</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<p>This test matches if the list displays similar to the following</p>
+
+<pre>1. A
+2. B
+3. C
+4. D
+5. E
+     1. F
+     2. G
+6. H
+     1. I
+     2. J
+          1. K
+          2. L</pre>
+
+<hr>
+
+<ol>
+  <li value="1">A</li>
+  <li value="2">B</li>
+  <li value="3">C</li>
+  <li value="4">D</li>
+  <li value="5">E</li>
+  <ol>
+    <li value="1">F</li>
+    <li value="2">G</li>
+  </ol>
+  <li value="6">H</li>
+  <ol>
+    <li value="1">I</li>
+    <li value="2">
+      J
+      <ol>
+        <li value="1">K</li>
+        <li value="2">L</li>
+      </ol>
+    </li>
+  </ol>
+</ol>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ul-ref.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ul-ref.html
new file mode 100644
index 0000000..22ee9f4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ul-ref.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>list owner is calculated to be nearest ancestor ul if it exists</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<p>This test matches if the list displays similar to the following</p>
+
+<pre>1. A
+2. B
+3. C
+4. D
+5. E
+     1. F
+     2. G
+6. H
+     1. I
+     2. J
+          1. K
+          2. L</pre>
+
+<hr>
+
+<ol>
+  <li value="1">A</li>
+  <li value="2">B</li>
+  <li value="3">C</li>
+  <li value="4">D</li>
+  <li value="5">E</li>
+  <ol>
+    <li value="1">F</li>
+    <li value="2">G</li>
+  </ol>
+  <li value="6">H</li>
+  <ol>
+    <li value="1">I</li>
+    <li value="2">
+      J
+      <ol>
+        <li value="1">K</li>
+        <li value="2">L</li>
+      </ol>
+    </li>
+  </ol>
+</ol>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ul.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ul.html
new file mode 100644
index 0000000..8f1c7e3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-list-owner-ul.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>list owner is calculated to be nearest ancestor ul if it exists</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<link rel="match" href="grouping-li-reftest-list-owner-ul-ref.html">
+
+<style>
+  li {
+    list-style-type: decimal;
+  }
+</style>
+
+<p>This test matches if the list displays similar to the following</p>
+
+<pre>1. A
+2. B
+3. C
+4. D
+5. E
+     1. F
+     2. G
+6. H
+     1. I
+     2. J
+          1. K
+          2. L</pre>
+
+<hr>
+
+<ul>
+  <li>A</li>
+  <li>B</li>
+  <div>
+    <li>C</li>
+    <span>
+      <li>D</li>
+      <li>E</li>
+    </span>
+    <ul>
+      <li>F</li>
+      <li>G</li>
+    </ul>
+  </div>
+  <li>H</li>
+  <ul>
+    <li>I</li>
+    <li>
+      J
+      <ul>
+        <li>K</li>
+        <li>L</li>
+      </ul>
+    </li>
+  </ul>
+</ul>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-no-list-owner-expected.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-no-list-owner-expected.html
new file mode 100644
index 0000000..e3ed2a51
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-no-list-owner-expected.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>If no ancestors generate CSS boxes, the list item has no owner, and thus gets numbered as 0</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<style>
+  html, body {
+    display: contents;
+  }
+
+  li {
+    list-style-type: decimal;
+    margin-left: 50px;
+  }
+</style>
+
+<p>This test matches if the list displays similar to the following</p>
+
+<pre>0. A
+0. B
+0. C</pre>
+
+<hr>
+
+<li value="0">A</li>
+<li value="0">B</li>
+<li value="0">C</li>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-no-list-owner-ref.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-no-list-owner-ref.html
new file mode 100644
index 0000000..e3ed2a51
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-no-list-owner-ref.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>If no ancestors generate CSS boxes, the list item has no owner, and thus gets numbered as 0</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<style>
+  html, body {
+    display: contents;
+  }
+
+  li {
+    list-style-type: decimal;
+    margin-left: 50px;
+  }
+</style>
+
+<p>This test matches if the list displays similar to the following</p>
+
+<pre>0. A
+0. B
+0. C</pre>
+
+<hr>
+
+<li value="0">A</li>
+<li value="0">B</li>
+<li value="0">C</li>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-no-list-owner.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-no-list-owner.html
new file mode 100644
index 0000000..02fdd63de
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-no-list-owner.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>If no ancestors generate CSS boxes, the list item has no owner, and thus gets numbered as 0</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<link rel="match" href="grouping-li-reftest-no-list-owner-ref.html">
+
+<style>
+  html, body {
+    display: contents;
+  }
+
+  li {
+    list-style-type: decimal;
+    margin-left: 50px;
+  }
+</style>
+
+<p>This test matches if the list displays similar to the following</p>
+
+<pre>0. A
+0. B
+0. C</pre>
+
+<hr>
+
+<li>A</li>
+<li>B</li>
+<li>C</li>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-not-being-rendered-expected.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-not-being-rendered-expected.html
new file mode 100644
index 0000000..9a018cf
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-not-being-rendered-expected.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>List items that are not being rendered do not participate in numbering</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<p>This test matches if the list displays similar to the following</p>
+
+<pre>1. A
+2. B
+3. D
+4. E
+5. G</pre>
+
+<hr>
+
+<ol>
+  <li value="1">A</li>
+  <li value="2">B</li>
+  <li value="3">D</li>
+  <li value="4">E</li>
+  <li value="5">G</li>
+</ol>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-not-being-rendered-ref.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-not-being-rendered-ref.html
new file mode 100644
index 0000000..9a018cf
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-not-being-rendered-ref.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>List items that are not being rendered do not participate in numbering</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<p>This test matches if the list displays similar to the following</p>
+
+<pre>1. A
+2. B
+3. D
+4. E
+5. G</pre>
+
+<hr>
+
+<ol>
+  <li value="1">A</li>
+  <li value="2">B</li>
+  <li value="3">D</li>
+  <li value="4">E</li>
+  <li value="5">G</li>
+</ol>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-not-being-rendered.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-not-being-rendered.html
new file mode 100644
index 0000000..da476c4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-not-being-rendered.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>List items that are not being rendered do not participate in numbering</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#ordinal-value">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#list-owner">
+
+<link rel="match" href="grouping-li-reftest-not-being-rendered-ref.html">
+
+<p>This test matches if the list displays similar to the following</p>
+
+<pre>1. A
+2. B
+3. D
+4. E
+5. G</pre>
+
+<hr>
+
+<ol>
+  <li>A</li>
+  <li>B</li>
+  <li hidden>C</li>
+  <li>D</li>
+  <div>
+    <li>E</li>
+    <li hidden>F</li>
+    <li>G</li>
+  </div>
+</ol>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-rev-reftest-001-expected.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-rev-reftest-001-expected.html
index 019aef1..8da3288 100644
--- a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-rev-reftest-001-expected.html
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-rev-reftest-001-expected.html
@@ -17,9 +17,6 @@
 
     <p>These reftests are necessary because the values of the ol's li children as calculated by the user agent are NOT available programatically. Only explicitly-set values are available programatically. Therefore, we need to check actual rendering against expected rendering.</p>
 
-    <p>The spec states:</p>
-    <blockquote>"The reversed attribute is a boolean attribute. If present, it indicates that the list is a descending list (..., 3, 2, 1). If the attribute is omitted, the list is an ascending list (1, 2, 3, ...)."</blockquote>
-
     <p><strong>This reftest passes if you see an ascending list followed by two descending lists.</strong></p>
     <p>(Note: each list item has no content; only the sequencing should appear.)</p>
 
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-rev-reftest-001-ref.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-rev-reftest-001-ref.html
index 019aef1..8da3288 100644
--- a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-rev-reftest-001-ref.html
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-rev-reftest-001-ref.html
@@ -17,9 +17,6 @@
 
     <p>These reftests are necessary because the values of the ol's li children as calculated by the user agent are NOT available programatically. Only explicitly-set values are available programatically. Therefore, we need to check actual rendering against expected rendering.</p>
 
-    <p>The spec states:</p>
-    <blockquote>"The reversed attribute is a boolean attribute. If present, it indicates that the list is a descending list (..., 3, 2, 1). If the attribute is omitted, the list is an ascending list (1, 2, 3, ...)."</blockquote>
-
     <p><strong>This reftest passes if you see an ascending list followed by two descending lists.</strong></p>
     <p>(Note: each list item has no content; only the sequencing should appear.)</p>
 
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-rev-reftest-001.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-rev-reftest-001.html
index 32d4f2c86f..7c502e38 100644
--- a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-rev-reftest-001.html
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-rev-reftest-001.html
@@ -20,9 +20,6 @@
 
     <p>These reftests are necessary because the values of the ol's li children as calculated by the user agent are NOT available programatically. Only explicitly-set values are available programatically. Therefore, we need to check actual rendering against expected rendering.</p>
 
-    <p>The spec states:</p>
-    <blockquote>"The reversed attribute is a boolean attribute. If present, it indicates that the list is a descending list (..., 3, 2, 1). If the attribute is omitted, the list is an ascending list (1, 2, 3, ...)."</blockquote>
-
     <p><strong>This reftest passes if you see an ascending list followed by two descending lists.</strong></p>
     <p>(Note: each list item has no content; only the sequencing should appear.)</p>
 
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-start-reftest-001-expected.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-start-reftest-001-expected.html
index f870748..0f65118 100644
--- a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-start-reftest-001-expected.html
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-start-reftest-001-expected.html
@@ -14,10 +14,6 @@
 <body>
     <p>This test continues to validate the ol element. This reftest is necessary because the values of the ol's li children as calculated and displayed by the user agent are NOT systematically available programatically. Only explicitly-set values are available programatically. Therefore, we need to check actual rendering against expected rendering.</p>
 
-    <p>The spec states:</p>
-    <blockquote><p>The first item in the list has the ordinal value given by the ol element's start attribute, unless that li element has a value attribute with a value that can be successfully parsed, in which case it has the ordinal value given by that value attribute.</p>
-                <p>Each subsequent item in the list has the ordinal value given by its value attribute, if it has one, or, if it doesn't, the ordinal value of the previous item, plus one if the reversed is absent, or minus one if it is present.</p></blockquote>
-
     <p><strong>This reftest passes if each list's items are numbered identically to the horizontal sequence immediately above those list items.</strong></p>
     <p>(Note: each list item has no content; only the sequencing should appear.)</p>
 
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-start-reftest-001-ref.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-start-reftest-001-ref.html
index f870748..0f65118 100644
--- a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-start-reftest-001-ref.html
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-start-reftest-001-ref.html
@@ -14,10 +14,6 @@
 <body>
     <p>This test continues to validate the ol element. This reftest is necessary because the values of the ol's li children as calculated and displayed by the user agent are NOT systematically available programatically. Only explicitly-set values are available programatically. Therefore, we need to check actual rendering against expected rendering.</p>
 
-    <p>The spec states:</p>
-    <blockquote><p>The first item in the list has the ordinal value given by the ol element's start attribute, unless that li element has a value attribute with a value that can be successfully parsed, in which case it has the ordinal value given by that value attribute.</p>
-                <p>Each subsequent item in the list has the ordinal value given by its value attribute, if it has one, or, if it doesn't, the ordinal value of the previous item, plus one if the reversed is absent, or minus one if it is present.</p></blockquote>
-
     <p><strong>This reftest passes if each list's items are numbered identically to the horizontal sequence immediately above those list items.</strong></p>
     <p>(Note: each list item has no content; only the sequencing should appear.)</p>
 
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-start-reftest-001.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-start-reftest-001.html
index dc373f91..b6de3dbb 100644
--- a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-start-reftest-001.html
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-start-reftest-001.html
@@ -17,10 +17,6 @@
 <body>
     <p>This test continues to validate the ol element. This reftest is necessary because the values of the ol's li children as calculated and displayed by the user agent are NOT systematically available programatically. Only explicitly-set values are available programatically. Therefore, we need to check actual rendering against expected rendering.</p>
 
-    <p>The spec states:</p>
-    <blockquote><p>The first item in the list has the ordinal value given by the ol element's start attribute, unless that li element has a value attribute with a value that can be successfully parsed, in which case it has the ordinal value given by that value attribute.</p>
-                <p>Each subsequent item in the list has the ordinal value given by its value attribute, if it has one, or, if it doesn't, the ordinal value of the previous item, plus one if the reversed is absent, or minus one if it is present.</p></blockquote>
-
     <p><strong>This reftest passes if each list's items are numbered identically to the horizontal sequence immediately above those list items.</strong></p>
     <p>(Note: each list item has no content; only the sequencing should appear.)</p>
 
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-start-reftest-002-expected.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-start-reftest-002-expected.html
index 2ba4af9b..2dbdf4a 100644
--- a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-start-reftest-002-expected.html
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-start-reftest-002-expected.html
@@ -14,10 +14,6 @@
 <body>
     <p>This test continues to validate the ol element. This reftest is necessary because the values of the ol's li children as calculated and displayed by the user agent are NOT systematically available programatically. Only explicitly-set values are available programatically. Therefore, we need to check actual rendering against expected rendering.</p>
 
-    <p>The spec states:</p>
-    <blockquote><p>The first item in the list has the ordinal value given by the ol element's start attribute, unless that li element has a value attribute with a value that can be successfully parsed, in which case it has the ordinal value given by that value attribute.</p>
-                <p>Each subsequent item in the list has the ordinal value given by its value attribute, if it has one, or, if it doesn't, the ordinal value of the previous item, plus one if the reversed is absent, or minus one if it is present.</p></blockquote>
-
     <p><strong>This reftest passes if each list's items are numbered identically to the horizontal sequence immediately above those list items.</strong></p>
     <p>(Note: each list item has no content; only the sequencing should appear.)</p>
 
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-start-reftest-002-ref.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-start-reftest-002-ref.html
index 2ba4af9b..2dbdf4a 100644
--- a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-start-reftest-002-ref.html
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-start-reftest-002-ref.html
@@ -14,10 +14,6 @@
 <body>
     <p>This test continues to validate the ol element. This reftest is necessary because the values of the ol's li children as calculated and displayed by the user agent are NOT systematically available programatically. Only explicitly-set values are available programatically. Therefore, we need to check actual rendering against expected rendering.</p>
 
-    <p>The spec states:</p>
-    <blockquote><p>The first item in the list has the ordinal value given by the ol element's start attribute, unless that li element has a value attribute with a value that can be successfully parsed, in which case it has the ordinal value given by that value attribute.</p>
-                <p>Each subsequent item in the list has the ordinal value given by its value attribute, if it has one, or, if it doesn't, the ordinal value of the previous item, plus one if the reversed is absent, or minus one if it is present.</p></blockquote>
-
     <p><strong>This reftest passes if each list's items are numbered identically to the horizontal sequence immediately above those list items.</strong></p>
     <p>(Note: each list item has no content; only the sequencing should appear.)</p>
 
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-start-reftest-002.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-start-reftest-002.html
index 559e3c21..9b21c79 100644
--- a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-start-reftest-002.html
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-start-reftest-002.html
@@ -17,10 +17,6 @@
 <body>
     <p>This test continues to validate the ol element. This reftest is necessary because the values of the ol's li children as calculated and displayed by the user agent are NOT systematically available programatically. Only explicitly-set values are available programatically. Therefore, we need to check actual rendering against expected rendering.</p>
 
-    <p>The spec states:</p>
-    <blockquote><p>The first item in the list has the ordinal value given by the ol element's start attribute, unless that li element has a value attribute with a value that can be successfully parsed, in which case it has the ordinal value given by that value attribute.</p>
-                <p>Each subsequent item in the list has the ordinal value given by its value attribute, if it has one, or, if it doesn't, the ordinal value of the previous item, plus one if the reversed is absent, or minus one if it is present.</p></blockquote>
-
     <p><strong>This reftest passes if each list's items are numbered identically to the horizontal sequence immediately above those list items.</strong></p>
     <p>(Note: each list item has no content; only the sequencing should appear.)</p>
 
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-001-expected.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-001-expected.html
index 294c9ab..391859e 100644
--- a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-001-expected.html
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-001-expected.html
@@ -14,9 +14,6 @@
 <body>
     <p>This test continues to validate the ol element. This reftest is necessary because the values of the ol's li children as calculated and displayed by the user agent are NOT systematically available programatically. Only explicitly-set values are available programatically. Therefore, we need to check actual rendering against expected rendering.</p>
 
-    <p>The spec states:</p>
-    <blockquote><p>The type attribute represents the state given in the cell in the second column of the row whose first cell matches the attribute's value; if none of the cells match, or if the attribute is omitted, then the attribute represents the decimal state.</p></blockquote>
-
     <p><strong>This reftest passes if each list's items are labelled identically to the horizontal sequence immediately above those list items:</strong></p>
     <p>(Note: each list item has no content; only the sequencing should appear.)</p>
 
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-001-ref.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-001-ref.html
index 294c9ab..391859e 100644
--- a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-001-ref.html
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-001-ref.html
@@ -14,9 +14,6 @@
 <body>
     <p>This test continues to validate the ol element. This reftest is necessary because the values of the ol's li children as calculated and displayed by the user agent are NOT systematically available programatically. Only explicitly-set values are available programatically. Therefore, we need to check actual rendering against expected rendering.</p>
 
-    <p>The spec states:</p>
-    <blockquote><p>The type attribute represents the state given in the cell in the second column of the row whose first cell matches the attribute's value; if none of the cells match, or if the attribute is omitted, then the attribute represents the decimal state.</p></blockquote>
-
     <p><strong>This reftest passes if each list's items are labelled identically to the horizontal sequence immediately above those list items:</strong></p>
     <p>(Note: each list item has no content; only the sequencing should appear.)</p>
 
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-001.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-001.html
index 32b0e20..b7dc22d7 100644
--- a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-001.html
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-001.html
@@ -16,9 +16,6 @@
 <body>
     <p>This test continues to validate the ol element. This reftest is necessary because the values of the ol's li children as calculated and displayed by the user agent are NOT systematically available programatically. Only explicitly-set values are available programatically. Therefore, we need to check actual rendering against expected rendering.</p>
 
-    <p>The spec states:</p>
-    <blockquote><p>The type attribute represents the state given in the cell in the second column of the row whose first cell matches the attribute's value; if none of the cells match, or if the attribute is omitted, then the attribute represents the decimal state.</p></blockquote>
-
     <p><strong>This reftest passes if each list's items are labelled identically to the horizontal sequence immediately above those list items:</strong></p>
     <p>(Note: each list item has no content; only the sequencing should appear.)</p>
 
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-002-expected.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-002-expected.html
index cf31b7a..8d8012e8 100644
--- a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-002-expected.html
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-002-expected.html
@@ -14,9 +14,6 @@
 <body>
     <p>This test continues to validate the ol element. This reftest is necessary because the values of the ol's li children as calculated and displayed by the user agent are NOT systematically available programatically. Only explicitly-set values are available programatically. Therefore, we need to check actual rendering against expected rendering.</p>
 
-    <p>The spec states:</p>
-    <blockquote>User agents should render the items of the list in a manner consistent with the state of the type attribute of the ol element.</blockquote>
-
     <p><strong>This reftest passes if each list's items are labelled identically to the horizontal sequence immediately above those list items:</strong></p>
     <p>(Note: each list item has no content; only the sequencing should appear.)</p>
 
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-002-ref.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-002-ref.html
index cf31b7a..8d8012e8 100644
--- a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-002-ref.html
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-002-ref.html
@@ -14,9 +14,6 @@
 <body>
     <p>This test continues to validate the ol element. This reftest is necessary because the values of the ol's li children as calculated and displayed by the user agent are NOT systematically available programatically. Only explicitly-set values are available programatically. Therefore, we need to check actual rendering against expected rendering.</p>
 
-    <p>The spec states:</p>
-    <blockquote>User agents should render the items of the list in a manner consistent with the state of the type attribute of the ol element.</blockquote>
-
     <p><strong>This reftest passes if each list's items are labelled identically to the horizontal sequence immediately above those list items:</strong></p>
     <p>(Note: each list item has no content; only the sequencing should appear.)</p>
 
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-002.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-002.html
index 7daea54..db55fd4 100644
--- a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-002.html
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-002.html
@@ -16,9 +16,6 @@
 <body>
     <p>This test continues to validate the ol element. This reftest is necessary because the values of the ol's li children as calculated and displayed by the user agent are NOT systematically available programatically. Only explicitly-set values are available programatically. Therefore, we need to check actual rendering against expected rendering.</p>
 
-    <p>The spec states:</p>
-    <blockquote>User agents should render the items of the list in a manner consistent with the state of the type attribute of the ol element.</blockquote>
-
     <p><strong>This reftest passes if each list's items are labelled identically to the horizontal sequence immediately above those list items:</strong></p>
     <p>(Note: each list item has no content; only the sequencing should appear.)</p>
 
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-003-expected.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-003-expected.html
index 4906f3c..6c836cf3 100644
--- a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-003-expected.html
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-003-expected.html
@@ -14,8 +14,6 @@
 <body>
     <p>This test continues to validate the ol element. This reftest is necessary because the values of the ol's li children as calculated and displayed by the user agent are NOT systematically available programatically. Only explicitly-set values are available programatically. Therefore, we need to check actual rendering against expected rendering.</p>
 
-    <p>The spec states: Numbers less than or equal to zero should always use the decimal system regardless of the type attribute.</p>
-
     <p><strong>This reftest passes if each list's items are labelled identically to the horizontal sequence immediately above those list items:</strong></p>
     <p>(Note: each list item has no content; only the sequencing should appear.)</p>
 
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-003-ref.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-003-ref.html
index 4906f3c..6c836cf3 100644
--- a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-003-ref.html
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-003-ref.html
@@ -14,8 +14,6 @@
 <body>
     <p>This test continues to validate the ol element. This reftest is necessary because the values of the ol's li children as calculated and displayed by the user agent are NOT systematically available programatically. Only explicitly-set values are available programatically. Therefore, we need to check actual rendering against expected rendering.</p>
 
-    <p>The spec states: Numbers less than or equal to zero should always use the decimal system regardless of the type attribute.</p>
-
     <p><strong>This reftest passes if each list's items are labelled identically to the horizontal sequence immediately above those list items:</strong></p>
     <p>(Note: each list item has no content; only the sequencing should appear.)</p>
 
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-003.html b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-003.html
index 5e4b5a5..6542872 100644
--- a/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-003.html
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/semantics/grouping-content/the-ol-element/grouping-ol-type-reftest-003.html
@@ -16,8 +16,6 @@
 <body>
     <p>This test continues to validate the ol element. This reftest is necessary because the values of the ol's li children as calculated and displayed by the user agent are NOT systematically available programatically. Only explicitly-set values are available programatically. Therefore, we need to check actual rendering against expected rendering.</p>
 
-    <p>The spec states: Numbers less than or equal to zero should always use the decimal system regardless of the type attribute.</p>
-
     <p><strong>This reftest passes if each list's items are labelled identically to the horizontal sequence immediately above those list items:</strong></p>
     <p>(Note: each list item has no content; only the sequencing should appear.)</p>
 
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/webappapis/scripting/events/messageevent-constructor.https-expected.txt b/third_party/WebKit/LayoutTests/imported/wpt/html/webappapis/scripting/events/messageevent-constructor.https-expected.txt
new file mode 100644
index 0000000..6aae471
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/webappapis/scripting/events/messageevent-constructor.https-expected.txt
@@ -0,0 +1,15 @@
+This is a testharness.js-based test.
+FAIL Default event values Cannot read property 'length' of null
+PASS MessageEventInit dictionary 
+FAIL Passing null for ports member assert_throws: function "function () {
+    new MessageEvent("test", { ports: null })
+  }" did not throw
+FAIL ports attribute should be a FrozenArray assert_true: Object.isFrozen() should return true expected true got false
+PASS initMessageEvent operation 
+FAIL Passing null for ports parameter to initMessageEvent assert_throws: function "function () {
+    ev.initMessageEvent("test", true, false, "testData", "testOrigin", "testId", window, null)
+  }" did not throw
+FAIL All parameters to initMessageEvent should be mandatory assert_equals: MessageEvent.prototype.initMessageEvent.length should be 8 expected 8 but got 0
+FAIL Passing ServiceWorker for source member assert_unreached: unregister should not fail: Failed to get a ServiceWorkerRegistration: The URL protocol of the current origin ('file://') is not supported. Reached unreachable code
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/webappapis/scripting/events/messageevent-constructor.html b/third_party/WebKit/LayoutTests/imported/wpt/html/webappapis/scripting/events/messageevent-constructor.https.html
similarity index 81%
rename from third_party/WebKit/LayoutTests/imported/wpt/html/webappapis/scripting/events/messageevent-constructor.html
rename to third_party/WebKit/LayoutTests/imported/wpt/html/webappapis/scripting/events/messageevent-constructor.https.html
index 47b536d2..8a8a8f6 100644
--- a/third_party/WebKit/LayoutTests/imported/wpt/html/webappapis/scripting/events/messageevent-constructor.html
+++ b/third_party/WebKit/LayoutTests/imported/wpt/html/webappapis/scripting/events/messageevent-constructor.https.html
@@ -2,6 +2,7 @@
 <title>MessageEvent constructor</title>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
 <script>
 test(function() {
   var ev = new MessageEvent("test")
@@ -75,4 +76,21 @@
   }, "Calling initMessageEvent with only 7 parameters should throw a TypeError")
 }, "All parameters to initMessageEvent should be mandatory")
 
+promise_test(function(t) {
+    var worker_url = "/service-workers/service-worker/resources/empty-worker.js";
+    var scope = "/service-workers/service-worker/resources/";
+    var registration;
+
+    return service_worker_unregister_and_register(t, worker_url, scope)
+      .then(function(r) {
+          registration = r;
+          return wait_for_state(t, r.installing, "activated");
+        })
+      .then(function() {
+          var ev = new MessageEvent("test", { source: registration.active });
+          assert_equals(ev.source, registration.active, "source attribute should return the ServiceWorker");
+          service_worker_unregister(t, scope);
+        });
+  }, "Passing ServiceWorker for source member");
+
 </script>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/imagebitmap-renderingcontext/bitmaprenderer-as-imagesource-expected.txt b/third_party/WebKit/LayoutTests/imported/wpt/imagebitmap-renderingcontext/bitmaprenderer-as-imagesource-expected.txt
new file mode 100644
index 0000000..4c3af0cf
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/imagebitmap-renderingcontext/bitmaprenderer-as-imagesource-expected.txt
@@ -0,0 +1,6 @@
+This is a testharness.js-based test.
+FAIL Test that createImageBitmap from a bitmaprenderer canvas produces correct result assert_equals: expected 10 but got 20
+PASS Test that createImageBitmap on a bitmaprenderer canvas that never consumes any source produces correct result 
+PASS Test that createImageBitmap on a bitmaprenderer canvas that consumes null produces correct result 
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/imagebitmap-renderingcontext/bitmaprenderer-as-imagesource.html b/third_party/WebKit/LayoutTests/imported/wpt/imagebitmap-renderingcontext/bitmaprenderer-as-imagesource.html
new file mode 100644
index 0000000..1dbfa2b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/imagebitmap-renderingcontext/bitmaprenderer-as-imagesource.html
@@ -0,0 +1,90 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Canvas's ImageBitmapRenderingContext test</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/scripting.html#the-imagebitmap-rendering-context">
+<script>
+var width = 10;
+var height = 10;
+
+function testCanvas(ctx, x, y, r, g, b, a)
+{
+    var color = ctx.getImageData(x, y, 1, 1).data;
+    assert_array_equals(color, [r, g, b, a]);
+}
+
+function consumeImageBitmap(image, t)
+{
+    var myCanvas = document.createElement('canvas');
+    myCanvas.width = myCanvas.height = 20;
+    var myCtx = myCanvas.getContext('bitmaprenderer');
+    myCtx.transferFromImageBitmap(image);
+
+    createImageBitmap(myCanvas).then(t.step_func_done(function(imageBitmap) {
+        // Per spec, when transferFromImageBitmap happens, the transferred
+        // ImageBitmap (|image| here) should be the intrinsic size of
+        // myCanvas, and hence myCanvas.width/height is ignored. Therefore,
+        // this created |imageBitmap| should have the same size as the |image|.
+        assert_equals(imageBitmap.width, width);
+        assert_equals(imageBitmap.height, height);
+
+        var dstCanvas = document.createElement('canvas');
+        dstCanvas.width = dstCanvas.height = 20;
+        var dstCtx = dstCanvas.getContext('2d');
+        dstCtx.drawImage(myCanvas, 0, 0);
+        testCanvas(dstCtx, 5, 5, 0, 255, 0, 255);
+        testCanvas(dstCtx, 15, 15, 0, 0, 0, 0);
+    }));
+}
+
+async_test(function(t) {
+    var canvas = document.createElement("canvas");
+    canvas.width = width;
+    canvas.height = height;
+    var ctx = canvas.getContext('2d');
+    ctx.fillStyle = '#0f0';
+    ctx.fillRect(0, 0, width, height);
+    createImageBitmap(canvas).then(t.step_func(function(image) {
+        consumeImageBitmap(image, t);
+    }));
+}, 'Test that createImageBitmap from a bitmaprenderer canvas produces correct result');
+
+async_test(function(t) {
+    var canvas = document.createElement("canvas");
+    canvas.width = width;
+    canvas.height = height;
+    var ctx = canvas.getContext('bitmaprenderer');
+    createImageBitmap(canvas).then(t.step_func_done(function(image) {
+        assert_equals(image.width, width);
+        assert_equals(image.height, height);
+
+        var dstCanvas = document.createElement('canvas');
+        dstCanvas.width = width;
+        dstCanvas.height = height;
+        var dstCtx = dstCanvas.getContext('2d');
+        dstCtx.drawImage(canvas, 0, 0);
+        testCanvas(dstCtx, 5, 5, 0, 0, 0, 0);
+    }));
+}, 'Test that createImageBitmap on a bitmaprenderer canvas that never consumes any source produces correct result');
+
+
+async_test(function(t) {
+    var canvas = document.createElement("canvas");
+    canvas.width = width;
+    canvas.height = height;
+    var ctx = canvas.getContext('bitmaprenderer');
+    ctx.transferFromImageBitmap(null);
+    createImageBitmap(canvas).then(t.step_func_done(function(image) {
+        assert_equals(image.width, width);
+        assert_equals(image.height, height);
+
+        var dstCanvas = document.createElement('canvas');
+        dstCanvas.width = width;
+        dstCanvas.height = height;
+        var dstCtx = dstCanvas.getContext('2d');
+        dstCtx.drawImage(canvas, 0, 0);
+        testCanvas(dstCtx, 5, 5, 0, 0, 0, 0);
+    }));
+}, 'Test that createImageBitmap on a bitmaprenderer canvas that consumes null produces correct result');
+</script>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/imagebitmap-renderingcontext/context-creation-with-alpha-expected.txt b/third_party/WebKit/LayoutTests/imported/wpt/imagebitmap-renderingcontext/context-creation-with-alpha-expected.txt
new file mode 100644
index 0000000..84b4e5f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/imagebitmap-renderingcontext/context-creation-with-alpha-expected.txt
@@ -0,0 +1,7 @@
+CONSOLE ERROR: Uncaught (in promise) Error: assert_array_equals: property 1, expected 127 but got 255
+This is a testharness.js-based test.
+FAIL Test that an ImageBitmapRenderingContext with alpha disabled makes the canvas opaque assert_not_equals: got disallowed value undefined
+FAIL Test that an ImageBitmapRenderingContext with alpha enabled preserves the alpha assert_not_equals: got disallowed value undefined
+FAIL Test that the 'alpha' context creation attribute is true by default assert_not_equals: got disallowed value undefined
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/imagebitmap-renderingcontext/context-creation-with-alpha.html b/third_party/WebKit/LayoutTests/imported/wpt/imagebitmap-renderingcontext/context-creation-with-alpha.html
new file mode 100644
index 0000000..88d12d1
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/imagebitmap-renderingcontext/context-creation-with-alpha.html
@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Canvas's ImageBitmapRenderingContext test</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/scripting.html#the-imagebitmap-rendering-context">
+<script>
+var width = 10;
+var height = 10;
+
+function testCanvas(ctx, r, g, b, a)
+{
+    var color = ctx.getImageData(5, 5, 1, 1).data;
+    assert_array_equals(color, [r, g, b, a]);
+}
+
+function consumeImageBitmap(image, alphaVal, expectedR, expectedG, expectedB, expectedA)
+{
+    var dstCanvas = document.createElement('canvas');
+    dstCanvas.width = width;
+    dstCanvas.height = height;
+    var dstCtx;
+    if (alphaVal == 'true')
+        dstCtx = dstCanvas.getContext('bitmaprenderer', { alpha: true });
+    else if (alphaVal == 'false')
+        dstCtx = dstCanvas.getContext('bitmaprenderer', { alpha: false });
+    else
+        dstCtx = dstCanvas.getContext('bitmaprenderer');
+    dstCtx.transferFromImageBitmap(image);
+
+    var myCanvas = document.createElement('canvas');
+    myCanvas.width = width;
+    myCanvas.height = height;
+    var myCtx = myCanvas.getContext('2d');
+    myCtx.drawImage(dstCanvas, 0, 0);
+    testCanvas(myCtx, expectedR, expectedG, expectedB, expectedA);
+}
+
+promise_test(function() {
+    var srcCanvas = document.createElement('canvas');
+    srcCanvas.width = width;
+    srcCanvas.height = height;
+    var ctx = srcCanvas.getContext('2d');
+    ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+    ctx.fillRect(0, 0, width, height);
+    createImageBitmap(srcCanvas).then(function(image) {
+        consumeImageBitmap(image, 'false', 0, 127, 0, 255);
+    });
+}, "Test that an ImageBitmapRenderingContext with alpha disabled makes the canvas opaque");
+
+promise_test(function() {
+    var srcCanvas = document.createElement('canvas');
+    srcCanvas.width = width;
+    srcCanvas.height = height;
+    var ctx = srcCanvas.getContext('2d');
+    ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+    ctx.fillRect(0, 0, width, height);
+    createImageBitmap(srcCanvas).then(function(image) {
+        consumeImageBitmap(image, 'true', 0, 255, 0, 127);
+    });
+}, "Test that an ImageBitmapRenderingContext with alpha enabled preserves the alpha");
+
+promise_test(function() {
+    var srcCanvas = document.createElement('canvas');
+    srcCanvas.width = width;
+    srcCanvas.height = height;
+    var ctx = srcCanvas.getContext('2d');
+    ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+    ctx.fillRect(0, 0, width, height);
+    createImageBitmap(srcCanvas).then(function(image) {
+        consumeImageBitmap(image, '', 0, 255, 0, 127);
+    });
+}, "Test that the 'alpha' context creation attribute is true by default");
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/imagebitmap-renderingcontext/context-creation.html b/third_party/WebKit/LayoutTests/imported/wpt/imagebitmap-renderingcontext/context-creation.html
new file mode 100644
index 0000000..3daa397
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/imagebitmap-renderingcontext/context-creation.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Canvas's ImageBitmapRenderingContext test</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/scripting.html#the-imagebitmap-rendering-context">
+<script>
+var width = 10;
+var height = 10;
+
+test(function() {
+    var canvas = document.createElement("canvas");
+    canvas.width = width;
+    canvas.height = height;
+    var ctx = canvas.getContext('bitmaprenderer');
+    assert_true(ctx instanceof ImageBitmapRenderingContext);
+}, "Test that canvas.getContext('bitmaprenderer') returns an instance of ImageBitmapRenderingContext");
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/imagebitmap-renderingcontext/context-preserves-canvas.html b/third_party/WebKit/LayoutTests/imported/wpt/imagebitmap-renderingcontext/context-preserves-canvas.html
new file mode 100644
index 0000000..eca7afe
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/imagebitmap-renderingcontext/context-preserves-canvas.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Canvas's ImageBitmapRenderingContext test</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/scripting.html#the-imagebitmap-rendering-context">
+<script>
+var width = 10;
+var height = 10;
+
+test(function() {
+    var canvas = document.createElement("canvas");
+    canvas.width = width;
+    canvas.height = height;
+    var ctx = canvas.getContext('bitmaprenderer');
+    var dstCanvas = ctx.canvas;
+    assert_equals(dstCanvas.width, width);
+    assert_equals(dstCanvas.height, height);
+}, "Test that ctx.canvas on a ImageBitmapRenderingContext returns the original canvas");
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/imagebitmap-renderingcontext/tranferFromImageBitmap-null.html b/third_party/WebKit/LayoutTests/imported/wpt/imagebitmap-renderingcontext/tranferFromImageBitmap-null.html
new file mode 100644
index 0000000..c12a8c9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/imagebitmap-renderingcontext/tranferFromImageBitmap-null.html
@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Canvas's ImageBitmapRenderingContext test</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/scripting.html#the-imagebitmap-rendering-context">
+<script>
+var width = 10;
+var height = 10;
+
+function testCanvas(ctx, r, g, b, a)
+{
+    var color = ctx.getImageData(5, 5, 1, 1).data;
+    assert_array_equals(color, [r, g, b, a]);
+}
+
+promise_test(function() {
+    function testTransferFromImageBitmapNullability(greenImage, redImage) {
+        var bitmapCanvas = document.createElement('canvas');
+        bitmapCanvas.width = width;
+        bitmapCanvas.height = height;
+        var bitmapCtx = bitmapCanvas.getContext('bitmaprenderer');
+        bitmapCtx.transferFromImageBitmap(greenImage);
+
+        // Make sure the bitmap renderer canvas is filled correctly.
+        var myCanvas = document.createElement('canvas');
+        myCanvas.width = width;
+        myCanvas.height = height;
+        var myCtx = myCanvas.getContext('2d');
+        myCtx.drawImage(bitmapCanvas, 0, 0);
+        testCanvas(myCtx, 0, 255, 0, 255);
+
+        // Test if passing null resets the bitmap renderer canvas.
+        // Drawing the resetted canvas cannot change the destination canvas.
+        bitmapCtx.transferFromImageBitmap(null);
+        var myCanvas2 = document.createElement('canvas');
+        myCanvas2.width = width;
+        myCanvas2.height = height;
+        var myCtx2 = myCanvas2.getContext('2d');
+        myCtx2.drawImage(bitmapCanvas, 0, 0);
+        testCanvas(myCtx2, 0, 0, 0, 0);
+
+        // Test if we can redraw the bitmap canvas correctly after reset.
+        bitmapCtx.transferFromImageBitmap(redImage);
+        var myCanvas3 = document.createElement('canvas');
+        myCanvas3.width = width;
+        myCanvas3.height = height;
+        var myCtx3 = myCanvas3.getContext('2d');
+        myCtx3.drawImage(bitmapCanvas, 0, 0);
+        testCanvas(myCtx3, 255, 0, 0, 255);
+    }
+
+    var greenCanvas = document.createElement('canvas');
+    greenCanvas.width = width;
+    greenCanvas.height = height;
+    var greenCtx = greenCanvas.getContext('2d');
+    greenCtx.fillStyle = '#0f0';
+    greenCtx.fillRect(0, 0, width, height);
+
+    var redCanvas = document.createElement('canvas');
+    redCanvas.width = width;
+    redCanvas.height = height;
+    var redCtx = redCanvas.getContext('2d');
+    redCtx.fillStyle = '#f00';
+    redCtx.fillRect(0, 0, width, height);
+
+    return Promise.all([
+        createImageBitmap(greenCanvas),
+        createImageBitmap(redCanvas),
+    ]).then(([greenImage, redImage]) => {
+        testTransferFromImageBitmapNullability(greenImage, redImage);
+    });
+},'Test that transferFromImageBitmap(null) discards the previously transferred image');
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/imagebitmap-renderingcontext/transferFromImageBitmap-detached-expected.txt b/third_party/WebKit/LayoutTests/imported/wpt/imagebitmap-renderingcontext/transferFromImageBitmap-detached-expected.txt
new file mode 100644
index 0000000..96e2baa
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/imagebitmap-renderingcontext/transferFromImageBitmap-detached-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Test transferFromImageBitmap(image) with a detached image should throw InvalidStateError assert_not_equals: got disallowed value undefined
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/imagebitmap-renderingcontext/transferFromImageBitmap-detached.html b/third_party/WebKit/LayoutTests/imported/wpt/imagebitmap-renderingcontext/transferFromImageBitmap-detached.html
new file mode 100644
index 0000000..2c547ee
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/imagebitmap-renderingcontext/transferFromImageBitmap-detached.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Canvas's ImageBitmapRenderingContext test</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/scripting.html#the-imagebitmap-rendering-context">
+<script>
+var width = 10;
+var height = 10;
+
+promise_test(function() {
+    function testException(image) {
+        var dstCanvas = document.createElement('canvas');
+        dstCanvas.width = width;
+        dstCanvas.height = height;
+        var dstCtx = dstCanvas.getContext('bitmaprenderer');
+        dstCtx.transferFromImageBitmap(image);
+
+        // The image should be detached after transferFromImageBitmap.
+        assert_equals(image.width, 0);
+        assert_equals(image.height, 0);
+        assert_throws("InvalidStateError", function() { dstCtx.transferFromImageBitmap(image); });
+    }
+
+    var srcCanvas = document.createElement('canvas');
+    srcCanvas.width = width;
+    srcCanvas.height = height;
+    var ctx = srcCanvas.getContext('2d');
+    ctx.fillStyle = 'rgba(0, 255, 0, 0.5)';
+    ctx.fillRect(0, 0, width, height);
+    createImageBitmap(srcCanvas).then(testException);
+}, "Test transferFromImageBitmap(image) with a detached image should throw InvalidStateError");
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/service-workers/service-worker/postmessage-to-client.https.html b/third_party/WebKit/LayoutTests/imported/wpt/service-workers/service-worker/postmessage-to-client.https.html
index a031ee2e..2ee9808 100644
--- a/third_party/WebKit/LayoutTests/imported/wpt/service-workers/service-worker/postmessage-to-client.https.html
+++ b/third_party/WebKit/LayoutTests/imported/wpt/service-workers/service-worker/postmessage-to-client.https.html
@@ -29,11 +29,14 @@
     var expected = ['Sending message via clients'];
 
     function onMessage(e) {
+      assert_equals(e.constructor, frame.contentWindow.MessageEvent,
+                    'message events should use MessageEvent interface.');
       assert_equals(e.bubbles, false, 'message events should not bubble.');
-      assert_equals(e.cancelable, false, 'message events should not be cancelable.');
-      assert_equals(e.origin, host_info['HTTPS_ORIGIN'], 'message event\'s origin should be set correctly.');
-// XXXkhuey fixme!
-//      assert_equals(e.source, sw.controller, 'source should be ServiceWorker.');
+      assert_equals(e.cancelable, false,
+                    'message events should not be cancelable.');
+      assert_equals(e.origin, host_info['HTTPS_ORIGIN'],
+                    'message event\'s origin should be set correctly.');
+      assert_equals(e.source, sw.controller, 'source should be ServiceWorker.');
 
       var message = e.data;
       if (message === 'quit') {
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/service-workers/service-worker/serviceworker-message-event-historical.https.html b/third_party/WebKit/LayoutTests/imported/wpt/service-workers/service-worker/serviceworker-message-event-historical.https.html
new file mode 100644
index 0000000..2f780a6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/service-workers/service-worker/serviceworker-message-event-historical.https.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<title>Service Worker: ServiceWorkerMessageEvent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/test-helpers.sub.js"></script>
+<script>
+
+promise_test(function(t) {
+    var scope = 'resources/blank.html';
+    var url = 'resources/postmessage-to-client-worker.js';
+    return service_worker_unregister_and_register(t, url, scope)
+      .then(function(r) {
+          return wait_for_state(t, r.installing, 'activated');
+        })
+      .then(function() {
+          return with_iframe(scope);
+        })
+      .then(function(frame) {
+          var w = frame.contentWindow;
+          var worker = w.navigator.serviceWorker.controller;
+          assert_equals(
+              self.ServiceWorkerMessageEvent, undefined,
+              'ServiceWorkerMessageEvent should not be defined.');
+          return new Promise(function(resolve) {
+              w.navigator.serviceWorker.onmessage = t.step_func(function(e) {
+                  assert_true(
+                      e instanceof w.MessageEvent,
+                      'message events should use MessageEvent interface.');
+                  assert_true(e.source instanceof w.ServiceWorker);
+                  assert_equals(e.type, 'message');
+                  assert_equals(e.source, worker,
+                                'source should equal to the controller.');
+                  assert_equals(e.ports.length, 0);
+                  resolve();
+                });
+              worker.postMessage('PING');
+            });
+        })
+      .then(function() {
+          return service_worker_unregister_and_done(t, scope);
+        });
+  }, 'Test MessageEvent supplants ServiceWorkerMessageEvent.');
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/streams/byte-length-queuing-strategy.https.html b/third_party/WebKit/LayoutTests/imported/wpt/streams/byte-length-queuing-strategy.https.html
new file mode 100644
index 0000000..9a4356ca
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/streams/byte-length-queuing-strategy.https.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+<script src="resources/test-initializer.js"></script>
+
+<script src="byte-length-queuing-strategy.js"></script>
+<script>
+'use strict';
+worker_test('byte-length-queuing-strategy.js');
+</script>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/streams/byte-length-queuing-strategy.js b/third_party/WebKit/LayoutTests/imported/wpt/streams/byte-length-queuing-strategy.js
new file mode 100644
index 0000000..54407af
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/streams/byte-length-queuing-strategy.js
@@ -0,0 +1,107 @@
+'use strict';
+
+if (self.importScripts) {
+  self.importScripts('/resources/testharness.js');
+}
+
+test(() => {
+
+  new ByteLengthQueuingStrategy({ highWaterMark: 4 });
+
+}, 'Can construct a ByteLengthQueuingStrategy with a valid high water mark');
+
+test(() => {
+
+  for (const highWaterMark of [-Infinity, NaN, 'foo', {}, () => {}]) {
+    const strategy = new ByteLengthQueuingStrategy({ highWaterMark });
+    assert_equals(strategy.highWaterMark, highWaterMark, `${highWaterMark} gets set correctly`);
+  }
+
+}, 'Can construct a ByteLengthQueuingStrategy with any value as its high water mark');
+
+test(() => {
+
+  const highWaterMark = 1;
+  const highWaterMarkObjectGetter = {
+    get highWaterMark() { return highWaterMark; }
+  };
+  const error = new Error('wow!');
+  const highWaterMarkObjectGetterThrowing = {
+    get highWaterMark() { throw error; }
+  };
+
+  assert_throws({ name: 'TypeError' }, () => new ByteLengthQueuingStrategy(), 'construction fails with undefined');
+  assert_throws({ name: 'TypeError' }, () => new ByteLengthQueuingStrategy(null), 'construction fails with null');
+  assert_throws({ name: 'Error' }, () => new ByteLengthQueuingStrategy(highWaterMarkObjectGetterThrowing),
+    'construction fails with an object with a throwing highWaterMark getter');
+
+  // Should not fail:
+  new ByteLengthQueuingStrategy('potato');
+  new ByteLengthQueuingStrategy({});
+  new ByteLengthQueuingStrategy(highWaterMarkObjectGetter);
+
+}, 'ByteLengthQueuingStrategy constructor behaves as expected with strange arguments');
+
+test(() => {
+
+  const size = 1024;
+  const chunk = { byteLength: size };
+  const chunkGetter = {
+    get byteLength() { return size; }
+  };
+  const error = new Error('wow!');
+  const chunkGetterThrowing = {
+    get byteLength() { throw error; }
+  };
+  assert_throws({ name: 'TypeError' }, () => ByteLengthQueuingStrategy.prototype.size(), 'size fails with undefined');
+  assert_throws({ name: 'TypeError' }, () => ByteLengthQueuingStrategy.prototype.size(null), 'size fails with null');
+  assert_equals(ByteLengthQueuingStrategy.prototype.size('potato'), undefined,
+    'size succeeds with undefined with a random non-object type');
+  assert_equals(ByteLengthQueuingStrategy.prototype.size({}), undefined,
+    'size succeeds with undefined with an object without hwm property');
+  assert_equals(ByteLengthQueuingStrategy.prototype.size(chunk), size,
+    'size succeeds with the right amount with an object with a hwm');
+  assert_equals(ByteLengthQueuingStrategy.prototype.size(chunkGetter), size,
+    'size succeeds with the right amount with an object with a hwm getter');
+  assert_throws({ name: 'Error' }, () => ByteLengthQueuingStrategy.prototype.size(chunkGetterThrowing),
+    'size fails with the error thrown by the getter');
+
+}, 'ByteLengthQueuingStrategy size behaves as expected with strange arguments');
+
+test(() => {
+
+  const thisValue = null;
+  const returnValue = { 'returned from': 'byteLength getter' };
+  const chunk = {
+    get byteLength() { return returnValue; }
+  };
+
+  assert_equals(ByteLengthQueuingStrategy.prototype.size.call(thisValue, chunk), returnValue);
+
+}, 'ByteLengthQueuingStrategy.prototype.size should work generically on its this and its arguments');
+
+test(() => {
+
+  const strategy = new ByteLengthQueuingStrategy({ highWaterMark: 4 });
+
+  assert_object_equals(Object.getOwnPropertyDescriptor(strategy, 'highWaterMark'),
+    { value: 4, writable: true, enumerable: true, configurable: true },
+    'highWaterMark property should be a data property with the value passed the constructor');
+  assert_equals(typeof strategy.size, 'function');
+
+}, 'ByteLengthQueuingStrategy instances have the correct properties');
+
+test(() => {
+
+  const strategy = new ByteLengthQueuingStrategy({ highWaterMark: 4 });
+  assert_equals(strategy.highWaterMark, 4);
+
+  strategy.highWaterMark = 10;
+  assert_equals(strategy.highWaterMark, 10);
+
+  strategy.highWaterMark = 'banana';
+  assert_equals(strategy.highWaterMark, 'banana');
+
+}, 'ByteLengthQueuingStrategy\'s highWaterMark property can be set to anything');
+
+done();
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/streams/count-queuing-strategy.https.html b/third_party/WebKit/LayoutTests/imported/wpt/streams/count-queuing-strategy.https.html
new file mode 100644
index 0000000..a7f5570
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/streams/count-queuing-strategy.https.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+<script src="resources/test-initializer.js"></script>
+
+<script src="count-queuing-strategy.js"></script>
+<script>
+'use strict';
+worker_test('count-queuing-strategy.js');
+</script>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/streams/count-queuing-strategy.js b/third_party/WebKit/LayoutTests/imported/wpt/streams/count-queuing-strategy.js
new file mode 100644
index 0000000..5ae0063
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/streams/count-queuing-strategy.js
@@ -0,0 +1,106 @@
+'use strict';
+
+if (self.importScripts) {
+  self.importScripts('/resources/testharness.js');
+}
+
+test(() => {
+
+  new CountQueuingStrategy({ highWaterMark: 4 });
+
+}, 'Can construct a CountQueuingStrategy with a valid high water mark');
+
+test(() => {
+
+  for (const highWaterMark of [-Infinity, NaN, 'foo', {}, () => {}]) {
+    const strategy = new CountQueuingStrategy({ highWaterMark });
+    assert_equals(strategy.highWaterMark, highWaterMark, `${highWaterMark} gets set correctly`);
+  }
+
+}, 'Can construct a CountQueuingStrategy with any value as its high water mark');
+
+test(() => {
+
+  const highWaterMark = 1;
+  const highWaterMarkObjectGetter = {
+    get highWaterMark() { return highWaterMark; }
+  };
+  const error = new Error('wow!');
+  const highWaterMarkObjectGetterThrowing = {
+    get highWaterMark() { throw error; }
+  };
+
+  assert_throws({ name: 'TypeError' }, () => new CountQueuingStrategy(), 'construction fails with undefined');
+  assert_throws({ name: 'TypeError' }, () => new CountQueuingStrategy(null), 'construction fails with null');
+  assert_throws({ name: 'Error' }, () => new CountQueuingStrategy(highWaterMarkObjectGetterThrowing),
+    'construction fails with an object with a throwing highWaterMark getter');
+
+  // Should not fail:
+  new CountQueuingStrategy('potato');
+  new CountQueuingStrategy({});
+  new CountQueuingStrategy(highWaterMarkObjectGetter);
+
+}, 'CountQueuingStrategy constructor behaves as expected with strange arguments');
+
+
+test(() => {
+
+  const thisValue = null;
+  const chunk = {
+    get byteLength() {
+      throw new TypeError('shouldn\'t be called');
+    }
+  };
+
+  assert_equals(CountQueuingStrategy.prototype.size.call(thisValue, chunk), 1);
+
+}, 'CountQueuingStrategy.prototype.size should work generically on its this and its arguments');
+
+test(() => {
+
+  const size = 1024;
+  const chunk = { byteLength: size };
+  const chunkGetter = {
+    get byteLength() { return size; }
+  };
+  const error = new Error('wow!');
+  const chunkGetterThrowing = {
+    get byteLength() { throw error; }
+  };
+
+  assert_equals(CountQueuingStrategy.prototype.size(), 1, 'size returns 1 with undefined');
+  assert_equals(CountQueuingStrategy.prototype.size(null), 1, 'size returns 1 with null');
+  assert_equals(CountQueuingStrategy.prototype.size('potato'), 1, 'size returns 1 with non-object type');
+  assert_equals(CountQueuingStrategy.prototype.size({}), 1, 'size returns 1 with empty object');
+  assert_equals(CountQueuingStrategy.prototype.size(chunk), 1, 'size returns 1 with a chunk');
+  assert_equals(CountQueuingStrategy.prototype.size(chunkGetter), 1, 'size returns 1 with chunk getter');
+  assert_equals(CountQueuingStrategy.prototype.size(chunkGetterThrowing), 1,
+    'size returns 1 with chunk getter that throws');
+
+}, 'CountQueuingStrategy size behaves as expected with strange arguments');
+
+test(() => {
+
+  const strategy = new CountQueuingStrategy({ highWaterMark: 4 });
+
+  assert_object_equals(Object.getOwnPropertyDescriptor(strategy, 'highWaterMark'),
+    { value: 4, writable: true, enumerable: true, configurable: true },
+    'highWaterMark property should be a data property with the value passed the constructor');
+  assert_equals(typeof strategy.size, 'function');
+
+}, 'CountQueuingStrategy instances have the correct properties');
+
+test(() => {
+
+  const strategy = new CountQueuingStrategy({ highWaterMark: 4 });
+  assert_equals(strategy.highWaterMark, 4);
+
+  strategy.highWaterMark = 10;
+  assert_equals(strategy.highWaterMark, 10);
+
+  strategy.highWaterMark = 'banana';
+  assert_equals(strategy.highWaterMark, 'banana');
+
+}, 'CountQueuingStrategy\'s highWaterMark property can be set to anything');
+
+done();
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/bad-strategies.https.html b/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/bad-strategies.https.html
new file mode 100644
index 0000000..6613ab7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/bad-strategies.https.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+<script src="../resources/test-initializer.js"></script>
+
+<script src="bad-strategies.js"></script>
+<script>
+'use strict';
+worker_test('bad-strategies.js');
+</script>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/bad-strategies.js b/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/bad-strategies.js
new file mode 100644
index 0000000..5a52d60
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/bad-strategies.js
@@ -0,0 +1,164 @@
+'use strict';
+
+if (self.importScripts) {
+  self.importScripts('/resources/testharness.js');
+}
+
+test(() => {
+
+  const theError = new Error('a unique string');
+
+  assert_throws(theError, () => {
+    new ReadableStream({}, {
+      get size() {
+        throw theError;
+      },
+      highWaterMark: 5
+    });
+  }, 'construction should re-throw the error');
+
+}, 'Readable stream: throwing strategy.size getter');
+
+promise_test(t => {
+
+  const controllerError = { name: 'controller error' };
+  const thrownError = { name: 'thrown error' };
+
+  let controller;
+  const rs = new ReadableStream(
+    {
+      start(c) {
+        controller = c;
+      }
+    },
+    {
+      size() {
+        controller.error(controllerError);
+        throw thrownError;
+      },
+      highWaterMark: 5
+    }
+  );
+
+  assert_throws(thrownError, () => controller.enqueue('a'), 'enqueue should re-throw the error');
+
+  return promise_rejects(t, controllerError, rs.getReader().closed);
+
+}, 'Readable stream: strategy.size errors the stream and then throws');
+
+promise_test(t => {
+
+  const theError = { name: 'my error' };
+
+  let controller;
+  const rs = new ReadableStream(
+    {
+      start(c) {
+        controller = c;
+      }
+    },
+    {
+      size() {
+        controller.error(theError);
+        return Infinity;
+      },
+      highWaterMark: 5
+    }
+  );
+
+  assert_throws(new RangeError(), () => controller.enqueue('a'), 'enqueue should throw a RangeError');
+
+  return promise_rejects(t, theError, rs.getReader().closed, 'closed should reject with the error');
+
+}, 'Readable stream: strategy.size errors the stream and then returns Infinity');
+
+promise_test(() => {
+
+  const theError = new Error('a unique string');
+  const rs = new ReadableStream(
+    {
+      start(c) {
+        assert_throws(theError, () => c.enqueue('a'), 'enqueue should throw the error');
+      }
+    },
+    {
+      size() {
+        throw theError;
+      },
+      highWaterMark: 5
+    }
+  );
+
+  return rs.getReader().closed.catch(e => {
+    assert_equals(e, theError, 'closed should reject with the error');
+  });
+
+}, 'Readable stream: throwing strategy.size method');
+
+test(() => {
+
+  const theError = new Error('a unique string');
+
+  assert_throws(theError, () => {
+    new ReadableStream({}, {
+      size() {
+        return 1;
+      },
+      get highWaterMark() {
+        throw theError;
+      }
+    });
+  }, 'construction should re-throw the error');
+
+}, 'Readable stream: throwing strategy.highWaterMark getter');
+
+test(() => {
+
+  for (const highWaterMark of [-1, -Infinity, NaN, 'foo', {}]) {
+    assert_throws(new RangeError(), () => {
+      new ReadableStream({}, {
+        size() {
+          return 1;
+        },
+        highWaterMark
+      });
+    }, 'construction should throw a RangeError for ' + highWaterMark);
+  }
+
+}, 'Readable stream: invalid strategy.highWaterMark');
+
+promise_test(() => {
+
+  const promises = [];
+  for (const size of [NaN, -Infinity, Infinity, -1]) {
+    let theError;
+    const rs = new ReadableStream(
+      {
+        start(c) {
+          try {
+            c.enqueue('hi');
+            assert_unreached('enqueue didn\'t throw');
+          } catch (error) {
+            assert_equals(error.name, 'RangeError', 'enqueue should throw a RangeError for ' + size);
+            theError = error;
+          }
+        }
+      },
+      {
+        size() {
+          return size;
+        },
+        highWaterMark: 5
+      }
+    );
+
+    promises.push(rs.getReader().closed.catch(e => {
+      assert_equals(e, theError, 'closed should reject with the error for ' + size);
+    }));
+  }
+
+  return Promise.all(promises);
+
+}, 'Readable stream: invalid strategy.size return value');
+
+done();
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/bad-underlying-sources.https.html b/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/bad-underlying-sources.https.html
new file mode 100644
index 0000000..2b779761
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/bad-underlying-sources.https.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+<script src="../resources/test-initializer.js"></script>
+
+<script src="bad-underlying-sources.js"></script>
+<script>
+'use strict';
+worker_test('bad-underlying-sources.js');
+</script>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/bad-underlying-sources.js b/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/bad-underlying-sources.js
new file mode 100644
index 0000000..b95b54bc
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/bad-underlying-sources.js
@@ -0,0 +1,383 @@
+'use strict';
+
+if (self.importScripts) {
+  self.importScripts('/resources/testharness.js');
+}
+
+
+test(() => {
+
+  const theError = new Error('a unique string');
+
+  assert_throws(theError, () => {
+    new ReadableStream({
+      get start() {
+        throw theError;
+      }
+    });
+  }, 'constructing the stream should re-throw the error');
+
+}, 'Underlying source start: throwing getter');
+
+
+test(() => {
+
+  const theError = new Error('a unique string');
+
+  assert_throws(theError, () => {
+    new ReadableStream({
+      start() {
+        throw theError;
+      }
+    });
+  }, 'constructing the stream should re-throw the error');
+
+}, 'Underlying source start: throwing method');
+
+
+promise_test(t => {
+
+  const theError = new Error('a unique string');
+  const rs = new ReadableStream({
+    get pull() {
+      throw theError;
+    }
+  });
+
+  return promise_rejects(t, theError, rs.getReader().closed);
+
+}, 'Underlying source: throwing pull getter (initial pull)');
+
+
+promise_test(t => {
+
+  const theError = new Error('a unique string');
+  const rs = new ReadableStream({
+    pull() {
+      throw theError;
+    }
+  });
+
+  return promise_rejects(t, theError, rs.getReader().closed);
+
+}, 'Underlying source: throwing pull method (initial pull)');
+
+
+promise_test(t => {
+
+  const theError = new Error('a unique string');
+
+  let counter = 0;
+  const rs = new ReadableStream({
+    get pull() {
+      ++counter;
+      if (counter === 1) {
+        return c => c.enqueue('a');
+      }
+
+      throw theError;
+    }
+  });
+  const reader = rs.getReader();
+
+  return Promise.all([
+    reader.read().then(r => {
+      assert_object_equals(r, { value: 'a', done: false }, 'the chunk read should be correct');
+    }),
+    promise_rejects(t, theError, reader.closed)
+  ]);
+
+}, 'Underlying source pull: throwing getter (second pull)');
+
+
+promise_test(t => {
+
+  const theError = new Error('a unique string');
+
+  let counter = 0;
+  const rs = new ReadableStream({
+    pull(c) {
+      ++counter;
+      if (counter === 1) {
+        c.enqueue('a');
+        return;
+      }
+
+      throw theError;
+    }
+  });
+  const reader = rs.getReader();
+
+  return Promise.all([
+    reader.read().then(r => {
+      assert_object_equals(r, { value: 'a', done: false }, 'the chunk read should be correct');
+    }),
+    promise_rejects(t, theError, reader.closed)
+  ]);
+
+}, 'Underlying source pull: throwing method (second pull)');
+
+promise_test(t => {
+
+  const theError = new Error('a unique string');
+  const rs = new ReadableStream({
+    get cancel() {
+      throw theError;
+    }
+  });
+
+  return promise_rejects(t, theError, rs.cancel());
+
+}, 'Underlying source cancel: throwing getter');
+
+promise_test(t => {
+
+  const theError = new Error('a unique string');
+  const rs = new ReadableStream({
+    cancel() {
+      throw theError;
+    }
+  });
+
+  return promise_rejects(t, theError, rs.cancel());
+
+}, 'Underlying source cancel: throwing method');
+
+promise_test(() => {
+
+  let controller;
+  const rs = new ReadableStream({
+    start(c) {
+      controller = c;
+    }
+  });
+
+  rs.cancel();
+  assert_throws(new TypeError, () => controller.enqueue('a'), 'Calling enqueue after canceling should throw');
+
+  return rs.getReader().closed;
+
+}, 'Underlying source: calling enqueue on an empty canceled stream should throw');
+
+promise_test(() => {
+
+  let controller;
+  const rs = new ReadableStream({
+    start(c) {
+      c.enqueue('a');
+      c.enqueue('b');
+      controller = c;
+    }
+  });
+
+  rs.cancel();
+  assert_throws(new TypeError, () => controller.enqueue('c'), 'Calling enqueue after canceling should throw');
+
+  return rs.getReader().closed;
+
+}, 'Underlying source: calling enqueue on a non-empty canceled stream should throw');
+
+promise_test(() => {
+
+  return new ReadableStream({
+    start(c) {
+      c.close();
+      assert_throws(new TypeError(), () => c.enqueue('a'), 'call to enqueue should throw a TypeError');
+    }
+  }).getReader().closed;
+
+}, 'Underlying source: calling enqueue on a closed stream should throw');
+
+promise_test(t => {
+
+  const theError = new Error('boo');
+  const closed = new ReadableStream({
+    start(c) {
+      c.error(theError);
+      assert_throws(new TypeError(), () => c.enqueue('a'), 'call to enqueue should throw the error');
+    }
+  }).getReader().closed;
+
+  return promise_rejects(t, theError, closed);
+
+}, 'Underlying source: calling enqueue on an errored stream should throw');
+
+promise_test(() => {
+
+  return new ReadableStream({
+    start(c) {
+      c.close();
+      assert_throws(new TypeError(), () => c.close(), 'second call to close should throw a TypeError');
+    }
+  }).getReader().closed;
+
+}, 'Underlying source: calling close twice on an empty stream should throw the second time');
+
+promise_test(() => {
+
+  let startCalled = false;
+  let readCalled = false;
+  const reader = new ReadableStream({
+    start(c) {
+      c.enqueue('a');
+      c.close();
+      assert_throws(new TypeError(), () => c.close(), 'second call to close should throw a TypeError');
+      startCalled = true;
+    }
+  }).getReader();
+
+  return Promise.all([
+    reader.read().then(r => {
+      assert_object_equals(r, { value: 'a', done: false }, 'read() should read the enqueued chunk');
+      readCalled = true;
+    }),
+    reader.closed.then(() => {
+      assert_true(startCalled);
+      assert_true(readCalled);
+    })
+  ]);
+
+}, 'Underlying source: calling close twice on a non-empty stream should throw the second time');
+
+promise_test(() => {
+
+  let controller;
+  let startCalled = false;
+  const rs = new ReadableStream({
+    start(c) {
+      controller = c;
+      startCalled = true;
+    }
+  });
+
+  rs.cancel();
+  assert_throws(new TypeError(), () => controller.close(), 'Calling close after canceling should throw');
+
+  return rs.getReader().closed.then(() => {
+    assert_true(startCalled);
+  });
+
+}, 'Underlying source: calling close on an empty canceled stream should throw');
+
+promise_test(() => {
+
+  let controller;
+  let startCalled = false;
+  const rs = new ReadableStream({
+    start(c) {
+      controller = c;
+      c.enqueue('a');
+      startCalled = true;
+    }
+  });
+
+  rs.cancel();
+  assert_throws(new TypeError(), () => controller.close(), 'Calling close after canceling should throw');
+
+  return rs.getReader().closed.then(() => {
+    assert_true(startCalled);
+  });
+
+}, 'Underlying source: calling close on a non-empty canceled stream should throw');
+
+promise_test(() => {
+
+  const theError = new Error('boo');
+  let startCalled = false;
+
+  const closed = new ReadableStream({
+    start(c) {
+      c.error(theError);
+      assert_throws(new TypeError(), () => c.close(), 'call to close should throw a TypeError');
+      startCalled = true;
+    }
+  }).getReader().closed;
+
+  return closed.catch(e => {
+    assert_true(startCalled);
+    assert_equals(e, theError, 'closed should reject with the error');
+  });
+
+}, 'Underlying source: calling close after error should throw');
+
+promise_test(() => {
+
+  const theError = new Error('boo');
+  let startCalled = false;
+
+  const closed = new ReadableStream({
+    start(c) {
+      c.error(theError);
+      assert_throws(new TypeError(), () => c.error(), 'second call to error should throw a TypeError');
+      startCalled = true;
+    }
+  }).getReader().closed;
+
+  return closed.catch(e => {
+    assert_true(startCalled);
+    assert_equals(e, theError, 'closed should reject with the error');
+  });
+
+}, 'Underlying source: calling error twice should throw the second time');
+
+promise_test(() => {
+
+  let startCalled = false;
+
+  const closed = new ReadableStream({
+    start(c) {
+      c.close();
+      assert_throws(new TypeError(), () => c.error(), 'second call to error should throw a TypeError');
+      startCalled = true;
+    }
+  }).getReader().closed;
+
+  return closed.then(() => assert_true(startCalled));
+
+}, 'Underlying source: calling error after close should throw');
+
+promise_test(() => {
+
+  let startCalled = false;
+  const firstError = new Error('1');
+  const secondError = new Error('2');
+
+  const closed = new ReadableStream({
+    start(c) {
+      c.error(firstError);
+      startCalled = true;
+      return Promise.reject(secondError);
+    }
+  }).getReader().closed;
+
+  return closed.catch(e => {
+    assert_true(startCalled);
+    assert_equals(e, firstError, 'closed should reject with the first error');
+  });
+
+}, 'Underlying source: calling error and returning a rejected promise from start should cause the stream to error ' +
+   'with the first error');
+
+promise_test(() => {
+
+  let startCalled = false;
+  const firstError = new Error('1');
+  const secondError = new Error('2');
+
+  const closed = new ReadableStream({
+    pull(c) {
+      c.error(firstError);
+      startCalled = true;
+      return Promise.reject(secondError);
+    }
+  }).getReader().closed;
+
+  return closed.catch(e => {
+    assert_true(startCalled);
+    assert_equals(e, firstError, 'closed should reject with the first error');
+  });
+
+}, 'Underlying source: calling error and returning a rejected promise from pull should cause the stream to error ' +
+   'with the first error');
+
+done();
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/brand-checks.https.html b/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/brand-checks.https.html
new file mode 100644
index 0000000..ef5f326e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/brand-checks.https.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+<script src="../resources/test-initializer.js"></script>
+
+<script src="../resources/test-utils.js"></script>
+<script src="brand-checks.js"></script>
+<script>
+'use strict';
+worker_test('brand-checks.js');
+</script>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/brand-checks.js b/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/brand-checks.js
new file mode 100644
index 0000000..32553cf9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/brand-checks.js
@@ -0,0 +1,151 @@
+'use strict';
+
+if (self.importScripts) {
+  self.importScripts('../resources/test-utils.js');
+  self.importScripts('/resources/testharness.js');
+}
+
+let ReadableStreamReader;
+let ReadableStreamController;
+
+test(() => {
+
+  // It's not exposed globally, but we test a few of its properties here.
+  ReadableStreamReader = (new ReadableStream()).getReader().constructor;
+
+}, 'Can get the ReadableStreamReader constructor indirectly');
+
+test(() => {
+
+  // It's not exposed globally, but we test a few of its properties here.
+  new ReadableStream({
+    start(c) {
+      ReadableStreamController = c.constructor;
+    }
+  });
+
+}, 'Can get the ReadableStreamController constructor indirectly');
+
+function fakeReadableStream() {
+  return {
+    cancel() { return Promise.resolve(); },
+    getReader() { return new ReadableStreamReader(new ReadableStream()); },
+    pipeThrough(obj) { return obj.readable; },
+    pipeTo() { return Promise.resolve(); },
+    tee() { return [realReadableStream(), realReadableStream()]; }
+  };
+}
+
+function realReadableStream() {
+  return new ReadableStream();
+}
+
+function fakeReadableStreamReader() {
+  return {
+    get closed() { return Promise.resolve(); },
+    cancel() { return Promise.resolve(); },
+    read() { return Promise.resolve({ value: undefined, done: true }); },
+    releaseLock() { return; }
+  };
+}
+
+function fakeReadableStreamController() {
+  return {
+    close() { },
+    enqueue() { },
+    error() { }
+  };
+}
+
+promise_test(t => {
+
+  return methodRejects(t, ReadableStream.prototype, 'cancel', fakeReadableStream());
+
+}, 'ReadableStream.prototype.cancel enforces a brand check');
+
+test(() => {
+
+  methodThrows(ReadableStream.prototype, 'getReader', fakeReadableStream());
+
+}, 'ReadableStream.prototype.getReader enforces a brand check');
+
+test(() => {
+
+  methodThrows(ReadableStream.prototype, 'tee', fakeReadableStream());
+
+}, 'ReadableStream.prototype.tee enforces a brand check');
+
+test(() => {
+
+  assert_throws(new TypeError(), () => new ReadableStreamReader(fakeReadableStream()),
+                'Constructing a ReadableStreamReader should throw');
+
+}, 'ReadableStreamReader enforces a brand check on its argument');
+
+promise_test(t => {
+
+  return Promise.all([
+    getterRejects(t, ReadableStreamReader.prototype, 'closed', fakeReadableStreamReader()),
+    getterRejects(t, ReadableStreamReader.prototype, 'closed', realReadableStream())
+  ]);
+
+}, 'ReadableStreamReader.prototype.closed enforces a brand check');
+
+promise_test(t => {
+
+  return Promise.all([
+    methodRejects(t, ReadableStreamReader.prototype, 'cancel', fakeReadableStreamReader()),
+    methodRejects(t, ReadableStreamReader.prototype, 'cancel', realReadableStream())
+  ]);
+
+}, 'ReadableStreamReader.prototype.cancel enforces a brand check');
+
+promise_test(t => {
+
+  return Promise.all([
+    methodRejects(t, ReadableStreamReader.prototype, 'read', fakeReadableStreamReader()),
+    methodRejects(t, ReadableStreamReader.prototype, 'read', realReadableStream())
+  ]);
+
+}, 'ReadableStreamReader.prototype.read enforces a brand check');
+
+test(() => {
+
+  methodThrows(ReadableStreamReader.prototype, 'releaseLock', fakeReadableStreamReader());
+  methodThrows(ReadableStreamReader.prototype, 'releaseLock', realReadableStream());
+
+}, 'ReadableStreamReader.prototype.releaseLock enforces a brand check');
+
+test(() => {
+
+  assert_throws(new TypeError(), () => new ReadableStreamController(fakeReadableStream()),
+                'Constructing a ReadableStreamController should throw');
+
+}, 'ReadableStreamController enforces a brand check on its argument');
+
+test(() => {
+
+  assert_throws(new TypeError(), () => new ReadableStreamController(realReadableStream()),
+                'Constructing a ReadableStreamController should throw');
+
+}, 'ReadableStreamController can\'t be given a fully-constructed ReadableStream');
+
+test(() => {
+
+  methodThrows(ReadableStreamController.prototype, 'close', fakeReadableStreamController());
+
+}, 'ReadableStreamController.prototype.close enforces a brand check');
+
+test(() => {
+
+  methodThrows(ReadableStreamController.prototype, 'enqueue', fakeReadableStreamController());
+
+}, 'ReadableStreamController.prototype.enqueue enforces a brand check');
+
+test(() => {
+
+  methodThrows(ReadableStreamController.prototype, 'error', fakeReadableStreamController());
+
+}, 'ReadableStreamController.prototype.error enforces a brand check');
+
+done();
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/cancel.https.html b/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/cancel.https.html
new file mode 100644
index 0000000..e43c620
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/cancel.https.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+<script src="../resources/test-initializer.js"></script>
+
+<script src="../resources/test-utils.js"></script>
+<script src="../resources/rs-utils.js"></script>
+<script src="cancel.js"></script>
+<script>
+'use strict';
+worker_test('cancel.js');
+</script>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/cancel.js b/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/cancel.js
new file mode 100644
index 0000000..3857c8e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/cancel.js
@@ -0,0 +1,241 @@
+'use strict';
+
+if (self.importScripts) {
+  self.importScripts('../resources/test-utils.js');
+  self.importScripts('../resources/rs-utils.js');
+  self.importScripts('/resources/testharness.js');
+}
+
+promise_test(() => {
+
+  const randomSource = new RandomPushSource();
+
+  let cancellationFinished = false;
+  const rs = new ReadableStream({
+    start(c) {
+      randomSource.ondata = c.enqueue.bind(c);
+      randomSource.onend = c.close.bind(c);
+      randomSource.onerror = c.error.bind(c);
+    },
+
+    pull() {
+      randomSource.readStart();
+    },
+
+    cancel() {
+      randomSource.readStop();
+
+      return new Promise(resolve => {
+        setTimeout(() => {
+          cancellationFinished = true;
+          resolve();
+        }, 1);
+      });
+    }
+  });
+
+  const reader = rs.getReader();
+
+  // We call delay multiple times to avoid cancelling too early for the
+  // source to enqueue at least one chunk.
+  const cancel = delay(5).then(() => delay(5)).then(() => delay(5)).then(() => {
+    let cancelPromise = reader.cancel();
+    assert_false(cancellationFinished, 'cancellation in source should happen later');
+    return cancelPromise;
+  })
+
+  return readableStreamToArray(rs, reader).then(chunks => {
+    assert_greater_than(chunks.length, 0, 'at least one chunk should be read');
+    for (let i = 0; i < chunks.length; i++) {
+      assert_equals(chunks[i].length, 128, 'chunk ' + i + ' should have 128 bytes');
+    }
+    return cancel;
+  }).then(() => {
+    assert_true(cancellationFinished, 'it returns a promise that is fulfilled when the cancellation finishes');
+  });
+
+}, 'ReadableStream cancellation: integration test on an infinite stream derived from a random push source');
+
+test(() => {
+
+  let recordedReason;
+  const rs = new ReadableStream({
+    cancel(reason) {
+      recordedReason = reason;
+    }
+  });
+
+  const passedReason = new Error('Sorry, it just wasn\'t meant to be.');
+  rs.cancel(passedReason);
+
+  assert_equals(recordedReason, passedReason,
+    'the error passed to the underlying source\'s cancel method should equal the one passed to the stream\'s cancel');
+
+}, 'ReadableStream cancellation: cancel(reason) should pass through the given reason to the underlying source');
+
+promise_test(() => {
+
+  const rs = new ReadableStream({
+    start(c) {
+      c.enqueue('a');
+      c.close();
+    },
+    cancel() {
+      assert_unreached('underlying source cancel() should not have been called');
+    }
+  });
+
+  const reader = rs.getReader();
+
+  return rs.cancel().then(() => {
+    assert_unreached('cancel() should be rejected');
+  }, e => {
+    assert_equals(e.name, 'TypeError', 'cancel() should be rejected with a TypeError');
+  }).then(() => {
+    return reader.read();
+  }).then(result => {
+    assert_object_equals(result, { value: 'a', done: false }, 'read() should still work after the attempted cancel');
+    return reader.closed;
+  });
+
+}, 'ReadableStream cancellation: cancel() on a locked stream should fail and not call the underlying source cancel');
+
+promise_test(() => {
+
+  let cancelReceived = false;
+  const cancelReason = new Error('I am tired of this stream, I prefer to cancel it');
+  const rs = new ReadableStream({
+    cancel(reason) {
+      cancelReceived = true;
+      assert_equals(reason, cancelReason, 'cancellation reason given to the underlying source should be equal to the one passed');
+    }
+  });
+
+  return rs.cancel(cancelReason).then(() => {
+    assert_true(cancelReceived);
+  });
+
+}, 'ReadableStream cancellation: should fulfill promise when cancel callback went fine');
+
+promise_test(() => {
+
+  const rs = new ReadableStream({
+    cancel() {
+      return 'Hello';
+    }
+  });
+
+  return rs.cancel().then(v => {
+    assert_equals(v, undefined, 'cancel() return value should be fulfilled with undefined');
+  });
+
+}, 'ReadableStream cancellation: returning a value from the underlying source\'s cancel should not affect the fulfillment value of the promise returned by the stream\'s cancel');
+
+promise_test(() => {
+
+  const thrownError = new Error('test');
+  let cancelCalled = false;
+
+  const rs = new ReadableStream({
+    cancel() {
+      cancelCalled = true;
+      throw thrownError;
+    }
+  });
+
+  return rs.cancel('test').then(() => {
+    assert_unreached('cancel should reject');
+  }, e => {
+    assert_true(cancelCalled);
+    assert_equals(e, thrownError);
+  });
+
+}, 'ReadableStream cancellation: should reject promise when cancel callback raises an exception');
+
+promise_test(() => {
+
+  const cancelReason = new Error('test');
+
+  const rs = new ReadableStream({
+    cancel(error) {
+      assert_equals(error, cancelReason);
+      return delay(1);
+    }
+  });
+
+  return rs.cancel(cancelReason);
+
+}, 'ReadableStream cancellation: if the underlying source\'s cancel method returns a promise, the promise returned by the stream\'s cancel should fulfill when that one does (1)');
+
+promise_test(() => {
+
+  let resolveSourceCancelPromise;
+  let sourceCancelPromiseHasFulfilled = false;
+
+  const rs = new ReadableStream({
+    cancel() {
+      const sourceCancelPromise = new Promise(resolve => resolveSourceCancelPromise = resolve);
+
+      sourceCancelPromise.then(() => {
+        sourceCancelPromiseHasFulfilled = true;
+      });
+
+      return sourceCancelPromise;
+    }
+  });
+
+  setTimeout(() => resolveSourceCancelPromise('Hello'), 1);
+
+  return rs.cancel().then(value => {
+    assert_true(sourceCancelPromiseHasFulfilled, 'cancel() return value should be fulfilled only after the promise returned by the underlying source\'s cancel');
+    assert_equals(value, undefined, 'cancel() return value should be fulfilled with undefined');
+  });
+
+}, 'ReadableStream cancellation: if the underlying source\'s cancel method returns a promise, the promise returned by the stream\'s cancel should fulfill when that one does (2)');
+
+promise_test(() => {
+
+  let rejectSourceCancelPromise;
+  let sourceCancelPromiseHasRejected = false;
+
+  const rs = new ReadableStream({
+    cancel() {
+      const sourceCancelPromise = new Promise((resolve, reject) => rejectSourceCancelPromise = reject);
+
+      sourceCancelPromise.catch(() => {
+        sourceCancelPromiseHasRejected = true;
+      });
+
+      return sourceCancelPromise;
+    }
+  });
+
+  const errorInCancel = new Error('Sorry, it just wasn\'t meant to be.');
+
+  setTimeout(() => rejectSourceCancelPromise(errorInCancel), 1);
+
+  return rs.cancel().then(() => {
+    assert_unreached('cancel() return value should be rejected');
+  }, r => {
+    assert_true(sourceCancelPromiseHasRejected, 'cancel() return value should be rejected only after the promise returned by the underlying source\'s cancel');
+    assert_equals(r, errorInCancel, 'cancel() return value should be rejected with the underlying source\'s rejection reason');
+  });
+
+}, 'ReadableStream cancellation: if the underlying source\'s cancel method returns a promise, the promise returned by the stream\'s cancel should reject when that one does');
+
+promise_test(() => {
+
+  const rs = new ReadableStream({
+    start() {
+      return new Promise(() => {});
+    },
+    pull() {
+      assert_unreached('pull should not have been called');
+    }
+  });
+
+  return Promise.all([rs.cancel(), rs.getReader().closed]);
+
+}, 'ReadableStream cancellation: cancelling before start finishes should prevent pull() from being called');
+
+done();
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/count-queuing-strategy-integration.https.html b/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/count-queuing-strategy-integration.https.html
new file mode 100644
index 0000000..a281f04
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/count-queuing-strategy-integration.https.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+<script src="../resources/test-initializer.js"></script>
+
+<script src="count-queuing-strategy-integration.js"></script>
+<script>
+'use strict';
+worker_test('count-queuing-strategy-integration.js');
+</script>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/count-queuing-strategy-integration.js b/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/count-queuing-strategy-integration.js
new file mode 100644
index 0000000..65c8b8cf0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/count-queuing-strategy-integration.js
@@ -0,0 +1,213 @@
+'use strict';
+
+if (self.importScripts) {
+  self.importScripts('/resources/testharness.js');
+}
+
+test(() => {
+
+  new ReadableStream({}, new CountQueuingStrategy({ highWaterMark: 4 }));
+
+}, 'Can construct a readable stream with a valid CountQueuingStrategy');
+
+promise_test(() => {
+
+  let controller;
+  const rs = new ReadableStream(
+    {
+      start(c) {
+        controller = c;
+      }
+    },
+    new CountQueuingStrategy({ highWaterMark: 0 })
+  );
+  const reader = rs.getReader();
+
+  assert_equals(controller.desiredSize, 0, '0 reads, 0 enqueues: desiredSize should be 0');
+  controller.enqueue('a');
+  assert_equals(controller.desiredSize, -1, '0 reads, 1 enqueue: desiredSize should be -1');
+  controller.enqueue('b');
+  assert_equals(controller.desiredSize, -2, '0 reads, 2 enqueues: desiredSize should be -2');
+  controller.enqueue('c');
+  assert_equals(controller.desiredSize, -3, '0 reads, 3 enqueues: desiredSize should be -3');
+  controller.enqueue('d');
+  assert_equals(controller.desiredSize, -4, '0 reads, 4 enqueues: desiredSize should be -4');
+
+  return reader.read()
+    .then(result => {
+      assert_object_equals(result, { value: 'a', done: false },
+                           '1st read gives back the 1st chunk enqueued (queue now contains 3 chunks)');
+      return reader.read();
+    })
+    .then(result => {
+      assert_object_equals(result, { value: 'b', done: false },
+                           '2nd read gives back the 2nd chunk enqueued (queue now contains 2 chunks)');
+      return reader.read();
+    })
+    .then(result => {
+      assert_object_equals(result, { value: 'c', done: false },
+                           '3rd read gives back the 3rd chunk enqueued (queue now contains 1 chunk)');
+
+      assert_equals(controller.desiredSize, -1, '3 reads, 4 enqueues: desiredSize should be -1');
+      controller.enqueue('e');
+      assert_equals(controller.desiredSize, -2, '3 reads, 5 enqueues: desiredSize should be -2');
+
+      return reader.read();
+    })
+    .then(result => {
+      assert_object_equals(result, { value: 'd', done: false },
+                           '4th read gives back the 4th chunk enqueued (queue now contains 1 chunks)');
+      return reader.read();
+
+    }).then(result => {
+      assert_object_equals(result, { value: 'e', done: false },
+                           '5th read gives back the 5th chunk enqueued (queue now contains 0 chunks)');
+
+      assert_equals(controller.desiredSize, 0, '5 reads, 5 enqueues: desiredSize should be 0');
+      controller.enqueue('f');
+      assert_equals(controller.desiredSize, -1, '5 reads, 6 enqueues: desiredSize should be -1');
+      controller.enqueue('g');
+      assert_equals(controller.desiredSize, -2, '5 reads, 7 enqueues: desiredSize should be -2');
+    });
+
+}, 'Correctly governs a ReadableStreamController\'s desiredSize property (HWM = 0)');
+
+promise_test(() => {
+
+  let controller;
+  const rs = new ReadableStream(
+    {
+      start(c) {
+        controller = c;
+      }
+    },
+    new CountQueuingStrategy({ highWaterMark: 1 })
+  );
+  const reader = rs.getReader();
+
+  assert_equals(controller.desiredSize, 1, '0 reads, 0 enqueues: desiredSize should be 1');
+  controller.enqueue('a');
+  assert_equals(controller.desiredSize, 0, '0 reads, 1 enqueue: desiredSize should be 0');
+  controller.enqueue('b');
+  assert_equals(controller.desiredSize, -1, '0 reads, 2 enqueues: desiredSize should be -1');
+  controller.enqueue('c');
+  assert_equals(controller.desiredSize, -2, '0 reads, 3 enqueues: desiredSize should be -2');
+  controller.enqueue('d');
+  assert_equals(controller.desiredSize, -3, '0 reads, 4 enqueues: desiredSize should be -3');
+
+  return reader.read()
+    .then(result => {
+      assert_object_equals(result, { value: 'a', done: false },
+                           '1st read gives back the 1st chunk enqueued (queue now contains 3 chunks)');
+      return reader.read();
+    })
+    .then(result => {
+      assert_object_equals(result, { value: 'b', done: false },
+                           '2nd read gives back the 2nd chunk enqueued (queue now contains 2 chunks)');
+      return reader.read();
+    })
+    .then(result => {
+      assert_object_equals(result, { value: 'c', done: false },
+                           '3rd read gives back the 3rd chunk enqueued (queue now contains 1 chunk)');
+
+      assert_equals(controller.desiredSize, 0, '3 reads, 4 enqueues: desiredSize should be 0');
+      controller.enqueue('e');
+      assert_equals(controller.desiredSize, -1, '3 reads, 5 enqueues: desiredSize should be -1');
+
+      return reader.read();
+    })
+    .then(result => {
+      assert_object_equals(result, { value: 'd', done: false },
+                           '4th read gives back the 4th chunk enqueued (queue now contains 1 chunks)');
+      return reader.read();
+    })
+    .then(result => {
+      assert_object_equals(result, { value: 'e', done: false },
+                           '5th read gives back the 5th chunk enqueued (queue now contains 0 chunks)');
+
+      assert_equals(controller.desiredSize, 1, '5 reads, 5 enqueues: desiredSize should be 1');
+      controller.enqueue('f');
+      assert_equals(controller.desiredSize, 0, '5 reads, 6 enqueues: desiredSize should be 0');
+      controller.enqueue('g');
+      assert_equals(controller.desiredSize, -1, '5 reads, 7 enqueues: desiredSize should be -1');
+    });
+
+}, 'Correctly governs a ReadableStreamController\'s desiredSize property (HWM = 1)');
+
+promise_test(() => {
+
+  let controller;
+  const rs = new ReadableStream(
+    {
+      start(c) {
+        controller = c;
+      }
+    },
+    new CountQueuingStrategy({ highWaterMark: 4 })
+  );
+  const reader = rs.getReader();
+
+  assert_equals(controller.desiredSize, 4, '0 reads, 0 enqueues: desiredSize should be 4');
+  controller.enqueue('a');
+  assert_equals(controller.desiredSize, 3, '0 reads, 1 enqueue: desiredSize should be 3');
+  controller.enqueue('b');
+  assert_equals(controller.desiredSize, 2, '0 reads, 2 enqueues: desiredSize should be 2');
+  controller.enqueue('c');
+  assert_equals(controller.desiredSize, 1, '0 reads, 3 enqueues: desiredSize should be 1');
+  controller.enqueue('d');
+  assert_equals(controller.desiredSize, 0, '0 reads, 4 enqueues: desiredSize should be 0');
+  controller.enqueue('e');
+  assert_equals(controller.desiredSize, -1, '0 reads, 5 enqueues: desiredSize should be -1');
+  controller.enqueue('f');
+  assert_equals(controller.desiredSize, -2, '0 reads, 6 enqueues: desiredSize should be -2');
+
+
+  return reader.read()
+    .then(result => {
+      assert_object_equals(result, { value: 'a', done: false },
+                           '1st read gives back the 1st chunk enqueued (queue now contains 5 chunks)');
+      return reader.read();
+    })
+    .then(result => {
+      assert_object_equals(result, { value: 'b', done: false },
+                           '2nd read gives back the 2nd chunk enqueued (queue now contains 4 chunks)');
+
+      assert_equals(controller.desiredSize, 0, '2 reads, 6 enqueues: desiredSize should be 0');
+      controller.enqueue('g');
+      assert_equals(controller.desiredSize, -1, '2 reads, 7 enqueues: desiredSize should be -1');
+
+      return reader.read();
+    })
+    .then(result => {
+      assert_object_equals(result, { value: 'c', done: false },
+                           '3rd read gives back the 3rd chunk enqueued (queue now contains 4 chunks)');
+      return reader.read();
+    })
+    .then(result => {
+      assert_object_equals(result, { value: 'd', done: false },
+                           '4th read gives back the 4th chunk enqueued (queue now contains 3 chunks)');
+      return reader.read();
+    })
+    .then(result => {
+      assert_object_equals(result, { value: 'e', done: false },
+                           '5th read gives back the 5th chunk enqueued (queue now contains 2 chunks)');
+      return reader.read();
+    })
+    .then(result => {
+      assert_object_equals(result, { value: 'f', done: false },
+                           '6th read gives back the 6th chunk enqueued (queue now contains 0 chunks)');
+
+      assert_equals(controller.desiredSize, 3, '6 reads, 7 enqueues: desiredSize should be 3');
+      controller.enqueue('h');
+      assert_equals(controller.desiredSize, 2, '6 reads, 8 enqueues: desiredSize should be 2');
+      controller.enqueue('i');
+      assert_equals(controller.desiredSize, 1, '6 reads, 9 enqueues: desiredSize should be 1');
+      controller.enqueue('j');
+      assert_equals(controller.desiredSize, 0, '6 reads, 10 enqueues: desiredSize should be 0');
+      controller.enqueue('k');
+      assert_equals(controller.desiredSize, -1, '6 reads, 11 enqueues: desiredSize should be -1');
+    });
+
+}, 'Correctly governs a ReadableStreamController\'s desiredSize property (HWM = 4)');
+
+done();
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/garbage-collection.https.html b/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/garbage-collection.https.html
new file mode 100644
index 0000000..9215eb7d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/garbage-collection.https.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+<script src="../resources/test-initializer.js"></script>
+
+<script src="../resources/test-utils.js"></script>
+<script src="garbage-collection.js"></script>
+<script>
+'use strict';
+worker_test('garbage-collection.js');
+</script>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/garbage-collection.js b/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/garbage-collection.js
new file mode 100644
index 0000000..fb00c94
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/garbage-collection.js
@@ -0,0 +1,75 @@
+'use strict';
+
+if (self.importScripts) {
+  self.importScripts('../resources/test-utils.js');
+  self.importScripts('/resources/testharness.js');
+}
+
+promise_test(() => {
+
+  let controller;
+  new ReadableStream({
+    start(c) {
+      controller = c;
+    }
+  });
+
+  garbageCollect();
+
+  return delay(50).then(() => {
+    controller.close();
+    assert_throws(new TypeError(), () => controller.close(), 'close should throw a TypeError the second time');
+    assert_throws(new TypeError(), () => controller.error(), 'error should throw a TypeError on a closed stream');
+  });
+
+}, 'ReadableStreamController methods should continue working properly when scripts lose their reference to the ' +
+   'readable stream');
+
+promise_test(() => {
+
+  let controller;
+
+  const closedPromise = new ReadableStream({
+    start(c) {
+      controller = c;
+    }
+  }).getReader().closed;
+
+  garbageCollect();
+
+  return delay(50).then(() => controller.close()).then(() => closedPromise);
+
+}, 'ReadableStream closed promise should fulfill even if the stream and reader JS references are lost');
+
+promise_test(t => {
+
+  const theError = new Error('boo');
+  let controller;
+
+  const closedPromise = new ReadableStream({
+    start(c) {
+      controller = c;
+    }
+  }).getReader().closed;
+
+  garbageCollect();
+
+  return delay(50).then(() => controller.error(theError))
+                  .then(() => promise_rejects(t, theError, closedPromise));
+
+}, 'ReadableStream closed promise should reject even if stream and reader JS references are lost');
+
+promise_test(() => {
+
+  const rs = new ReadableStream({});
+
+  rs.getReader();
+
+  garbageCollect();
+
+  return delay(50).then(() => assert_throws(new TypeError(), () => rs.getReader(),
+    'old reader should still be locking the stream even after garbage collection'));
+
+}, 'Garbage-collecting a ReadableStreamReader should not unlock its stream');
+
+done();
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/general.https.html b/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/general.https.html
new file mode 100644
index 0000000..465271a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/general.https.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+<script src="../resources/test-initializer.js"></script>
+
+<script src="../resources/test-utils.js"></script>
+<script src="../resources/rs-utils.js"></script>
+<script src="general.js"></script>
+<script>
+'use strict';
+worker_test('general.js');
+</script>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/general.js b/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/general.js
new file mode 100644
index 0000000..a8924be0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/general.js
@@ -0,0 +1,859 @@
+'use strict';
+
+if (self.importScripts) {
+  self.importScripts('../resources/test-utils.js');
+  self.importScripts('../resources/rs-utils.js');
+  self.importScripts('/resources/testharness.js');
+}
+
+test(() => {
+
+  new ReadableStream(); // ReadableStream constructed with no parameters
+  new ReadableStream({ }); // ReadableStream constructed with an empty object as parameter
+  new ReadableStream({ type: undefined }); // ReadableStream constructed with undefined type
+  new ReadableStream(undefined); // ReadableStream constructed with undefined as parameter
+
+  let x;
+  new ReadableStream(x); // ReadableStream constructed with an undefined variable as parameter
+
+}, 'ReadableStream can be constructed with no errors');
+
+test(() => {
+
+  assert_throws(new TypeError(), () => new ReadableStream(null), 'constructor should throw when the source is null');
+
+}, 'ReadableStream can\'t be constructed with garbage');
+
+test(() => {
+
+  assert_throws(new RangeError(), () => new ReadableStream({ type: null }),
+    'constructor should throw when the type is null');
+  assert_throws(new RangeError(), () => new ReadableStream({ type: '' }),
+    'constructor should throw when the type is empty string');
+  assert_throws(new RangeError(), () => new ReadableStream({ type: 'asdf' }),
+    'constructor should throw when the type is asdf');
+
+}, 'ReadableStream can\'t be constructed with an invalid type');
+
+test(() => {
+
+  const methods = ['cancel', 'constructor', 'getReader', 'pipeThrough', 'pipeTo', 'tee'];
+  const properties = methods.concat(['locked']).sort();
+
+  const rs = new ReadableStream();
+  const proto = Object.getPrototypeOf(rs);
+
+  assert_array_equals(Object.getOwnPropertyNames(proto).sort(), properties, 'should have all the correct methods');
+
+  for (const m of methods) {
+    const propDesc = Object.getOwnPropertyDescriptor(proto, m);
+    assert_false(propDesc.enumerable, 'method should be non-enumerable');
+    assert_true(propDesc.configurable, 'method should be configurable');
+    assert_true(propDesc.writable, 'method should be writable');
+    assert_equals(typeof rs[m], 'function', 'method should be a function');
+  }
+
+  const lockedPropDesc = Object.getOwnPropertyDescriptor(proto, 'locked');
+  assert_false(lockedPropDesc.enumerable, 'locked should be non-enumerable');
+  assert_equals(lockedPropDesc.writable, undefined, 'locked should not be a data property');
+  assert_equals(typeof lockedPropDesc.get, 'function', 'locked should have a getter');
+  assert_equals(lockedPropDesc.set, undefined, 'locked should not have a setter');
+  assert_true(lockedPropDesc.configurable, 'locked should be configurable');
+
+  assert_equals(rs.cancel.length, 1, 'cancel should have 1 parameter');
+  assert_equals(rs.constructor.length, 0, 'constructor should have no parameters');
+  assert_equals(rs.getReader.length, 0, 'getReader should have no parameters');
+  assert_equals(rs.pipeThrough.length, 2, 'pipeThrough should have 2 parameters');
+  assert_equals(rs.pipeTo.length, 1, 'pipeTo should have 1 parameter');
+  assert_equals(rs.tee.length, 0, 'tee should have no parameters');
+
+}, 'ReadableStream instances should have the correct list of properties');
+
+test(() => {
+
+  assert_throws(new TypeError(), () => {
+    new ReadableStream({ start: 'potato' });
+  }, 'constructor should throw when start is not a function');
+
+}, 'ReadableStream constructor should throw for non-function start arguments');
+
+test(() => {
+
+  new ReadableStream({ cancel: '2' });
+
+}, 'ReadableStream constructor can get initial garbage as cancel argument');
+
+test(() => {
+
+  new ReadableStream({ pull: { } });
+
+}, 'ReadableStream constructor can get initial garbage as pull argument');
+
+test(() => {
+
+  let startCalled = false;
+
+  const source = {
+    start(controller) {
+      assert_equals(this, source, 'source is this during start');
+
+      const methods = ['close', 'enqueue', 'error', 'constructor'];
+      const properties = ['desiredSize'].concat(methods).sort();
+      const proto = Object.getPrototypeOf(controller);
+
+      assert_array_equals(Object.getOwnPropertyNames(proto).sort(), properties,
+        'the controller should have the right properties');
+
+      for (const m of methods) {
+        const propDesc = Object.getOwnPropertyDescriptor(proto, m);
+        assert_equals(typeof controller[m], 'function', `should have a ${m} method`);
+        assert_false(propDesc.enumerable, m + ' should be non-enumerable');
+        assert_true(propDesc.configurable, m + ' should be configurable');
+        assert_true(propDesc.writable, m + ' should be writable');
+      }
+
+      const desiredSizePropDesc = Object.getOwnPropertyDescriptor(proto, 'desiredSize');
+      assert_false(desiredSizePropDesc.enumerable, 'desiredSize should be non-enumerable');
+      assert_equals(desiredSizePropDesc.writable, undefined, 'desiredSize should not be a data property');
+      assert_equals(typeof desiredSizePropDesc.get, 'function', 'desiredSize should have a getter');
+      assert_equals(desiredSizePropDesc.set, undefined, 'desiredSize should not have a setter');
+      assert_true(desiredSizePropDesc.configurable, 'desiredSize should be configurable');
+
+      assert_equals(controller.close.length, 0, 'close should have no parameters');
+      assert_equals(controller.constructor.length, 4, 'constructor should have 4 parameter');
+      assert_equals(controller.enqueue.length, 1, 'enqueue should have 1 parameter');
+      assert_equals(controller.error.length, 1, 'error should have 1 parameter');
+
+      startCalled = true;
+    }
+  };
+
+  new ReadableStream(source);
+  assert_true(startCalled);
+
+}, 'ReadableStream start should be called with the proper parameters');
+
+test(() => {
+
+  let startCalled = false;
+  const source = {
+    start(controller) {
+      const properties = ['close', 'constructor', 'desiredSize', 'enqueue', 'error'];
+      assert_array_equals(Object.getOwnPropertyNames(Object.getPrototypeOf(controller)).sort(), properties,
+        'prototype should have the right properties');
+
+      controller.test = '';
+      assert_array_equals(Object.getOwnPropertyNames(Object.getPrototypeOf(controller)).sort(), properties,
+        'prototype should still have the right properties');
+      assert_not_equals(Object.getOwnPropertyNames(controller).indexOf('test'), -1,
+        '"test" should be a property of the controller');
+
+      startCalled = true;
+    }
+  };
+
+  new ReadableStream(source);
+  assert_true(startCalled);
+
+}, 'ReadableStream start controller parameter should be extensible');
+
+promise_test(() => {
+
+  function SimpleStreamSource() {}
+  let resolve;
+  const promise = new Promise(r => resolve = r);
+  SimpleStreamSource.prototype = {
+    start: resolve
+  };
+
+  new ReadableStream(new SimpleStreamSource());
+  return promise;
+
+}, 'ReadableStream should be able to call start method within prototype chain of its source');
+
+promise_test(() => {
+
+  const rs = new ReadableStream({
+    start(c) {
+      return delay(5).then(() => {
+        c.enqueue('a');
+        c.close();
+      });
+    }
+  });
+
+  const reader = rs.getReader();
+  return reader.read().then(r => {
+    assert_object_equals(r, { value: 'a', done: false }, 'value read should be the one enqueued');
+    return reader.closed;
+  });
+
+}, 'ReadableStream start should be able to return a promise');
+
+promise_test(() => {
+
+  const theError = new Error('rejected!');
+  const rs = new ReadableStream({
+    start() {
+      return delay(1).then(() => { throw theError; });
+    }
+  });
+
+  return rs.getReader().closed.then(() => {
+    assert_unreached('closed promise should be rejected');
+  }, e => {
+    assert_equals(e, theError, 'promise should be rejected with the same error');
+  });
+
+}, 'ReadableStream start should be able to return a promise and reject it');
+
+promise_test(() => {
+
+  const objects = [
+    { potato: 'Give me more!' },
+    'test',
+    1
+  ];
+
+  const rs = new ReadableStream({
+    start(c) {
+      for (const o of objects) {
+        c.enqueue(o);
+      }
+      c.close();
+    }
+  });
+
+  const reader = rs.getReader();
+
+  return Promise.all([reader.read(), reader.read(), reader.read(), reader.closed]).then(r => {
+    assert_object_equals(r[0], { value: objects[0], done: false }, 'value read should be the one enqueued');
+    assert_object_equals(r[1], { value: objects[1], done: false }, 'value read should be the one enqueued');
+    assert_object_equals(r[2], { value: objects[2], done: false }, 'value read should be the one enqueued');
+  });
+
+}, 'ReadableStream should be able to enqueue different objects.');
+
+promise_test(() => {
+
+  const error = new Error('pull failure');
+  const rs = new ReadableStream({
+    pull() {
+      return Promise.reject(error);
+    }
+  });
+
+  const reader = rs.getReader();
+
+  let closed = false;
+  let read = false;
+
+  return Promise.all([
+    reader.closed.then(() => {
+      assert_unreached('closed should be rejected');
+    }, e => {
+      closed = true;
+      assert_true(read);
+      assert_equals(e, error, 'closed should be rejected with the thrown error');
+    }),
+    reader.read().then(() => {
+      assert_unreached('read() should be rejected');
+    }, e => {
+      read = true;
+      assert_false(closed);
+      assert_equals(e, error, 'read() should be rejected with the thrown error');
+    })
+  ]);
+
+}, 'ReadableStream: if pull rejects, it should error the stream');
+
+promise_test(() => {
+
+  let pullCount = 0;
+  const startPromise = Promise.resolve();
+
+  new ReadableStream({
+    start() {
+      return startPromise;
+    },
+    pull() {
+      pullCount++;
+    }
+  });
+
+  return startPromise.then(() => {
+    assert_equals(pullCount, 1, 'pull should be called once start finishes');
+    return delay(10);
+  }).then(() => {
+    assert_equals(pullCount, 1, 'pull should be called exactly once');
+  });
+
+}, 'ReadableStream: should only call pull once upon starting the stream');
+
+promise_test(() => {
+
+  let pullCount = 0;
+  const startPromise = Promise.resolve();
+
+  const rs = new ReadableStream({
+    start() {
+      return startPromise;
+    },
+    pull(c) {
+      // Don't enqueue immediately after start. We want the stream to be empty when we call .read() on it.
+      if (pullCount > 0) {
+        c.enqueue(pullCount);
+      }
+      ++pullCount;
+    }
+  });
+
+  return startPromise.then(() => {
+    assert_equals(pullCount, 1, 'pull should be called once start finishes');
+  }).then(() => {
+    const reader = rs.getReader();
+    const read = reader.read();
+    assert_equals(pullCount, 2, 'pull should be called when read is called');
+    return read;
+  }).then(result => {
+    assert_equals(pullCount, 3, 'pull should be called again in reaction to calling read');
+    assert_object_equals(result, { value: 1, done: false }, 'the result read should be the one enqueued');
+  });
+
+}, 'ReadableStream: should call pull when trying to read from a started, empty stream');
+
+promise_test(() => {
+
+  let pullCount = 0;
+  const startPromise = Promise.resolve();
+
+  const rs = new ReadableStream({
+    start(c) {
+      c.enqueue('a');
+      return startPromise;
+    },
+    pull() {
+      pullCount++;
+    }
+  });
+
+  const read = rs.getReader().read();
+  assert_equals(pullCount, 0, 'calling read() should not cause pull to be called yet');
+
+  return startPromise.then(() => {
+    assert_equals(pullCount, 1, 'pull should be called once start finishes');
+    return read;
+  }).then(r => {
+    assert_object_equals(r, { value: 'a', done: false }, 'first read() should return first chunk');
+    assert_equals(pullCount, 1, 'pull should not have been called again');
+    return delay(10);
+  }).then(() => {
+    assert_equals(pullCount, 1, 'pull should be called exactly once');
+  });
+
+}, 'ReadableStream: should only call pull once on a non-empty stream read from before start fulfills');
+
+promise_test(() => {
+
+  let pullCount = 0;
+  const startPromise = Promise.resolve();
+
+  const rs = new ReadableStream({
+    start(c) {
+      c.enqueue('a');
+      return startPromise;
+    },
+    pull() {
+      pullCount++;
+    }
+  });
+
+  return startPromise.then(() => {
+    assert_equals(pullCount, 0, 'pull should not be called once start finishes, since the queue is full');
+
+    const read = rs.getReader().read();
+    assert_equals(pullCount, 1, 'calling read() should cause pull to be called immediately');
+    return read;
+  }).then(r => {
+    assert_object_equals(r, { value: 'a', done: false }, 'first read() should return first chunk');
+    return delay(10);
+  }).then(() => {
+    assert_equals(pullCount, 1, 'pull should be called exactly once');
+  });
+
+}, 'ReadableStream: should only call pull once on a non-empty stream read from after start fulfills');
+
+promise_test(() => {
+
+  let pullCount = 0;
+  let controller;
+  const startPromise = Promise.resolve();
+
+  const rs = new ReadableStream({
+    start(c) {
+      controller = c;
+      return startPromise;
+    },
+    pull() {
+      ++pullCount;
+    }
+  });
+
+  const reader = rs.getReader();
+  return startPromise.then(() => {
+    assert_equals(pullCount, 1, 'pull should have been called once by the time the stream starts');
+
+    controller.enqueue('a');
+    assert_equals(pullCount, 1, 'pull should not have been called again after enqueue');
+
+    return reader.read();
+  }).then(() => {
+    assert_equals(pullCount, 2, 'pull should have been called again after read');
+
+    return delay(10);
+  }).then(() => {
+    assert_equals(pullCount, 2, 'pull should be called exactly twice');
+  });
+}, 'ReadableStream: should call pull in reaction to read()ing the last chunk, if not draining');
+
+promise_test(() => {
+
+  let pullCount = 0;
+  let controller;
+  const startPromise = Promise.resolve();
+
+  const rs = new ReadableStream({
+    start(c) {
+      controller = c;
+      return startPromise;
+    },
+    pull() {
+      ++pullCount;
+    }
+  });
+
+  const reader = rs.getReader();
+
+  return startPromise.then(() => {
+    assert_equals(pullCount, 1, 'pull should have been called once by the time the stream starts');
+
+    controller.enqueue('a');
+    assert_equals(pullCount, 1, 'pull should not have been called again after enqueue');
+
+    controller.close();
+
+    return reader.read();
+  }).then(() => {
+    assert_equals(pullCount, 1, 'pull should not have been called a second time after read');
+
+    return delay(10);
+  }).then(() => {
+    assert_equals(pullCount, 1, 'pull should be called exactly once');
+  });
+
+}, 'ReadableStream: should not call pull() in reaction to read()ing the last chunk, if draining');
+
+promise_test(() => {
+
+  let resolve;
+  let returnedPromise;
+  let timesCalled = 0;
+  const startPromise = Promise.resolve();
+
+  const rs = new ReadableStream({
+    start() {
+      return startPromise;
+    },
+    pull(c) {
+      c.enqueue(++timesCalled);
+      returnedPromise = new Promise(r => resolve = r);
+      return returnedPromise;
+    }
+  });
+  const reader = rs.getReader();
+
+  return startPromise.then(() => {
+    return reader.read();
+  }).then(result1 => {
+    assert_equals(timesCalled, 1,
+      'pull should have been called once after start, but not yet have been called a second time');
+    assert_object_equals(result1, { value: 1, done: false }, 'read() should fulfill with the enqueued value');
+
+    return delay(10);
+  }).then(() => {
+    assert_equals(timesCalled, 1, 'after 10 ms, pull should still only have been called once');
+
+    resolve();
+    return returnedPromise;
+  }).then(() => {
+    assert_equals(timesCalled, 2,
+      'after the promise returned by pull is fulfilled, pull should be called a second time');
+  });
+
+}, 'ReadableStream: should not call pull until the previous pull call\'s promise fulfills');
+
+promise_test(() => {
+
+  let timesCalled = 0;
+  const startPromise = Promise.resolve();
+
+  const rs = new ReadableStream(
+    {
+      start(c) {
+        c.enqueue('a');
+        c.enqueue('b');
+        c.enqueue('c');
+        return startPromise;
+      },
+      pull() {
+        ++timesCalled;
+      }
+    },
+    {
+      size() {
+        return 1;
+      },
+      highWaterMark: Infinity
+    }
+  );
+  const reader = rs.getReader();
+
+  return startPromise.then(() => {
+    return reader.read();
+  }).then(result1 => {
+    assert_object_equals(result1, { value: 'a', done: false }, 'first chunk should be as expected');
+
+    return reader.read();
+  }).then(result2 => {
+    assert_object_equals(result2, { value: 'b', done: false }, 'second chunk should be as expected');
+
+    return reader.read();
+  }).then(result3 => {
+    assert_object_equals(result3, { value: 'c', done: false }, 'third chunk should be as expected');
+
+    return delay(10);
+  }).then(() => {
+    // Once for after start, and once for every read.
+    assert_equals(timesCalled, 4, 'pull() should be called exactly four times');
+  });
+
+}, 'ReadableStream: should pull after start, and after every read');
+
+promise_test(() => {
+
+  let timesCalled = 0;
+  const startPromise = Promise.resolve();
+
+  const rs = new ReadableStream({
+    start(c) {
+      c.enqueue('a');
+      c.close();
+      return startPromise;
+    },
+    pull() {
+      ++timesCalled;
+    }
+  });
+
+  const reader = rs.getReader();
+  return startPromise.then(() => {
+    assert_equals(timesCalled, 0, 'after start finishes, pull should not have been called');
+
+    return reader.read();
+  }).then(() => {
+    assert_equals(timesCalled, 0, 'reading should not have triggered a pull call');
+
+    return reader.closed;
+  }).then(() => {
+    assert_equals(timesCalled, 0, 'stream should have closed with still no calls to pull');
+  });
+
+}, 'ReadableStream: should not call pull after start if the stream is now closed');
+
+promise_test(() => {
+
+  let timesCalled = 0;
+  let resolve;
+  const ready = new Promise(r => resolve = r);
+
+  new ReadableStream(
+    {
+      start() {},
+      pull(c) {
+        c.enqueue(++timesCalled);
+
+        if (timesCalled === 4) {
+          resolve();
+        }
+      }
+    },
+    {
+      size() {
+        return 1;
+      },
+      highWaterMark: 4
+    }
+  );
+
+  return ready.then(() => {
+    // after start: size = 0, pull()
+    // after enqueue(1): size = 1, pull()
+    // after enqueue(2): size = 2, pull()
+    // after enqueue(3): size = 3, pull()
+    // after enqueue(4): size = 4, do not pull
+    assert_equals(timesCalled, 4, 'pull() should have been called four times');
+  });
+
+}, 'ReadableStream: should call pull after enqueueing from inside pull (with no read requests), if strategy allows');
+
+promise_test(() => {
+
+  let pullCalled = false;
+
+  const rs = new ReadableStream({
+    pull(c) {
+      pullCalled = true;
+      c.close();
+    }
+  });
+
+  const reader = rs.getReader();
+  return reader.closed.then(() => {
+    assert_true(pullCalled);
+  });
+
+}, 'ReadableStream pull should be able to close a stream.');
+
+promise_test(t => {
+
+  const controllerError = { name: 'controller error' };
+
+  const rs = new ReadableStream({
+    pull(c) {
+      c.error(controllerError);
+    }
+  });
+
+  return promise_rejects(t, controllerError, rs.getReader().closed);
+
+}, 'ReadableStream pull should be able to error a stream.');
+
+promise_test(t => {
+
+  const controllerError = { name: 'controller error' };
+  const thrownError = { name: 'thrown error' };
+
+  const rs = new ReadableStream({
+    pull(c) {
+      c.error(controllerError);
+      throw thrownError;
+    }
+  });
+
+  return promise_rejects(t, controllerError, rs.getReader().closed);
+
+}, 'ReadableStream pull should be able to error a stream and throw.');
+
+test(() => {
+
+  let startCalled = false;
+
+  new ReadableStream({
+    start(c) {
+      assert_equals(c.enqueue('a'), undefined, 'the first enqueue should return undefined');
+      c.close();
+
+      assert_throws(new TypeError(), () => c.enqueue('b'), 'enqueue after close should throw a TypeError');
+      startCalled = true;
+    }
+  });
+
+  assert_true(startCalled);
+
+}, 'ReadableStream: enqueue should throw when the stream is readable but draining');
+
+test(() => {
+
+  let startCalled = false;
+
+  new ReadableStream({
+    start(c) {
+      c.close();
+
+      assert_throws(new TypeError(), () => c.enqueue('a'), 'enqueue after close should throw a TypeError');
+      startCalled = true;
+    }
+  });
+
+  assert_true(startCalled);
+
+}, 'ReadableStream: enqueue should throw when the stream is closed');
+
+promise_test(() => {
+
+  let startCalled = 0;
+  let pullCalled = 0;
+  let cancelCalled = 0;
+
+  /* eslint-disable no-use-before-define */
+  class Source {
+    start(c) {
+      startCalled++;
+      assert_equals(this, theSource, 'start() should be called with the correct this');
+      c.enqueue('a');
+    }
+
+    pull() {
+      pullCalled++;
+      assert_equals(this, theSource, 'pull() should be called with the correct this');
+    }
+
+    cancel() {
+      cancelCalled++;
+      assert_equals(this, theSource, 'cancel() should be called with the correct this');
+    }
+  }
+  /* eslint-enable no-use-before-define */
+
+  const theSource = new Source();
+  theSource.debugName = 'the source object passed to the constructor'; // makes test failures easier to diagnose
+
+  const rs = new ReadableStream(theSource);
+  const reader = rs.getReader();
+
+  return reader.read().then(() => {
+    reader.releaseLock();
+    rs.cancel();
+    assert_equals(startCalled, 1);
+    assert_equals(pullCalled, 1);
+    assert_equals(cancelCalled, 1);
+    return rs.getReader().closed;
+  });
+
+}, 'ReadableStream: should call underlying source methods as methods');
+
+test(() => {
+
+  let startCalled = false;
+  new ReadableStream({
+    start(c) {
+      assert_equals(c.desiredSize, 1);
+      c.enqueue('a');
+      assert_equals(c.desiredSize, 0);
+      c.enqueue('b');
+      assert_equals(c.desiredSize, -1);
+      c.enqueue('c');
+      assert_equals(c.desiredSize, -2);
+      c.enqueue('d');
+      assert_equals(c.desiredSize, -3);
+      c.enqueue('e');
+      startCalled = true;
+    }
+  });
+
+  assert_true(startCalled);
+
+}, 'ReadableStream strategies: the default strategy should give desiredSize of 1 to start, decreasing by 1 per enqueue');
+
+promise_test(() => {
+
+  let controller;
+  const rs = new ReadableStream({
+    start(c) {
+      controller = c;
+    }
+  });
+  const reader = rs.getReader();
+
+  assert_equals(controller.desiredSize, 1, 'desiredSize should start at 1');
+  controller.enqueue('a');
+  assert_equals(controller.desiredSize, 0, 'desiredSize should decrease to 0 after first enqueue');
+
+  return reader.read().then(result1 => {
+    assert_object_equals(result1, { value: 'a', done: false }, 'first chunk read should be correct');
+
+    assert_equals(controller.desiredSize, 1, 'desiredSize should go up to 1 after the first read');
+    controller.enqueue('b');
+    assert_equals(controller.desiredSize, 0, 'desiredSize should go down to 0 after the second enqueue');
+
+    return reader.read();
+  }).then(result2 => {
+    assert_object_equals(result2, { value: 'b', done: false }, 'second chunk read should be correct');
+
+    assert_equals(controller.desiredSize, 1, 'desiredSize should go up to 1 after the second read');
+    controller.enqueue('c');
+    assert_equals(controller.desiredSize, 0, 'desiredSize should go down to 0 after the third enqueue');
+
+    return reader.read();
+  }).then(result3 => {
+    assert_object_equals(result3, { value: 'c', done: false }, 'third chunk read should be correct');
+
+    assert_equals(controller.desiredSize, 1, 'desiredSize should go up to 1 after the third read');
+    controller.enqueue('d');
+    assert_equals(controller.desiredSize, 0, 'desiredSize should go down to 0 after the fourth enqueue');
+  });
+
+}, 'ReadableStream strategies: the default strategy should continue giving desiredSize of 1 if the chunks are read immediately');
+
+promise_test(t => {
+
+  const randomSource = new RandomPushSource(8);
+
+  const rs = new ReadableStream({
+    start(c) {
+      assert_equals(typeof c, 'object', 'c should be an object in start');
+      assert_equals(typeof c.enqueue, 'function', 'enqueue should be a function in start');
+      assert_equals(typeof c.close, 'function', 'close should be a function in start');
+      assert_equals(typeof c.error, 'function', 'error should be a function in start');
+
+      randomSource.ondata = t.step_func(chunk => {
+        if (!c.enqueue(chunk) <= 0) {
+          randomSource.readStop();
+        }
+      });
+
+      randomSource.onend = c.close.bind(c);
+      randomSource.onerror = c.error.bind(c);
+    },
+
+    pull(c) {
+      assert_equals(typeof c, 'object', 'c should be an object in pull');
+      assert_equals(typeof c.enqueue, 'function', 'enqueue should be a function in pull');
+      assert_equals(typeof c.close, 'function', 'close should be a function in pull');
+
+      randomSource.readStart();
+    }
+  });
+
+  return readableStreamToArray(rs).then(chunks => {
+    assert_equals(chunks.length, 8, '8 chunks should be read');
+    for (const chunk of chunks) {
+      assert_equals(chunk.length, 128, 'chunk should have 128 bytes');
+    }
+  });
+
+}, 'ReadableStream integration test: adapting a random push source');
+
+promise_test(() => {
+
+  const rs = sequentialReadableStream(10);
+
+  return readableStreamToArray(rs).then(chunks => {
+    assert_true(rs.source.closed, 'source should be closed after all chunks are read');
+    assert_array_equals(chunks, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 'the expected 10 chunks should be read');
+  });
+
+}, 'ReadableStream integration test: adapting a sync pull source');
+
+promise_test(() => {
+
+  const rs = sequentialReadableStream(10, { async: true });
+
+  return readableStreamToArray(rs).then(chunks => {
+    assert_true(rs.source.closed, 'source should be closed after all chunks are read');
+    assert_array_equals(chunks, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 'the expected 10 chunks should be read');
+  });
+
+}, 'ReadableStream integration test: adapting an async pull source');
+
+done();
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/pipe-through.https.html b/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/pipe-through.https.html
new file mode 100644
index 0000000..fbac05a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/pipe-through.https.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+<script src="../resources/test-initializer.js"></script>
+
+<script src="../resources/test-utils.js"></script>
+<script src="pipe-through.js"></script>
+<script>
+'use strict';
+worker_test('pipe-through.js');
+</script>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/pipe-through.js b/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/pipe-through.js
new file mode 100644
index 0000000..4988928e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/pipe-through.js
@@ -0,0 +1,108 @@
+'use strict';
+
+if (self.importScripts) {
+  self.importScripts('../resources/test-utils.js');
+  self.importScripts('/resources/testharness.js');
+}
+
+test(() => {
+
+  let pipeToArguments;
+  const thisValue = {
+    pipeTo() {
+      pipeToArguments = arguments;
+    }
+  };
+
+  const input = { readable: {}, writable: {} };
+  const options = {};
+  const result = ReadableStream.prototype.pipeThrough.call(thisValue, input, options);
+
+  assert_array_equals(pipeToArguments, [input.writable, options],
+    'correct arguments should be passed to thisValue.pipeTo');
+  assert_equals(result, input.readable, 'return value should be the passed readable property');
+
+}, 'ReadableStream.prototype.pipeThrough should work generically on its this and its arguments');
+
+test(() => {
+
+  const thisValue = {
+    pipeTo() {
+      assert_unreached('pipeTo should not be called');
+    }
+  };
+
+  methodThrows(ReadableStream.prototype, 'pipeThrough', thisValue, [undefined, {}]);
+  methodThrows(ReadableStream.prototype, 'pipeThrough', thisValue, [null, {}]);
+
+}, 'ReadableStream.prototype.pipeThrough should throw when its first argument is not convertible to an object');
+
+test(() => {
+
+  const args = [{ readable: {}, writable: {} }, {}];
+
+  methodThrows(ReadableStream.prototype, 'pipeThrough', undefined, args);
+  methodThrows(ReadableStream.prototype, 'pipeThrough', null, args);
+  methodThrows(ReadableStream.prototype, 'pipeThrough', 1, args);
+  methodThrows(ReadableStream.prototype, 'pipeThrough', { pipeTo: 'test' }, args);
+
+}, 'ReadableStream.prototype.pipeThrough should throw when "this" has no pipeTo method');
+
+test(() => {
+  const error = new Error('potato');
+
+  const throwingPipeTo = {
+    get pipeTo() {
+      throw error;
+    }
+  };
+  assert_throws(error,
+    () => ReadableStream.prototype.pipeThrough.call(throwingPipeTo, { readable: { }, writable: { } }, {}),
+    'pipeThrough should rethrow the error thrown by pipeTo');
+
+  const thisValue = {
+    pipeTo() {
+      assert_unreached('pipeTo should not be called');
+    }
+  };
+
+  const throwingWritable = {
+    readable: {},
+    get writable() {
+      throw error;
+    }
+  };
+  assert_throws(error,
+    () => ReadableStream.prototype.pipeThrough.call(thisValue, throwingWritable, {}),
+    'pipeThrough should rethrow the error thrown by the writable getter');
+
+  const throwingReadable = {
+    get readable() {
+      throw error;
+    },
+    writable: {}
+  };
+  assert_throws(error,
+    () => ReadableStream.prototype.pipeThrough.call(thisValue, throwingReadable, {}),
+    'pipeThrough should rethrow the error thrown by the readable getter');
+
+}, 'ReadableStream.prototype.pipeThrough should rethrow errors from accessing pipeTo, readable, or writable');
+
+test(() => {
+
+  let count = 0;
+  const thisValue = {
+    pipeTo() {
+      ++count;
+    }
+  };
+
+  ReadableStream.prototype.pipeThrough.call(thisValue, { readable: {}, writable: {} });
+  ReadableStream.prototype.pipeThrough.call(thisValue, { readable: {} }, {});
+  ReadableStream.prototype.pipeThrough.call(thisValue, { writable: {} }, {});
+
+  assert_equals(count, 3, 'pipeTo was called 3 times');
+
+}, 'ReadableStream.prototype.pipeThrough should work with missing readable, writable, or options');
+
+done();
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/readable-stream-reader.https.html b/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/readable-stream-reader.https.html
new file mode 100644
index 0000000..cc56135
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/readable-stream-reader.https.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+<script src="../resources/test-initializer.js"></script>
+
+<script src="readable-stream-reader.js"></script>
+<script>
+'use strict';
+worker_test('readable-stream-reader.js');
+</script>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/readable-stream-reader.js b/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/readable-stream-reader.js
new file mode 100644
index 0000000..1d5bc13
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/readable-stream-reader.js
@@ -0,0 +1,485 @@
+'use strict';
+
+if (self.importScripts) {
+  self.importScripts('../resources/rs-utils.js');
+  self.importScripts('/resources/testharness.js');
+}
+
+let ReadableStreamReader;
+
+test(() => {
+
+  // It's not exposed globally, but we test a few of its properties here.
+  ReadableStreamReader = (new ReadableStream()).getReader().constructor;
+
+}, 'Can get the ReadableStreamReader constructor indirectly');
+
+test(() => {
+
+  assert_throws(new TypeError(), () => new ReadableStreamReader('potato'));
+  assert_throws(new TypeError(), () => new ReadableStreamReader({}));
+  assert_throws(new TypeError(), () => new ReadableStreamReader());
+
+}, 'ReadableStreamReader constructor should get a ReadableStream object as argument');
+
+test(() => {
+
+  const methods = ['cancel', 'constructor', 'read', 'releaseLock'];
+  const properties = methods.concat(['closed']).sort();
+
+  const rsReader = new ReadableStreamReader(new ReadableStream());
+  const proto = Object.getPrototypeOf(rsReader);
+
+  assert_array_equals(Object.getOwnPropertyNames(proto).sort(), properties);
+
+  for (const m of methods) {
+    const propDesc = Object.getOwnPropertyDescriptor(proto, m);
+    assert_equals(propDesc.enumerable, false, 'method should be non-enumerable');
+    assert_equals(propDesc.configurable, true, 'method should be configurable');
+    assert_equals(propDesc.writable, true, 'method should be writable');
+    assert_equals(typeof rsReader[m], 'function', 'should have be a method');
+  }
+
+  const closedPropDesc = Object.getOwnPropertyDescriptor(proto, 'closed');
+  assert_equals(closedPropDesc.enumerable, false, 'closed should be non-enumerable');
+  assert_equals(closedPropDesc.configurable, true, 'closed should be configurable');
+  assert_not_equals(closedPropDesc.get, undefined, 'closed should have a getter');
+  assert_equals(closedPropDesc.set, undefined, 'closed should not have a setter');
+
+  assert_equals(rsReader.cancel.length, 1, 'cancel has 1 parameter');
+  assert_not_equals(rsReader.closed, undefined, 'has a non-undefined closed property');
+  assert_equals(typeof rsReader.closed.then, 'function', 'closed property is thenable');
+  assert_equals(typeof rsReader.constructor, 'function', 'has a constructor method');
+  assert_equals(rsReader.constructor.length, 1, 'constructor has 1 parameter');
+  assert_equals(typeof rsReader.read, 'function', 'has a getReader method');
+  assert_equals(rsReader.read.length, 0, 'read has no parameters');
+  assert_equals(typeof rsReader.releaseLock, 'function', 'has a releaseLock method');
+  assert_equals(rsReader.releaseLock.length, 0, 'releaseLock has no parameters');
+
+}, 'ReadableStreamReader instances should have the correct list of properties');
+
+test(() => {
+
+  const rsReader = new ReadableStreamReader(new ReadableStream());
+  assert_equals(rsReader.closed, rsReader.closed, 'closed should return the same promise');
+
+}, 'ReadableStreamReader closed should always return the same promise object');
+
+test(() => {
+
+  const rs = new ReadableStream();
+  new ReadableStreamReader(rs); // Constructing directly the first time should be fine.
+  assert_throws(new TypeError(), () => new ReadableStreamReader(rs),
+                'constructing directly the second time should fail');
+
+}, 'Constructing a ReadableStreamReader directly should fail if the stream is already locked (via direct ' +
+   'construction)');
+
+test(() => {
+
+  const rs = new ReadableStream();
+  new ReadableStreamReader(rs); // Constructing directly should be fine.
+  assert_throws(new TypeError(), () => rs.getReader(), 'getReader() should fail');
+
+}, 'Getting a ReadableStreamReader via getReader should fail if the stream is already locked (via direct ' +
+   'construction)');
+
+test(() => {
+
+  const rs = new ReadableStream();
+  rs.getReader(); // getReader() should be fine.
+  assert_throws(new TypeError(), () => new ReadableStreamReader(rs), 'constructing directly should fail');
+
+}, 'Constructing a ReadableStreamReader directly should fail if the stream is already locked (via getReader)');
+
+test(() => {
+
+  const rs = new ReadableStream();
+  rs.getReader(); // getReader() should be fine.
+  assert_throws(new TypeError(), () => rs.getReader(), 'getReader() should fail');
+
+}, 'Getting a ReadableStreamReader via getReader should fail if the stream is already locked (via getReader)');
+
+test(() => {
+
+  const rs = new ReadableStream({
+    start(c) {
+      c.close();
+    }
+  });
+
+  new ReadableStreamReader(rs); // Constructing directly should not throw.
+
+}, 'Constructing a ReadableStreamReader directly should be OK if the stream is closed');
+
+test(() => {
+
+  const theError = new Error('don\'t say i didn\'t warn ya');
+  const rs = new ReadableStream({
+    start(c) {
+      c.error(theError);
+    }
+  });
+
+  new ReadableStreamReader(rs); // Constructing directly should not throw.
+
+}, 'Constructing a ReadableStreamReader directly should be OK if the stream is errored');
+
+promise_test(() => {
+
+  let controller;
+  const rs = new ReadableStream({
+    start(c) {
+      controller = c;
+    }
+  });
+  const reader = rs.getReader();
+
+  const promise = reader.read().then(result => {
+    assert_object_equals(result, { value: 'a', done: false }, 'read() should fulfill with the enqueued chunk');
+  });
+
+  controller.enqueue('a');
+  return promise;
+
+}, 'Reading from a reader for an empty stream will wait until a chunk is available');
+
+promise_test(() => {
+
+  let cancelCalled = false;
+  const passedReason = new Error('it wasn\'t the right time, sorry');
+  const rs = new ReadableStream({
+    cancel(reason) {
+      assert_true(rs.locked, 'the stream should still be locked');
+      assert_throws(new TypeError(), () => rs.getReader(), 'should not be able to get another reader');
+      assert_equals(reason, passedReason, 'the cancellation reason is passed through to the underlying source');
+      cancelCalled = true;
+    }
+  });
+
+  const reader = rs.getReader();
+  return reader.cancel(passedReason).then(() => assert_true(cancelCalled));
+
+}, 'cancel() on a reader does not release the reader');
+
+promise_test(() => {
+
+  let controller;
+  const rs = new ReadableStream({
+    start(c) {
+      controller = c;
+    }
+  });
+
+  const reader = rs.getReader();
+  const promise = reader.closed;
+
+  controller.close();
+  return promise;
+
+}, 'closed should be fulfilled after stream is closed (.closed access before acquiring)');
+
+promise_test(t => {
+
+  let controller;
+  const rs = new ReadableStream({
+    start(c) {
+      controller = c;
+    }
+  });
+
+  const reader1 = rs.getReader();
+
+  reader1.releaseLock();
+
+  const reader2 = rs.getReader();
+  controller.close();
+
+  return Promise.all([
+    promise_rejects(t, new TypeError(), reader1.closed),
+    reader2.closed
+  ]);
+
+}, 'closed should be rejected after reader releases its lock (multiple stream locks)');
+
+promise_test(() => {
+
+  const rs = new ReadableStream({
+    start(c) {
+      c.enqueue('a');
+      c.enqueue('b');
+      c.close();
+    }
+  });
+
+  const reader1 = rs.getReader();
+  const promise1 = reader1.read().then(r => {
+    assert_object_equals(r, { value: 'a', done: false }, 'reading the first chunk from reader1 works');
+  });
+  reader1.releaseLock();
+
+  const reader2 = rs.getReader();
+  const promise2 = reader2.read().then(r => {
+    assert_object_equals(r, { value: 'b', done: false }, 'reading the second chunk from reader2 works');
+  });
+  reader2.releaseLock();
+
+  return Promise.all([promise1, promise2]);
+
+}, 'Multiple readers can access the stream in sequence');
+
+promise_test(() => {
+  const rs = new ReadableStream({
+    start(c) {
+      c.enqueue('a');
+    }
+  });
+
+  const reader1 = rs.getReader();
+  reader1.releaseLock();
+
+  const reader2 = rs.getReader();
+
+  // Should be a no-op
+  reader1.releaseLock();
+
+  return reader2.read().then(result => {
+    assert_object_equals(result, { value: 'a', done: false },
+                         'read() should still work on reader2 even after reader1 is released');
+  });
+
+}, 'Cannot use an already-released reader to unlock a stream again');
+
+promise_test(t => {
+
+  const rs = new ReadableStream({
+    start(c) {
+      c.enqueue('a');
+    },
+    cancel() {
+      assert_unreached('underlying source cancel should not be called');
+    }
+  });
+
+  const reader = rs.getReader();
+  reader.releaseLock();
+  const cancelPromise = reader.cancel();
+
+  const reader2 = rs.getReader();
+  const readPromise = reader2.read().then(r => {
+    assert_object_equals(r, { value: 'a', done: false }, 'a new reader should be able to read a chunk');
+  });
+
+  return Promise.all([
+    promise_rejects(t, new TypeError(), cancelPromise),
+    readPromise
+  ]);
+
+}, 'cancel() on a released reader is a no-op and does not pass through');
+
+promise_test(t => {
+
+  const promiseAsserts = [];
+
+  let controller;
+  const theError = { name: 'unique error' };
+  const rs = new ReadableStream({
+    start(c) {
+      controller = c;
+    }
+  });
+
+  const reader1 = rs.getReader();
+
+  promiseAsserts.push(
+    promise_rejects(t, theError, reader1.closed),
+    promise_rejects(t, theError, reader1.read())
+  );
+
+  assert_throws(new TypeError(), () => rs.getReader(), 'trying to get another reader before erroring should throw');
+
+  controller.error(theError);
+
+  reader1.releaseLock();
+
+  const reader2 = rs.getReader();
+
+  promiseAsserts.push(
+    promise_rejects(t, theError, reader2.closed),
+    promise_rejects(t, theError, reader2.read())
+  );
+
+  return Promise.all(promiseAsserts);
+
+}, 'Getting a second reader after erroring the stream and releasing the reader should succeed');
+
+promise_test(t => {
+
+  let controller;
+  const rs = new ReadableStream({
+    start(c) {
+      controller = c;
+    }
+  });
+
+  const promise = rs.getReader().closed.then(
+    t.unreached_func('closed promise should not be fulfilled when stream is errored'),
+    err => {
+      assert_equals(err, undefined, 'passed error should be undefined as it was');
+    }
+  );
+
+  controller.error();
+  return promise;
+
+}, 'ReadableStreamReader closed promise should be rejected with undefined if that is the error');
+
+
+promise_test(t => {
+
+  const rs = new ReadableStream({
+    start() {
+      return Promise.reject();
+    }
+  });
+
+  return rs.getReader().read().then(
+    t.unreached_func('read promise should not be fulfilled when stream is errored'),
+    err => {
+      assert_equals(err, undefined, 'passed error should be undefined as it was');
+    }
+  );
+
+}, 'ReadableStreamReader: if start rejects with no parameter, it should error the stream with an undefined error');
+
+promise_test(t => {
+
+  const theError = { name: 'unique string' };
+  let controller;
+  const rs = new ReadableStream({
+    start(c) {
+      controller = c;
+    }
+  });
+
+  const promise = promise_rejects(t, theError, rs.getReader().closed);
+
+  controller.error(theError);
+  return promise;
+
+}, 'Erroring a ReadableStream after checking closed should reject ReadableStreamReader closed promise');
+
+promise_test(t => {
+
+  const theError = { name: 'unique string' };
+  let controller;
+  const rs = new ReadableStream({
+    start(c) {
+      controller = c;
+    }
+  });
+
+  controller.error(theError);
+
+  // Let's call getReader twice for extra test coverage of this code path.
+  rs.getReader().releaseLock();
+
+  return promise_rejects(t, theError, rs.getReader().closed);
+
+}, 'Erroring a ReadableStream before checking closed should reject ReadableStreamReader closed promise');
+
+promise_test(() => {
+
+  let controller;
+  const rs = new ReadableStream({
+    start(c) {
+      controller = c;
+    }
+  });
+  const reader = rs.getReader();
+
+  const promise = Promise.all([
+    reader.read().then(result => {
+      assert_object_equals(result, { value: undefined, done: true }, 'read() should fulfill with close (1)');
+    }),
+    reader.read().then(result => {
+      assert_object_equals(result, { value: undefined, done: true }, 'read() should fulfill with close (2)');
+    }),
+    reader.closed
+  ]);
+
+  controller.close();
+  return promise;
+
+}, 'Reading twice on a stream that gets closed');
+
+promise_test(() => {
+
+  let controller;
+  const rs = new ReadableStream({
+    start(c) {
+      controller = c;
+    }
+  });
+
+  controller.close();
+  const reader = rs.getReader();
+
+  return Promise.all([
+    reader.read().then(result => {
+      assert_object_equals(result, { value: undefined, done: true }, 'read() should fulfill with close (1)');
+    }),
+    reader.read().then(result => {
+      assert_object_equals(result, { value: undefined, done: true }, 'read() should fulfill with close (2)');
+    }),
+    reader.closed
+  ]);
+
+}, 'Reading twice on a closed stream');
+
+promise_test(t => {
+
+  let controller;
+  const rs = new ReadableStream({
+    start(c) {
+      controller = c;
+    }
+  });
+
+  const myError = { name: 'mashed potatoes' };
+  controller.error(myError);
+
+  const reader = rs.getReader();
+
+  return Promise.all([
+    promise_rejects(t, myError, reader.read()),
+    promise_rejects(t, myError, reader.read()),
+    promise_rejects(t, myError, reader.closed)
+  ]);
+
+}, 'Reading twice on an errored stream');
+
+promise_test(t => {
+
+  let controller;
+  const rs = new ReadableStream({
+    start(c) {
+      controller = c;
+    }
+  });
+
+  const myError = { name: 'mashed potatoes' };
+  const reader = rs.getReader();
+
+  const promise = Promise.all([
+    promise_rejects(t, myError, reader.read()),
+    promise_rejects(t, myError, reader.read()),
+    promise_rejects(t, myError, reader.closed)
+  ]);
+
+  controller.error(myError);
+  return promise;
+
+}, 'Reading twice on a stream that gets errored');
+
+done();
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/tee.https.html b/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/tee.https.html
new file mode 100644
index 0000000..37c3e8d8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/tee.https.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+<script src="../resources/test-initializer.js"></script>
+
+<script src="../resources/rs-utils.js"></script>
+<script src="tee.js"></script>
+<script>
+'use strict';
+worker_test('tee.js');
+</script>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/tee.js b/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/tee.js
new file mode 100644
index 0000000..485f2af
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/tee.js
@@ -0,0 +1,254 @@
+'use strict';
+
+if (self.importScripts) {
+  self.importScripts('../resources/rs-utils.js');
+  self.importScripts('/resources/testharness.js');
+}
+
+test(() => {
+
+  const rs = new ReadableStream();
+  const result = rs.tee();
+
+  assert_true(Array.isArray(result), 'return value should be an array');
+  assert_equals(result.length, 2, 'array should have length 2');
+  assert_equals(result[0].constructor, ReadableStream, '0th element should be a ReadableStream');
+  assert_equals(result[1].constructor, ReadableStream, '1st element should be a ReadableStream');
+
+}, 'ReadableStream teeing: rs.tee() returns an array of two ReadableStreams');
+
+promise_test(t => {
+
+  const rs = new ReadableStream({
+    start(c) {
+      c.enqueue('a');
+      c.enqueue('b');
+      c.close();
+    }
+  });
+
+  const branch = rs.tee();
+  const branch1 = branch[0];
+  const branch2 = branch[1];
+  const reader1 = branch1.getReader();
+  const reader2 = branch2.getReader();
+
+  reader2.closed.then(t.unreached_func('branch2 should not be closed'));
+
+  return Promise.all([
+    reader1.closed,
+    reader1.read().then(r => {
+      assert_object_equals(r, { value: 'a', done: false }, 'first chunk from branch1 should be correct');
+    }),
+    reader1.read().then(r => {
+      assert_object_equals(r, { value: 'b', done: false }, 'second chunk from branch1 should be correct');
+    }),
+    reader1.read().then(r => {
+      assert_object_equals(r, { value: undefined, done: true }, 'third read() from branch1 should be done');
+    }),
+    reader2.read().then(r => {
+      assert_object_equals(r, { value: 'a', done: false }, 'first chunk from branch2 should be correct');
+    })
+  ]);
+
+}, 'ReadableStream teeing: should be able to read one branch to the end without affecting the other');
+
+promise_test(() => {
+
+  const theObject = { the: 'test object' };
+  const rs = new ReadableStream({
+    start(c) {
+      c.enqueue(theObject);
+    }
+  });
+
+  const branch = rs.tee();
+  const branch1 = branch[0];
+  const branch2 = branch[1];
+  const reader1 = branch1.getReader();
+  const reader2 = branch2.getReader();
+
+  return Promise.all([reader1.read(), reader2.read()]).then(values => {
+    assert_object_equals(values[0], values[1], 'the values should be equal');
+  });
+
+}, 'ReadableStream teeing: values should be equal across each branch');
+
+promise_test(t => {
+
+  const theError = { name: 'boo!' };
+  const rs = new ReadableStream({
+    start(c) {
+      c.enqueue('a');
+      c.enqueue('b');
+    },
+    pull() {
+      throw theError;
+    }
+  });
+
+  const branches = rs.tee();
+  const reader1 = branches[0].getReader();
+  const reader2 = branches[1].getReader();
+
+  reader1.label = 'reader1';
+  reader2.label = 'reader2';
+
+  return Promise.all([
+    promise_rejects(t, theError, reader1.closed),
+    promise_rejects(t, theError, reader2.closed),
+    reader1.read().then(r => {
+      assert_object_equals(r, { value: 'a', done: false }, 'should be able to read the first chunk in branch1');
+    }),
+    reader1.read().then(r => {
+      assert_object_equals(r, { value: 'b', done: false }, 'should be able to read the second chunk in branch1');
+
+      return promise_rejects(t, theError, reader2.read());
+    })
+    .then(() => promise_rejects(t, theError, reader1.read()))
+  ]);
+
+}, 'ReadableStream teeing: errors in the source should propagate to both branches');
+
+promise_test(() => {
+
+  const rs = new ReadableStream({
+    start(c) {
+      c.enqueue('a');
+      c.enqueue('b');
+      c.close();
+    }
+  });
+
+  const branches = rs.tee();
+  const branch1 = branches[0];
+  const branch2 = branches[1];
+  branch1.cancel();
+
+  return Promise.all([
+    readableStreamToArray(branch1).then(chunks => {
+      assert_array_equals(chunks, [], 'branch1 should have no chunks');
+    }),
+    readableStreamToArray(branch2).then(chunks => {
+      assert_array_equals(chunks, ['a', 'b'], 'branch2 should have two chunks');
+    })
+  ]);
+
+}, 'ReadableStream teeing: canceling branch1 should not impact branch2');
+
+promise_test(() => {
+
+  const rs = new ReadableStream({
+    start(c) {
+      c.enqueue('a');
+      c.enqueue('b');
+      c.close();
+    }
+  });
+
+  const branches = rs.tee();
+  const branch1 = branches[0];
+  const branch2 = branches[1];
+  branch2.cancel();
+
+  return Promise.all([
+    readableStreamToArray(branch1).then(chunks => {
+      assert_array_equals(chunks, ['a', 'b'], 'branch1 should have two chunks');
+    }),
+    readableStreamToArray(branch2).then(chunks => {
+      assert_array_equals(chunks, [], 'branch2 should have no chunks');
+    })
+  ]);
+
+}, 'ReadableStream teeing: canceling branch2 should not impact branch2');
+
+promise_test(() => {
+
+  const reason1 = new Error('We\'re wanted men.');
+  const reason2 = new Error('I have the death sentence on twelve systems.');
+
+  let resolve;
+  const promise = new Promise(r => resolve = r);
+  const rs = new ReadableStream({
+    cancel(reason) {
+      assert_array_equals(reason, [reason1, reason2],
+                          'the cancel reason should be an array containing those from the branches');
+      resolve();
+    }
+  });
+
+  const branch = rs.tee();
+  const branch1 = branch[0];
+  const branch2 = branch[1];
+  branch1.cancel(reason1);
+  branch2.cancel(reason2);
+
+  return promise;
+
+}, 'ReadableStream teeing: canceling both branches should aggregate the cancel reasons into an array');
+
+promise_test(t => {
+
+  const theError = { name: 'I\'ll be careful.' };
+  const rs = new ReadableStream({
+    cancel() {
+      throw theError;
+    }
+  });
+
+  const branch = rs.tee();
+  const branch1 = branch[0];
+  const branch2 = branch[1];
+
+  return Promise.all([
+    promise_rejects(t, theError, branch1.cancel()),
+    promise_rejects(t, theError, branch2.cancel())
+  ]);
+
+}, 'ReadableStream teeing: failing to cancel the original stream should cause cancel() to reject on branches');
+
+promise_test(() => {
+
+  let controller;
+  const rs = new ReadableStream({
+    start(c) {
+      controller = c;
+    }
+  });
+
+  const branches = rs.tee();
+  const reader1 = branches[0].getReader();
+  const reader2 = branches[1].getReader();
+
+  const promise = Promise.all([reader1.closed, reader2.closed]);
+
+  controller.close();
+  return promise;
+
+}, 'ReadableStream teeing: closing the original should immediately close the branches');
+
+promise_test(t => {
+
+  let controller;
+  const rs = new ReadableStream({
+    start(c) {
+      controller = c;
+    }
+  });
+
+  const branches = rs.tee();
+  const reader1 = branches[0].getReader();
+  const reader2 = branches[1].getReader();
+
+  const theError = { name: 'boo!' };
+  const promise = Promise.all([
+    promise_rejects(t, theError, reader1.closed),
+    promise_rejects(t, theError, reader2.closed)
+  ]);
+
+  controller.error(theError);
+  return promise;
+
+}, 'ReadableStream teeing: erroring the original should immediately error the branches');
+
+done();
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/templated.https.html b/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/templated.https.html
new file mode 100644
index 0000000..7719eee
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/templated.https.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+<script src="../resources/test-initializer.js"></script>
+
+<script src="../resources/test-utils.js"></script>
+<script src="../resources/rs-test-templates.js"></script>
+<script src="templated.js"></script>
+<script>
+'use strict';
+worker_test('templated.js');
+</script>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/templated.js b/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/templated.js
new file mode 100644
index 0000000..6db04299
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/streams/readable-streams/templated.js
@@ -0,0 +1,148 @@
+'use strict';
+
+if (self.importScripts) {
+  self.importScripts('/resources/testharness.js');
+  self.importScripts('../resources/test-utils.js');
+  self.importScripts('../resources/rs-test-templates.js');
+}
+
+// Run the readable stream test templates against readable streams created directly using the constructor
+
+const theError = { name: 'boo!' };
+const chunks = ['a', 'b'];
+
+templatedRSEmpty('ReadableStream (empty)', () => {
+  return new ReadableStream();
+});
+
+templatedRSEmptyReader('ReadableStream (empty) reader', () => {
+  return streamAndDefaultReader(new ReadableStream());
+});
+
+templatedRSClosed('ReadableStream (closed via call in start)', () => {
+  return new ReadableStream({
+    start(c) {
+      c.close();
+    }
+  });
+});
+
+templatedRSClosedReader('ReadableStream reader (closed before getting reader)', () => {
+  let controller;
+  const stream = new ReadableStream({
+    start(c) {
+      controller = c;
+    }
+  });
+  controller.close();
+  const result = streamAndDefaultReader(stream);
+  return result;
+});
+
+templatedRSClosedReader('ReadableStream reader (closed after getting reader)', () => {
+  let controller;
+  const stream = new ReadableStream({
+    start(c) {
+      controller = c;
+    }
+  });
+  const result = streamAndDefaultReader(stream);
+  controller.close();
+  return result;
+});
+
+templatedRSClosed('ReadableStream (closed via cancel)', () => {
+  const stream = new ReadableStream();
+  stream.cancel();
+  return stream;
+});
+
+templatedRSClosedReader('ReadableStream reader (closed via cancel after getting reader)', () => {
+  const stream = new ReadableStream();
+  const result = streamAndDefaultReader(stream);
+  result.reader.cancel();
+  return result;
+});
+
+templatedRSErrored('ReadableStream (errored via call in start)', () => {
+  return new ReadableStream({
+    start(c) {
+      c.error(theError);
+    }
+  });
+}, theError);
+
+templatedRSErroredSyncOnly('ReadableStream (errored via call in start)', () => {
+  return new ReadableStream({
+    start(c) {
+      c.error(theError);
+    }
+  });
+}, theError);
+
+templatedRSErrored('ReadableStream (errored via returning a rejected promise in start)', () => {
+  return new ReadableStream({
+    start() {
+      return Promise.reject(theError);
+    }
+  });
+}, theError);
+
+templatedRSErroredReader('ReadableStream (errored via returning a rejected promise in start) reader', () => {
+  return streamAndDefaultReader(new ReadableStream({
+    start() {
+      return Promise.reject(theError);
+    }
+  }));
+}, theError);
+
+templatedRSErroredReader('ReadableStream reader (errored before getting reader)', () => {
+  let controller;
+  const stream = new ReadableStream({
+    start(c) {
+      controller = c;
+    }
+  });
+  controller.error(theError);
+  return streamAndDefaultReader(stream);
+}, theError);
+
+templatedRSErroredReader('ReadableStream reader (errored after getting reader)', () => {
+  let controller;
+  const result = streamAndDefaultReader(new ReadableStream({
+    start(c) {
+      controller = c;
+    }
+  }));
+  controller.error(theError);
+  return result;
+}, theError);
+
+templatedRSTwoChunksOpenReader('ReadableStream (two chunks enqueued, still open) reader', () => {
+  return streamAndDefaultReader(new ReadableStream({
+    start(c) {
+      c.enqueue(chunks[0]);
+      c.enqueue(chunks[1]);
+    }
+  }));
+}, chunks);
+
+templatedRSTwoChunksClosedReader('ReadableStream (two chunks enqueued, then closed) reader', () => {
+  let doClose;
+  const stream = new ReadableStream({
+    start(c) {
+      c.enqueue(chunks[0]);
+      c.enqueue(chunks[1]);
+      doClose = c.close.bind(c);
+    }
+  });
+  const result = streamAndDefaultReader(stream);
+  doClose();
+  return result;
+}, chunks);
+
+function streamAndDefaultReader(stream) {
+  return { stream, reader: stream.getReader() };
+}
+
+done();
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/streams/resources/rs-test-templates.js b/third_party/WebKit/LayoutTests/imported/wpt/streams/resources/rs-test-templates.js
new file mode 100644
index 0000000..e36463ba
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/streams/resources/rs-test-templates.js
@@ -0,0 +1,634 @@
+'use strict';
+
+// These tests can be run against any readable stream produced by the web platform that meets the given descriptions.
+// For readable stream tests, the factory should return the stream. For reader tests, the factory should return a
+// { stream, reader } object. (You can use this to vary the time at which you acquire a reader.)
+
+self.templatedRSEmpty = (label, factory) => {
+  test(() => {}, 'Running templatedRSEmpty with ' + label);
+
+  test(() => {
+
+    const rs = factory();
+
+    assert_equals(typeof rs.locked, 'boolean', 'has a boolean locked getter');
+    assert_equals(typeof rs.cancel, 'function', 'has a cancel method');
+    assert_equals(typeof rs.getReader, 'function', 'has a getReader method');
+    assert_equals(typeof rs.pipeThrough, 'function', 'has a pipeThrough method');
+    assert_equals(typeof rs.pipeTo, 'function', 'has a pipeTo method');
+    assert_equals(typeof rs.tee, 'function', 'has a tee method');
+
+  }, 'instances have the correct methods and properties');
+
+  test(() => {
+    const rs = factory();
+
+    assert_throws(new RangeError(), () => rs.getReader({ mode: '' }), 'empty string mode should throw');
+    assert_throws(new RangeError(), () => rs.getReader({ mode: null }), 'null mode should throw');
+    assert_throws(new RangeError(), () => rs.getReader({ mode: 'asdf' }), 'asdf mode should throw');
+    assert_throws(new TypeError(), () => rs.getReader(null), 'null should throw');
+
+  }, 'calling getReader with invalid arguments should throw appropriate errors');
+};
+
+self.templatedRSClosed = (label, factory) => {
+  test(() => {}, 'Running templatedRSClosed with ' + label);
+
+  promise_test(() => {
+
+    const rs = factory();
+    const cancelPromise1 = rs.cancel();
+    const cancelPromise2 = rs.cancel();
+
+    assert_not_equals(cancelPromise1, cancelPromise2, 'cancel() calls should return distinct promises');
+
+    return Promise.all([
+      cancelPromise1.then(v => assert_equals(v, undefined, 'first cancel() call should fulfill with undefined')),
+      cancelPromise2.then(v => assert_equals(v, undefined, 'second cancel() call should fulfill with undefined'))
+    ]);
+
+  }, 'cancel() should return a distinct fulfilled promise each time');
+
+  test(() => {
+
+    const rs = factory();
+    assert_false(rs.locked, 'locked getter should return false');
+
+  }, 'locked should be false');
+
+  test(() => {
+
+    const rs = factory();
+    rs.getReader(); // getReader() should not throw.
+
+  }, 'getReader() should be OK');
+
+  test(() => {
+
+    const rs = factory();
+
+    const reader = rs.getReader();
+    reader.releaseLock();
+
+    const reader2 = rs.getReader(); // Getting a second reader should not throw.
+    reader2.releaseLock();
+
+    rs.getReader(); // Getting a third reader should not throw.
+
+  }, 'should be able to acquire multiple readers if they are released in succession');
+
+  test(() => {
+
+    const rs = factory();
+
+    rs.getReader();
+
+    assert_throws(new TypeError(), () => rs.getReader(), 'getting a second reader should throw');
+    assert_throws(new TypeError(), () => rs.getReader(), 'getting a third reader should throw');
+
+  }, 'should not be able to acquire a second reader if we don\'t release the first one');
+};
+
+self.templatedRSErrored = (label, factory, error) => {
+  test(() => {}, 'Running templatedRSErrored with ' + label);
+
+  promise_test(t => {
+
+    const rs = factory();
+    const reader = rs.getReader();
+
+    return Promise.all([
+      promise_rejects(t, error, reader.closed),
+      promise_rejects(t, error, reader.read())
+    ]);
+
+  }, 'getReader() should return a reader that acts errored');
+
+  promise_test(t => {
+
+    const rs = factory();
+    const reader = rs.getReader();
+
+    return Promise.all([
+      promise_rejects(t, error, reader.read()),
+      promise_rejects(t, error, reader.read()),
+      promise_rejects(t, error, reader.closed)
+    ]);
+
+  }, 'read() twice should give the error each time');
+
+  test(() => {
+    const rs = factory();
+
+    assert_false(rs.locked, 'locked getter should return false');
+  }, 'locked should be false');
+};
+
+self.templatedRSErroredSyncOnly = (label, factory, error) => {
+  test(() => {}, 'Running templatedRSErroredSyncOnly with ' + label);
+
+  promise_test(t => {
+
+    const rs = factory();
+    rs.getReader().releaseLock();
+    const reader = rs.getReader(); // Calling getReader() twice does not throw (the stream is not locked).
+
+    return promise_rejects(t, error, reader.closed);
+
+  }, 'should be able to obtain a second reader, with the correct closed promise');
+
+  test(() => {
+
+    const rs = factory();
+    rs.getReader();
+
+    assert_throws(new TypeError(), () => rs.getReader(), 'getting a second reader should throw a TypeError');
+    assert_throws(new TypeError(), () => rs.getReader(), 'getting a third reader should throw a TypeError');
+
+  }, 'should not be able to obtain additional readers if we don\'t release the first lock');
+
+  promise_test(t => {
+
+    const rs = factory();
+    const cancelPromise1 = rs.cancel();
+    const cancelPromise2 = rs.cancel();
+
+    assert_not_equals(cancelPromise1, cancelPromise2, 'cancel() calls should return distinct promises');
+
+    return Promise.all([
+      promise_rejects(t, error, cancelPromise1),
+      promise_rejects(t, error, cancelPromise2)
+    ]);
+
+  }, 'cancel() should return a distinct rejected promise each time');
+
+  promise_test(t => {
+
+    const rs = factory();
+    const reader = rs.getReader();
+    const cancelPromise1 = reader.cancel();
+    const cancelPromise2 = reader.cancel();
+
+    assert_not_equals(cancelPromise1, cancelPromise2, 'cancel() calls should return distinct promises');
+
+    return Promise.all([
+      promise_rejects(t, error, cancelPromise1),
+      promise_rejects(t, error, cancelPromise2)
+    ]);
+
+  }, 'reader cancel() should return a distinct rejected promise each time');
+};
+
+self.templatedRSEmptyReader = (label, factory) => {
+  test(() => {}, 'Running templatedRSEmptyReader with ' + label);
+
+  test(() => {
+
+    const reader = factory().reader;
+
+    assert_true('closed' in reader, 'has a closed property');
+    assert_equals(typeof reader.closed.then, 'function', 'closed property is thenable');
+
+    assert_equals(typeof reader.cancel, 'function', 'has a cancel method');
+    assert_equals(typeof reader.read, 'function', 'has a read method');
+    assert_equals(typeof reader.releaseLock, 'function', 'has a releaseLock method');
+
+  }, 'instances have the correct methods and properties');
+
+  test(() => {
+
+    const stream = factory().stream;
+
+    assert_true(stream.locked, 'locked getter should return true');
+
+  }, 'locked should be true');
+
+  promise_test(t => {
+
+    const reader = factory().reader;
+
+    reader.read().then(
+      t.unreached_func('read() should not fulfill'),
+      t.unreached_func('read() should not reject')
+    );
+
+    return delay(500);
+
+  }, 'read() should never settle');
+
+  promise_test(t => {
+
+    const reader = factory().reader;
+
+    reader.read().then(
+      t.unreached_func('read() should not fulfill'),
+      t.unreached_func('read() should not reject')
+    );
+
+    reader.read().then(
+      t.unreached_func('read() should not fulfill'),
+      t.unreached_func('read() should not reject')
+    );
+
+    return delay(500);
+
+  }, 'two read()s should both never settle');
+
+  test(() => {
+
+    const reader = factory().reader;
+    assert_not_equals(reader.read(), reader.read(), 'the promises returned should be distinct');
+
+  }, 'read() should return distinct promises each time');
+
+  test(() => {
+
+    const stream = factory().stream;
+    assert_throws(new TypeError(), () => stream.getReader(), 'stream.getReader() should throw a TypeError');
+
+  }, 'getReader() again on the stream should fail');
+
+  promise_test(t => {
+
+    const streamAndReader = factory();
+    const stream = streamAndReader.stream;
+    const reader = streamAndReader.reader;
+
+    reader.read().then(
+      t.unreached_func('first read() should not fulfill'),
+      t.unreached_func('first read() should not reject')
+    );
+
+    reader.read().then(
+      t.unreached_func('second read() should not fulfill'),
+      t.unreached_func('second read() should not reject')
+    );
+
+    reader.closed.then(
+      t.unreached_func('closed should not fulfill'),
+      t.unreached_func('closed should not reject')
+    );
+
+    assert_throws(new TypeError(), () => reader.releaseLock(), 'releaseLock should throw a TypeError');
+
+    assert_true(stream.locked, 'the stream should still be locked');
+
+    return delay(500);
+
+  }, 'releasing the lock with pending read requests should throw but the read requests should stay pending');
+
+  promise_test(t => {
+
+    const reader = factory().reader;
+    reader.releaseLock();
+
+    return Promise.all([
+      promise_rejects(t, new TypeError(), reader.read()),
+      promise_rejects(t, new TypeError(), reader.read())
+    ]);
+
+  }, 'releasing the lock should cause further read() calls to reject with a TypeError');
+
+  promise_test(t => {
+
+    const reader = factory().reader;
+
+    const closedBefore = reader.closed;
+    reader.releaseLock();
+    const closedAfter = reader.closed;
+
+    assert_equals(closedBefore, closedAfter, 'the closed promise should not change identity');
+
+    return promise_rejects(t, new TypeError(), closedBefore);
+
+  }, 'releasing the lock should cause closed calls to reject with a TypeError');
+
+  test(() => {
+
+    const streamAndReader = factory();
+    const stream = streamAndReader.stream;
+    const reader = streamAndReader.reader;
+
+    reader.releaseLock();
+    assert_false(stream.locked, 'locked getter should return false');
+
+  }, 'releasing the lock should cause locked to become false');
+
+  promise_test(() => {
+
+    const reader = factory().reader;
+    reader.cancel();
+
+    return reader.read().then(r => {
+      assert_object_equals(r, { value: undefined, done: true }, 'read()ing from the reader should give a done result');
+    });
+
+  }, 'canceling via the reader should cause the reader to act closed');
+
+  promise_test(t => {
+
+    const stream = factory().stream;
+    return promise_rejects(t, new TypeError(), stream.cancel());
+
+  }, 'canceling via the stream should fail');
+};
+
+self.templatedRSClosedReader = (label, factory) => {
+  test(() => {}, 'Running templatedRSClosedReader with ' + label);
+
+  promise_test(() => {
+
+    const reader = factory().reader;
+
+    return reader.read().then(v => {
+      assert_object_equals(v, { value: undefined, done: true }, 'read() should fulfill correctly');
+    });
+
+  }, 'read() should fulfill with { value: undefined, done: true }');
+
+  promise_test(() => {
+
+    const reader = factory().reader;
+
+    return Promise.all([
+      reader.read().then(v => {
+        assert_object_equals(v, { value: undefined, done: true }, 'read() should fulfill correctly');
+      }),
+      reader.read().then(v => {
+        assert_object_equals(v, { value: undefined, done: true }, 'read() should fulfill correctly');
+      })
+    ]);
+
+  }, 'read() multiple times should fulfill with { value: undefined, done: true }');
+
+  promise_test(() => {
+
+    const reader = factory().reader;
+
+    return reader.read().then(() => reader.read()).then(v => {
+      assert_object_equals(v, { value: undefined, done: true }, 'read() should fulfill correctly');
+    });
+
+  }, 'read() should work when used within another read() fulfill callback');
+
+  promise_test(() => {
+
+    const reader = factory().reader;
+
+    return reader.closed.then(v => assert_equals(v, undefined, 'reader closed should fulfill with undefined'));
+
+  }, 'closed should fulfill with undefined');
+
+  promise_test(t => {
+
+    const reader = factory().reader;
+
+    const closedBefore = reader.closed;
+    reader.releaseLock();
+    const closedAfter = reader.closed;
+
+    assert_not_equals(closedBefore, closedAfter, 'the closed promise should change identity');
+
+    return Promise.all([
+      closedBefore.then(v => assert_equals(v, undefined, 'reader.closed acquired before release should fulfill')),
+      promise_rejects(t, new TypeError(), closedAfter)
+    ]);
+
+  }, 'releasing the lock should cause closed to reject and change identity');
+
+  promise_test(() => {
+
+    const reader = factory().reader;
+    const cancelPromise1 = reader.cancel();
+    const cancelPromise2 = reader.cancel();
+    const closedReaderPromise = reader.closed;
+
+    assert_not_equals(cancelPromise1, cancelPromise2, 'cancel() calls should return distinct promises');
+    assert_not_equals(cancelPromise1, closedReaderPromise, 'cancel() promise 1 should be distinct from reader.closed');
+    assert_not_equals(cancelPromise2, closedReaderPromise, 'cancel() promise 2 should be distinct from reader.closed');
+
+    return Promise.all([
+      cancelPromise1.then(v => assert_equals(v, undefined, 'first cancel() should fulfill with undefined')),
+      cancelPromise2.then(v => assert_equals(v, undefined, 'second cancel() should fulfill with undefined'))
+    ]);
+
+  }, 'cancel() should return a distinct fulfilled promise each time');
+};
+
+self.templatedRSErroredReader = (label, factory, error) => {
+  test(() => {}, 'Running templatedRSErroredReader with ' + label);
+
+  promise_test(t => {
+
+    const reader = factory().reader;
+    return promise_rejects(t, error, reader.closed);
+
+  }, 'closed should reject with the error');
+
+  promise_test(t => {
+
+    const reader = factory().reader;
+    const closedBefore = reader.closed;
+
+    return promise_rejects(t, error, closedBefore).then(() => {
+      reader.releaseLock();
+
+      const closedAfter = reader.closed;
+      assert_not_equals(closedBefore, closedAfter, 'the closed promise should change identity');
+
+      return promise_rejects(t, new TypeError(), closedAfter);
+    });
+
+  }, 'releasing the lock should cause closed to reject and change identity');
+
+  promise_test(t => {
+
+    const reader = factory().reader;
+    return promise_rejects(t, error, reader.read());
+
+  }, 'read() should reject with the error');
+};
+
+self.templatedRSTwoChunksOpenReader = (label, factory, chunks) => {
+  test(() => {}, 'Running templatedRSTwoChunksOpenReader with ' + label);
+
+  promise_test(() => {
+
+    const reader = factory().reader;
+
+    return Promise.all([
+      reader.read().then(r => {
+        assert_object_equals(r, { value: chunks[0], done: false }, 'first result should be correct');
+      }),
+      reader.read().then(r => {
+        assert_object_equals(r, { value: chunks[1], done: false }, 'second result should be correct');
+      })
+    ]);
+
+  }, 'calling read() twice without waiting will eventually give both chunks (sequential)');
+
+  promise_test(() => {
+
+    const reader = factory().reader;
+
+    return reader.read().then(r => {
+      assert_object_equals(r, { value: chunks[0], done: false }, 'first result should be correct');
+
+      return reader.read().then(r2 => {
+        assert_object_equals(r2, { value: chunks[1], done: false }, 'second result should be correct');
+      });
+    });
+
+  }, 'calling read() twice without waiting will eventually give both chunks (nested)');
+
+  test(() => {
+
+    const reader = factory().reader;
+    assert_not_equals(reader.read(), reader.read(), 'the promises returned should be distinct');
+
+  }, 'read() should return distinct promises each time');
+
+  promise_test(() => {
+
+    const reader = factory().reader;
+
+    const promise1 = reader.closed.then(v => {
+      assert_equals(v, undefined, 'reader closed should fulfill with undefined');
+    });
+
+    const promise2 = reader.read().then(r => {
+      assert_object_equals(r, { value: chunks[0], done: false },
+                           'promise returned before cancellation should fulfill with a chunk');
+    });
+
+    reader.cancel();
+
+    const promise3 = reader.read().then(r => {
+      assert_object_equals(r, { value: undefined, done: true },
+                           'promise returned after cancellation should fulfill with an end-of-stream signal');
+    });
+
+    return Promise.all([promise1, promise2, promise3]);
+
+  }, 'cancel() after a read() should still give that single read result');
+};
+
+self.templatedRSTwoChunksClosedReader = function (label, factory, chunks) {
+  test(() => {}, 'Running templatedRSTwoChunksClosedReader with ' + label);
+
+  promise_test(() => {
+
+    const reader = factory().reader;
+
+    return Promise.all([
+      reader.read().then(r => {
+        assert_object_equals(r, { value: chunks[0], done: false }, 'first result should be correct');
+      }),
+      reader.read().then(r => {
+        assert_object_equals(r, { value: chunks[1], done: false }, 'second result should be correct');
+      }),
+      reader.read().then(r => {
+        assert_object_equals(r, { value: undefined, done: true }, 'third result should be correct');
+      })
+    ]);
+
+  }, 'third read(), without waiting, should give { value: undefined, done: true } (sequential)');
+
+  promise_test(() => {
+
+    const reader = factory().reader;
+
+    return reader.read().then(r => {
+      assert_object_equals(r, { value: chunks[0], done: false }, 'first result should be correct');
+
+      return reader.read().then(r2 => {
+        assert_object_equals(r2, { value: chunks[1], done: false }, 'second result should be correct');
+
+        return reader.read().then(r3 => {
+          assert_object_equals(r3, { value: undefined, done: true }, 'third result should be correct');
+        });
+      });
+    });
+
+  }, 'third read(), without waiting, should give { value: undefined, done: true } (nested)');
+
+  promise_test(() => {
+
+    const streamAndReader = factory();
+    const stream = streamAndReader.stream;
+    const reader = streamAndReader.reader;
+
+    assert_true(stream.locked, 'stream should start locked');
+
+    const promise = reader.closed.then(v => {
+      assert_equals(v, undefined, 'reader closed should fulfill with undefined');
+      assert_true(stream.locked, 'stream should remain locked');
+    });
+
+    reader.read();
+    reader.read();
+
+    return promise;
+
+  }, 'draining the stream via read() should cause the reader closed promise to fulfill, but locked stays true');
+
+  promise_test(() => {
+
+    const streamAndReader = factory();
+    const stream = streamAndReader.stream;
+    const reader = streamAndReader.reader;
+
+    const promise = reader.closed.then(() => {
+      assert_true(stream.locked, 'the stream should start locked');
+      reader.releaseLock(); // Releasing the lock after reader closed should not throw.
+      assert_false(stream.locked, 'the stream should end unlocked');
+    });
+
+    reader.read();
+    reader.read();
+
+    return promise;
+
+  }, 'releasing the lock after the stream is closed should cause locked to become false');
+
+  promise_test(t => {
+
+    const reader = factory().reader;
+
+    reader.releaseLock();
+
+    return Promise.all([
+      promise_rejects(t, new TypeError(), reader.read()),
+      promise_rejects(t, new TypeError(), reader.read()),
+      promise_rejects(t, new TypeError(), reader.read())
+    ]);
+
+  }, 'releasing the lock should cause further read() calls to reject with a TypeError');
+
+  promise_test(() => {
+
+    const streamAndReader = factory();
+    const stream = streamAndReader.stream;
+    const reader = streamAndReader.reader;
+
+    const readerClosed = reader.closed;
+
+    assert_equals(reader.closed, readerClosed, 'accessing reader.closed twice in succession gives the same value');
+
+    const promise = reader.read().then(() => {
+      assert_equals(reader.closed, readerClosed, 'reader.closed is the same after read() fulfills');
+
+      reader.releaseLock();
+
+      assert_equals(reader.closed, readerClosed, 'reader.closed is the same after releasing the lock');
+
+      const newReader = stream.getReader();
+      return newReader.read();
+    });
+
+    assert_equals(reader.closed, readerClosed, 'reader.closed is the same after calling read()');
+
+    return promise;
+
+  }, 'reader\'s closed property always returns the same promise');
+};
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/streams/resources/rs-utils.js b/third_party/WebKit/LayoutTests/imported/wpt/streams/resources/rs-utils.js
new file mode 100644
index 0000000..0f3222e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/streams/resources/rs-utils.js
@@ -0,0 +1,185 @@
+'use strict';
+(function () {
+
+  class RandomPushSource {
+    constructor(toPush) {
+      this.pushed = 0;
+      this.toPush = toPush;
+      this.started = false;
+      this.paused = false;
+      this.closed = false;
+
+      this._intervalHandle = null;
+    }
+
+    readStart() {
+      if (this.closed) {
+        return;
+      }
+
+      if (!this.started) {
+        this._intervalHandle = setInterval(writeChunk, 2);
+        this.started = true;
+      }
+
+      if (this.paused) {
+        this._intervalHandle = setInterval(writeChunk, 2);
+        this.paused = false;
+      }
+
+      const source = this;
+      function writeChunk() {
+        if (source.paused) {
+          return;
+        }
+
+        source.pushed++;
+
+        if (source.toPush > 0 && source.pushed > source.toPush) {
+          if (source._intervalHandle) {
+            clearInterval(source._intervalHandle);
+            source._intervalHandle = undefined;
+          }
+          source.closed = true;
+          source.onend();
+        } else {
+          source.ondata(randomChunk(128));
+        }
+      }
+    }
+
+    readStop() {
+      if (this.paused) {
+        return;
+      }
+
+      if (this.started) {
+        this.paused = true;
+        clearInterval(this._intervalHandle);
+        this._intervalHandle = undefined;
+      } else {
+        throw new Error('Can\'t pause reading an unstarted source.');
+      }
+    }
+  }
+
+  function randomChunk(size) {
+    let chunk = '';
+
+    for (let i = 0; i < size; ++i) {
+      // Add a random character from the basic printable ASCII set.
+      chunk += String.fromCharCode(Math.round(Math.random() * 84) + 32);
+    }
+
+    return chunk;
+  }
+
+  function readableStreamToArray(readable, reader) {
+    if (reader === undefined) {
+      reader = readable.getReader();
+    }
+
+    const chunks = [];
+
+    return pump();
+
+    function pump() {
+      return reader.read().then(result => {
+        if (result.done) {
+          return chunks;
+        }
+
+        chunks.push(result.value);
+        return pump();
+      });
+    }
+  }
+
+  class SequentialPullSource {
+    constructor(limit, options) {
+      const async = options && options.async;
+
+      this.current = 0;
+      this.limit = limit;
+      this.opened = false;
+      this.closed = false;
+
+      this._exec = f => f();
+      if (async) {
+        this._exec = f => setTimeout(f, 0);
+      }
+    }
+
+    open(cb) {
+      this._exec(() => {
+        this.opened = true;
+        cb();
+      });
+    }
+
+    read(cb) {
+      this._exec(() => {
+        if (++this.current <= this.limit) {
+          cb(null, false, this.current);
+        } else {
+          cb(null, true, null);
+        }
+      });
+    }
+
+    close(cb) {
+      this._exec(() => {
+        this.closed = true;
+        cb();
+      });
+    }
+  }
+
+  function sequentialReadableStream(limit, options) {
+    const sequentialSource = new SequentialPullSource(limit, options);
+
+    const stream = new ReadableStream({
+      start() {
+        return new Promise((resolve, reject) => {
+          sequentialSource.open(err => {
+            if (err) {
+              reject(err);
+            }
+            resolve();
+          });
+        });
+      },
+
+      pull(c) {
+        return new Promise((resolve, reject) => {
+          sequentialSource.read((err, done, chunk) => {
+            if (err) {
+              reject(err);
+            } else if (done) {
+              sequentialSource.close(err2 => {
+                if (err2) {
+                  reject(err2);
+                }
+                c.close();
+                resolve();
+              });
+            } else {
+              c.enqueue(chunk);
+              resolve();
+            }
+          });
+        });
+      }
+    });
+
+    stream.source = sequentialSource;
+
+    return stream;
+  }
+
+
+  self.RandomPushSource = RandomPushSource;
+  self.readableStreamToArray = readableStreamToArray;
+  self.sequentialReadableStream = sequentialReadableStream;
+
+}());
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/streams/resources/test-initializer.js b/third_party/WebKit/LayoutTests/imported/wpt/streams/resources/test-initializer.js
new file mode 100644
index 0000000..d6ed4a7aa
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/streams/resources/test-initializer.js
@@ -0,0 +1,14 @@
+'use strict';
+
+function worker_test(file) {
+  fetch_tests_from_worker(new Worker(file));
+  if (typeof SharedWorker === 'function') {
+    fetch_tests_from_worker(new SharedWorker(file));
+  } else {
+    test(() => {
+      assert_unreached('SharedWorker is unavailable');
+    }, 'Load ' + file + ' with SharedWorker');
+  }
+  service_worker_test(file);
+}
+
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/streams/resources/test-utils.js b/third_party/WebKit/LayoutTests/imported/wpt/streams/resources/test-utils.js
new file mode 100644
index 0000000..cc47898a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/streams/resources/test-utils.js
@@ -0,0 +1,43 @@
+'use strict';
+
+self.getterRejects = (t, obj, getterName, target) => {
+  const getter = Object.getOwnPropertyDescriptor(obj, getterName).get;
+
+  return promise_rejects(t, new TypeError(), getter.call(target));
+};
+
+self.methodRejects = (t, obj, methodName, target) => {
+  const method = obj[methodName];
+
+  return promise_rejects(t, new TypeError(), method.call(target));
+};
+
+self.getterThrows = (obj, getterName, target) => {
+  const getter = Object.getOwnPropertyDescriptor(obj, getterName).get;
+
+  assert_throws(new TypeError(), () => getter.call(target), getterName + ' should throw a TypeError');
+};
+
+self.methodThrows = (obj, methodName, target, args) => {
+  const method = obj[methodName];
+
+  assert_throws(new TypeError(), () => method.apply(target, args), methodName + ' should throw a TypeError');
+};
+
+self.garbageCollect = () => {
+  if (self.gc) {
+    // Use --expose_gc for V8 (and Node.js)
+    // Exposed in SpiderMonkey shell as well
+    self.gc();
+  } else if (self.GCController) {
+    // Present in some WebKit development environments
+    GCController.collect();
+  } else {
+    /* eslint-disable no-console */
+    console.warn('Tests are running without the ability to do manual garbage collection. They will still work, but ' +
+      'coverage will be suboptimal.');
+    /* eslint-enable no-console */
+  }
+};
+
+self.delay = ms => new Promise(resolve => step_timeout(resolve, ms));
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/svg/historical-expected.txt b/third_party/WebKit/LayoutTests/imported/wpt/svg/historical-expected.txt
index 6dbbd66a..ea0c68c 100644
--- a/third_party/WebKit/LayoutTests/imported/wpt/svg/historical-expected.txt
+++ b/third_party/WebKit/LayoutTests/imported/wpt/svg/historical-expected.txt
@@ -31,6 +31,7 @@
 FAIL SVGSVGElement.prototype.currentView must be removed assert_false: expected false got true
 FAIL SVGSVGElement.prototype.useCurrentView must be removed assert_false: expected false got true
 PASS SVGSVGElement.prototype.viewport must be removed 
+FAIL SVGViewElement.prototype.viewTarget must be removed assert_false: expected false got true
 PASS SVGPathElement.prototype.getPointAtLength must be moved to SVGGeometryElement.prototype 
 PASS SVGPathElement.prototype.getTotalLength must be moved to SVGGeometryElement.prototype 
 FAIL SVGPathElement.prototype.pathLength must be moved to SVGGeometryElement.prototype assert_false: expected false got true
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/svg/historical.html b/third_party/WebKit/LayoutTests/imported/wpt/svg/historical.html
index 8f28002..de15f81 100644
--- a/third_party/WebKit/LayoutTests/imported/wpt/svg/historical.html
+++ b/third_party/WebKit/LayoutTests/imported/wpt/svg/historical.html
@@ -57,6 +57,9 @@
     "currentView",
     "useCurrentView",
     "viewport"
+  ],
+  "SVGViewElement": [
+    "viewTarget"
   ]
 }
 for (var name in removedMembers) {
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/testharness_runner.html b/third_party/WebKit/LayoutTests/imported/wpt/testharness_runner.html
new file mode 100644
index 0000000..1cc80a2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imported/wpt/testharness_runner.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<title></title>
+<script>
+var timeout_multiplier = 1;
+var win = null;
+</script>