1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "third_party/blink/renderer/core/loader/modulescript/module_script_loader.h"
6
7 #include "base/test/scoped_feature_list.h"
8 #include "testing/gmock/include/gmock/gmock.h"
9 #include "testing/gtest/include/gtest/gtest.h"
10 #include "third_party/blink/public/common/features.h"
11 #include "third_party/blink/public/platform/platform.h"
12 #include "third_party/blink/public/platform/task_type.h"
13 #include "third_party/blink/public/platform/web_url_loader_mock_factory.h"
14 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
15 #include "third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.h"
16 #include "third_party/blink/renderer/core/dom/document.h"
17 #include "third_party/blink/renderer/core/loader/modulescript/document_module_script_fetcher.h"
18 #include "third_party/blink/renderer/core/loader/modulescript/module_script_fetch_request.h"
19 #include "third_party/blink/renderer/core/loader/modulescript/module_script_loader_client.h"
20 #include "third_party/blink/renderer/core/loader/modulescript/module_script_loader_registry.h"
21 #include "third_party/blink/renderer/core/loader/modulescript/worklet_module_script_fetcher.h"
22 #include "third_party/blink/renderer/core/origin_trials/origin_trial_context.h"
23 #include "third_party/blink/renderer/core/script/modulator.h"
24 #include "third_party/blink/renderer/core/script/module_script.h"
25 #include "third_party/blink/renderer/core/script/script.h"
26 #include "third_party/blink/renderer/core/testing/dummy_modulator.h"
27 #include "third_party/blink/renderer/core/testing/page_test_base.h"
28 #include "third_party/blink/renderer/core/workers/global_scope_creation_params.h"
29 #include "third_party/blink/renderer/core/workers/worker_thread_test_helper.h"
30 #include "third_party/blink/renderer/core/workers/worklet_global_scope.h"
31 #include "third_party/blink/renderer/core/workers/worklet_module_responses_map.h"
32 #include "third_party/blink/renderer/platform/heap/handle.h"
33 #include "third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object_snapshot.h"
34 #include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
35 #include "third_party/blink/renderer/platform/loader/testing/fetch_testing_platform_support.h"
36 #include "third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h"
37 #include "third_party/blink/renderer/platform/loader/testing/test_loader_factory.h"
38 #include "third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.h"
39 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
40 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
41 #include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
42
43 namespace blink {
44
45 namespace {
46
47 class TestModuleScriptLoaderClient final
48 : public GarbageCollected<TestModuleScriptLoaderClient>,
49 public ModuleScriptLoaderClient {
50 USING_GARBAGE_COLLECTED_MIXIN(TestModuleScriptLoaderClient);
51
52 public:
53 TestModuleScriptLoaderClient() = default;
54 ~TestModuleScriptLoaderClient() override = default;
55
Trace(Visitor * visitor)56 void Trace(Visitor* visitor) override { visitor->Trace(module_script_); }
57
NotifyNewSingleModuleFinished(ModuleScript * module_script)58 void NotifyNewSingleModuleFinished(ModuleScript* module_script) override {
59 was_notify_finished_ = true;
60 module_script_ = module_script;
61 }
62
WasNotifyFinished() const63 bool WasNotifyFinished() const { return was_notify_finished_; }
GetModuleScript()64 ModuleScript* GetModuleScript() { return module_script_; }
65
66 private:
67 bool was_notify_finished_ = false;
68 Member<ModuleScript> module_script_;
69 };
70
71 class ModuleScriptLoaderTestModulator final : public DummyModulator {
72 public:
ModuleScriptLoaderTestModulator(ScriptState * script_state)73 explicit ModuleScriptLoaderTestModulator(ScriptState* script_state)
74 : script_state_(script_state) {}
75
76 ~ModuleScriptLoaderTestModulator() override = default;
77
ResolveModuleSpecifier(const String & module_request,const KURL & base_url,String * failure_reason)78 KURL ResolveModuleSpecifier(const String& module_request,
79 const KURL& base_url,
80 String* failure_reason) final {
81 return KURL(base_url, module_request);
82 }
83
GetScriptState()84 ScriptState* GetScriptState() override { return script_state_; }
85
SetModuleRequests(const Vector<String> & requests)86 void SetModuleRequests(const Vector<String>& requests) {
87 requests_.clear();
88 for (const String& request : requests) {
89 requests_.emplace_back(request, TextPosition::MinimumPosition());
90 }
91 }
ModuleRequestsFromModuleRecord(v8::Local<v8::Module>)92 Vector<ModuleRequest> ModuleRequestsFromModuleRecord(
93 v8::Local<v8::Module>) override {
94 return requests_;
95 }
96
CreateModuleScriptFetcher(ModuleScriptCustomFetchType custom_fetch_type,util::PassKey<ModuleScriptLoader> pass_key)97 ModuleScriptFetcher* CreateModuleScriptFetcher(
98 ModuleScriptCustomFetchType custom_fetch_type,
99 util::PassKey<ModuleScriptLoader> pass_key) override {
100 auto* execution_context = ExecutionContext::From(script_state_);
101 if (auto* scope = DynamicTo<WorkletGlobalScope>(execution_context)) {
102 EXPECT_EQ(ModuleScriptCustomFetchType::kWorkletAddModule,
103 custom_fetch_type);
104 return MakeGarbageCollected<WorkletModuleScriptFetcher>(
105 scope->GetModuleResponsesMap(), pass_key);
106 }
107 EXPECT_EQ(ModuleScriptCustomFetchType::kNone, custom_fetch_type);
108 return MakeGarbageCollected<DocumentModuleScriptFetcher>(pass_key);
109 }
110
111 void Trace(Visitor*) override;
112
113 private:
114 Member<ScriptState> script_state_;
115 Vector<ModuleRequest> requests_;
116 };
117
Trace(Visitor * visitor)118 void ModuleScriptLoaderTestModulator::Trace(Visitor* visitor) {
119 visitor->Trace(script_state_);
120 DummyModulator::Trace(visitor);
121 }
122
123 } // namespace
124
125 class ModuleScriptLoaderTest : public PageTestBase {
126 DISALLOW_COPY_AND_ASSIGN(ModuleScriptLoaderTest);
127
128 public:
129 ModuleScriptLoaderTest();
130 void SetUp() override;
131
132 void InitializeForDocument();
133 void InitializeForWorklet();
134
135 void TestFetchDataURL(ModuleScriptCustomFetchType,
136 TestModuleScriptLoaderClient*);
137 void TestInvalidSpecifier(ModuleScriptCustomFetchType,
138 TestModuleScriptLoaderClient*);
139 void TestFetchInvalidURL(ModuleScriptCustomFetchType,
140 TestModuleScriptLoaderClient*);
141 void TestFetchURL(ModuleScriptCustomFetchType, TestModuleScriptLoaderClient*);
142 void TestFetchDataURLJSONModule(ModuleScriptCustomFetchType custom_fetch_type,
143 TestModuleScriptLoaderClient* client);
144 void TestFetchDataURLInvalidJSONModule(
145 ModuleScriptCustomFetchType custom_fetch_type,
146 TestModuleScriptLoaderClient* client);
147
GetModulator()148 ModuleScriptLoaderTestModulator* GetModulator() { return modulator_.Get(); }
149
RunUntilIdle()150 void RunUntilIdle() {
151 static_cast<scheduler::FakeTaskRunner*>(fetcher_->GetTaskRunner().get())
152 ->RunUntilIdle();
153 }
154
155 private:
GetTickClock()156 const base::TickClock* GetTickClock() override {
157 return platform_->test_task_runner()->GetMockTickClock();
158 }
159 base::test::ScopedFeatureList scoped_feature_list_;
160
161 protected:
162 const KURL url_;
163 const scoped_refptr<const SecurityOrigin> security_origin_;
164
165 Persistent<ResourceFetcher> fetcher_;
166
167 ScopedTestingPlatformSupport<FetchTestingPlatformSupport> platform_;
168 std::unique_ptr<MockWorkerReportingProxy> reporting_proxy_;
169 Persistent<ModuleScriptLoaderTestModulator> modulator_;
170 Persistent<WorkletGlobalScope> global_scope_;
171 };
172
SetUp()173 void ModuleScriptLoaderTest::SetUp() {
174 PageTestBase::SetUp(IntSize(500, 500));
175 }
176
ModuleScriptLoaderTest()177 ModuleScriptLoaderTest::ModuleScriptLoaderTest()
178 : url_("https://example.test"),
179 security_origin_(SecurityOrigin::Create(url_)) {
180 scoped_feature_list_.InitAndEnableFeature(blink::features::kJSONModules);
181 platform_->AdvanceClockSeconds(1.); // For non-zero DocumentParserTimings
182 }
183
InitializeForDocument()184 void ModuleScriptLoaderTest::InitializeForDocument() {
185 auto* fetch_context = MakeGarbageCollected<MockFetchContext>();
186 auto* properties =
187 MakeGarbageCollected<TestResourceFetcherProperties>(security_origin_);
188 fetcher_ = MakeGarbageCollected<ResourceFetcher>(
189 ResourceFetcherInit(properties->MakeDetachable(), fetch_context,
190 base::MakeRefCounted<scheduler::FakeTaskRunner>(),
191 MakeGarbageCollected<TestLoaderFactory>()));
192 modulator_ = MakeGarbageCollected<ModuleScriptLoaderTestModulator>(
193 ToScriptStateForMainWorld(&GetFrame()));
194 }
195
InitializeForWorklet()196 void ModuleScriptLoaderTest::InitializeForWorklet() {
197 auto* fetch_context = MakeGarbageCollected<MockFetchContext>();
198 auto* properties =
199 MakeGarbageCollected<TestResourceFetcherProperties>(security_origin_);
200 fetcher_ = MakeGarbageCollected<ResourceFetcher>(
201 ResourceFetcherInit(properties->MakeDetachable(), fetch_context,
202 base::MakeRefCounted<scheduler::FakeTaskRunner>(),
203 MakeGarbageCollected<TestLoaderFactory>()));
204 reporting_proxy_ = std::make_unique<MockWorkerReportingProxy>();
205 auto creation_params = std::make_unique<GlobalScopeCreationParams>(
206 url_, mojom::blink::ScriptType::kModule, "GlobalScopeName", "UserAgent",
207 UserAgentMetadata(), nullptr /* web_worker_fetch_context */,
208 Vector<CSPHeaderAndType>(), network::mojom::ReferrerPolicy::kDefault,
209 security_origin_.get(), true /* is_secure_context */, HttpsState::kModern,
210 nullptr /* worker_clients */, nullptr /* content_settings_client */,
211 network::mojom::IPAddressSpace::kLocal, nullptr /* origin_trial_token */,
212 base::UnguessableToken::Create(), nullptr /* worker_settings */,
213 kV8CacheOptionsDefault,
214 MakeGarbageCollected<WorkletModuleResponsesMap>());
215 global_scope_ = MakeGarbageCollected<WorkletGlobalScope>(
216 std::move(creation_params), *reporting_proxy_, &GetFrame());
217 global_scope_->ScriptController()->Initialize(NullURL());
218 modulator_ = MakeGarbageCollected<ModuleScriptLoaderTestModulator>(
219 global_scope_->ScriptController()->GetScriptState());
220 }
221 // TODO(nhiroki): Add tests for workers.
222
TestFetchDataURL(ModuleScriptCustomFetchType custom_fetch_type,TestModuleScriptLoaderClient * client)223 void ModuleScriptLoaderTest::TestFetchDataURL(
224 ModuleScriptCustomFetchType custom_fetch_type,
225 TestModuleScriptLoaderClient* client) {
226 auto* registry = MakeGarbageCollected<ModuleScriptLoaderRegistry>();
227 KURL url("data:text/javascript,export default 'grapes';");
228 ModuleScriptLoader::Fetch(ModuleScriptFetchRequest::CreateForTest(url),
229 fetcher_, ModuleGraphLevel::kTopLevelModuleFetch,
230 GetModulator(), custom_fetch_type, registry,
231 client);
232 }
233
TestFetchDataURLJSONModule(ModuleScriptCustomFetchType custom_fetch_type,TestModuleScriptLoaderClient * client)234 void ModuleScriptLoaderTest::TestFetchDataURLJSONModule(
235 ModuleScriptCustomFetchType custom_fetch_type,
236 TestModuleScriptLoaderClient* client) {
237 auto* registry = MakeGarbageCollected<ModuleScriptLoaderRegistry>();
238 KURL url(
239 "data:application/"
240 "json,{\"1\":{\"name\":\"MIKE\",\"surname\":\"TAYLOR\"},\"2\":{\"name\":"
241 "\"TOM\",\"surname\":\"JERRY\"}}");
242 ModuleScriptLoader::Fetch(ModuleScriptFetchRequest::CreateForTest(url),
243 fetcher_, ModuleGraphLevel::kTopLevelModuleFetch,
244 GetModulator(), custom_fetch_type, registry,
245 client);
246 }
247
TestFetchDataURLInvalidJSONModule(ModuleScriptCustomFetchType custom_fetch_type,TestModuleScriptLoaderClient * client)248 void ModuleScriptLoaderTest::TestFetchDataURLInvalidJSONModule(
249 ModuleScriptCustomFetchType custom_fetch_type,
250 TestModuleScriptLoaderClient* client) {
251 auto* registry = MakeGarbageCollected<ModuleScriptLoaderRegistry>();
252 KURL url(
253 "data:application/"
254 "json,{{{");
255 ModuleScriptLoader::Fetch(ModuleScriptFetchRequest::CreateForTest(url),
256 fetcher_, ModuleGraphLevel::kTopLevelModuleFetch,
257 GetModulator(), custom_fetch_type, registry,
258 client);
259 }
260
TEST_F(ModuleScriptLoaderTest,FetchDataURL)261 TEST_F(ModuleScriptLoaderTest, FetchDataURL) {
262 InitializeForDocument();
263 TestModuleScriptLoaderClient* client =
264 MakeGarbageCollected<TestModuleScriptLoaderClient>();
265 TestFetchDataURL(ModuleScriptCustomFetchType::kNone, client);
266
267 // TODO(leszeks): This should finish synchronously, but currently due
268 // to the script resource/script streamer interaction, it does not.
269 RunUntilIdle();
270 EXPECT_TRUE(client->WasNotifyFinished());
271 ASSERT_TRUE(client->GetModuleScript());
272 EXPECT_FALSE(client->GetModuleScript()->HasEmptyRecord());
273 EXPECT_FALSE(client->GetModuleScript()->HasParseError());
274 }
275
TEST_F(ModuleScriptLoaderTest,FetchDataURLJSONModule)276 TEST_F(ModuleScriptLoaderTest, FetchDataURLJSONModule) {
277 InitializeForDocument();
278 TestModuleScriptLoaderClient* client =
279 MakeGarbageCollected<TestModuleScriptLoaderClient>();
280 TestFetchDataURLJSONModule(ModuleScriptCustomFetchType::kNone, client);
281
282 // TODO(leszeks): This should finish synchronously, but currently due
283 // to the script resource/script streamer interaction, it does not.
284 RunUntilIdle();
285 EXPECT_TRUE(client->WasNotifyFinished());
286 ASSERT_TRUE(client->GetModuleScript());
287 EXPECT_FALSE(client->GetModuleScript()->HasEmptyRecord());
288 EXPECT_FALSE(client->GetModuleScript()->HasParseError());
289 }
290
TEST_F(ModuleScriptLoaderTest,FetchDataURLInvalidJSONModule)291 TEST_F(ModuleScriptLoaderTest, FetchDataURLInvalidJSONModule) {
292 InitializeForDocument();
293 TestModuleScriptLoaderClient* client =
294 MakeGarbageCollected<TestModuleScriptLoaderClient>();
295 TestFetchDataURLInvalidJSONModule(ModuleScriptCustomFetchType::kNone, client);
296
297 // TODO(leszeks): This should finish synchronously, but currently due
298 // to the script resource/script streamer interaction, it does not.
299 RunUntilIdle();
300 EXPECT_TRUE(client->WasNotifyFinished());
301 ASSERT_TRUE(client->GetModuleScript());
302 EXPECT_TRUE(client->GetModuleScript()->HasEmptyRecord());
303 EXPECT_TRUE(client->GetModuleScript()->HasParseError());
304 }
305
TEST_F(ModuleScriptLoaderTest,FetchDataURL_OnWorklet)306 TEST_F(ModuleScriptLoaderTest, FetchDataURL_OnWorklet) {
307 InitializeForWorklet();
308 TestModuleScriptLoaderClient* client1 =
309 MakeGarbageCollected<TestModuleScriptLoaderClient>();
310 TestFetchDataURL(ModuleScriptCustomFetchType::kWorkletAddModule, client1);
311
312 EXPECT_FALSE(client1->WasNotifyFinished())
313 << "ModuleScriptLoader should finish asynchronously.";
314 RunUntilIdle();
315
316 EXPECT_TRUE(client1->WasNotifyFinished());
317 ASSERT_TRUE(client1->GetModuleScript());
318 EXPECT_FALSE(client1->GetModuleScript()->HasEmptyRecord());
319 EXPECT_FALSE(client1->GetModuleScript()->HasParseError());
320
321 // Try to fetch the same URL again in order to verify the case where
322 // WorkletModuleResponsesMap serves a cache.
323 TestModuleScriptLoaderClient* client2 =
324 MakeGarbageCollected<TestModuleScriptLoaderClient>();
325 TestFetchDataURL(ModuleScriptCustomFetchType::kWorkletAddModule, client2);
326
327 EXPECT_FALSE(client2->WasNotifyFinished())
328 << "ModuleScriptLoader should finish asynchronously.";
329 RunUntilIdle();
330
331 EXPECT_TRUE(client2->WasNotifyFinished());
332 ASSERT_TRUE(client2->GetModuleScript());
333 EXPECT_FALSE(client2->GetModuleScript()->HasEmptyRecord());
334 EXPECT_FALSE(client2->GetModuleScript()->HasParseError());
335 }
336
TEST_F(ModuleScriptLoaderTest,FetchDataURLJSONModule_OnWorklet)337 TEST_F(ModuleScriptLoaderTest, FetchDataURLJSONModule_OnWorklet) {
338 InitializeForWorklet();
339 TestModuleScriptLoaderClient* client1 =
340 MakeGarbageCollected<TestModuleScriptLoaderClient>();
341 TestFetchDataURLJSONModule(ModuleScriptCustomFetchType::kWorkletAddModule,
342 client1);
343
344 EXPECT_FALSE(client1->WasNotifyFinished())
345 << "ModuleScriptLoader should finish asynchronously.";
346 RunUntilIdle();
347
348 EXPECT_TRUE(client1->WasNotifyFinished());
349 ASSERT_TRUE(client1->GetModuleScript());
350 EXPECT_FALSE(client1->GetModuleScript()->HasEmptyRecord());
351 EXPECT_FALSE(client1->GetModuleScript()->HasParseError());
352
353 // Try to fetch the same URL again in order to verify the case where
354 // WorkletModuleResponsesMap serves a cache.
355 TestModuleScriptLoaderClient* client2 =
356 MakeGarbageCollected<TestModuleScriptLoaderClient>();
357 TestFetchDataURLJSONModule(ModuleScriptCustomFetchType::kWorkletAddModule,
358 client2);
359
360 EXPECT_FALSE(client2->WasNotifyFinished())
361 << "ModuleScriptLoader should finish asynchronously.";
362 RunUntilIdle();
363
364 EXPECT_TRUE(client2->WasNotifyFinished());
365 ASSERT_TRUE(client2->GetModuleScript());
366 EXPECT_FALSE(client2->GetModuleScript()->HasEmptyRecord());
367 EXPECT_FALSE(client2->GetModuleScript()->HasParseError());
368 }
369
TEST_F(ModuleScriptLoaderTest,FetchDataURLInvalidJSONModule_OnWorklet)370 TEST_F(ModuleScriptLoaderTest, FetchDataURLInvalidJSONModule_OnWorklet) {
371 InitializeForWorklet();
372 TestModuleScriptLoaderClient* client1 =
373 MakeGarbageCollected<TestModuleScriptLoaderClient>();
374 TestFetchDataURLInvalidJSONModule(
375 ModuleScriptCustomFetchType::kWorkletAddModule, client1);
376
377 EXPECT_FALSE(client1->WasNotifyFinished())
378 << "ModuleScriptLoader should finish asynchronously.";
379 RunUntilIdle();
380
381 EXPECT_TRUE(client1->WasNotifyFinished());
382 ASSERT_TRUE(client1->GetModuleScript());
383 EXPECT_TRUE(client1->GetModuleScript()->HasEmptyRecord());
384 EXPECT_TRUE(client1->GetModuleScript()->HasParseError());
385
386 // Try to fetch the same URL again in order to verify the case where
387 // WorkletModuleResponsesMap serves a cache.
388 TestModuleScriptLoaderClient* client2 =
389 MakeGarbageCollected<TestModuleScriptLoaderClient>();
390 TestFetchDataURLInvalidJSONModule(
391 ModuleScriptCustomFetchType::kWorkletAddModule, client2);
392
393 EXPECT_FALSE(client2->WasNotifyFinished())
394 << "ModuleScriptLoader should finish asynchronously.";
395 RunUntilIdle();
396
397 EXPECT_TRUE(client2->WasNotifyFinished());
398 ASSERT_TRUE(client2->GetModuleScript());
399 EXPECT_TRUE(client2->GetModuleScript()->HasEmptyRecord());
400 EXPECT_TRUE(client2->GetModuleScript()->HasParseError());
401 }
402
TestInvalidSpecifier(ModuleScriptCustomFetchType custom_fetch_type,TestModuleScriptLoaderClient * client)403 void ModuleScriptLoaderTest::TestInvalidSpecifier(
404 ModuleScriptCustomFetchType custom_fetch_type,
405 TestModuleScriptLoaderClient* client) {
406 auto* registry = MakeGarbageCollected<ModuleScriptLoaderRegistry>();
407 KURL url("data:text/javascript,import 'invalid';export default 'grapes';");
408 GetModulator()->SetModuleRequests({"invalid"});
409 ModuleScriptLoader::Fetch(ModuleScriptFetchRequest::CreateForTest(url),
410 fetcher_, ModuleGraphLevel::kTopLevelModuleFetch,
411 GetModulator(), custom_fetch_type, registry,
412 client);
413 }
414
TEST_F(ModuleScriptLoaderTest,InvalidSpecifier)415 TEST_F(ModuleScriptLoaderTest, InvalidSpecifier) {
416 InitializeForDocument();
417 TestModuleScriptLoaderClient* client =
418 MakeGarbageCollected<TestModuleScriptLoaderClient>();
419 TestInvalidSpecifier(ModuleScriptCustomFetchType::kNone, client);
420
421 // TODO(leszeks): This should finish synchronously, but currently due
422 // to the script resource/script streamer interaction, it does not.
423 RunUntilIdle();
424 EXPECT_TRUE(client->WasNotifyFinished());
425
426 ASSERT_TRUE(client->GetModuleScript());
427 EXPECT_TRUE(client->GetModuleScript()->HasEmptyRecord());
428 EXPECT_TRUE(client->GetModuleScript()->HasParseError());
429 }
430
TEST_F(ModuleScriptLoaderTest,InvalidSpecifier_OnWorklet)431 TEST_F(ModuleScriptLoaderTest, InvalidSpecifier_OnWorklet) {
432 InitializeForWorklet();
433 TestModuleScriptLoaderClient* client =
434 MakeGarbageCollected<TestModuleScriptLoaderClient>();
435 TestInvalidSpecifier(ModuleScriptCustomFetchType::kWorkletAddModule, client);
436
437 EXPECT_FALSE(client->WasNotifyFinished())
438 << "ModuleScriptLoader should finish asynchronously.";
439 RunUntilIdle();
440
441 EXPECT_TRUE(client->WasNotifyFinished());
442 ASSERT_TRUE(client->GetModuleScript());
443 EXPECT_TRUE(client->GetModuleScript()->HasEmptyRecord());
444 EXPECT_TRUE(client->GetModuleScript()->HasParseError());
445 }
446
TestFetchInvalidURL(ModuleScriptCustomFetchType custom_fetch_type,TestModuleScriptLoaderClient * client)447 void ModuleScriptLoaderTest::TestFetchInvalidURL(
448 ModuleScriptCustomFetchType custom_fetch_type,
449 TestModuleScriptLoaderClient* client) {
450 auto* registry = MakeGarbageCollected<ModuleScriptLoaderRegistry>();
451 KURL url;
452 EXPECT_FALSE(url.IsValid());
453 ModuleScriptLoader::Fetch(ModuleScriptFetchRequest::CreateForTest(url),
454 fetcher_, ModuleGraphLevel::kTopLevelModuleFetch,
455 GetModulator(), custom_fetch_type, registry,
456 client);
457 }
458
TEST_F(ModuleScriptLoaderTest,FetchInvalidURL)459 TEST_F(ModuleScriptLoaderTest, FetchInvalidURL) {
460 InitializeForDocument();
461 TestModuleScriptLoaderClient* client =
462 MakeGarbageCollected<TestModuleScriptLoaderClient>();
463 TestFetchInvalidURL(ModuleScriptCustomFetchType::kNone, client);
464
465 // TODO(leszeks): This should finish synchronously, but currently due
466 // to the script resource/script streamer interaction, it does not.
467 RunUntilIdle();
468 EXPECT_TRUE(client->WasNotifyFinished());
469 EXPECT_FALSE(client->GetModuleScript());
470 }
471
TEST_F(ModuleScriptLoaderTest,FetchInvalidURL_OnWorklet)472 TEST_F(ModuleScriptLoaderTest, FetchInvalidURL_OnWorklet) {
473 InitializeForWorklet();
474 TestModuleScriptLoaderClient* client =
475 MakeGarbageCollected<TestModuleScriptLoaderClient>();
476 TestFetchInvalidURL(ModuleScriptCustomFetchType::kWorkletAddModule, client);
477
478 EXPECT_FALSE(client->WasNotifyFinished())
479 << "ModuleScriptLoader should finish asynchronously.";
480 RunUntilIdle();
481
482 EXPECT_TRUE(client->WasNotifyFinished());
483 EXPECT_FALSE(client->GetModuleScript());
484 }
485
TestFetchURL(ModuleScriptCustomFetchType custom_fetch_type,TestModuleScriptLoaderClient * client)486 void ModuleScriptLoaderTest::TestFetchURL(
487 ModuleScriptCustomFetchType custom_fetch_type,
488 TestModuleScriptLoaderClient* client) {
489 KURL url("https://example.test/module.js");
490 url_test_helpers::RegisterMockedURLLoad(
491 url, test::CoreTestDataPath("module.js"), "text/javascript");
492
493 auto* registry = MakeGarbageCollected<ModuleScriptLoaderRegistry>();
494 ModuleScriptLoader::Fetch(ModuleScriptFetchRequest::CreateForTest(url),
495 fetcher_, ModuleGraphLevel::kTopLevelModuleFetch,
496 GetModulator(), custom_fetch_type, registry,
497 client);
498 }
499
TEST_F(ModuleScriptLoaderTest,FetchURL)500 TEST_F(ModuleScriptLoaderTest, FetchURL) {
501 InitializeForDocument();
502 TestModuleScriptLoaderClient* client =
503 MakeGarbageCollected<TestModuleScriptLoaderClient>();
504 TestFetchURL(ModuleScriptCustomFetchType::kNone, client);
505
506 EXPECT_FALSE(client->WasNotifyFinished())
507 << "ModuleScriptLoader unexpectedly finished synchronously.";
508 platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
509 // TODO(leszeks): This should finish synchronously, but currently due
510 // to the script resource/script streamer interaction, it does not.
511 RunUntilIdle();
512
513 EXPECT_TRUE(client->WasNotifyFinished());
514 EXPECT_TRUE(client->GetModuleScript());
515 }
516
TEST_F(ModuleScriptLoaderTest,FetchURL_OnWorklet)517 TEST_F(ModuleScriptLoaderTest, FetchURL_OnWorklet) {
518 InitializeForWorklet();
519 TestModuleScriptLoaderClient* client =
520 MakeGarbageCollected<TestModuleScriptLoaderClient>();
521 TestFetchURL(ModuleScriptCustomFetchType::kWorkletAddModule, client);
522
523 EXPECT_FALSE(client->WasNotifyFinished())
524 << "ModuleScriptLoader unexpectedly finished synchronously.";
525 platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
526 RunUntilIdle();
527
528 EXPECT_TRUE(client->WasNotifyFinished());
529 EXPECT_TRUE(client->GetModuleScript());
530 }
531
532 } // namespace blink
533