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..8eee813065b26f6c7263cb5ef05655f350407eab |
--- /dev/null |
+++ b/third_party/WebKit/Source/core/dom/ModuleMap.cpp |
@@ -0,0 +1,148 @@ |
+// Copyright 2017 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 "core/dom/ModuleMap.h" |
+ |
+#include "bindings/core/v8/ScriptModuleResolver.h" |
+#include "core/dom/Modulator.h" |
+#include "core/dom/ModuleScript.h" |
+#include "core/loader/modulescript/ModuleScriptLoaderClient.h" |
+#include "platform/WebTaskRunner.h" |
+ |
+namespace blink { |
+ |
+// Entry struct represents a value in "module map" spec object. |
+class ModuleMap::Entry : public GarbageCollectedFinalized<Entry>, |
+ public ModuleScriptLoaderClient { |
+ USING_GARBAGE_COLLECTED_MIXIN(ModuleMap::Entry); |
+ |
+ public: |
+ static Entry* create(ModuleMap* map) { return new Entry(map); } |
+ virtual ~Entry() = default; |
+ |
+ DECLARE_TRACE(); |
+ |
+ // Asynchronously notify the client fetched |m_moduleScript|, or register the |
+ // client to receive further callbacks if |m_isFetching|. |
+ void addClient(SingleModuleClient*); |
+ |
+ // This is only to be used from ScriptModuleResolver implementations. |
+ ModuleScript* retrieveModuleScriptSync() const; |
+ |
+ private: |
+ Entry(ModuleMap*); |
+ |
+ void dispatchFinishedNotificationAsync(SingleModuleClient*); |
+ |
+ // Implements ModuleScriptLoaderClient |
+ void notifyFinishedNewSingleModule(ModuleScript*) override; |
+ |
+ Member<ModuleScript> m_moduleScript; |
+ Member<ModuleMap> m_map; |
+ std::unique_ptr<WebTaskRunner> m_taskRunner; |
+ |
+ // Correspond to the HTML spec: "fetching" state. |
+ bool m_isFetching = true; |
+ |
+ HeapHashSet<Member<SingleModuleClient>> m_clients; |
+}; |
+ |
+ModuleMap::Entry::Entry(ModuleMap* map) : m_map(map) { |
+ DCHECK(m_map); |
+ m_taskRunner = m_map->modulator()->taskRunner()->clone(); |
+} |
+ |
+DEFINE_TRACE(ModuleMap::Entry) { |
+ visitor->trace(m_moduleScript); |
+ visitor->trace(m_map); |
+ visitor->trace(m_clients); |
+} |
+ |
+void ModuleMap::Entry::dispatchFinishedNotificationAsync( |
+ SingleModuleClient* client) { |
+ m_taskRunner->postTask( |
+ BLINK_FROM_HERE, |
+ WTF::bind(&SingleModuleClient::notifyFinishedSingleModule, |
+ wrapPersistent(client), wrapPersistent(m_moduleScript.get()))); |
+} |
+ |
+void ModuleMap::Entry::addClient(SingleModuleClient* newClient) { |
+ DCHECK(!m_clients.contains(newClient)); |
+ if (!m_isFetching) { |
+ DCHECK(m_clients.isEmpty()); |
+ dispatchFinishedNotificationAsync(newClient); |
+ return; |
+ } |
+ |
+ m_clients.add(newClient); |
+} |
+ |
+void ModuleMap::Entry::notifyFinishedNewSingleModule( |
+ ModuleScript* moduleScript) { |
+ m_moduleScript = moduleScript; |
+ m_isFetching = false; |
+ |
+ if (m_moduleScript) { |
+ m_map->modulator()->scriptModuleResolver()->registerModuleScript( |
+ m_moduleScript); |
+ } |
+ |
+ for (const auto& client : m_clients) { |
+ dispatchFinishedNotificationAsync(client); |
+ } |
+ m_clients.clear(); |
+} |
+ |
+ModuleScript* ModuleMap::Entry::retrieveModuleScriptSync() const { |
+ DCHECK(!m_isFetching); |
+ return m_moduleScript.get(); |
+} |
+ |
+ModuleMap::ModuleMap(Modulator* modulator) : m_modulator(modulator) { |
+ DCHECK(modulator); |
+} |
+ |
+DEFINE_TRACE(ModuleMap) { |
+ visitor->trace(m_map); |
+ visitor->trace(m_modulator); |
+} |
+ |
+void ModuleMap::retrieveAndFetchIfNeeded(const KURL& url, |
+ const KURL& baseURL, |
+ SingleModuleClient* client) { |
+ // https://html.spec.whatwg.org/#fetch-a-single-module-script |
+ |
+ // Step 1. Let moduleMap be module map settings object's module map. |
+ // Note: This is the ModuleMap. |
+ |
+ // Step 2. If moduleMap[url] is "fetching", wait in parallel until that |
+ // entry's value changes, then queue a task on the networking task source to |
+ // proceed with running the following steps. |
+ printf("ModuleMap::fetch(%s, base=%s)\n", url.getString().utf8().data(), |
+ baseURL.getString().utf8().data()); |
+ |
+ MapImpl::AddResult result = m_map.set(url, nullptr); |
+ Member<Entry>& entry = result.storedValue->value; |
+ if (result.isNewEntry) { |
+ entry = Entry::create(this); |
+ |
+ // Steps 4-9 loads a new single module script. |
+ // Delegate to ModuleScriptLoader via Modulator. |
+ m_modulator->fetchNewSingleModule(url, baseURL, entry); |
+ } |
+ |
+ // Step 3. If moduleMap[url] exists, asynchronously complete this algorithm |
+ // with moduleMap[url], and abort these steps. |
+ // Step 10. Set moduleMap[url] to module script, and asynchronously complete |
+ // this algorithm with module script. |
+ entry->addClient(client); |
+} |
+ |
+ModuleScript* ModuleMap::retrieveFetchedModuleScript(const KURL& url) { |
+ MapImpl::iterator it = m_map.find(url); |
+ CHECK_NE(it, m_map.end()); |
+ return it->value->retrieveModuleScriptSync(); |
+} |
+ |
+} // namespace blink |