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/script/module_record_resolver_impl.h"
6
7 #include "testing/gtest/include/gtest/gtest.h"
8 #include "third_party/blink/public/common/features.h"
9 #include "third_party/blink/public/platform/platform.h"
10 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
11 #include "third_party/blink/renderer/core/script/js_module_script.h"
12 #include "third_party/blink/renderer/core/script/modulator.h"
13 #include "third_party/blink/renderer/core/testing/dummy_modulator.h"
14 #include "third_party/blink/renderer/core/testing/module_test_base.h"
15 #include "third_party/blink/renderer/platform/bindings/script_state.h"
16 #include "third_party/blink/renderer/platform/bindings/v8_throw_exception.h"
17 #include "third_party/blink/renderer/platform/heap/handle.h"
18 #include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h"
19 #include "v8/include/v8.h"
20
21 namespace blink {
22
23 namespace {
24
25 class ModuleRecordResolverImplTestModulator final : public DummyModulator {
26 public:
ModuleRecordResolverImplTestModulator()27 ModuleRecordResolverImplTestModulator() {}
~ModuleRecordResolverImplTestModulator()28 ~ModuleRecordResolverImplTestModulator() override {}
29
30 void Trace(Visitor*) const override;
31
SetScriptState(ScriptState * script_state)32 void SetScriptState(ScriptState* script_state) {
33 script_state_ = script_state;
34 }
35
GetFetchedModuleScriptCalled() const36 int GetFetchedModuleScriptCalled() const {
37 return get_fetched_module_script_called_;
38 }
SetModuleScript(ModuleScript * module_script)39 void SetModuleScript(ModuleScript* module_script) {
40 module_script_ = module_script;
41 }
FetchedUrl() const42 const KURL& FetchedUrl() const { return fetched_url_; }
43
44 private:
45 // Implements Modulator:
GetScriptState()46 ScriptState* GetScriptState() override { return script_state_; }
47
ResolveModuleSpecifier(const String & module_request,const KURL & base_url,String * failure_reason)48 KURL ResolveModuleSpecifier(const String& module_request,
49 const KURL& base_url,
50 String* failure_reason) final {
51 return KURL(base_url, module_request);
52 }
53
54 ModuleScript* GetFetchedModuleScript(const KURL&) override;
55
56 Member<ScriptState> script_state_;
57 int get_fetched_module_script_called_ = 0;
58 KURL fetched_url_;
59 Member<ModuleScript> module_script_;
60 };
61
Trace(Visitor * visitor) const62 void ModuleRecordResolverImplTestModulator::Trace(Visitor* visitor) const {
63 visitor->Trace(script_state_);
64 visitor->Trace(module_script_);
65 DummyModulator::Trace(visitor);
66 }
67
GetFetchedModuleScript(const KURL & url)68 ModuleScript* ModuleRecordResolverImplTestModulator::GetFetchedModuleScript(
69 const KURL& url) {
70 get_fetched_module_script_called_++;
71 fetched_url_ = url;
72 return module_script_.Get();
73 }
74
CreateReferrerModuleScript(Modulator * modulator,V8TestingScope & scope)75 ModuleScript* CreateReferrerModuleScript(Modulator* modulator,
76 V8TestingScope& scope) {
77 KURL js_url("https://example.com/referrer.js");
78 v8::Local<v8::Module> referrer_record = ModuleRecord::Compile(
79 scope.GetIsolate(), "import './target.js'; export const a = 42;", js_url,
80 js_url, ScriptFetchOptions(), TextPosition::MinimumPosition(),
81 ASSERT_NO_EXCEPTION);
82 KURL referrer_url("https://example.com/referrer.js");
83 auto* referrer_module_script =
84 JSModuleScript::CreateForTest(modulator, referrer_record, referrer_url);
85 return referrer_module_script;
86 }
87
CreateTargetModuleScript(Modulator * modulator,V8TestingScope & scope,bool has_parse_error=false)88 ModuleScript* CreateTargetModuleScript(Modulator* modulator,
89 V8TestingScope& scope,
90 bool has_parse_error = false) {
91 KURL js_url("https://example.com/target.js");
92 v8::Local<v8::Module> record = ModuleRecord::Compile(
93 scope.GetIsolate(), "export const pi = 3.14;", js_url, js_url,
94 ScriptFetchOptions(), TextPosition::MinimumPosition(),
95 ASSERT_NO_EXCEPTION);
96 KURL url("https://example.com/target.js");
97 auto* module_script = JSModuleScript::CreateForTest(modulator, record, url);
98 if (has_parse_error) {
99 v8::Local<v8::Value> error =
100 V8ThrowException::CreateError(scope.GetIsolate(), "hoge");
101 module_script->SetParseErrorAndClearRecord(
102 ScriptValue(scope.GetIsolate(), error));
103 }
104 return module_script;
105 }
106
107 } // namespace
108
109 class ModuleRecordResolverImplTest : public testing::Test,
110 public ParametrizedModuleTest {
111 public:
112 void SetUp() override;
113 void TearDown() override;
114
Modulator()115 ModuleRecordResolverImplTestModulator* Modulator() {
116 return modulator_.Get();
117 }
118
119 protected:
120 ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
121 platform_;
122 Persistent<ModuleRecordResolverImplTestModulator> modulator_;
123 };
124
SetUp()125 void ModuleRecordResolverImplTest::SetUp() {
126 ParametrizedModuleTest::SetUp();
127 platform_->AdvanceClockSeconds(1.); // For non-zero DocumentParserTimings
128 modulator_ = MakeGarbageCollected<ModuleRecordResolverImplTestModulator>();
129 }
130
TearDown()131 void ModuleRecordResolverImplTest::TearDown() {
132 ParametrizedModuleTest::TearDown();
133 }
134
TEST_P(ModuleRecordResolverImplTest,RegisterResolveSuccess)135 TEST_P(ModuleRecordResolverImplTest, RegisterResolveSuccess) {
136 V8TestingScope scope;
137 ModuleRecordResolver* resolver =
138 MakeGarbageCollected<ModuleRecordResolverImpl>(
139 Modulator(), scope.GetExecutionContext());
140 Modulator()->SetScriptState(scope.GetScriptState());
141
142 ModuleScript* referrer_module_script =
143 CreateReferrerModuleScript(modulator_, scope);
144 resolver->RegisterModuleScript(referrer_module_script);
145
146 ModuleScript* target_module_script =
147 CreateTargetModuleScript(modulator_, scope);
148 Modulator()->SetModuleScript(target_module_script);
149
150 v8::Local<v8::Module> resolved =
151 resolver->Resolve("./target.js", referrer_module_script->V8Module(),
152 scope.GetExceptionState());
153 EXPECT_FALSE(scope.GetExceptionState().HadException());
154 EXPECT_EQ(resolved, target_module_script->V8Module());
155 EXPECT_EQ(1, modulator_->GetFetchedModuleScriptCalled());
156 EXPECT_EQ(modulator_->FetchedUrl(), target_module_script->BaseURL())
157 << "Unexpectedly fetched URL: " << modulator_->FetchedUrl().GetString();
158 }
159
160 // Instantiate tests once with TLA and once without:
161 INSTANTIATE_TEST_SUITE_P(ModuleRecordResolverImplTestGroup,
162 ModuleRecordResolverImplTest,
163 testing::Bool(),
164 ParametrizedModuleTestParamName());
165
166 } // namespace blink
167