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