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.
4
5#include "gin/modules/module_registry.h"
6
7#include <string>
8#include <vector>
9
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"
18
19using v8::Context;
20using v8::External;
21using v8::Function;
22using v8::FunctionTemplate;
23using v8::Isolate;
24using v8::Local;
25using v8::Object;
26using v8::ObjectTemplate;
27using v8::Persistent;
28using v8::StackTrace;
29using v8::String;
30using v8::Value;
31
32namespace gin {
33
34struct PendingModule {
35  PendingModule();
36  ~PendingModule();
37
38  std::string id;
39  std::vector<std::string> dependencies;
40  Persistent<Value> factory;
41};
42
43PendingModule::PendingModule() {
44}
45
46PendingModule::~PendingModule() {
47  factory.Reset();
48}
49
50namespace {
51
52// Key for base::SupportsUserData::Data.
53const char kModuleRegistryKey[] = "ModuleRegistry";
54
55struct ModuleRegistryData : public base::SupportsUserData::Data {
56  scoped_ptr<ModuleRegistry> registry;
57};
58
59void Define(const v8::FunctionCallbackInfo<Value>& info) {
60  Arguments args(info);
61
62  if (!info.Length())
63    return args.ThrowTypeError("At least one argument is required.");
64
65  std::string id;
66  std::vector<std::string> dependencies;
67  v8::Handle<Value> factory;
68
69  if (args.PeekNext()->IsString())
70    args.GetNext(&id);
71  if (args.PeekNext()->IsArray())
72    args.GetNext(&dependencies);
73  if (!args.GetNext(&factory))
74    return args.ThrowError();
75
76  scoped_ptr<PendingModule> pending(new PendingModule);
77  pending->id = id;
78  pending->dependencies = dependencies;
79  pending->factory.Reset(args.isolate(), factory);
80
81  ModuleRegistry* registry =
82      ModuleRegistry::From(args.isolate()->GetCurrentContext());
83  registry->AddPendingModule(args.isolate(), pending.Pass());
84}
85
86WrapperInfo g_wrapper_info = { kEmbedderNativeGin };
87
88Local<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);
95  }
96  return templ;
97}
98
99}  // namespace
100
101ModuleRegistry::ModuleRegistry(Isolate* isolate)
102    : modules_(isolate, Object::New(isolate)) {
103}
104
105ModuleRegistry::~ModuleRegistry() {
106  modules_.Reset();
107}
108
109// static
110void ModuleRegistry::RegisterGlobals(Isolate* isolate,
111                                     v8::Handle<ObjectTemplate> templ) {
112  templ->Set(StringToSymbol(isolate, "define"), GetDefineTemplate(isolate));
113}
114
115// static
116void ModuleRegistry::InstallGlobals(v8::Isolate* isolate,
117                                    v8::Handle<v8::Object> obj) {
118  obj->Set(StringToSymbol(isolate, "define"),
119           GetDefineTemplate(isolate)->GetFunction());
120}
121
122// static
123ModuleRegistry* ModuleRegistry::From(v8::Handle<Context> context) {
124  PerContextData* data = PerContextData::From(context);
125  if (!data)
126    return NULL;
127
128  ModuleRegistryData* registry_data = static_cast<ModuleRegistryData*>(
129      data->GetUserData(kModuleRegistryKey));
130  if (!registry_data) {
131    // PerContextData takes ownership of ModuleRegistryData.
132    registry_data = new ModuleRegistryData;
133    registry_data->registry.reset(new ModuleRegistry(context->GetIsolate()));
134    data->SetUserData(kModuleRegistryKey, registry_data);
135  }
136  return registry_data->registry.get();
137}
138
139void ModuleRegistry::AddObserver(ModuleRegistryObserver* observer) {
140  observer_list_.AddObserver(observer);
141}
142
143void ModuleRegistry::RemoveObserver(ModuleRegistryObserver* observer) {
144  observer_list_.RemoveObserver(observer);
145}
146
147void ModuleRegistry::AddBuiltinModule(Isolate* isolate, const std::string& id,
148                                      v8::Handle<Value> module) {
149  DCHECK(!id.empty());
150  RegisterModule(isolate, id, module);
151}
152
153void ModuleRegistry::AddPendingModule(Isolate* isolate,
154                                      scoped_ptr<PendingModule> pending) {
155  const std::string pending_id = pending->id;
156  const std::vector<std::string> pending_dependencies = pending->dependencies;
157  AttemptToLoad(isolate, pending.Pass());
158  FOR_EACH_OBSERVER(ModuleRegistryObserver, observer_list_,
159                    OnDidAddPendingModule(pending_id, pending_dependencies));
160}
161
162void ModuleRegistry::LoadModule(Isolate* isolate,
163                                const std::string& id,
164                                LoadModuleCallback callback) {
165  if (available_modules_.find(id) != available_modules_.end()) {
166    // Should we call the callback asynchronously?
167    callback.Run(GetModule(isolate, id));
168    return;
169  }
170  waiting_callbacks_.insert(std::make_pair(id, callback));
171  unsatisfied_dependencies_.insert(id);
172}
173
174void ModuleRegistry::RegisterModule(Isolate* isolate,
175                                    const std::string& id,
176                                    v8::Handle<Value> module) {
177  if (id.empty() || module.IsEmpty())
178    return;
179
180  unsatisfied_dependencies_.erase(id);
181  available_modules_.insert(id);
182  v8::Handle<Object> modules = Local<Object>::New(isolate, modules_);
183  modules->Set(StringToSymbol(isolate, id), module);
184
185  std::pair<LoadModuleCallbackMap::iterator, LoadModuleCallbackMap::iterator>
186      range = waiting_callbacks_.equal_range(id);
187  std::vector<LoadModuleCallback> callbacks;
188  callbacks.reserve(waiting_callbacks_.count(id));
189  for (LoadModuleCallbackMap::iterator it = range.first; it != range.second;
190       ++it) {
191    callbacks.push_back(it->second);
192  }
193  waiting_callbacks_.erase(range.first, range.second);
194  for (std::vector<LoadModuleCallback>::iterator it = callbacks.begin();
195       it != callbacks.end();
196       ++it) {
197    // Should we call the callback asynchronously?
198    it->Run(module);
199  }
200}
201
202bool ModuleRegistry::CheckDependencies(PendingModule* pending) {
203  size_t num_missing_dependencies = 0;
204  size_t len = pending->dependencies.size();
205  for (size_t i = 0; i < len; ++i) {
206    const std::string& dependency = pending->dependencies[i];
207    if (available_modules_.count(dependency))
208      continue;
209    unsatisfied_dependencies_.insert(dependency);
210    num_missing_dependencies++;
211  }
212  return num_missing_dependencies == 0;
213}
214
215void ModuleRegistry::Load(Isolate* isolate, scoped_ptr<PendingModule> pending) {
216  if (!pending->id.empty() && available_modules_.count(pending->id))
217    return;  // We've already loaded this module.
218
219  uint32_t argc = static_cast<uint32_t>(pending->dependencies.size());
220  std::vector<v8::Handle<Value> > argv(argc);
221  for (uint32_t i = 0; i < argc; ++i)
222    argv[i] = GetModule(isolate, pending->dependencies[i]);
223
224  v8::Handle<Value> module = Local<Value>::New(isolate, pending->factory);
225
226  v8::Handle<Function> factory;
227  if (ConvertFromV8(isolate, module, &factory)) {
228    PerContextData* data = PerContextData::From(isolate->GetCurrentContext());
229    Runner* runner = data->runner();
230    module = runner->Call(factory, runner->global(), argc,
231                          argv.empty() ? NULL : &argv.front());
232    if (pending->id.empty())
233      ConvertFromV8(isolate, factory->GetScriptOrigin().ResourceName(),
234                    &pending->id);
235  }
236
237  RegisterModule(isolate, pending->id, module);
238}
239
240bool ModuleRegistry::AttemptToLoad(Isolate* isolate,
241                                   scoped_ptr<PendingModule> pending) {
242  if (!CheckDependencies(pending.get())) {
243    pending_modules_.push_back(pending.release());
244    return false;
245  }
246  Load(isolate, pending.Pass());
247  return true;
248}
249
250v8::Handle<v8::Value> ModuleRegistry::GetModule(v8::Isolate* isolate,
251                                                const std::string& id) {
252  v8::Handle<Object> modules = Local<Object>::New(isolate, modules_);
253  v8::Handle<String> key = StringToSymbol(isolate, id);
254  DCHECK(modules->HasOwnProperty(key));
255  return modules->Get(key);
256}
257
258void ModuleRegistry::AttemptToLoadMoreModules(Isolate* isolate) {
259  bool keep_trying = true;
260  while (keep_trying) {
261    keep_trying = false;
262    PendingModuleVector pending_modules;
263    pending_modules.swap(pending_modules_);
264    for (size_t i = 0; i < pending_modules.size(); ++i) {
265      scoped_ptr<PendingModule> pending(pending_modules[i]);
266      pending_modules[i] = NULL;
267      if (AttemptToLoad(isolate, pending.Pass()))
268        keep_trying = true;
269    }
270  }
271}
272
273}  // namespace gin
274