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 waiting_callbacks_
.insert(std::make_pair(id
, callback
));
171 unsatisfied_dependencies_
.insert(id
);
174 void ModuleRegistry::RegisterModule(Isolate
* isolate
,
175 const std::string
& id
,
176 v8::Handle
<Value
> module
) {
177 if (id
.empty() || module
.IsEmpty())
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
);
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
;
191 callbacks
.push_back(it
->second
);
193 waiting_callbacks_
.erase(range
.first
, range
.second
);
194 for (std::vector
<LoadModuleCallback
>::iterator it
= callbacks
.begin();
195 it
!= callbacks
.end();
197 // Should we call the callback asynchronously?
202 bool 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
))
209 unsatisfied_dependencies_
.insert(dependency
);
210 num_missing_dependencies
++;
212 return num_missing_dependencies
== 0;
215 void 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.
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
]);
224 v8::Handle
<Value
> module
= Local
<Value
>::New(isolate
, pending
->factory
);
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(),
237 RegisterModule(isolate
, pending
->id
, module
);
240 bool ModuleRegistry::AttemptToLoad(Isolate
* isolate
,
241 scoped_ptr
<PendingModule
> pending
) {
242 if (!CheckDependencies(pending
.get())) {
243 pending_modules_
.push_back(pending
.release());
246 Load(isolate
, pending
.Pass());
250 v8::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
);
258 void ModuleRegistry::AttemptToLoadMoreModules(Isolate
* isolate
) {
259 bool keep_trying
= true;
260 while (keep_trying
) {
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()))