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"
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"
22 using v8::FunctionTemplate
;
26 using v8::ObjectTemplate
;
34 struct PendingModule
{
39 std::vector
<std::string
> dependencies
;
40 Persistent
<Value
> factory
;
43 PendingModule::PendingModule() {
46 PendingModule::~PendingModule() {
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
) {
63 return args
.ThrowTypeError("At least one argument is required.");
66 std::vector
<std::string
> dependencies
;
67 v8::Handle
<Value
> factory
;
69 if (args
.PeekNext()->IsString())
71 if (args
.PeekNext()->IsArray())
72 args
.GetNext(&dependencies
);
73 if (!args
.GetNext(&factory
))
74 return args
.ThrowError();
76 scoped_ptr
<PendingModule
> pending(new PendingModule
);
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(
92 if (templ
.IsEmpty()) {
93 templ
= FunctionTemplate::New(isolate
, Define
);
94 data
->SetFunctionTemplate(&g_wrapper_info
, templ
);
101 ModuleRegistry::ModuleRegistry(Isolate
* isolate
)
102 : modules_(isolate
, Object::New(isolate
)) {
105 ModuleRegistry::~ModuleRegistry() {
110 void ModuleRegistry::RegisterGlobals(Isolate
* isolate
,
111 v8::Handle
<ObjectTemplate
> templ
) {
112 templ
->Set(StringToSymbol(isolate
, "define"), GetDefineTemplate(isolate
));
116 void ModuleRegistry::InstallGlobals(v8::Isolate
* isolate
,
117 v8::Handle
<v8::Object
> obj
) {
118 obj
->Set(StringToSymbol(isolate
, "define"),
119 GetDefineTemplate(isolate
)->GetFunction());
123 ModuleRegistry
* ModuleRegistry::From(v8::Handle
<Context
> context
) {
124 PerContextData
* data
= PerContextData::From(context
);
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
);
136 return registry_data
->registry
.get();
139 void ModuleRegistry::AddObserver(ModuleRegistryObserver
* observer
) {
140 observer_list_
.AddObserver(observer
);
143 void ModuleRegistry::RemoveObserver(ModuleRegistryObserver
* observer
) {
144 observer_list_
.RemoveObserver(observer
);
147 void ModuleRegistry::AddBuiltinModule(Isolate
* isolate
, const std::string
& id
,
148 v8::Handle
<Value
> module
) {
150 RegisterModule(isolate
, id
, module
);
153 void 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
));
162 void 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
));
170 // Should we support multiple callers waiting on the same module?
171 DCHECK(waiting_callbacks_
.find(id
) == waiting_callbacks_
.end());
172 waiting_callbacks_
[id
] = callback
;
173 unsatisfied_dependencies_
.insert(id
);
176 void ModuleRegistry::RegisterModule(Isolate
* isolate
,
177 const std::string
& id
,
178 v8::Handle
<Value
> module
) {
179 if (id
.empty() || module
.IsEmpty())
182 unsatisfied_dependencies_
.erase(id
);
183 available_modules_
.insert(id
);
184 v8::Handle
<Object
> modules
= Local
<Object
>::New(isolate
, modules_
);
185 modules
->Set(StringToSymbol(isolate
, id
), module
);
187 LoadModuleCallbackMap::iterator it
= waiting_callbacks_
.find(id
);
188 if (it
== waiting_callbacks_
.end())
190 LoadModuleCallback callback
= it
->second
;
191 waiting_callbacks_
.erase(it
);
192 // Should we call the callback asynchronously?
193 callback
.Run(module
);
196 bool ModuleRegistry::CheckDependencies(PendingModule
* pending
) {
197 size_t num_missing_dependencies
= 0;
198 size_t len
= pending
->dependencies
.size();
199 for (size_t i
= 0; i
< len
; ++i
) {
200 const std::string
& dependency
= pending
->dependencies
[i
];
201 if (available_modules_
.count(dependency
))
203 unsatisfied_dependencies_
.insert(dependency
);
204 num_missing_dependencies
++;
206 return num_missing_dependencies
== 0;
209 void ModuleRegistry::Load(Isolate
* isolate
, scoped_ptr
<PendingModule
> pending
) {
210 if (!pending
->id
.empty() && available_modules_
.count(pending
->id
))
211 return; // We've already loaded this module.
213 uint32_t argc
= static_cast<uint32_t>(pending
->dependencies
.size());
214 std::vector
<v8::Handle
<Value
> > argv(argc
);
215 for (uint32_t i
= 0; i
< argc
; ++i
)
216 argv
[i
] = GetModule(isolate
, pending
->dependencies
[i
]);
218 v8::Handle
<Value
> module
= Local
<Value
>::New(isolate
, pending
->factory
);
220 v8::Handle
<Function
> factory
;
221 if (ConvertFromV8(isolate
, module
, &factory
)) {
222 PerContextData
* data
= PerContextData::From(isolate
->GetCurrentContext());
223 Runner
* runner
= data
->runner();
224 module
= runner
->Call(factory
, runner
->global(), argc
,
225 argv
.empty() ? NULL
: &argv
.front());
226 if (pending
->id
.empty())
227 ConvertFromV8(isolate
, factory
->GetScriptOrigin().ResourceName(),
231 RegisterModule(isolate
, pending
->id
, module
);
234 bool ModuleRegistry::AttemptToLoad(Isolate
* isolate
,
235 scoped_ptr
<PendingModule
> pending
) {
236 if (!CheckDependencies(pending
.get())) {
237 pending_modules_
.push_back(pending
.release());
240 Load(isolate
, pending
.Pass());
244 v8::Handle
<v8::Value
> ModuleRegistry::GetModule(v8::Isolate
* isolate
,
245 const std::string
& id
) {
246 v8::Handle
<Object
> modules
= Local
<Object
>::New(isolate
, modules_
);
247 v8::Handle
<String
> key
= StringToSymbol(isolate
, id
);
248 DCHECK(modules
->HasOwnProperty(key
));
249 return modules
->Get(key
);
252 void ModuleRegistry::AttemptToLoadMoreModules(Isolate
* isolate
) {
253 bool keep_trying
= true;
254 while (keep_trying
) {
256 PendingModuleVector pending_modules
;
257 pending_modules
.swap(pending_modules_
);
258 for (size_t i
= 0; i
< pending_modules
.size(); ++i
) {
259 scoped_ptr
<PendingModule
> pending(pending_modules
[i
]);
260 pending_modules
[i
] = NULL
;
261 if (AttemptToLoad(isolate
, pending
.Pass()))