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 "bindings/core/v8/V8PerIsolateData.h" |
| 6 #include "core/dom/ModuleMap.h" |
| 7 #include "core/fetch/ResourceFetcher.h" |
| 8 #include "core/frame/LocalFrame.h" |
| 9 #include "wtf/AutoReset.h" |
| 10 #include <v8.h> |
| 11 |
| 12 namespace blink { |
| 13 |
| 14 LocalFrame* g_localFrame = nullptr; |
| 15 |
| 16 ModuleLoaderClient::~ModuleLoaderClient() |
| 17 { |
| 18 } |
| 19 |
| 20 void ModuleLoaderClient::catchUpToLatestStateIfNeeded(ModuleLoaderState latetstS
tate) |
| 21 { |
| 22 if (m_state == State::Fetched || m_state == State::Failed) |
| 23 return; |
| 24 if (latestState == State::Fetched || latestState == State::Failed) { |
| 25 notifyFinished(); |
| 26 m_state = latestState; |
| 27 } |
| 28 } |
| 29 |
| 30 ModuleLoader::ModuleLoader(const KURL& url) |
| 31 : m_url(url), m_state(State::Initial) {} |
| 32 |
| 33 ModuleLoader::~ModuleLoader() {} |
| 34 |
| 35 void ModuleLoader::advanceState(ModuleLoader::State newState) { |
| 36 switch (m_state) { |
| 37 case State::Initial: |
| 38 DCHECK_EQ(newState, State::Fetching); |
| 39 break; |
| 40 case State::Fetching: |
| 41 DCHECK(newState == State::Fetched || newState == State::Failed); |
| 42 break; |
| 43 |
| 44 case State::Fetched: |
| 45 case State::Failed: |
| 46 default: |
| 47 NOTREACHED(); |
| 48 } |
| 49 m_state = newState; |
| 50 |
| 51 notifyAndFlushPendingClients(); |
| 52 } |
| 53 |
| 54 void ModuleLoader::fetch(ResourceFetcher* fetcher) { |
| 55 // Step 4. Set moduleMap[url] to "fetching". |
| 56 advanceState(State::Fetching); |
| 57 |
| 58 // Step 5. Let request be a new request whose url is url, destination is |
| 59 // destination, type is "script", mode is "cors", credentials mode is |
| 60 // credentials mode, cryptographic nonce metadata is cryptographic nonce, |
| 61 // parser metadata is parser state, referrer is referrer, and client is fetch |
| 62 // client settings object. |
| 63 FetchRequest request(ResourceRequest(m_url), "module"); |
| 64 // request.setDefer(FetchRequest::LazyLoad); //? always async -> defer? |
| 65 Member<ScriptResource> resource = ScriptResource::fetch(request, fetcher); |
| 66 setResource(resource); |
| 67 |
| 68 // Step 6. If the caller specified custom steps to perform the fetch, perform |
| 69 // them on request, setting the is top-level flag if the top-level module |
| 70 // fetch flag is set. Return from this algorithm, and when the custom perform |
| 71 // the fetch steps complete with response response, run the remaining steps. |
| 72 // Otherwise, fetch request. Return from this algorithm, and run the remaining |
| 73 // steps as part of the fetch's process response for the response response. |
| 74 // Note: response is always CORS-same-origin. |
| 75 |
| 76 // TODO(kouhei): I don't know what Step 6 means. ScriptLoader seems to ignore |
| 77 // the step? |
| 78 } |
| 79 |
| 80 void ModuleLoader::notifyFinished(Resource*) { |
| 81 printf("notifyFinished!\n"); |
| 82 |
| 83 // Step 7. If any of the following conditions are met, set moduleMap[url] to |
| 84 // null, asynchronously complete this algorithm with null, and abort these |
| 85 // steps: |
| 86 // - response's type is "error" |
| 87 // - response's status is not an ok status |
| 88 // The result of extracting a MIME type from response's header list (ignoring |
| 89 // parameters) is not a JavaScript MIME type |
| 90 // Note: For historical reasons, fetching a classic script does not include |
| 91 // MIME type checking. In contrast, module scripts will fail to load if they |
| 92 // are not of a correct MIME type. |
| 93 if (resource()->errorOccurred()) { |
| 94 advanceState(State::Failed); |
| 95 return; |
| 96 } |
| 97 |
| 98 // Step 8. Let source text be the result of UTF-8 decoding response's body. |
| 99 String script = resource()->script(); |
| 100 printf("fetched script: %s\n", script.utf8().data()); |
| 101 |
| 102 // Step 9. Let module script be the result of creating a module script given |
| 103 // source text, module map settings object, response's url, cryptographic |
| 104 // nonce, parser state, and credentials mode. |
| 105 v8::Isolate* isolate = V8PerIsolateData::mainThreadIsolate(); |
| 106 v8::HandleScope handleScope(isolate); |
| 107 |
| 108 ScriptState* scriptState = ScriptState::forMainWorld(g_localFrame); |
| 109 DCHECK(scriptState); |
| 110 |
| 111 // ??? : I'm not sure why this requres scriptState to run compile step. |
| 112 ScriptState::Scope scope(scriptState); |
| 113 m_scriptModule = ScriptModule::compile(isolate, script, m_url.getString()); |
| 114 |
| 115 advanceState(State::Fetched); |
| 116 } |
| 117 |
| 118 void ModuleLoader::addClient(ModuleLoaderClient* newClient) { |
| 119 DCHECK(!m_pendingClients.contains(newClient)); |
| 120 DCHECK(!m_clients.contains(newClient)); |
| 121 m_pendingClients.add(newClient); |
| 122 |
| 123 notifyAndFlushPendingClients(); |
| 124 } |
| 125 |
| 126 void ModuleLoader::notifyAndFlushPendingClients(FlushType flushType) { |
| 127 if (m_insidePendingClientsLoop) { |
| 128 // This notifyAndFlushPendingClients call was reentrant. |
| 129 // Defer to first notifyAndFlushPendingClients to do the job. |
| 130 return; |
| 131 } |
| 132 |
| 133 AutoReset<bool> forbidClientModificationScope(&m_insideAddPendingClientsLoop,
true); |
| 134 |
| 135 DCHECK(m_pendingClients.isEmpty()); |
| 136 if (flushType == FlushType::MayHaveAdvancedState) |
| 137 m_pendingClients.swap(m_clients); |
| 138 |
| 139 while (!m_pendingClients.isEmpty()) { |
| 140 // Iterate on copy as m_pendingClients may be added in the notification loop
. |
| 141 HeapHashSet<Member<ModuleLoaderClient>> clientsToCatchUp; |
| 142 clientsToCatchUp.swap(m_pendingClients); |
| 143 for (const auto& client : clientsToCatchUp) { |
| 144 client->catchUpToLatestStateIfNeeded(m_state); |
| 145 } |
| 146 |
| 147 // Caught up clients do not have any notifications to receive after the |
| 148 // load is finished. |
| 149 if (isFinished()) |
| 150 continue; |
| 151 |
| 152 while (clientsToCatchUp.isEmpty()) |
| 153 m_clients.add(clientsToCatchUp.takeAny()); |
| 154 } |
| 155 } |
| 156 |
| 157 DEFINE_TRACE(ModuleLoader) { |
| 158 visitor->trace(m_clients); |
| 159 ResourceOwner<ScriptResource>::trace(visitor); |
| 160 } |
| 161 |
| 162 ModuleMap::ModuleMap(LocalFrame* frame, ResourceFetcher* fetcher) |
| 163 : m_frame(frame), m_fetcher(fetcher) { |
| 164 DCHECK(m_frame); |
| 165 DCHECK(m_fetcher); |
| 166 } |
| 167 |
| 168 void ModuleMap::fetch(const KURL& url, ModuleLoaderClient* client) { |
| 169 g_localFrame = m_frame; // TODO(kouhei): AAAAAAAArrrrrgghhh |
| 170 printf("ModuleMap::fetch(%s)\n", url.getString().utf8().data()); |
| 171 |
| 172 MapImpl::AddResult result = m_map.set(url, nullptr); |
| 173 Member<ModuleLoader>& loaderEntry = result.storedValue->value; |
| 174 if (!result.isNewEntry) { |
| 175 printf("not new entry\n"); |
| 176 |
| 177 // - If moduleMap[url] is "fetching", wait (in parallel) until that |
| 178 // loaderEntry's value changes, then proceed to the next step. |
| 179 // - If moduleMap[url] exists, asynchronously complete this algorithm |
| 180 // with moduleMap[url], and abort these steps. |
| 181 loaderEntry->addClient(client); |
| 182 return; |
| 183 } |
| 184 |
| 185 ModuleLoader* loader = ModuleLoader::create(url); |
| 186 loader->fetch(m_fetcher.get()); |
| 187 |
| 188 // Step 10. Set moduleMap[url] to module script, and asynchronously complete |
| 189 // this algorithm with module script. |
| 190 loaderEntry = loader; |
| 191 loaderEntry->addClient(client); |
| 192 } |
| 193 |
| 194 DEFINE_TRACE(ModuleMap) { |
| 195 visitor->trace(m_map); |
| 196 visitor->trace(m_frame); |
| 197 visitor->trace(m_fetcher); |
| 198 } |
| 199 |
| 200 } // namespace blink |
OLD | NEW |