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 19 using v8::Context; 20 using v8::External; 21 using v8::Function; 22 using v8::FunctionTemplate; 23 using v8::Isolate; 24 using v8::Local; 25 using v8::Object; 26 using v8::ObjectTemplate; 27 using v8::Persistent; 28 using v8::StackTrace; 29 using v8::String; 30 using v8::Value; 31 32 namespace gin { 33 34 struct PendingModule { 35 PendingModule(); 36 ~PendingModule(); 37 38 std::string id; 39 std::vector<std::string> dependencies; 40 Persistent<Value> factory; 41 }; 42 43 PendingModule::PendingModule() { 44 } 45 46 PendingModule::~PendingModule() { 47 factory.Reset(); 48 } 49 50 namespace { 51 52 // Key for base::SupportsUserData::Data. 53 const char kModuleRegistryKey[] = "ModuleRegistry"; 54 55 struct ModuleRegistryData : public base::SupportsUserData::Data { 56 scoped_ptr<ModuleRegistry> registry; 57 }; 58 59 void 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 86 WrapperInfo g_wrapper_info = { kEmbedderNativeGin }; 87 88 Local<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 101 ModuleRegistry::ModuleRegistry(Isolate* isolate) 102 : modules_(isolate, Object::New(isolate)) { 103 } 104 105 ModuleRegistry::~ModuleRegistry() { 106 modules_.Reset(); 107 } 108 109 // static 110 void ModuleRegistry::RegisterGlobals(Isolate* isolate, 111 v8::Handle<ObjectTemplate> templ) { 112 templ->Set(StringToSymbol(isolate, "define"), GetDefineTemplate(isolate)); 113 } 114 115 // static 116 void 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 123 ModuleRegistry* 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 139 void ModuleRegistry::AddObserver(ModuleRegistryObserver* observer) { 140 observer_list_.AddObserver(observer); 141 } 142 143 void ModuleRegistry::RemoveObserver(ModuleRegistryObserver* observer) { 144 observer_list_.RemoveObserver(observer); 145 } 146 147 void ModuleRegistry::AddBuiltinModule(Isolate* isolate, const std::string& id, 148 v8::Handle<Value> module) { 149 DCHECK(!id.empty()); 150 RegisterModule(isolate, id, module); 151 } 152 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)); 160 } 161 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)); 168 return; 169 } 170 waiting_callbacks_.insert(std::make_pair(id, callback)); 171 unsatisfied_dependencies_.insert(id); 172 } 173 174 void 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 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)) 208 continue; 209 unsatisfied_dependencies_.insert(dependency); 210 num_missing_dependencies++; 211 } 212 return num_missing_dependencies == 0; 213 } 214 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. 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 240 bool 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 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); 256 } 257 258 void 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