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