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 // 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); 174 } 175 176 void ModuleRegistry::RegisterModule(Isolate* isolate, 177 const std::string& id, 178 v8::Handle<Value> module) { 179 if (id.empty() || module.IsEmpty()) 180 return; 181 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); 186 187 LoadModuleCallbackMap::iterator it = waiting_callbacks_.find(id); 188 if (it == waiting_callbacks_.end()) 189 return; 190 LoadModuleCallback callback = it->second; 191 waiting_callbacks_.erase(it); 192 // Should we call the callback asynchronously? 193 callback.Run(module); 194 } 195 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)) 202 continue; 203 unsatisfied_dependencies_.insert(dependency); 204 num_missing_dependencies++; 205 } 206 return num_missing_dependencies == 0; 207 } 208 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. 212 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]); 217 218 v8::Handle<Value> module = Local<Value>::New(isolate, pending->factory); 219 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(), 228 &pending->id); 229 } 230 231 RegisterModule(isolate, pending->id, module); 232 } 233 234 bool ModuleRegistry::AttemptToLoad(Isolate* isolate, 235 scoped_ptr<PendingModule> pending) { 236 if (!CheckDependencies(pending.get())) { 237 pending_modules_.push_back(pending.release()); 238 return false; 239 } 240 Load(isolate, pending.Pass()); 241 return true; 242 } 243 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); 250 } 251 252 void ModuleRegistry::AttemptToLoadMoreModules(Isolate* isolate) { 253 bool keep_trying = true; 254 while (keep_trying) { 255 keep_trying = false; 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())) 262 keep_trying = true; 263 } 264 } 265 } 266 267 } // namespace gin 268