Index: third_party/WebKit/Source/core/dom/ModuleMap.cpp |
diff --git a/third_party/WebKit/Source/core/dom/ModuleMap.cpp b/third_party/WebKit/Source/core/dom/ModuleMap.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..3773b82cbae66e02d72a7d36df7158f505252472 |
--- /dev/null |
+++ b/third_party/WebKit/Source/core/dom/ModuleMap.cpp |
@@ -0,0 +1,200 @@ |
+// Copyright 2016 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "bindings/core/v8/V8PerIsolateData.h" |
+#include "core/dom/ModuleMap.h" |
+#include "core/fetch/ResourceFetcher.h" |
+#include "core/frame/LocalFrame.h" |
+#include "wtf/AutoReset.h" |
+#include <v8.h> |
+ |
+namespace blink { |
+ |
+LocalFrame* g_localFrame = nullptr; |
+ |
+ModuleLoaderClient::~ModuleLoaderClient() |
+{ |
+} |
+ |
+void ModuleLoaderClient::catchUpToLatestStateIfNeeded(ModuleLoaderState latetstState) |
+{ |
+ if (m_state == State::Fetched || m_state == State::Failed) |
+ return; |
+ if (latestState == State::Fetched || latestState == State::Failed) { |
+ notifyFinished(); |
+ m_state = latestState; |
+ } |
+} |
+ |
+ModuleLoader::ModuleLoader(const KURL& url) |
+ : m_url(url), m_state(State::Initial) {} |
+ |
+ModuleLoader::~ModuleLoader() {} |
+ |
+void ModuleLoader::advanceState(ModuleLoader::State newState) { |
+ switch (m_state) { |
+ case State::Initial: |
+ DCHECK_EQ(newState, State::Fetching); |
+ break; |
+ case State::Fetching: |
+ DCHECK(newState == State::Fetched || newState == State::Failed); |
+ break; |
+ |
+ case State::Fetched: |
+ case State::Failed: |
+ default: |
+ NOTREACHED(); |
+ } |
+ m_state = newState; |
+ |
+ notifyAndFlushPendingClients(); |
+} |
+ |
+void ModuleLoader::fetch(ResourceFetcher* fetcher) { |
+ // Step 4. Set moduleMap[url] to "fetching". |
+ advanceState(State::Fetching); |
+ |
+ // Step 5. Let request be a new request whose url is url, destination is |
+ // destination, type is "script", mode is "cors", credentials mode is |
+ // credentials mode, cryptographic nonce metadata is cryptographic nonce, |
+ // parser metadata is parser state, referrer is referrer, and client is fetch |
+ // client settings object. |
+ FetchRequest request(ResourceRequest(m_url), "module"); |
+ // request.setDefer(FetchRequest::LazyLoad); //? always async -> defer? |
+ Member<ScriptResource> resource = ScriptResource::fetch(request, fetcher); |
+ setResource(resource); |
+ |
+ // Step 6. If the caller specified custom steps to perform the fetch, perform |
+ // them on request, setting the is top-level flag if the top-level module |
+ // fetch flag is set. Return from this algorithm, and when the custom perform |
+ // the fetch steps complete with response response, run the remaining steps. |
+ // Otherwise, fetch request. Return from this algorithm, and run the remaining |
+ // steps as part of the fetch's process response for the response response. |
+ // Note: response is always CORS-same-origin. |
+ |
+ // TODO(kouhei): I don't know what Step 6 means. ScriptLoader seems to ignore |
+ // the step? |
+} |
+ |
+void ModuleLoader::notifyFinished(Resource*) { |
+ printf("notifyFinished!\n"); |
+ |
+ // Step 7. If any of the following conditions are met, set moduleMap[url] to |
+ // null, asynchronously complete this algorithm with null, and abort these |
+ // steps: |
+ // - response's type is "error" |
+ // - response's status is not an ok status |
+ // The result of extracting a MIME type from response's header list (ignoring |
+ // parameters) is not a JavaScript MIME type |
+ // Note: For historical reasons, fetching a classic script does not include |
+ // MIME type checking. In contrast, module scripts will fail to load if they |
+ // are not of a correct MIME type. |
+ if (resource()->errorOccurred()) { |
+ advanceState(State::Failed); |
+ return; |
+ } |
+ |
+ // Step 8. Let source text be the result of UTF-8 decoding response's body. |
+ String script = resource()->script(); |
+ printf("fetched script: %s\n", script.utf8().data()); |
+ |
+ // Step 9. Let module script be the result of creating a module script given |
+ // source text, module map settings object, response's url, cryptographic |
+ // nonce, parser state, and credentials mode. |
+ v8::Isolate* isolate = V8PerIsolateData::mainThreadIsolate(); |
+ v8::HandleScope handleScope(isolate); |
+ |
+ ScriptState* scriptState = ScriptState::forMainWorld(g_localFrame); |
+ DCHECK(scriptState); |
+ |
+ // ??? : I'm not sure why this requres scriptState to run compile step. |
+ ScriptState::Scope scope(scriptState); |
+ m_scriptModule = ScriptModule::compile(isolate, script, m_url.getString()); |
+ |
+ advanceState(State::Fetched); |
+} |
+ |
+void ModuleLoader::addClient(ModuleLoaderClient* newClient) { |
+ DCHECK(!m_pendingClients.contains(newClient)); |
+ DCHECK(!m_clients.contains(newClient)); |
+ m_pendingClients.add(newClient); |
+ |
+ notifyAndFlushPendingClients(); |
+} |
+ |
+void ModuleLoader::notifyAndFlushPendingClients(FlushType flushType) { |
+ if (m_insidePendingClientsLoop) { |
+ // This notifyAndFlushPendingClients call was reentrant. |
+ // Defer to first notifyAndFlushPendingClients to do the job. |
+ return; |
+ } |
+ |
+ AutoReset<bool> forbidClientModificationScope(&m_insideAddPendingClientsLoop, true); |
+ |
+ DCHECK(m_pendingClients.isEmpty()); |
+ if (flushType == FlushType::MayHaveAdvancedState) |
+ m_pendingClients.swap(m_clients); |
+ |
+ while (!m_pendingClients.isEmpty()) { |
+ // Iterate on copy as m_pendingClients may be added in the notification loop. |
+ HeapHashSet<Member<ModuleLoaderClient>> clientsToCatchUp; |
+ clientsToCatchUp.swap(m_pendingClients); |
+ for (const auto& client : clientsToCatchUp) { |
+ client->catchUpToLatestStateIfNeeded(m_state); |
+ } |
+ |
+ // Caught up clients do not have any notifications to receive after the |
+ // load is finished. |
+ if (isFinished()) |
+ continue; |
+ |
+ while (clientsToCatchUp.isEmpty()) |
+ m_clients.add(clientsToCatchUp.takeAny()); |
+ } |
+} |
+ |
+DEFINE_TRACE(ModuleLoader) { |
+ visitor->trace(m_clients); |
+ ResourceOwner<ScriptResource>::trace(visitor); |
+} |
+ |
+ModuleMap::ModuleMap(LocalFrame* frame, ResourceFetcher* fetcher) |
+ : m_frame(frame), m_fetcher(fetcher) { |
+ DCHECK(m_frame); |
+ DCHECK(m_fetcher); |
+} |
+ |
+void ModuleMap::fetch(const KURL& url, ModuleLoaderClient* client) { |
+ g_localFrame = m_frame; // TODO(kouhei): AAAAAAAArrrrrgghhh |
+ printf("ModuleMap::fetch(%s)\n", url.getString().utf8().data()); |
+ |
+ MapImpl::AddResult result = m_map.set(url, nullptr); |
+ Member<ModuleLoader>& loaderEntry = result.storedValue->value; |
+ if (!result.isNewEntry) { |
+ printf("not new entry\n"); |
+ |
+ // - If moduleMap[url] is "fetching", wait (in parallel) until that |
+ // loaderEntry's value changes, then proceed to the next step. |
+ // - If moduleMap[url] exists, asynchronously complete this algorithm |
+ // with moduleMap[url], and abort these steps. |
+ loaderEntry->addClient(client); |
+ return; |
+ } |
+ |
+ ModuleLoader* loader = ModuleLoader::create(url); |
+ loader->fetch(m_fetcher.get()); |
+ |
+ // Step 10. Set moduleMap[url] to module script, and asynchronously complete |
+ // this algorithm with module script. |
+ loaderEntry = loader; |
+ loaderEntry->addClient(client); |
+} |
+ |
+DEFINE_TRACE(ModuleMap) { |
+ visitor->trace(m_map); |
+ visitor->trace(m_frame); |
+ visitor->trace(m_fetcher); |
+} |
+ |
+} // namespace blink |