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