Home | History | Annotate | Download | only in modules
      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