Chromium Code Reviews| 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/loader/modulescript/ModuleTreeLinker.h" | |
| 6 | |
| 7 #include "bindings/core/v8/ModuleController.h" | |
| 8 #include "bindings/core/v8/ScriptModule.h" | |
| 9 #include "core/dom/ModuleScript.h" | |
| 10 #include "core/loader/modulescript/ModuleTreeLinkerRegistry.h" | |
| 11 | |
| 12 namespace blink { | |
| 13 | |
| 14 ModuleTreeLinker::ModuleTreeLinker(Modulator* modulator, | |
| 15 ModuleTreeLinkerRegistry* registry, | |
| 16 ModuleTreeClient* client) | |
| 17 : m_modulator(modulator), m_registry(registry), m_client(client) { | |
| 18 DCHECK(modulator); | |
| 19 DCHECK(registry); | |
| 20 DCHECK(client); | |
| 21 } | |
| 22 | |
| 23 DEFINE_TRACE(ModuleTreeLinker) { | |
| 24 visitor->trace(m_modulator); | |
| 25 visitor->trace(m_registry); | |
| 26 visitor->trace(m_client); | |
| 27 visitor->trace(m_moduleScript); | |
| 28 visitor->trace(m_dependencyClients); | |
| 29 SingleModuleClient::trace(visitor); | |
| 30 } | |
| 31 | |
| 32 void ModuleTreeLinker::advanceState(State newState) { | |
| 33 printf("mtf %p adv state: %d\n", this, newState); | |
| 34 switch (m_state) { | |
| 35 case State::Initial: | |
| 36 DCHECK_EQ(newState, State::FetchingSelf); | |
| 37 break; | |
| 38 case State::FetchingSelf: | |
| 39 DCHECK(newState == State::FetchingDependencies || | |
| 40 newState == State::Finished); | |
| 41 break; | |
| 42 case State::FetchingDependencies: | |
| 43 DCHECK(newState == State::Instantiating || newState == State::Finished); | |
| 44 break; | |
| 45 case State::Instantiating: | |
| 46 DCHECK_EQ(newState, State::Finished); | |
| 47 break; | |
| 48 case State::Finished: | |
| 49 NOTREACHED(); | |
| 50 break; | |
| 51 default: | |
| 52 NOTREACHED(); | |
| 53 } | |
| 54 | |
| 55 m_state = newState; | |
| 56 | |
| 57 if (m_state == State::Finished) { | |
| 58 m_registry->releaseFinishedFetcher(this); | |
| 59 m_client->notifyFinishedModuleTree(m_moduleScript); | |
| 60 } | |
| 61 } | |
| 62 | |
| 63 ModuleTreeLinker* ModuleTreeLinker::fetch(const KURL& url, | |
| 64 const KURL& baseURL, | |
| 65 Modulator* modulator, | |
| 66 ModuleTreeLinkerRegistry* registry, | |
| 67 ModuleTreeClient* client) { | |
| 68 ModuleTreeLinker* fetcher = new ModuleTreeLinker(modulator, registry, client); | |
| 69 fetcher->fetchSelf(url, baseURL); | |
| 70 return fetcher; | |
| 71 } | |
| 72 | |
| 73 void ModuleTreeLinker::fetchSelf(const KURL& url, const KURL& baseURL) { | |
| 74 // https://html.spec.whatwg.org/multipage/webappapis.html#internal-module-scri pt-graph-fetching-procedure | |
| 75 | |
| 76 // Step 1. Fetch a single module script given url, fetch client settings | |
| 77 // object, destination, cryptographic nonce, parser state, credentials mode, | |
| 78 // module map settings object, referrer, and the top-level module fetch flag. | |
| 79 // If the caller of this algorithm specified custom perform the fetch steps, | |
| 80 // pass those along while fetching a single module script. | |
| 81 // Step 2. Return from this algorithm, and run the following steps when | |
| 82 // fetching a single module script asynchronously completes with result. | |
| 83 advanceState(State::FetchingSelf); | |
| 84 m_modulator->fetchSingle(url, baseURL, this); | |
| 85 } | |
| 86 | |
| 87 class ModuleTreeLinker::DependencyModuleClient | |
| 88 : public GarbageCollectedFinalized< | |
| 89 ModuleTreeLinker::DependencyModuleClient>, | |
| 90 public ModuleTreeClient { | |
| 91 USING_GARBAGE_COLLECTED_MIXIN(ModuleTreeLinker::DependencyModuleClient); | |
| 92 | |
| 93 public: | |
| 94 static DependencyModuleClient* create(ModuleTreeLinker* moduleTreeLinkers) { | |
| 95 return new DependencyModuleClient(moduleTreeLinkers); | |
| 96 } | |
| 97 virtual ~DependencyModuleClient() = default; | |
| 98 | |
| 99 DEFINE_INLINE_TRACE() { | |
| 100 visitor->trace(m_moduleTreeLinkers); | |
| 101 ModuleTreeClient::trace(visitor); | |
| 102 } | |
| 103 | |
| 104 private: | |
| 105 DependencyModuleClient(ModuleTreeLinker* moduleTreeLinkers) | |
| 106 : m_moduleTreeLinkers(moduleTreeLinkers) { | |
| 107 DCHECK(moduleTreeLinkers); | |
| 108 } | |
| 109 | |
| 110 // Implements ModuleTreeClient | |
| 111 void notifyFinishedModuleTree(ModuleScript*) override; | |
| 112 | |
| 113 Member<ModuleTreeLinker> m_moduleTreeLinkers; | |
| 114 }; | |
| 115 | |
| 116 void ModuleTreeLinker::notifyFinishedSingleModule(ModuleScript* moduleScript) { | |
| 117 // https://html.spec.whatwg.org/multipage/webappapis.html#internal-module-scri pt-graph-fetching-procedure | |
| 118 | |
| 119 // Step 3. If result is null, asynchronously complete this algorithm with null | |
| 120 // and abort these steps. | |
| 121 if (!moduleScript) { | |
| 122 advanceState(State::Finished); | |
| 123 return; | |
| 124 } | |
| 125 | |
| 126 // Step 4. Otherwise, result is a module script. Fetch the descendants of | |
| 127 // result given destination and an ancestor list obtained by appending url to | |
| 128 // ancestor list. Wait for fetching the descendants of a module script to | |
| 129 // asynchronously complete with descendants result before proceeding to the | |
| 130 // next step. | |
| 131 m_moduleScript = moduleScript; | |
| 132 fetchDescendants(); | |
| 133 | |
| 134 // Note: Step 5- continues in instantiate() method, after | |
| 135 // "fetch the descendants of a module script" procedure completes. | |
| 136 } | |
| 137 | |
| 138 void ModuleTreeLinker::fetchDescendants() { | |
| 139 DCHECK(m_moduleScript); | |
| 140 advanceState(State::FetchingDependencies); | |
| 141 | |
| 142 // https://html.spec.whatwg.org/multipage/webappapis.html#fetch-the-descendant s-of-a-module-script | |
| 143 | |
| 144 // Step 1. Let record be module script's module record. | |
| 145 ScriptModule record = m_moduleScript->record(); | |
| 146 | |
| 147 // Step 2. If record.[[RequestedModules]] is empty, asynchronously complete | |
| 148 // this algorithm with module script. | |
| 149 Vector<String> moduleRequests = | |
| 150 m_modulator->moduleController()->moduleRequestsFromScriptModule(record); | |
| 151 if (moduleRequests.isEmpty()) { | |
| 152 // Continue to instantiate() to process "internal module script graph | |
| 153 // fetching procedure" Step 5-. | |
| 154 instantiate(); | |
| 155 return; | |
| 156 } | |
| 157 | |
| 158 // Step 3. Let urls be a new empty list. | |
| 159 Vector<KURL> urls; | |
| 160 | |
| 161 // Step 4. For each string requested of record.[[RequestedModules]], | |
| 162 for (const auto& moduleRequest : moduleRequests) { | |
| 163 // Step 4.1. Let url be the result of resolving a module specifier given | |
| 164 // module script and requested. | |
| 165 KURL url = m_modulator->resolveModuleSpecifier(moduleRequest, | |
| 166 m_moduleScript->baseURL()); | |
| 167 printf("resolveModuleSpecifier \"%s\" -> \"%s\"\n", | |
| 168 moduleRequest.utf8().data(), url.getString().utf8().data()); | |
| 169 | |
| 170 // Step 4.2. If the result is error: ... | |
| 171 if (url.isNull()) { | |
| 172 // Let error be a new TypeError exception. | |
| 173 // Report the exception error for module script. | |
| 174 // Abort this algorithm, and asynchronously complete it with null. | |
| 175 m_moduleScript = nullptr; | |
| 176 advanceState(State::Finished); | |
| 177 } | |
| 178 | |
| 179 // Step 4.3. Otherwise, if ancestor list does not contain url, append url to | |
|
yhirano
2017/01/06 07:27:01
Where is the ancestor list? Is it guaranteed that
kouhei (in TOK)
2017/01/11 01:41:58
Implemented "ancestor list" algorithm.
kouhei (in TOK)
2017/01/11 02:30:17
Sorry this was still incomplete. Fixing.
| |
| 180 // urls. | |
| 181 DCHECK(url.isValid()); | |
| 182 urls.push_back(url); | |
| 183 } | |
| 184 | |
| 185 // Step 5. For each url in urls, perform the internal module script graph | |
| 186 // fetching procedure given url, module script's credentials mode, module | |
| 187 // script's cryptographic nonce, module script's parser state, destination, | |
| 188 // module script's settings object, module script's settings object, ancestor | |
| 189 // list, module script's base URL, and with the top-level module fetch flag | |
| 190 // unset. If the caller of this algorithm specified custom perform the fetch | |
| 191 // steps, pass those along while performing the internal module script graph | |
| 192 // fetching procedure. | |
| 193 DCHECK_EQ(m_numIncompleteDescendants, 0u); | |
| 194 m_numIncompleteDescendants = urls.size(); | |
| 195 for (const KURL& url : urls) { | |
| 196 DependencyModuleClient* dependencyClient = | |
| 197 DependencyModuleClient::create(this); | |
| 198 m_dependencyClients.add(dependencyClient); | |
| 199 m_modulator->fetchTree(url, m_moduleScript->baseURL(), dependencyClient); | |
| 200 } | |
| 201 | |
| 202 // Asynchronously continue processing after notifyOneDescendantFinished() is | |
| 203 // called m_numIncompleteDescendants times. | |
| 204 DCHECK(m_numIncompleteDescendants); | |
| 205 } | |
| 206 | |
| 207 void ModuleTreeLinker::DependencyModuleClient::notifyFinishedModuleTree( | |
| 208 ModuleScript* moduleScript) { | |
| 209 bool wasSuccess = !!moduleScript; | |
| 210 m_moduleTreeLinkers->notifyOneDescendantFinished(wasSuccess); | |
| 211 } | |
| 212 | |
| 213 void ModuleTreeLinker::notifyOneDescendantFinished(bool wasSuccess) { | |
| 214 if (m_state == State::Finished) { | |
| 215 // We may reach here if one of the descendant failed to load, and the other | |
| 216 // descendants fetches were in flight. | |
| 217 DCHECK(!m_moduleScript); | |
| 218 return; | |
| 219 } | |
| 220 | |
| 221 DCHECK(m_state == State::FetchingDependencies); | |
| 222 | |
| 223 DCHECK_GT(m_numIncompleteDescendants, 0u); | |
| 224 --m_numIncompleteDescendants; | |
| 225 | |
| 226 if (!wasSuccess) { | |
| 227 m_moduleScript = nullptr; | |
| 228 advanceState(State::Finished); | |
| 229 return; | |
| 230 } | |
| 231 | |
| 232 DCHECK(m_moduleScript); | |
| 233 printf("remaining desc %zu\n", m_numIncompleteDescendants); | |
| 234 if (!m_numIncompleteDescendants) | |
| 235 instantiate(); | |
| 236 } | |
| 237 | |
| 238 void ModuleTreeLinker::instantiate() { | |
| 239 DCHECK(m_moduleScript); | |
| 240 // https://html.spec.whatwg.org/multipage/webappapis.html#internal-module-scri pt-graph-fetching-procedure | |
| 241 | |
| 242 // Step 5. Let record be result's module record. | |
| 243 ScriptModule record = m_moduleScript->record(); | |
| 244 | |
| 245 // Step 6. Let instantiationStatus be record.ModuleDeclarationInstantiation(). | |
| 246 bool instantiateSuccess = | |
| 247 m_modulator->moduleController()->instantiateModule(record); | |
| 248 DCHECK(instantiateSuccess); // TODO(kouhei) | |
| 249 m_moduleScript->updateStateAfterInstantiation(ScriptValue()); | |
| 250 InstantiationState instantiationStatus = m_moduleScript->instantiationState(); | |
| 251 | |
| 252 // Step 7. For each module script script in result's uninstantiated inclusive | |
| 253 // descendant module scripts, perform the following steps: | |
| 254 HeapHashSet<Member<ModuleScript>> uninstantiatedSet = | |
|
yhirano
2017/01/06 07:27:01
Is there an ordering issue? "set" in the spec look
kouhei (in TOK)
2017/01/11 01:41:58
No. Added a spec note.
| |
| 255 uninstantiatedInclusiveDescendants(); | |
| 256 for (const auto& descendant : uninstantiatedSet) { | |
| 257 if (instantiationStatus == InstantiationState::Errored) { | |
| 258 // Step 7.1. If instantiationStatus is an abrupt completion, then set | |
| 259 // script's | |
| 260 // instantiation state to "errored", its instantiation error to | |
| 261 // instantiationStatus.[[Value]], and its module record to null. | |
| 262 descendant->propagateUpstreamError(m_moduleScript->instantiationError()); | |
| 263 } else { | |
| 264 // Step 7.2. Otherwise, set script's instantiation state to | |
| 265 // "instantiated". | |
| 266 descendant->propagateUpstreamSuccess(); | |
| 267 } | |
| 268 } | |
| 269 | |
| 270 // Step 8. Asynchronously complete this algorithm with descendants result. | |
| 271 advanceState(State::Finished); | |
| 272 } | |
| 273 | |
| 274 HeapHashSet<Member<ModuleScript>> | |
| 275 ModuleTreeLinker::uninstantiatedInclusiveDescendants() { | |
| 276 // https://html.spec.whatwg.org/multipage/webappapis.html#uninstantiated-inclu sive-descendant-module-scripts | |
| 277 // Step 1. Let moduleMap be script's settings object's module map. | |
| 278 // Note: Modulator is our "settings object". | |
| 279 // Note: We won't reference the ModuleMap directly here to aid testing. | |
| 280 | |
| 281 // Step 2. Let stack be the stack « script ». | |
| 282 HeapDeque<Member<ModuleScript>> stack; | |
| 283 stack.prepend(m_moduleScript); | |
| 284 | |
| 285 // Step 3. Let inclusive descendants be an empty set. | |
| 286 HeapHashSet<Member<ModuleScript>> inclusiveDescendants; | |
| 287 | |
| 288 // Step 4. While stack is not empty: | |
| 289 while (!stack.isEmpty()) { | |
| 290 // Step 4.1. Let current the result of popping from stack. | |
| 291 ModuleScript* current = stack.takeFirst(); | |
| 292 | |
| 293 // Step 4.2. If inclusive descendants and stack both do not contain current, | |
| 294 // then: | |
| 295 if (inclusiveDescendants.contains(current)) | |
| 296 continue; | |
| 297 if (std::find(stack.begin(), stack.end(), current) == stack.end()) | |
|
yhirano
2017/01/06 07:27:01
!=
kouhei (in TOK)
2017/01/11 01:41:58
Done.
| |
| 298 continue; | |
| 299 | |
| 300 // Step 4.2.1. Append current to inclusive descendants. | |
| 301 inclusiveDescendants.add(current); | |
| 302 | |
| 303 // TODO(kouhei): This implementation is a direct transliteration of the | |
| 304 // spec. Omit intermediate vectors at the least. | |
| 305 | |
| 306 // Step 4.2.2. Let child specifiers be the value of current's module | |
| 307 // record's [[RequestedModules]] internal slot. | |
| 308 Vector<String> childSpecifiers = | |
| 309 m_modulator->moduleController()->moduleRequestsFromScriptModule( | |
| 310 current->record()); | |
| 311 // Step 4.2.3. Let child URLs be the list obtained by calling resolve a | |
| 312 // module specifier once for each item of child specifiers, given current | |
| 313 // and that item. Omit any failures. | |
| 314 Vector<KURL> childURLs; | |
| 315 for (const auto& childSpecifier : childSpecifiers) { | |
| 316 KURL childURL = m_modulator->resolveModuleSpecifier(childSpecifier, | |
| 317 current->baseURL()); | |
| 318 if (childURL.isValid()) | |
| 319 childURLs.push_back(childURL); | |
| 320 } | |
| 321 // Step 4.2.4. Let child modules be the list obtained by getting each value | |
| 322 // in moduleMap whose key is given by an item of child URLs. | |
| 323 HeapVector<Member<ModuleScript>> childModules; | |
| 324 for (const auto& childURL : childURLs) { | |
| 325 ModuleScript* moduleScript = | |
| 326 m_modulator->retrieveFetchedModuleScript(childURL); | |
| 327 childModules.push_back(moduleScript); | |
| 328 } | |
| 329 // Step 4.2.5. For each s in child modules that is not contained by | |
| 330 // inclusive descendants, push s onto stack. | |
| 331 for (const auto& s : childModules) { | |
| 332 if (!inclusiveDescendants.contains(s)) | |
| 333 stack.prepend(s); | |
| 334 } | |
| 335 } | |
| 336 | |
| 337 // Step 5. Return a set containing all items of inclusive descendants whose | |
| 338 // instantiation state is "uninstantiated". | |
| 339 HeapHashSet<Member<ModuleScript>> uninstantiatedSet; | |
| 340 for (const auto& script : inclusiveDescendants) { | |
| 341 if (script->instantiationState() == InstantiationState::Uninstantiated) | |
| 342 uninstantiatedSet.add(script); | |
| 343 } | |
| 344 return uninstantiatedSet; | |
| 345 } | |
| 346 | |
| 347 } // namespace blink | |
| OLD | NEW |