Implement subresource loading with Web Bundles
- Adds WebBundleLoader class which loads a WebBundle resource using
ThreadableLoader, and creates a WebBundleSubresourceLoaderFactory.
- LinkWebBundle is registered in ResourceFetcher, and used to forward
subresource requests that match the corresponding <link>'s resources=
attribute to the WebBundleSubresourceLoaderFactory. This is done via
the SubresourceWebBundle interface.
Bug: 1082020
Change-Id: I3f26bd6b1fbd5855caa5175cdcb3db0fe103a093
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2291809
Reviewed-by: Kinuko Yasuda <kinuko@chromium.org>
Reviewed-by: Tsuyoshi Horo <horo@chromium.org>
Reviewed-by: Hayato Ito <hayato@chromium.org>
Commit-Queue: Kunihiko Sakamoto <ksakamoto@chromium.org>
Cr-Commit-Position: refs/heads/master@{#788842}
diff --git a/third_party/blink/renderer/core/html/link_web_bundle.cc b/third_party/blink/renderer/core/html/link_web_bundle.cc
index 630a103..15a54a9 100644
--- a/third_party/blink/renderer/core/html/link_web_bundle.cc
+++ b/third_party/blink/renderer/core/html/link_web_bundle.cc
@@ -4,12 +4,134 @@
#include "third_party/blink/renderer/core/html/link_web_bundle.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
+#include "third_party/blink/renderer/core/html/html_link_element.h"
+#include "third_party/blink/renderer/core/loader/threadable_loader.h"
+#include "third_party/blink/renderer/core/loader/threadable_loader_client.h"
+#include "third_party/blink/renderer/platform/loader/cors/cors.h"
+#include "third_party/blink/renderer/platform/loader/fetch/bytes_consumer.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
+#include "third_party/blink/renderer/platform/loader/fetch/url_loader/web_bundle_subresource_loader.h"
+
namespace blink {
+// WebBundleLoader is responsible for loading a WebBundle resource.
+class WebBundleLoader : public GarbageCollected<WebBundleLoader>,
+ public ThreadableLoaderClient {
+ USING_GARBAGE_COLLECTED_MIXIN(WebBundleLoader);
+
+ public:
+ WebBundleLoader(LinkWebBundle& link_web_bundle,
+ ExecutionContext& execution_context,
+ const KURL& url)
+ : link_web_bundle_(&link_web_bundle),
+ pending_factory_receiver_(
+ loader_factory_.BindNewPipeAndPassReceiver()) {
+ ResourceRequest request(url);
+ request.SetUseStreamOnResponse(true);
+ // TODO(crbug.com/1082020): Revisit these once the fetch and process the
+ // linked resource algorithm [1] for <link rel=webbundle> is defined.
+ // [1]
+ // https://html.spec.whatwg.org/multipage/semantics.html#fetch-and-process-the-linked-resource
+ request.SetRequestContext(mojom::blink::RequestContextType::SUBRESOURCE);
+ request.SetMode(network::mojom::blink::RequestMode::kCors);
+ request.SetCredentialsMode(network::mojom::blink::CredentialsMode::kOmit);
+
+ ResourceLoaderOptions resource_loader_options;
+ resource_loader_options.data_buffering_policy = kDoNotBufferData;
+
+ loader_ = MakeGarbageCollected<ThreadableLoader>(execution_context, this,
+ resource_loader_options);
+ loader_->Start(std::move(request));
+ }
+
+ void Trace(Visitor* visitor) const override {
+ visitor->Trace(link_web_bundle_);
+ visitor->Trace(loader_);
+ }
+
+ bool HasLoaded() const { return !failed_; }
+
+ mojo::PendingRemote<network::mojom::blink::URLLoaderFactory>
+ GetURLLoaderFactory() {
+ mojo::PendingRemote<network::mojom::blink::URLLoaderFactory> factory_clone;
+ loader_factory_->Clone(factory_clone.InitWithNewPipeAndPassReceiver());
+ return factory_clone;
+ }
+
+ // ThreadableLoaderClient
+ void DidReceiveResponse(uint64_t, const ResourceResponse& response) override {
+ if (!cors::IsOkStatus(response.HttpStatusCode()))
+ failed_ = true;
+ // TODO(crbug.com/1082020): Check response headers, as spec'ed in
+ // https://wicg.github.io/webpackage/draft-yasskin-wpack-bundled-exchanges.html#name-serving-constraints.
+ }
+
+ void DidStartLoadingResponseBody(BytesConsumer& consumer) override {
+ DCHECK(pending_factory_receiver_);
+ CreateWebBundleSubresourceLoaderFactory(
+ mojo::PendingReceiver<network::mojom::URLLoaderFactory>(
+ pending_factory_receiver_.PassPipe()),
+ consumer.DrainAsDataPipe());
+ // TODO(crbug.com/1082020): Set |failed_| to true on metadata parse error,
+ // so that "error" event is dispatched.
+ }
+
+ void DidFinishLoading(uint64_t) override { link_web_bundle_->NotifyLoaded(); }
+ void DidFail(const ResourceError&) override { DidFailInternal(); }
+ void DidFailRedirectCheck() override { DidFailInternal(); }
+
+ private:
+ void DidFailInternal() {
+ if (pending_factory_receiver_) {
+ // If we haven't create a WebBundleSubresourceLoaderFactory, create it
+ // with an empty bundle body so that requests to
+ // |pending_factory_receiver_| are processed (and fail).
+ CreateWebBundleSubresourceLoaderFactory(
+ mojo::PendingReceiver<network::mojom::URLLoaderFactory>(
+ pending_factory_receiver_.PassPipe()),
+ mojo::ScopedDataPipeConsumerHandle());
+ }
+ failed_ = true;
+ link_web_bundle_->NotifyLoaded();
+ }
+
+ Member<LinkWebBundle> link_web_bundle_;
+ Member<ThreadableLoader> loader_;
+ mojo::Remote<network::mojom::blink::URLLoaderFactory> loader_factory_;
+ mojo::PendingReceiver<network::mojom::blink::URLLoaderFactory>
+ pending_factory_receiver_;
+ bool failed_ = false;
+};
+
LinkWebBundle::LinkWebBundle(HTMLLinkElement* owner) : LinkResource(owner) {}
+LinkWebBundle::~LinkWebBundle() = default;
+
+void LinkWebBundle::Trace(Visitor* visitor) const {
+ visitor->Trace(bundle_loader_);
+ LinkResource::Trace(visitor);
+ SubresourceWebBundle::Trace(visitor);
+}
+
+void LinkWebBundle::NotifyLoaded() {
+ if (owner_)
+ owner_->ScheduleEvent();
+}
void LinkWebBundle::Process() {
- // TODO(crbug.com/1082020): Implement this.
+ if (!owner_ || !owner_->GetDocument().GetFrame())
+ return;
+ if (!owner_->ShouldLoadLink())
+ return;
+
+ ResourceFetcher* resource_fetcher = owner_->GetDocument().Fetcher();
+ if (!resource_fetcher)
+ return;
+
+ bundle_loader_ = MakeGarbageCollected<WebBundleLoader>(
+ *this, *owner_->GetDocument().GetExecutionContext(), owner_->Href());
+
+ resource_fetcher->AddSubresourceWebBundle(*this);
}
LinkResource::LinkResourceType LinkWebBundle::GetType() const {
@@ -17,10 +139,28 @@
}
bool LinkWebBundle::HasLoaded() const {
- return false;
+ return bundle_loader_ && bundle_loader_->HasLoaded();
}
-void LinkWebBundle::OwnerRemoved() {}
+void LinkWebBundle::OwnerRemoved() {
+ if (!owner_)
+ return;
+ ResourceFetcher* resource_fetcher = owner_->GetDocument().Fetcher();
+ if (!resource_fetcher)
+ return;
+ resource_fetcher->RemoveSubresourceWebBundle(*this);
+ bundle_loader_ = nullptr;
+}
+
+bool LinkWebBundle::CanHandleRequest(const KURL& url) const {
+ return owner_ && owner_->ValidResourceUrls().Contains(url);
+}
+
+mojo::PendingRemote<network::mojom::blink::URLLoaderFactory>
+LinkWebBundle::GetURLLoaderFactory() {
+ DCHECK(bundle_loader_);
+ return bundle_loader_->GetURLLoaderFactory();
+}
// static
KURL LinkWebBundle::ParseResourceUrl(const AtomicString& str) {
diff --git a/third_party/blink/renderer/core/html/link_web_bundle.h b/third_party/blink/renderer/core/html/link_web_bundle.h
index 9d786edd..96cabd1 100644
--- a/third_party/blink/renderer/core/html/link_web_bundle.h
+++ b/third_party/blink/renderer/core/html/link_web_bundle.h
@@ -5,31 +5,50 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_HTML_LINK_WEB_BUNDLE_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_LINK_WEB_BUNDLE_H_
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "services/network/public/mojom/url_loader_factory.mojom-blink.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/html/link_resource.h"
+#include "third_party/blink/renderer/platform/loader/fetch/subresource_web_bundle.h"
#include "third_party/blink/renderer/platform/weborigin/kurl.h"
namespace blink {
+class WebBundleLoader;
+
// LinkWebBundle is used in the Subresource loading with Web Bundles feature.
// See crbug.com/1082020 for details.
// A <link rel="webbundle" ...> element creates LinkWebBundle.
-class CORE_EXPORT LinkWebBundle final : public LinkResource {
+class CORE_EXPORT LinkWebBundle final : public LinkResource,
+ public SubresourceWebBundle {
+ USING_GARBAGE_COLLECTED_MIXIN(LinkWebBundle);
+
public:
explicit LinkWebBundle(HTMLLinkElement* owner);
- ~LinkWebBundle() override = default;
+ ~LinkWebBundle() override;
+ void Trace(Visitor* visitor) const override;
+ void NotifyLoaded();
+
+ // LinkResource overrides:
void Process() override;
LinkResourceType GetType() const override;
bool HasLoaded() const override;
void OwnerRemoved() override;
+ // SubresourceWebBundle overrides:
+ bool CanHandleRequest(const KURL& url) const override;
+ mojo::PendingRemote<network::mojom::blink::URLLoaderFactory>
+ GetURLLoaderFactory() override;
+
// Parse the given |str| as a url. If |str| doesn't meet the criteria which
// WebBundles specification requires, this returns invalid empty KURL as an
// error.
// See
// https://wicg.github.io/webpackage/draft-yasskin-wpack-bundled-exchanges.html#name-parsing-the-index-section
static KURL ParseResourceUrl(const AtomicString& str);
+
+ Member<WebBundleLoader> bundle_loader_;
};
} // namespace blink
diff --git a/third_party/blink/renderer/platform/loader/BUILD.gn b/third_party/blink/renderer/platform/loader/BUILD.gn
index 900230a..1f64193 100644
--- a/third_party/blink/renderer/platform/loader/BUILD.gn
+++ b/third_party/blink/renderer/platform/loader/BUILD.gn
@@ -104,6 +104,7 @@
"fetch/source_keyed_cached_metadata_handler.h",
"fetch/stale_revalidation_resource_client.cc",
"fetch/stale_revalidation_resource_client.h",
+ "fetch/subresource_web_bundle.h",
"fetch/text_resource_decoder_options.cc",
"fetch/text_resource_decoder_options.h",
"fetch/trust_token_params_conversion.cc",
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
index be894cdc..2d452ac 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
@@ -37,6 +37,7 @@
#include "base/metrics/histogram_macros.h"
#include "base/time/time.h"
#include "services/network/public/cpp/request_mode.h"
+#include "services/network/public/mojom/url_loader_factory.mojom-blink.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/mime_util/mime_util.h"
#include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h"
@@ -69,6 +70,7 @@
#include "third_party/blink/renderer/platform/loader/fetch/resource_loading_log.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_timing_info.h"
#include "third_party/blink/renderer/platform/loader/fetch/stale_revalidation_resource_client.h"
+#include "third_party/blink/renderer/platform/loader/fetch/subresource_web_bundle.h"
#include "third_party/blink/renderer/platform/loader/fetch/unique_identifier.h"
#include "third_party/blink/renderer/platform/mhtml/archive_resource.h"
#include "third_party/blink/renderer/platform/mhtml/mhtml_archive.h"
@@ -1260,6 +1262,15 @@
const ResourceLoaderOptions& options) {
DCHECK(!GetProperties().IsDetached());
DCHECK(loader_factory_);
+ for (auto& bundle : subresource_web_bundles_) {
+ if (!bundle->CanHandleRequest(request.Url()))
+ continue;
+ ResourceLoaderOptions new_options(options);
+ new_options.url_loader_factory = base::MakeRefCounted<base::RefCountedData<
+ mojo::PendingRemote<network::mojom::blink::URLLoaderFactory>>>(
+ bundle->GetURLLoaderFactory());
+ return loader_factory_->CreateURLLoader(request, new_options, task_runner_);
+ }
return loader_factory_->CreateURLLoader(request, options, task_runner_);
}
@@ -2227,6 +2238,17 @@
return frame_or_worker_scheduler_.get();
}
+void ResourceFetcher::AddSubresourceWebBundle(
+ SubresourceWebBundle& subresource_web_bundle) {
+ DCHECK(RuntimeEnabledFeatures::SubresourceWebBundlesEnabled());
+ subresource_web_bundles_.insert(&subresource_web_bundle);
+}
+
+void ResourceFetcher::RemoveSubresourceWebBundle(
+ SubresourceWebBundle& subresource_web_bundle) {
+ subresource_web_bundles_.erase(&subresource_web_bundle);
+}
+
void ResourceFetcher::Trace(Visitor* visitor) const {
visitor->Trace(context_);
visitor->Trace(properties_);
@@ -2245,6 +2267,7 @@
visitor->Trace(matched_preloads_);
visitor->Trace(resource_timing_info_map_);
visitor->Trace(blob_registry_remote_);
+ visitor->Trace(subresource_web_bundles_);
}
// static
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h
index 1e52ae1..c49fea4 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h
@@ -61,6 +61,7 @@
class ResourceError;
class ResourceLoadObserver;
class ResourceTimingInfo;
+class SubresourceWebBundle;
class WebURLLoader;
struct ResourceFetcherInit;
struct ResourceLoaderOptions;
@@ -282,6 +283,9 @@
scheduler_->SetThrottleOptionOverride(throttle_option_override);
}
+ void AddSubresourceWebBundle(SubresourceWebBundle& subresource_web_bundle);
+ void RemoveSubresourceWebBundle(SubresourceWebBundle& subresource_web_bundle);
+
private:
friend class ResourceCacheValidationSuppressor;
enum class StopFetchingTarget {
@@ -437,6 +441,8 @@
HeapMojoWrapperMode::kWithoutContextObserver>
blob_registry_remote_;
+ HeapHashSet<Member<SubresourceWebBundle>> subresource_web_bundles_;
+
// This is not in the bit field below because we want to use AutoReset.
bool is_in_request_resource_ = false;
diff --git a/third_party/blink/renderer/platform/loader/fetch/subresource_web_bundle.h b/third_party/blink/renderer/platform/loader/fetch/subresource_web_bundle.h
new file mode 100644
index 0000000..6079ec8
--- /dev/null
+++ b/third_party/blink/renderer/platform/loader/fetch/subresource_web_bundle.h
@@ -0,0 +1,31 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_SUBRESOURCE_WEB_BUNDLE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_SUBRESOURCE_WEB_BUNDLE_H_
+
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "services/network/public/mojom/url_loader_factory.mojom-blink-forward.h"
+#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+
+class KURL;
+
+// SubresourceWebBundle is attached to ResourceFetcher and used to intercept
+// subresource requests for a certain set of URLs and serve responses from a
+// WebBundle. This is used for Subresource loading with Web Bundles
+// (https://github.com/WICG/webpackage/blob/master/explainers/subresource-loading.md).
+class PLATFORM_EXPORT SubresourceWebBundle : public GarbageCollectedMixin {
+ public:
+ void Trace(Visitor* visitor) const override {}
+ virtual bool CanHandleRequest(const KURL& url) const = 0;
+ virtual mojo::PendingRemote<network::mojom::blink::URLLoaderFactory>
+ GetURLLoaderFactory() = 0;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_SUBRESOURCE_WEB_BUNDLE_H_
diff --git a/third_party/blink/web_tests/external/wpt/lint.ignore b/third_party/blink/web_tests/external/wpt/lint.ignore
index 23cf860..40b7cb2 100644
--- a/third_party/blink/web_tests/external/wpt/lint.ignore
+++ b/third_party/blink/web_tests/external/wpt/lint.ignore
@@ -45,6 +45,7 @@
TRAILING WHITESPACE, INDENT TABS, CR AT EOL: *.wasm
TRAILING WHITESPACE, INDENT TABS, CR AT EOL: *.bmp
TRAILING WHITESPACE, INDENT TABS, CR AT EOL: *.sxg
+TRAILING WHITESPACE, INDENT TABS, CR AT EOL: *.wbn
## .gitignore
W3C-TEST.ORG: .gitignore
@@ -706,6 +707,7 @@
# Web Bundle files have hard-coded URLs
WEB-PLATFORM.TEST:web-bundle/resources/generate-test-wbns.sh
WEB-PLATFORM.TEST:web-bundle/resources/wbn/*.wbn
+WEB-PLATFORM.TEST:web-bundle/subresource-loading/subresource-loading-from-web-bundle.tentative.html
# Tests that depend on resources in /gen/ in Chromium:
# https://github.com/web-platform-tests/wpt/issues/16455
diff --git a/third_party/blink/web_tests/external/wpt/web-bundle/resources/dynamic/resource1.js b/third_party/blink/web_tests/external/wpt/web-bundle/resources/dynamic/resource1.js
new file mode 100644
index 0000000..7bd43736
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/web-bundle/resources/dynamic/resource1.js
@@ -0,0 +1 @@
+export const result = 'resource1 from network';
diff --git a/third_party/blink/web_tests/external/wpt/web-bundle/resources/dynamic/resource2.js b/third_party/blink/web_tests/external/wpt/web-bundle/resources/dynamic/resource2.js
new file mode 100644
index 0000000..09a4ee7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/web-bundle/resources/dynamic/resource2.js
@@ -0,0 +1 @@
+export const result = 'resource2 from network';
diff --git a/third_party/blink/web_tests/external/wpt/web-bundle/resources/dynamic/resource3.js b/third_party/blink/web_tests/external/wpt/web-bundle/resources/dynamic/resource3.js
new file mode 100644
index 0000000..851168c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/web-bundle/resources/dynamic/resource3.js
@@ -0,0 +1 @@
+export const result = 'resource3 from network';
diff --git a/third_party/blink/web_tests/external/wpt/web-bundle/resources/dynamic/resource4.js b/third_party/blink/web_tests/external/wpt/web-bundle/resources/dynamic/resource4.js
new file mode 100644
index 0000000..ea456b8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/web-bundle/resources/dynamic/resource4.js
@@ -0,0 +1 @@
+export const result = 'resource4 from network';
diff --git a/third_party/blink/web_tests/external/wpt/web-bundle/resources/dynamic1/resource1.js b/third_party/blink/web_tests/external/wpt/web-bundle/resources/dynamic1/resource1.js
new file mode 100644
index 0000000..6fd1de6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/web-bundle/resources/dynamic1/resource1.js
@@ -0,0 +1 @@
+export const result = 'resource1 from dynamic1.wbn';
diff --git a/third_party/blink/web_tests/external/wpt/web-bundle/resources/dynamic1/resource2.js b/third_party/blink/web_tests/external/wpt/web-bundle/resources/dynamic1/resource2.js
new file mode 100644
index 0000000..3cf012b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/web-bundle/resources/dynamic1/resource2.js
@@ -0,0 +1 @@
+export const result = 'resource2 from dynamic1.wbn';
diff --git a/third_party/blink/web_tests/external/wpt/web-bundle/resources/dynamic1/resource3.js b/third_party/blink/web_tests/external/wpt/web-bundle/resources/dynamic1/resource3.js
new file mode 100644
index 0000000..c8a4e25
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/web-bundle/resources/dynamic1/resource3.js
@@ -0,0 +1 @@
+export const result = 'resource3 from dynamic1.wbn';
diff --git a/third_party/blink/web_tests/external/wpt/web-bundle/resources/dynamic1/resource4.js b/third_party/blink/web_tests/external/wpt/web-bundle/resources/dynamic1/resource4.js
new file mode 100644
index 0000000..0b2f792
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/web-bundle/resources/dynamic1/resource4.js
@@ -0,0 +1 @@
+export const result = 'resource4 from dynamic1.wbn';
diff --git a/third_party/blink/web_tests/external/wpt/web-bundle/resources/dynamic2/resource1.js b/third_party/blink/web_tests/external/wpt/web-bundle/resources/dynamic2/resource1.js
new file mode 100644
index 0000000..c6f751ce
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/web-bundle/resources/dynamic2/resource1.js
@@ -0,0 +1 @@
+export const result = 'resource1 from dynamic2.wbn';
diff --git a/third_party/blink/web_tests/external/wpt/web-bundle/resources/dynamic2/resource2.js b/third_party/blink/web_tests/external/wpt/web-bundle/resources/dynamic2/resource2.js
new file mode 100644
index 0000000..b4278ee1c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/web-bundle/resources/dynamic2/resource2.js
@@ -0,0 +1 @@
+export const result = 'resource2 from dynamic2.wbn';
diff --git a/third_party/blink/web_tests/external/wpt/web-bundle/resources/dynamic2/resource3.js b/third_party/blink/web_tests/external/wpt/web-bundle/resources/dynamic2/resource3.js
new file mode 100644
index 0000000..0dad7ea
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/web-bundle/resources/dynamic2/resource3.js
@@ -0,0 +1 @@
+export const result = 'resource3 from dynamic2.wbn';
diff --git a/third_party/blink/web_tests/external/wpt/web-bundle/resources/dynamic2/resource4.js b/third_party/blink/web_tests/external/wpt/web-bundle/resources/dynamic2/resource4.js
new file mode 100644
index 0000000..e51cf7f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/web-bundle/resources/dynamic2/resource4.js
@@ -0,0 +1 @@
+export const result = 'resource4 from dynamic2.wbn';
diff --git a/third_party/blink/web_tests/external/wpt/web-bundle/resources/generate-test-wbns.sh b/third_party/blink/web_tests/external/wpt/web-bundle/resources/generate-test-wbns.sh
index 820bb12..c3dd8eb 100755
--- a/third_party/blink/web_tests/external/wpt/web-bundle/resources/generate-test-wbns.sh
+++ b/third_party/blink/web_tests/external/wpt/web-bundle/resources/generate-test-wbns.sh
@@ -10,11 +10,33 @@
# TODO: Stop hard-coding "web-platform.test" when generating Web Bundles on the
# fly.
-wpt_test_origin=https://web-platform.test:8444
+wpt_test_https_origin=https://web-platform.test:8444
+wpt_test_http_origin=http://web-platform.test:8001
gen-bundle \
-version b1 \
- -baseURL $wpt_test_origin/web-bundle/resources/wbn/ \
- -primaryURL $wpt_test_origin/web-bundle/resources/wbn/location.html \
+ -baseURL $wpt_test_https_origin/web-bundle/resources/wbn/ \
+ -primaryURL $wpt_test_https_origin/web-bundle/resources/wbn/location.html \
-dir location/ \
-o wbn/location.wbn
+
+gen-bundle \
+ -version b1 \
+ -baseURL https://subresource-wbn.example/ \
+ -primaryURL https://subresource-wbn.example/root.js \
+ -dir subresource/ \
+ -o wbn/subresource.wbn
+
+gen-bundle \
+ -version b1 \
+ -baseURL $wpt_test_http_origin/web-bundle/resources/dynamic/ \
+ -primaryURL $wpt_test_http_origin/web-bundle/resources/dynamic/resource1.js \
+ -dir dynamic1/ \
+ -o wbn/dynamic1.wbn
+
+gen-bundle \
+ -version b1 \
+ -baseURL $wpt_test_http_origin/web-bundle/resources/dynamic/ \
+ -primaryURL $wpt_test_http_origin/web-bundle/resources/dynamic/resource1.js \
+ -dir dynamic2/ \
+ -o wbn/dynamic2.wbn
diff --git a/third_party/blink/web_tests/external/wpt/web-bundle/resources/subresource/root.js b/third_party/blink/web_tests/external/wpt/web-bundle/resources/subresource/root.js
new file mode 100644
index 0000000..2c2a465
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/web-bundle/resources/subresource/root.js
@@ -0,0 +1 @@
+export * from './submodule.js';
diff --git a/third_party/blink/web_tests/external/wpt/web-bundle/resources/subresource/submodule.js b/third_party/blink/web_tests/external/wpt/web-bundle/resources/subresource/submodule.js
new file mode 100644
index 0000000..4561aaf0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/web-bundle/resources/subresource/submodule.js
@@ -0,0 +1 @@
+export const result = 'OK';
diff --git a/third_party/blink/web_tests/external/wpt/web-bundle/resources/wbn/dynamic1.wbn b/third_party/blink/web_tests/external/wpt/web-bundle/resources/wbn/dynamic1.wbn
new file mode 100644
index 0000000..22db19e7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/web-bundle/resources/wbn/dynamic1.wbn
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/web-bundle/resources/wbn/dynamic2.wbn b/third_party/blink/web_tests/external/wpt/web-bundle/resources/wbn/dynamic2.wbn
new file mode 100644
index 0000000..44c4b1e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/web-bundle/resources/wbn/dynamic2.wbn
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/web-bundle/resources/wbn/subresource.wbn b/third_party/blink/web_tests/external/wpt/web-bundle/resources/wbn/subresource.wbn
new file mode 100644
index 0000000..395039dc
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/web-bundle/resources/wbn/subresource.wbn
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/web-bundle/subresource-loading/subresource-loading-from-web-bundle.tentative.html b/third_party/blink/web_tests/external/wpt/web-bundle/subresource-loading/subresource-loading-from-web-bundle.tentative.html
new file mode 100644
index 0000000..e352646
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/web-bundle/subresource-loading/subresource-loading-from-web-bundle.tentative.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<title>Subresource loading with link rel="webbundle"</title>
+<link
+ rel="help"
+ href="https://github.com/WICG/webpackage/blob/master/explainers/subresource-loading.md"
+/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+ <link rel="webbundle" href="../resources/wbn/subresource.wbn"
+ resources="https://subresource-wbn.example/root.js https://subresource-wbn.example/submodule.js" />
+ <script>
+ promise_test(async () => {
+ const module = await import('https://subresource-wbn.example/root.js');
+ assert_equals(module.result, 'OK');
+ }, "Subresource loading with WebBundle");
+
+ promise_test(async () => {
+ const link = document.createElement("link");
+ link.rel = "webbundle";
+ link.href = "../resources/wbn/dynamic1.wbn";
+ link.resources.add('http://web-platform.test:8001/web-bundle/resources/dynamic/resource1.js',
+ 'http://web-platform.test:8001/web-bundle/resources/dynamic/resource2.js',
+ 'http://web-platform.test:8001/web-bundle/resources/dynamic/resource4.js');
+ document.body.appendChild(link);
+
+ const module = await import('http://web-platform.test:8001/web-bundle/resources/dynamic/resource1.js');
+ assert_equals(module.result, 'resource1 from dynamic1.wbn');
+
+ link.href = "../resources/wbn/dynamic2.wbn";
+ const module2 = await import('http://web-platform.test:8001/web-bundle/resources/dynamic/resource2.js');
+ assert_equals(module2.result, 'resource2 from dynamic2.wbn');
+
+ // A resource not specified in the resources attribute, but in the bundle.
+ const module3 = await import('http://web-platform.test:8001/web-bundle/resources/dynamic/resource3.js');
+ assert_equals(module3.result, 'resource3 from network');
+
+ document.body.removeChild(link);
+ const module4 = await import('http://web-platform.test:8001/web-bundle/resources/dynamic/resource4.js');
+ assert_equals(module4.result, 'resource4 from network');
+ }, 'Dynamically adding / updating / removing "<link rel=webbundle>"');
+
+ promise_test(() => {
+ return new Promise((resolve, reject) => {
+ const link = document.createElement("link");
+ link.rel = "webbundle";
+ link.href = "../resources/wbn/dynamic1.wbn?test-event";
+ link.>
+ link.>
+ document.body.appendChild(link);
+ });
+ }, '<link rel="webbundle"> fires a load event on load success');
+
+ promise_test(() => {
+ return new Promise((resolve, reject) => {
+ const link = document.createElement("link");
+ link.rel = "webbundle";
+ link.href = "../resources/wbn/nonexistent.wbn";
+ link.>
+ link.>
+ document.body.appendChild(link);
+ });
+ }, '<link rel="webbundle"> fires an error event on load failure');
+ </script>
+</body>