| 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
|
|
|