// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "gin/modules/module_registry.h" #include #include #include "base/logging.h" #include "gin/arguments.h" #include "gin/converter.h" #include "gin/modules/module_registry_observer.h" #include "gin/per_context_data.h" #include "gin/per_isolate_data.h" #include "gin/public/wrapper_info.h" #include "gin/runner.h" using v8::Context; using v8::External; using v8::Function; using v8::FunctionTemplate; using v8::Isolate; using v8::Local; using v8::Object; using v8::ObjectTemplate; using v8::Persistent; using v8::StackTrace; using v8::String; using v8::Value; namespace gin { struct PendingModule { PendingModule(); ~PendingModule(); std::string id; std::vector dependencies; Persistent factory; }; PendingModule::PendingModule() { } PendingModule::~PendingModule() { factory.Reset(); } namespace { // Key for base::SupportsUserData::Data. const char kModuleRegistryKey[] = "ModuleRegistry"; struct ModuleRegistryData : public base::SupportsUserData::Data { scoped_ptr registry; }; void Define(const v8::FunctionCallbackInfo& info) { Arguments args(info); if (!info.Length()) return args.ThrowTypeError("At least one argument is required."); std::string id; std::vector dependencies; v8::Handle factory; if (args.PeekNext()->IsString()) args.GetNext(&id); if (args.PeekNext()->IsArray()) args.GetNext(&dependencies); if (!args.GetNext(&factory)) return args.ThrowError(); scoped_ptr pending(new PendingModule); pending->id = id; pending->dependencies = dependencies; pending->factory.Reset(args.isolate(), factory); ModuleRegistry* registry = ModuleRegistry::From(args.isolate()->GetCurrentContext()); registry->AddPendingModule(args.isolate(), pending.Pass()); } WrapperInfo g_wrapper_info = { kEmbedderNativeGin }; Local GetDefineTemplate(Isolate* isolate) { PerIsolateData* data = PerIsolateData::From(isolate); Local templ = data->GetFunctionTemplate( &g_wrapper_info); if (templ.IsEmpty()) { templ = FunctionTemplate::New(isolate, Define); data->SetFunctionTemplate(&g_wrapper_info, templ); } return templ; } } // namespace ModuleRegistry::ModuleRegistry(Isolate* isolate) : modules_(isolate, Object::New(isolate)) { } ModuleRegistry::~ModuleRegistry() { modules_.Reset(); } // static void ModuleRegistry::RegisterGlobals(Isolate* isolate, v8::Handle templ) { templ->Set(StringToSymbol(isolate, "define"), GetDefineTemplate(isolate)); } // static void ModuleRegistry::InstallGlobals(v8::Isolate* isolate, v8::Handle obj) { obj->Set(StringToSymbol(isolate, "define"), GetDefineTemplate(isolate)->GetFunction()); } // static ModuleRegistry* ModuleRegistry::From(v8::Handle context) { PerContextData* data = PerContextData::From(context); if (!data) return NULL; ModuleRegistryData* registry_data = static_cast( data->GetUserData(kModuleRegistryKey)); if (!registry_data) { // PerContextData takes ownership of ModuleRegistryData. registry_data = new ModuleRegistryData; registry_data->registry.reset(new ModuleRegistry(context->GetIsolate())); data->SetUserData(kModuleRegistryKey, registry_data); } return registry_data->registry.get(); } void ModuleRegistry::AddObserver(ModuleRegistryObserver* observer) { observer_list_.AddObserver(observer); } void ModuleRegistry::RemoveObserver(ModuleRegistryObserver* observer) { observer_list_.RemoveObserver(observer); } void ModuleRegistry::AddBuiltinModule(Isolate* isolate, const std::string& id, v8::Handle module) { DCHECK(!id.empty()); RegisterModule(isolate, id, module); } void ModuleRegistry::AddPendingModule(Isolate* isolate, scoped_ptr pending) { const std::string pending_id = pending->id; const std::vector pending_dependencies = pending->dependencies; AttemptToLoad(isolate, pending.Pass()); FOR_EACH_OBSERVER(ModuleRegistryObserver, observer_list_, OnDidAddPendingModule(pending_id, pending_dependencies)); } void ModuleRegistry::LoadModule(Isolate* isolate, const std::string& id, LoadModuleCallback callback) { if (available_modules_.find(id) != available_modules_.end()) { // Should we call the callback asynchronously? callback.Run(GetModule(isolate, id)); return; } waiting_callbacks_.insert(std::make_pair(id, callback)); unsatisfied_dependencies_.insert(id); } void ModuleRegistry::RegisterModule(Isolate* isolate, const std::string& id, v8::Handle module) { if (id.empty() || module.IsEmpty()) return; unsatisfied_dependencies_.erase(id); available_modules_.insert(id); v8::Handle modules = Local::New(isolate, modules_); modules->Set(StringToSymbol(isolate, id), module); std::pair range = waiting_callbacks_.equal_range(id); std::vector callbacks; callbacks.reserve(waiting_callbacks_.count(id)); for (LoadModuleCallbackMap::iterator it = range.first; it != range.second; ++it) { callbacks.push_back(it->second); } waiting_callbacks_.erase(range.first, range.second); for (std::vector::iterator it = callbacks.begin(); it != callbacks.end(); ++it) { // Should we call the callback asynchronously? it->Run(module); } } bool ModuleRegistry::CheckDependencies(PendingModule* pending) { size_t num_missing_dependencies = 0; size_t len = pending->dependencies.size(); for (size_t i = 0; i < len; ++i) { const std::string& dependency = pending->dependencies[i]; if (available_modules_.count(dependency)) continue; unsatisfied_dependencies_.insert(dependency); num_missing_dependencies++; } return num_missing_dependencies == 0; } void ModuleRegistry::Load(Isolate* isolate, scoped_ptr pending) { if (!pending->id.empty() && available_modules_.count(pending->id)) return; // We've already loaded this module. uint32_t argc = static_cast(pending->dependencies.size()); std::vector > argv(argc); for (uint32_t i = 0; i < argc; ++i) argv[i] = GetModule(isolate, pending->dependencies[i]); v8::Handle module = Local::New(isolate, pending->factory); v8::Handle factory; if (ConvertFromV8(isolate, module, &factory)) { PerContextData* data = PerContextData::From(isolate->GetCurrentContext()); Runner* runner = data->runner(); module = runner->Call(factory, runner->global(), argc, argv.empty() ? NULL : &argv.front()); if (pending->id.empty()) ConvertFromV8(isolate, factory->GetScriptOrigin().ResourceName(), &pending->id); } RegisterModule(isolate, pending->id, module); } bool ModuleRegistry::AttemptToLoad(Isolate* isolate, scoped_ptr pending) { if (!CheckDependencies(pending.get())) { pending_modules_.push_back(pending.release()); return false; } Load(isolate, pending.Pass()); return true; } v8::Handle ModuleRegistry::GetModule(v8::Isolate* isolate, const std::string& id) { v8::Handle modules = Local::New(isolate, modules_); v8::Handle key = StringToSymbol(isolate, id); DCHECK(modules->HasOwnProperty(key)); return modules->Get(key); } void ModuleRegistry::AttemptToLoadMoreModules(Isolate* isolate) { bool keep_trying = true; while (keep_trying) { keep_trying = false; PendingModuleVector pending_modules; pending_modules.swap(pending_modules_); for (size_t i = 0; i < pending_modules.size(); ++i) { scoped_ptr pending(pending_modules[i]); pending_modules[i] = NULL; if (AttemptToLoad(isolate, pending.Pass())) keep_trying = true; } } } } // namespace gin