Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(60)

Side by Side Diff: third_party/WebKit/Source/core/dom/ModuleMap.cpp

Issue 2555653002: [WIP Prototype] ES6 https://html.spec.whatwg.org/#fetch-a-single-module-script implementation (Closed)
Patch Set: ScriptModule interaction refactored to ScriptController Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698