OLD | NEW |
(Empty) | |
| 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 "core/dom/ModuleMap.h" |
| 6 |
| 7 #include "core/dom/Document.h" |
| 8 #include "core/dom/Modulator.h" |
| 9 #include "core/dom/ModuleScript.h" |
| 10 #include "core/dom/ScriptModuleResolver.h" |
| 11 #include "core/loader/modulescript/ModuleScriptFetchRequest.h" |
| 12 #include "core/loader/modulescript/ModuleScriptLoaderClient.h" |
| 13 #include "core/testing/DummyModulator.h" |
| 14 #include "core/testing/DummyPageHolder.h" |
| 15 #include "platform/heap/Handle.h" |
| 16 #include "platform/testing/TestingPlatformSupport.h" |
| 17 #include "public/platform/Platform.h" |
| 18 #include "testing/gtest/include/gtest/gtest.h" |
| 19 |
| 20 namespace blink { |
| 21 |
| 22 namespace { |
| 23 |
| 24 class TestSingleModuleClient final |
| 25 : public GarbageCollectedFinalized<TestSingleModuleClient>, |
| 26 public SingleModuleClient { |
| 27 USING_GARBAGE_COLLECTED_MIXIN(TestSingleModuleClient); |
| 28 |
| 29 public: |
| 30 TestSingleModuleClient() = default; |
| 31 virtual ~TestSingleModuleClient() {} |
| 32 |
| 33 DEFINE_INLINE_TRACE() { visitor->trace(m_moduleScript); } |
| 34 |
| 35 void notifyModuleLoadFinished(ModuleScript* moduleScript) override { |
| 36 m_wasNotifyFinished = true; |
| 37 m_moduleScript = moduleScript; |
| 38 } |
| 39 |
| 40 bool wasNotifyFinished() const { return m_wasNotifyFinished; } |
| 41 ModuleScript* moduleScript() { return m_moduleScript; } |
| 42 |
| 43 private: |
| 44 bool m_wasNotifyFinished = false; |
| 45 Member<ModuleScript> m_moduleScript; |
| 46 }; |
| 47 |
| 48 class TestScriptModuleResolver final : public ScriptModuleResolver { |
| 49 public: |
| 50 TestScriptModuleResolver() {} |
| 51 |
| 52 int registerModuleScriptCallCount() const { |
| 53 return m_registerModuleScriptCallCount; |
| 54 } |
| 55 |
| 56 void registerModuleScript(ModuleScript*) override { |
| 57 m_registerModuleScriptCallCount++; |
| 58 } |
| 59 |
| 60 ScriptModule resolve(const String& specifier, |
| 61 const ScriptModule& referrer, |
| 62 ExceptionState&) override { |
| 63 NOTREACHED(); |
| 64 return ScriptModule(); |
| 65 } |
| 66 |
| 67 private: |
| 68 int m_registerModuleScriptCallCount = 0; |
| 69 }; |
| 70 |
| 71 } // namespace |
| 72 |
| 73 class ModuleMapTestModulator final : public DummyModulator { |
| 74 public: |
| 75 ModuleMapTestModulator(); |
| 76 virtual ~ModuleMapTestModulator() {} |
| 77 |
| 78 DECLARE_TRACE(); |
| 79 |
| 80 TestScriptModuleResolver* testScriptModuleResolver() { |
| 81 return m_resolver.get(); |
| 82 } |
| 83 void resolveFetches(); |
| 84 |
| 85 private: |
| 86 // Implements Modulator: |
| 87 |
| 88 ScriptModuleResolver* scriptModuleResolver() override { |
| 89 return m_resolver.get(); |
| 90 } |
| 91 |
| 92 WebTaskRunner* taskRunner() override { |
| 93 return Platform::current()->currentThread()->getWebTaskRunner(); |
| 94 }; |
| 95 |
| 96 void fetchNewSingleModule(const ModuleScriptFetchRequest&, |
| 97 ModuleGraphLevel, |
| 98 ModuleScriptLoaderClient*) override; |
| 99 |
| 100 struct TestRequest : public GarbageCollectedFinalized<TestRequest> { |
| 101 KURL url; |
| 102 String nonce; |
| 103 Member<ModuleScriptLoaderClient> client; |
| 104 |
| 105 DEFINE_INLINE_TRACE() { visitor->trace(client); } |
| 106 }; |
| 107 HeapVector<Member<TestRequest>> m_testRequests; |
| 108 |
| 109 Member<TestScriptModuleResolver> m_resolver; |
| 110 }; |
| 111 |
| 112 ModuleMapTestModulator::ModuleMapTestModulator() |
| 113 : m_resolver(new TestScriptModuleResolver) {} |
| 114 |
| 115 DEFINE_TRACE(ModuleMapTestModulator) { |
| 116 visitor->trace(m_testRequests); |
| 117 visitor->trace(m_resolver); |
| 118 DummyModulator::trace(visitor); |
| 119 } |
| 120 |
| 121 void ModuleMapTestModulator::fetchNewSingleModule( |
| 122 const ModuleScriptFetchRequest& request, |
| 123 ModuleGraphLevel, |
| 124 ModuleScriptLoaderClient* client) { |
| 125 TestRequest* testRequest = new TestRequest; |
| 126 testRequest->url = request.url(); |
| 127 testRequest->nonce = request.nonce(); |
| 128 testRequest->client = client; |
| 129 m_testRequests.push_back(testRequest); |
| 130 } |
| 131 |
| 132 void ModuleMapTestModulator::resolveFetches() { |
| 133 for (const auto& testRequest : m_testRequests) { |
| 134 ModuleScript* moduleScript = ModuleScript::create( |
| 135 ScriptModule(), testRequest->url, testRequest->nonce, ParserInserted, |
| 136 WebURLRequest::FetchCredentialsModeOmit); |
| 137 taskRunner()->postTask( |
| 138 BLINK_FROM_HERE, |
| 139 WTF::bind(&ModuleScriptLoaderClient::notifyNewSingleModuleFinished, |
| 140 wrapPersistent(testRequest->client.get()), |
| 141 wrapPersistent(moduleScript))); |
| 142 } |
| 143 m_testRequests.clear(); |
| 144 } |
| 145 |
| 146 class ModuleMapTest : public testing::Test { |
| 147 public: |
| 148 void SetUp() override; |
| 149 |
| 150 ModuleMapTestModulator* modulator() { return m_modulator.get(); } |
| 151 ModuleMap* map() { return m_map; } |
| 152 |
| 153 protected: |
| 154 Persistent<ModuleMapTestModulator> m_modulator; |
| 155 Persistent<ModuleMap> m_map; |
| 156 }; |
| 157 |
| 158 void ModuleMapTest::SetUp() { |
| 159 m_modulator = new ModuleMapTestModulator(); |
| 160 m_map = ModuleMap::create(m_modulator.get()); |
| 161 } |
| 162 |
| 163 TEST_F(ModuleMapTest, sequentialRequests) { |
| 164 ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler> |
| 165 platform; |
| 166 platform->advanceClockSeconds(1.); // For non-zero DocumentParserTimings |
| 167 |
| 168 KURL url(KURL(), "https://example.com/foo.js"); |
| 169 ModuleScriptFetchRequest moduleRequest( |
| 170 url, String(), ParserInserted, WebURLRequest::FetchCredentialsModeOmit); |
| 171 |
| 172 // First request |
| 173 TestSingleModuleClient* client = new TestSingleModuleClient; |
| 174 map()->fetchSingleModuleScript(moduleRequest, |
| 175 ModuleGraphLevel::TopLevelModuleFetch, client); |
| 176 modulator()->resolveFetches(); |
| 177 EXPECT_FALSE(client->wasNotifyFinished()) |
| 178 << "fetchSingleModuleScript shouldn't complete synchronously"; |
| 179 platform->runUntilIdle(); |
| 180 |
| 181 EXPECT_EQ( |
| 182 modulator()->testScriptModuleResolver()->registerModuleScriptCallCount(), |
| 183 1); |
| 184 EXPECT_TRUE(client->wasNotifyFinished()); |
| 185 EXPECT_TRUE(client->moduleScript()); |
| 186 EXPECT_EQ(client->moduleScript()->instantiationState(), |
| 187 ModuleInstantiationState::Uninstantiated); |
| 188 |
| 189 // Secondary request |
| 190 TestSingleModuleClient* client2 = new TestSingleModuleClient; |
| 191 map()->fetchSingleModuleScript( |
| 192 moduleRequest, ModuleGraphLevel::TopLevelModuleFetch, client2); |
| 193 modulator()->resolveFetches(); |
| 194 EXPECT_FALSE(client2->wasNotifyFinished()) |
| 195 << "fetchSingleModuleScript shouldn't complete synchronously"; |
| 196 platform->runUntilIdle(); |
| 197 |
| 198 EXPECT_EQ( |
| 199 modulator()->testScriptModuleResolver()->registerModuleScriptCallCount(), |
| 200 1) |
| 201 << "registerModuleScript sholudn't be called in secondary request."; |
| 202 EXPECT_TRUE(client2->wasNotifyFinished()); |
| 203 EXPECT_TRUE(client2->moduleScript()); |
| 204 EXPECT_EQ(client2->moduleScript()->instantiationState(), |
| 205 ModuleInstantiationState::Uninstantiated); |
| 206 } |
| 207 |
| 208 TEST_F(ModuleMapTest, concurrentRequestsShouldJoin) { |
| 209 ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler> |
| 210 platform; |
| 211 platform->advanceClockSeconds(1.); // For non-zero DocumentParserTimings |
| 212 |
| 213 KURL url(KURL(), "https://example.com/foo.js"); |
| 214 ModuleScriptFetchRequest moduleRequest( |
| 215 url, String(), ParserInserted, WebURLRequest::FetchCredentialsModeOmit); |
| 216 |
| 217 // First request |
| 218 TestSingleModuleClient* client = new TestSingleModuleClient; |
| 219 map()->fetchSingleModuleScript(moduleRequest, |
| 220 ModuleGraphLevel::TopLevelModuleFetch, client); |
| 221 |
| 222 // Secondary request (which should join the first request) |
| 223 TestSingleModuleClient* client2 = new TestSingleModuleClient; |
| 224 map()->fetchSingleModuleScript( |
| 225 moduleRequest, ModuleGraphLevel::TopLevelModuleFetch, client2); |
| 226 |
| 227 modulator()->resolveFetches(); |
| 228 EXPECT_FALSE(client->wasNotifyFinished()) |
| 229 << "fetchSingleModuleScript shouldn't complete synchronously"; |
| 230 EXPECT_FALSE(client2->wasNotifyFinished()) |
| 231 << "fetchSingleModuleScript shouldn't complete synchronously"; |
| 232 platform->runUntilIdle(); |
| 233 |
| 234 EXPECT_EQ( |
| 235 modulator()->testScriptModuleResolver()->registerModuleScriptCallCount(), |
| 236 1); |
| 237 |
| 238 EXPECT_TRUE(client->wasNotifyFinished()); |
| 239 EXPECT_TRUE(client->moduleScript()); |
| 240 EXPECT_EQ(client->moduleScript()->instantiationState(), |
| 241 ModuleInstantiationState::Uninstantiated); |
| 242 EXPECT_TRUE(client2->wasNotifyFinished()); |
| 243 EXPECT_TRUE(client2->moduleScript()); |
| 244 EXPECT_EQ(client2->moduleScript()->instantiationState(), |
| 245 ModuleInstantiationState::Uninstantiated); |
| 246 } |
| 247 |
| 248 } // namespace blink |
OLD | NEW |