Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / gin / modules / module_registry.cc
blobb75894cf4cb71ab5bb69fd074985d661e2bf6b8b
1 // Copyright 2013 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.
5 #include "gin/modules/module_registry.h"
7 #include <string>
8 #include <vector>
10 #include "base/logging.h"
11 #include "gin/arguments.h"
12 #include "gin/converter.h"
13 #include "gin/modules/module_registry_observer.h"
14 #include "gin/per_context_data.h"
15 #include "gin/per_isolate_data.h"
16 #include "gin/public/wrapper_info.h"
17 #include "gin/runner.h"
19 using v8::Context;
20 using v8::External;
21 using v8::Function;
22 using v8::FunctionTemplate;
23 using v8::Isolate;
24 using v8::Local;
25 using v8::Object;
26 using v8::ObjectTemplate;
27 using v8::Persistent;
28 using v8::StackTrace;
29 using v8::String;
30 using v8::Value;
32 namespace gin {
34 struct PendingModule {
35 PendingModule();
36 ~PendingModule();
38 std::string id;
39 std::vector<std::string> dependencies;
40 Persistent<Value> factory;
43 PendingModule::PendingModule() {
46 PendingModule::~PendingModule() {
47 factory.Reset();
50 namespace {
52 // Key for base::SupportsUserData::Data.
53 const char kModuleRegistryKey[] = "ModuleRegistry";
55 struct ModuleRegistryData : public base::SupportsUserData::Data {
56 scoped_ptr<ModuleRegistry> registry;
59 void Define(const v8::FunctionCallbackInfo<Value>& info) {
60 Arguments args(info);
62 if (!info.Length())
63 return args.ThrowTypeError("At least one argument is required.");
65 std::string id;
66 std::vector<std::string> dependencies;
67 v8::Local<Value> factory;
69 if (!args.PeekNext().IsEmpty() && args.PeekNext()->IsString())
70 args.GetNext(&id);
71 if (!args.PeekNext().IsEmpty() && args.PeekNext()->IsArray())
72 args.GetNext(&dependencies);
73 if (!args.GetNext(&factory))
74 return args.ThrowError();
76 scoped_ptr<PendingModule> pending(new PendingModule);
77 pending->id = id;
78 pending->dependencies = dependencies;
79 pending->factory.Reset(args.isolate(), factory);
81 ModuleRegistry* registry =
82 ModuleRegistry::From(args.isolate()->GetCurrentContext());
83 registry->AddPendingModule(args.isolate(), pending.Pass());
86 WrapperInfo g_wrapper_info = { kEmbedderNativeGin };
88 Local<FunctionTemplate> GetDefineTemplate(Isolate* isolate) {
89 PerIsolateData* data = PerIsolateData::From(isolate);
90 Local<FunctionTemplate> templ = data->GetFunctionTemplate(
91 &g_wrapper_info);
92 if (templ.IsEmpty()) {
93 templ = FunctionTemplate::New(isolate, Define);
94 data->SetFunctionTemplate(&g_wrapper_info, templ);
96 return templ;
99 } // namespace
101 ModuleRegistry::ModuleRegistry(Isolate* isolate)
102 : modules_(isolate, Object::New(isolate)) {
105 ModuleRegistry::~ModuleRegistry() {
106 modules_.Reset();
109 // static
110 void ModuleRegistry::RegisterGlobals(Isolate* isolate,
111 v8::Local<ObjectTemplate> templ) {
112 templ->Set(StringToSymbol(isolate, "define"), GetDefineTemplate(isolate));
115 // static
116 bool ModuleRegistry::InstallGlobals(v8::Isolate* isolate,
117 v8::Local<v8::Object> obj) {
118 v8::Local<v8::Function> function;
119 auto maybe_function =
120 GetDefineTemplate(isolate)->GetFunction(isolate->GetCurrentContext());
121 if (!maybe_function.ToLocal(&function))
122 return false;
123 return SetProperty(isolate, obj, StringToSymbol(isolate, "define"), function);
126 // static
127 ModuleRegistry* ModuleRegistry::From(v8::Local<Context> context) {
128 PerContextData* data = PerContextData::From(context);
129 if (!data)
130 return NULL;
132 ModuleRegistryData* registry_data = static_cast<ModuleRegistryData*>(
133 data->GetUserData(kModuleRegistryKey));
134 if (!registry_data) {
135 // PerContextData takes ownership of ModuleRegistryData.
136 registry_data = new ModuleRegistryData;
137 registry_data->registry.reset(new ModuleRegistry(context->GetIsolate()));
138 data->SetUserData(kModuleRegistryKey, registry_data);
140 return registry_data->registry.get();
143 void ModuleRegistry::AddObserver(ModuleRegistryObserver* observer) {
144 observer_list_.AddObserver(observer);
147 void ModuleRegistry::RemoveObserver(ModuleRegistryObserver* observer) {
148 observer_list_.RemoveObserver(observer);
151 void ModuleRegistry::AddBuiltinModule(Isolate* isolate, const std::string& id,
152 v8::Local<Value> module) {
153 DCHECK(!id.empty());
154 RegisterModule(isolate, id, module);
157 void ModuleRegistry::AddPendingModule(Isolate* isolate,
158 scoped_ptr<PendingModule> pending) {
159 const std::string pending_id = pending->id;
160 const std::vector<std::string> pending_dependencies = pending->dependencies;
161 AttemptToLoad(isolate, pending.Pass());
162 FOR_EACH_OBSERVER(ModuleRegistryObserver, observer_list_,
163 OnDidAddPendingModule(pending_id, pending_dependencies));
166 void ModuleRegistry::LoadModule(Isolate* isolate,
167 const std::string& id,
168 LoadModuleCallback callback) {
169 if (available_modules_.find(id) != available_modules_.end()) {
170 // Should we call the callback asynchronously?
171 callback.Run(GetModule(isolate, id));
172 return;
174 waiting_callbacks_.insert(std::make_pair(id, callback));
176 for (size_t i = 0; i < pending_modules_.size(); ++i) {
177 if (pending_modules_[i]->id == id)
178 return;
181 unsatisfied_dependencies_.insert(id);
184 bool ModuleRegistry::RegisterModule(Isolate* isolate,
185 const std::string& id,
186 v8::Local<Value> module) {
187 if (id.empty() || module.IsEmpty())
188 return false;
190 v8::Local<Object> modules = Local<Object>::New(isolate, modules_);
191 if (!SetProperty(isolate, modules, StringToSymbol(isolate, id), module))
192 return false;
193 unsatisfied_dependencies_.erase(id);
194 available_modules_.insert(id);
196 std::pair<LoadModuleCallbackMap::iterator, LoadModuleCallbackMap::iterator>
197 range = waiting_callbacks_.equal_range(id);
198 std::vector<LoadModuleCallback> callbacks;
199 callbacks.reserve(waiting_callbacks_.count(id));
200 for (LoadModuleCallbackMap::iterator it = range.first; it != range.second;
201 ++it) {
202 callbacks.push_back(it->second);
204 waiting_callbacks_.erase(range.first, range.second);
205 for (std::vector<LoadModuleCallback>::iterator it = callbacks.begin();
206 it != callbacks.end();
207 ++it) {
208 // Should we call the callback asynchronously?
209 it->Run(module);
211 return true;
214 bool ModuleRegistry::CheckDependencies(PendingModule* pending) {
215 size_t num_missing_dependencies = 0;
216 size_t len = pending->dependencies.size();
217 for (size_t i = 0; i < len; ++i) {
218 const std::string& dependency = pending->dependencies[i];
219 if (available_modules_.count(dependency))
220 continue;
221 unsatisfied_dependencies_.insert(dependency);
222 num_missing_dependencies++;
224 return num_missing_dependencies == 0;
227 bool ModuleRegistry::Load(Isolate* isolate, scoped_ptr<PendingModule> pending) {
228 if (!pending->id.empty() && available_modules_.count(pending->id))
229 return true; // We've already loaded this module.
231 uint32_t argc = static_cast<uint32_t>(pending->dependencies.size());
232 std::vector<v8::Local<Value> > argv(argc);
233 for (uint32_t i = 0; i < argc; ++i)
234 argv[i] = GetModule(isolate, pending->dependencies[i]);
236 v8::Local<Value> module = Local<Value>::New(isolate, pending->factory);
238 v8::Local<Function> factory;
239 if (ConvertFromV8(isolate, module, &factory)) {
240 PerContextData* data = PerContextData::From(isolate->GetCurrentContext());
241 Runner* runner = data->runner();
242 module = runner->Call(factory, runner->global(), argc,
243 argv.empty() ? NULL : &argv.front());
244 if (pending->id.empty())
245 ConvertFromV8(isolate, factory->GetScriptOrigin().ResourceName(),
246 &pending->id);
249 return RegisterModule(isolate, pending->id, module);
252 bool ModuleRegistry::AttemptToLoad(Isolate* isolate,
253 scoped_ptr<PendingModule> pending) {
254 if (!CheckDependencies(pending.get())) {
255 pending_modules_.push_back(pending.release());
256 return false;
258 return Load(isolate, pending.Pass());
261 v8::Local<v8::Value> ModuleRegistry::GetModule(v8::Isolate* isolate,
262 const std::string& id) {
263 v8::Local<Object> modules = Local<Object>::New(isolate, modules_);
264 v8::Local<String> key = StringToSymbol(isolate, id);
265 DCHECK(modules->HasOwnProperty(isolate->GetCurrentContext(), key).FromJust());
266 return modules->Get(isolate->GetCurrentContext(), key).ToLocalChecked();
269 void ModuleRegistry::AttemptToLoadMoreModules(Isolate* isolate) {
270 bool keep_trying = true;
271 while (keep_trying) {
272 keep_trying = false;
273 PendingModuleVector pending_modules;
274 pending_modules.swap(pending_modules_);
275 for (size_t i = 0; i < pending_modules.size(); ++i) {
276 scoped_ptr<PendingModule> pending(pending_modules[i]);
277 pending_modules[i] = NULL;
278 if (AttemptToLoad(isolate, pending.Pass()))
279 keep_trying = true;
284 } // namespace gin