Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2016 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 "bindings/core/v8/ScriptController.h" | |
| 8 #include "bindings/core/v8/V8PerIsolateData.h" | |
| 9 #include "core/fetch/ResourceFetcher.h" | |
| 10 #include "platform/network/mime/MIMETypeRegistry.h" | |
| 11 #include "wtf/AutoReset.h" | |
| 12 #include <v8.h> | |
| 13 | |
| 14 namespace blink { | |
| 15 | |
| 16 ModuleLoaderClient::~ModuleLoaderClient() {} | |
| 17 | |
| 18 void ModuleLoaderClient::catchUpToLatestStateIfNeeded( | |
| 19 ModuleLoaderState latestState) { | |
| 20 if (m_state == ModuleLoaderState::Finished) | |
| 21 return; | |
| 22 if (latestState == ModuleLoaderState::Finished) { | |
| 23 notifyFinished(); | |
| 24 m_state = latestState; | |
| 25 } | |
| 26 } | |
| 27 | |
| 28 ModuleLoader::ModuleLoader(const KURL& url, ScriptController* scriptController) | |
| 29 : m_url(url), m_scriptController(scriptController) {} | |
| 30 | |
| 31 ModuleLoader::~ModuleLoader() {} | |
| 32 | |
| 33 void ModuleLoader::advanceState(ModuleLoaderState newState) { | |
| 34 switch (m_state) { | |
| 35 case ModuleLoaderState::Initial: | |
| 36 DCHECK_EQ(newState, ModuleLoaderState::Fetching); | |
| 37 break; | |
| 38 case ModuleLoaderState::Fetching: | |
| 39 DCHECK_EQ(newState, ModuleLoaderState::Finished); | |
| 40 break; | |
| 41 case ModuleLoaderState::Finished: | |
| 42 default: | |
| 43 NOTREACHED(); | |
| 44 } | |
| 45 | |
| 46 m_state = newState; | |
| 47 | |
| 48 notifyAndFlushPendingClients(); | |
| 49 } | |
| 50 | |
| 51 ModuleLoader* ModuleLoader::fetch(const KURL& url, | |
| 52 ScriptController* scriptController, | |
| 53 ResourceFetcher* fetcher) { | |
| 54 ModuleLoader* loader = new ModuleLoader(url, scriptController); | |
| 55 loader->fetchInternal(fetcher); | |
| 56 return loader; | |
| 57 } | |
| 58 | |
| 59 void ModuleLoader::fetchInternal(ResourceFetcher* fetcher) { | |
| 60 // Step 4. Set moduleMap[url] to "fetching". | |
| 61 advanceState(ModuleLoaderState::Fetching); | |
| 62 | |
| 63 // Step 5. Let request be a new request whose url is url, destination is | |
| 64 // destination, type is "script", mode is "cors", credentials mode is | |
| 65 // credentials mode, cryptographic nonce metadata is cryptographic nonce, | |
| 66 // parser metadata is parser state, referrer is referrer, and client is | |
| 67 // fetch | |
| 68 // client settings object. | |
| 69 FetchRequest request(ResourceRequest(m_url), "module"); | |
| 70 // request.setDefer(FetchRequest::LazyLoad); //? always async -> defer? | |
| 71 Member<ScriptResource> resource = ScriptResource::fetch(request, fetcher); | |
| 72 setResource(resource); | |
| 73 | |
| 74 // Step 6. If the caller specified custom steps to perform the fetch, | |
| 75 // perform | |
| 76 // them on request, setting the is top-level flag if the top-level module | |
| 77 // fetch flag is set. Return from this algorithm, and when the custom | |
| 78 // perform | |
| 79 // the fetch steps complete with response response, run the remaining steps. | |
| 80 // Otherwise, fetch request. Return from this algorithm, and run the | |
| 81 // remaining | |
| 82 // steps as part of the fetch's process response for the response response. | |
| 83 // Note: response is always CORS-same-origin. | |
| 84 | |
| 85 // TODO(kouhei): I don't know what Step 6 means. ScriptLoader seems to | |
| 86 // ignore | |
| 87 // the step? | |
| 88 } | |
| 89 | |
| 90 bool ModuleLoader::wasModuleLoadSuccessful(Resource* resource) { | |
| 91 // Implements conditions in Step 7 of | |
| 92 // https://html.spec.whatwg.org/#fetch-a-single-module-script | |
| 93 | |
| 94 // - response's type is "error" | |
| 95 if (resource->errorOccurred()) | |
| 96 return false; | |
| 97 | |
| 98 const auto& response = resource->response(); | |
| 99 // - response's status is not an ok status | |
| 100 if (response.isHTTP() && | |
| 101 (response.httpStatusCode() < 200 || response.httpStatusCode() >= 300)) { | |
| 102 return false; | |
| 103 } | |
| 104 | |
| 105 // The result of extracting a MIME type from response's header list | |
| 106 // (ignoring parameters) is not a JavaScript MIME type | |
| 107 // Note: For historical reasons, fetching a classic script does not include | |
| 108 // MIME type checking. In contrast, module scripts will fail to load if they | |
| 109 // are not of a correct MIME type. | |
| 110 if (!MIMETypeRegistry::isSupportedJavaScriptMIMEType(response.mimeType())) | |
| 111 return false; | |
| 112 | |
| 113 return true; | |
| 114 } | |
| 115 | |
| 116 void ModuleLoader::notifyFinished(Resource*) { | |
| 117 printf("notifyFinished!\n"); | |
| 118 | |
| 119 // Note: "conditions" referred in Step 7 is implemented in | |
| 120 // wasModuleLoadSuccessful(). | |
| 121 // Step 7. If any of the following conditions are met, set moduleMap[url] to | |
| 122 // null, asynchronously complete this algorithm with null, and abort these | |
| 123 // steps. | |
| 124 if (!wasModuleLoadSuccessful(resource())) { | |
| 125 printf("notifyFinished but load was not successful\n"); | |
| 126 | |
| 127 m_wasLoadSuccessful = false; | |
| 128 advanceState(ModuleLoaderState::Finished); | |
| 129 setResource(nullptr); | |
| 130 return; | |
| 131 } | |
| 132 | |
| 133 // Step 8. Let source text be the result of UTF-8 decoding response's body. | |
| 134 String script = resource()->script(); | |
| 135 printf("fetched script: %s\n", script.utf8().data()); | |
| 136 | |
| 137 // Step 9. Let module script be the result of creating a module script given | |
| 138 // source text, module map settings object, response's url, cryptographic | |
| 139 // nonce, parser state, and credentials mode. | |
| 140 | |
| 141 // TODO(kouhei): post a task to compile module? | |
| 142 m_scriptModule = m_scriptController->compileModule(script, m_url.getString()); | |
| 143 m_wasLoadSuccessful = !m_scriptModule.isNull(); | |
| 144 advanceState(ModuleLoaderState::Finished); | |
| 145 setResource(nullptr); | |
| 146 } | |
| 147 | |
| 148 void ModuleLoader::addClient(ModuleLoaderClient* newClient) { | |
| 149 DCHECK(!m_pendingClients.contains(newClient)); | |
| 150 DCHECK(!m_clients.contains(newClient)); | |
| 151 newClient->setLoader(this); | |
| 152 m_pendingClients.add(newClient); | |
| 153 | |
| 154 notifyAndFlushPendingClients(); | |
| 155 } | |
| 156 | |
| 157 void ModuleLoader::notifyAndFlushPendingClients() { | |
| 158 if (m_isFlushingPendingClients) { | |
| 159 // This notifyAndFlushPendingClients call was reentrant. | |
| 160 // Defer to first notifyAndFlushPendingClients to do the job. | |
| 161 return; | |
| 162 } | |
| 163 | |
| 164 AutoReset<bool> forbidClientModificationScope(&m_isFlushingPendingClients, | |
| 165 true); | |
| 166 | |
| 167 if (m_lastClientNotifiedState != m_state) { | |
| 168 // if m_state has changed since last update, put all m_clients back into | |
| 169 // m_pendingClients so that they catchUpToLatestStateIfNeeded() | |
| 170 m_pendingClients.swap(m_clients); | |
| 171 while (!m_clients.isEmpty()) { | |
| 172 m_pendingClients.add(m_clients.takeAny()); | |
| 173 } | |
| 174 | |
| 175 m_lastClientNotifiedState = m_state; | |
| 176 } | |
| 177 | |
| 178 while (!m_pendingClients.isEmpty()) { | |
| 179 // Iterate on copy as m_pendingClients may be added in the notification | |
| 180 // loop. | |
| 181 HeapHashSet<Member<ModuleLoaderClient>> clientsToCatchUp; | |
| 182 clientsToCatchUp.swap(m_pendingClients); | |
| 183 for (const auto& client : clientsToCatchUp) { | |
| 184 client->catchUpToLatestStateIfNeeded(m_state); | |
| 185 } | |
| 186 | |
| 187 // Caught up clients do not have any notifications to receive after the | |
| 188 // load is finished. | |
| 189 if (hasFinished()) | |
| 190 continue; | |
| 191 | |
| 192 while (!clientsToCatchUp.isEmpty()) | |
| 193 m_clients.add(clientsToCatchUp.takeAny()); | |
| 194 } | |
| 195 } | |
| 196 | |
| 197 DEFINE_TRACE(ModuleLoader) { | |
| 198 visitor->trace(m_scriptController); | |
| 199 visitor->trace(m_pendingClients); | |
| 200 visitor->trace(m_clients); | |
| 201 ResourceOwner<ScriptResource>::trace(visitor); | |
| 202 } | |
| 203 | |
| 204 ModuleMap::ModuleMap(ScriptController* scriptController, | |
| 205 ResourceFetcher* fetcher) | |
| 206 : m_scriptController(scriptController), m_fetcher(fetcher) { | |
| 207 DCHECK(m_scriptController); | |
| 208 DCHECK(m_fetcher); | |
| 209 } | |
| 210 | |
| 211 void ModuleMap::fetchSingle(const KURL& url, ModuleLoaderClient* client) { | |
| 212 printf("ModuleMap::fetch(%s)\n", url.getString().utf8().data()); | |
| 213 | |
| 214 MapImpl::AddResult result = m_map.set(url, nullptr); | |
| 215 Member<ModuleLoader>& loaderEntry = result.storedValue->value; | |
| 216 if (!result.isNewEntry) { | |
| 217 printf("not new entry\n"); | |
| 218 | |
| 219 // - If moduleMap[url] is "fetching", wait (in parallel) until that | |
| 220 // loaderEntry's value changes, then proceed to the next step. | |
| 221 // - If moduleMap[url] exists, asynchronously complete this algorithm | |
| 222 // with moduleMap[url], and abort these steps. | |
| 223 loaderEntry->addClient(client); | |
|
dominicc (has gone to gerrit)
2016/12/13 07:45:29
I think this design has to be careful with the tra
kouhei (in TOK)
2016/12/15 04:56:27
I'd like to discuss this offline (probably need a
| |
| 224 return; | |
| 225 } | |
| 226 | |
| 227 ModuleLoader* loader = | |
| 228 ModuleLoader::fetch(url, m_scriptController.get(), m_fetcher.get()); | |
| 229 | |
| 230 // Step 10. Set moduleMap[url] to module script, and asynchronously complete | |
| 231 // this algorithm with module script. | |
| 232 loaderEntry = loader; | |
| 233 loaderEntry->addClient(client); | |
| 234 } | |
| 235 | |
| 236 DEFINE_TRACE(ModuleMap) { | |
| 237 visitor->trace(m_map); | |
| 238 visitor->trace(m_scriptController); | |
| 239 visitor->trace(m_fetcher); | |
| 240 } | |
| 241 | |
| 242 } // namespace blink | |
| OLD | NEW |