1 // Copyright 2017 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 "third_party/blink/renderer/core/script/module_map.h"
6 
7 #include "third_party/blink/renderer/core/loader/modulescript/module_script_fetch_request.h"
8 #include "third_party/blink/renderer/core/loader/modulescript/module_script_loader.h"
9 #include "third_party/blink/renderer/core/loader/modulescript/module_script_loader_client.h"
10 #include "third_party/blink/renderer/core/loader/modulescript/module_script_loader_registry.h"
11 #include "third_party/blink/renderer/core/script/modulator.h"
12 #include "third_party/blink/renderer/core/script/module_script.h"
13 #include "third_party/blink/renderer/platform/bindings/name_client.h"
14 
15 namespace blink {
16 
17 // Entry struct represents a value in "module map" spec object.
18 // https://html.spec.whatwg.org/C/#module-map
19 class ModuleMap::Entry final : public GarbageCollected<Entry>,
20                                public NameClient,
21                                public ModuleScriptLoaderClient {
22   USING_GARBAGE_COLLECTED_MIXIN(ModuleMap::Entry);
23 
24  public:
25   explicit Entry(ModuleMap*);
~Entry()26   ~Entry() override {}
27 
28   void Trace(Visitor*) override;
NameInHeapSnapshot() const29   const char* NameInHeapSnapshot() const override { return "ModuleMap::Entry"; }
30 
31   // Notify fetched |m_moduleScript| to the client asynchronously.
32   void AddClient(SingleModuleClient*);
33 
34   // This is only to be used from ModuleRecordResolver implementations.
35   ModuleScript* GetModuleScript() const;
36 
37  private:
38   void DispatchFinishedNotificationAsync(SingleModuleClient*);
39 
40   // Implements ModuleScriptLoaderClient
41   void NotifyNewSingleModuleFinished(ModuleScript*) override;
42 
43   Member<ModuleScript> module_script_;
44   Member<ModuleMap> map_;
45 
46   // Correspond to the HTML spec: "fetching" state.
47   bool is_fetching_ = true;
48 
49   HeapHashSet<Member<SingleModuleClient>> clients_;
50 };
51 
Entry(ModuleMap * map)52 ModuleMap::Entry::Entry(ModuleMap* map) : map_(map) {
53   DCHECK(map_);
54 }
55 
Trace(Visitor * visitor)56 void ModuleMap::Entry::Trace(Visitor* visitor) {
57   visitor->Trace(module_script_);
58   visitor->Trace(map_);
59   visitor->Trace(clients_);
60 }
61 
DispatchFinishedNotificationAsync(SingleModuleClient * client)62 void ModuleMap::Entry::DispatchFinishedNotificationAsync(
63     SingleModuleClient* client) {
64   map_->GetModulator()->TaskRunner()->PostTask(
65       FROM_HERE,
66       WTF::Bind(&SingleModuleClient::NotifyModuleLoadFinished,
67                 WrapPersistent(client), WrapPersistent(module_script_.Get())));
68 }
69 
AddClient(SingleModuleClient * new_client)70 void ModuleMap::Entry::AddClient(SingleModuleClient* new_client) {
71   DCHECK(!clients_.Contains(new_client));
72   if (!is_fetching_) {
73     DCHECK(clients_.IsEmpty());
74     DispatchFinishedNotificationAsync(new_client);
75     return;
76   }
77 
78   clients_.insert(new_client);
79 }
80 
NotifyNewSingleModuleFinished(ModuleScript * module_script)81 void ModuleMap::Entry::NotifyNewSingleModuleFinished(
82     ModuleScript* module_script) {
83   CHECK(is_fetching_);
84   module_script_ = module_script;
85   is_fetching_ = false;
86 
87   for (const auto& client : clients_) {
88     DispatchFinishedNotificationAsync(client);
89   }
90   clients_.clear();
91 }
92 
GetModuleScript() const93 ModuleScript* ModuleMap::Entry::GetModuleScript() const {
94   return module_script_.Get();
95 }
96 
ModuleMap(Modulator * modulator)97 ModuleMap::ModuleMap(Modulator* modulator)
98     : modulator_(modulator),
99       loader_registry_(MakeGarbageCollected<ModuleScriptLoaderRegistry>()) {
100   DCHECK(modulator);
101 }
102 
Trace(Visitor * visitor)103 void ModuleMap::Trace(Visitor* visitor) {
104   visitor->Trace(map_);
105   visitor->Trace(modulator_);
106   visitor->Trace(loader_registry_);
107 }
108 
109 // <specdef href="https://html.spec.whatwg.org/C/#fetch-a-single-module-script">
FetchSingleModuleScript(const ModuleScriptFetchRequest & request,ResourceFetcher * fetch_client_settings_object_fetcher,ModuleGraphLevel level,ModuleScriptCustomFetchType custom_fetch_type,SingleModuleClient * client)110 void ModuleMap::FetchSingleModuleScript(
111     const ModuleScriptFetchRequest& request,
112     ResourceFetcher* fetch_client_settings_object_fetcher,
113     ModuleGraphLevel level,
114     ModuleScriptCustomFetchType custom_fetch_type,
115     SingleModuleClient* client) {
116   // <spec step="1">Let moduleMap be module map settings object's module
117   // map.</spec>
118   //
119   // Note: |this| is the ModuleMap.
120 
121   // <spec step="2">If moduleMap[url] is "fetching", wait in parallel until that
122   // entry's value changes, then queue a task on the networking task source to
123   // proceed with running the following steps.</spec>
124   MapImpl::AddResult result = map_.insert(request.Url(), nullptr);
125   Member<Entry>& entry = result.stored_value->value;
126   if (result.is_new_entry) {
127     entry = MakeGarbageCollected<Entry>(this);
128 
129     // Steps 4-9 loads a new single module script.
130     // Delegates to ModuleScriptLoader via Modulator.
131     ModuleScriptLoader::Fetch(request, fetch_client_settings_object_fetcher,
132                               level, modulator_, custom_fetch_type,
133                               loader_registry_, entry);
134   }
135   DCHECK(entry);
136 
137   // <spec step="3">If moduleMap[url] exists, asynchronously complete this
138   // algorithm with moduleMap[url], and abort these steps.</spec>
139   //
140   // <spec step="14">Set moduleMap[url] to module script, and asynchronously
141   // complete this algorithm with module script.</spec>
142   if (client)
143     entry->AddClient(client);
144 }
145 
GetFetchedModuleScript(const KURL & url) const146 ModuleScript* ModuleMap::GetFetchedModuleScript(const KURL& url) const {
147   MapImpl::const_iterator it = map_.find(url);
148   if (it == map_.end())
149     return nullptr;
150   return it->value->GetModuleScript();
151 }
152 
153 }  // namespace blink
154