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::Local
<Value
> factory
;
69 if (!args
.PeekNext().IsEmpty() && args
.PeekNext()->IsString())
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
);
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::Local
<ObjectTemplate
> templ
) {
112 templ
->Set(StringToSymbol(isolate
, "define"), GetDefineTemplate(isolate
));
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
))
123 return SetProperty(isolate
, obj
, StringToSymbol(isolate
, "define"), function
);
127 ModuleRegistry
* ModuleRegistry::From(v8::Local
<Context
> context
) {
128 PerContextData
* data
= PerContextData::From(context
);
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
) {
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
));
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
)
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())
190 v8::Local
<Object
> modules
= Local
<Object
>::New(isolate
, modules_
);
191 if (!SetProperty(isolate
, modules
, StringToSymbol(isolate
, id
), module
))
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
;
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();
208 // Should we call the callback asynchronously?
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
))
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(),
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());
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
) {
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()))