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