Home | History | Annotate | Download | only in extensions
      1 // Copyright (c) 2012 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 "chrome/renderer/extensions/module_system.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/command_line.h"
      9 #include "base/debug/trace_event.h"
     10 #include "base/stl_util.h"
     11 #include "base/strings/string_util.h"
     12 #include "base/strings/stringprintf.h"
     13 #include "chrome/common/chrome_switches.h"
     14 #include "chrome/common/extensions/extension_messages.h"
     15 #include "chrome/common/extensions/features/feature_channel.h"
     16 #include "chrome/renderer/extensions/chrome_v8_context.h"
     17 #include "chrome/renderer/extensions/console.h"
     18 #include "chrome/renderer/extensions/safe_builtins.h"
     19 #include "content/public/renderer/render_view.h"
     20 #include "third_party/WebKit/public/web/WebFrame.h"
     21 #include "third_party/WebKit/public/web/WebScopedMicrotaskSuppression.h"
     22 
     23 namespace extensions {
     24 
     25 namespace {
     26 
     27 const char* kModuleSystem = "module_system";
     28 const char* kModuleName = "module_name";
     29 const char* kModuleField = "module_field";
     30 const char* kModulesField = "modules";
     31 
     32 // Logs a fatal error for the calling context, with some added metadata about
     33 // the context:
     34 //  - Its type (blessed, unblessed, etc).
     35 //  - Whether it's valid.
     36 //  - The extension ID, if one exists.
     37 //
     38 // This will only actual be fatal in in dev/canary, since in too many cases
     39 // we're at the mercy of the extension or web page's environment. They can mess
     40 // up our JS in unexpected ways. Hopefully dev/canary channel will pick up such
     41 // problems, but given the wider variety on stable/beta it's impossible to know.
     42 void Fatal(ChromeV8Context* context, const std::string& message) {
     43   // Prepend some context metadata.
     44   std::string full_message = "(";
     45   if (!context->is_valid())
     46     full_message += "Invalid ";
     47   full_message += context->GetContextTypeDescription();
     48   full_message += " context";
     49   if (context->extension()) {
     50     full_message += " for ";
     51     full_message += context->extension()->id();
     52   }
     53   full_message += ") ";
     54   full_message += message;
     55 
     56   // <= dev means dev, canary, and trunk.
     57   if (GetCurrentChannel() <= chrome::VersionInfo::CHANNEL_DEV)
     58     console::Fatal(v8::Context::GetCalling(), full_message);
     59   else
     60     console::Error(v8::Context::GetCalling(), full_message);
     61 }
     62 
     63 void Warn(const std::string& message) {
     64   console::Warn(v8::Context::GetCalling(), message);
     65 }
     66 
     67 // Default exception handler which logs the exception.
     68 class DefaultExceptionHandler : public ModuleSystem::ExceptionHandler {
     69  public:
     70   explicit DefaultExceptionHandler(ChromeV8Context* context)
     71       : context_(context) {}
     72 
     73   // Fatally dumps the debug info from |try_catch| to the console.
     74   // Make sure this is never used for exceptions that originate in external
     75   // code!
     76   virtual void HandleUncaughtException(const v8::TryCatch& try_catch) OVERRIDE {
     77     v8::HandleScope handle_scope;
     78     std::string stack_trace = "<stack trace unavailable>";
     79     if (!try_catch.StackTrace().IsEmpty()) {
     80       v8::String::Utf8Value stack_value(try_catch.StackTrace());
     81       if (*stack_value)
     82         stack_trace.assign(*stack_value, stack_value.length());
     83       else
     84         stack_trace = "<could not convert stack trace to string>";
     85     }
     86     Fatal(context_, CreateExceptionString(try_catch) + "{" + stack_trace + "}");
     87   }
     88 
     89  private:
     90   ChromeV8Context* context_;
     91 };
     92 
     93 } // namespace
     94 
     95 std::string ModuleSystem::ExceptionHandler::CreateExceptionString(
     96     const v8::TryCatch& try_catch) {
     97   v8::Handle<v8::Message> message(try_catch.Message());
     98   if (message.IsEmpty()) {
     99     return "try_catch has no message";
    100   }
    101 
    102   std::string resource_name = "<unknown resource>";
    103   if (!message->GetScriptResourceName().IsEmpty()) {
    104     v8::String::Utf8Value resource_name_v8(
    105         message->GetScriptResourceName()->ToString());
    106     resource_name.assign(*resource_name_v8, resource_name_v8.length());
    107   }
    108 
    109   std::string error_message = "<no error message>";
    110   if (!message->Get().IsEmpty()) {
    111     v8::String::Utf8Value error_message_v8(message->Get());
    112     error_message.assign(*error_message_v8, error_message_v8.length());
    113   }
    114 
    115   return base::StringPrintf("%s:%d: %s",
    116                             resource_name.c_str(),
    117                             message->GetLineNumber(),
    118                             error_message.c_str());
    119 }
    120 
    121 ModuleSystem::ModuleSystem(ChromeV8Context* context, SourceMap* source_map)
    122     : ObjectBackedNativeHandler(context),
    123       context_(context),
    124       source_map_(source_map),
    125       natives_enabled_(0),
    126       exception_handler_(new DefaultExceptionHandler(context)) {
    127   RouteFunction("require",
    128       base::Bind(&ModuleSystem::RequireForJs, base::Unretained(this)));
    129   RouteFunction("requireNative",
    130       base::Bind(&ModuleSystem::RequireNative, base::Unretained(this)));
    131 
    132   v8::Handle<v8::Object> global(context->v8_context()->Global());
    133   global->SetHiddenValue(v8::String::New(kModulesField), v8::Object::New());
    134   global->SetHiddenValue(v8::String::New(kModuleSystem),
    135                          v8::External::New(this));
    136 }
    137 
    138 ModuleSystem::~ModuleSystem() {
    139   Invalidate();
    140 }
    141 
    142 void ModuleSystem::Invalidate() {
    143   if (!is_valid())
    144     return;
    145 
    146   // Clear the module system properties from the global context. It's polite,
    147   // and we use this as a signal in lazy handlers that we no longer exist.
    148   {
    149     v8::HandleScope scope;
    150     v8::Handle<v8::Object> global = context()->v8_context()->Global();
    151     global->DeleteHiddenValue(v8::String::New(kModulesField));
    152     global->DeleteHiddenValue(v8::String::New(kModuleSystem));
    153   }
    154 
    155   // Invalidate all of the successfully required handlers we own.
    156   for (NativeHandlerMap::iterator it = native_handler_map_.begin();
    157        it != native_handler_map_.end(); ++it) {
    158     it->second->Invalidate();
    159   }
    160 
    161   ObjectBackedNativeHandler::Invalidate();
    162 }
    163 
    164 ModuleSystem::NativesEnabledScope::NativesEnabledScope(
    165     ModuleSystem* module_system)
    166     : module_system_(module_system) {
    167   module_system_->natives_enabled_++;
    168 }
    169 
    170 ModuleSystem::NativesEnabledScope::~NativesEnabledScope() {
    171   module_system_->natives_enabled_--;
    172   CHECK_GE(module_system_->natives_enabled_, 0);
    173 }
    174 
    175 void ModuleSystem::HandleException(const v8::TryCatch& try_catch) {
    176   exception_handler_->HandleUncaughtException(try_catch);
    177 }
    178 
    179 v8::Handle<v8::Value> ModuleSystem::Require(const std::string& module_name) {
    180   v8::HandleScope handle_scope;
    181   return handle_scope.Close(
    182       RequireForJsInner(v8::String::New(module_name.c_str())));
    183 }
    184 
    185 void ModuleSystem::RequireForJs(
    186   const v8::FunctionCallbackInfo<v8::Value>& args) {
    187   v8::Handle<v8::String> module_name = args[0]->ToString();
    188   args.GetReturnValue().Set(RequireForJsInner(module_name));
    189 }
    190 
    191 v8::Handle<v8::Value> ModuleSystem::RequireForJsInner(
    192     v8::Handle<v8::String> module_name) {
    193   v8::HandleScope handle_scope;
    194   v8::Context::Scope context_scope(context()->v8_context());
    195 
    196   v8::Handle<v8::Object> global(context()->v8_context()->Global());
    197 
    198   // The module system might have been deleted. This can happen if a different
    199   // context keeps a reference to us, but our frame is destroyed (e.g.
    200   // background page keeps reference to chrome object in a closed popup).
    201   v8::Handle<v8::Value> modules_value =
    202       global->GetHiddenValue(v8::String::New(kModulesField));
    203   if (modules_value.IsEmpty() || modules_value->IsUndefined()) {
    204     Warn("Extension view no longer exists");
    205     return v8::Undefined();
    206   }
    207 
    208   v8::Handle<v8::Object> modules(v8::Handle<v8::Object>::Cast(modules_value));
    209   v8::Handle<v8::Value> exports(modules->Get(module_name));
    210   if (!exports->IsUndefined())
    211     return handle_scope.Close(exports);
    212 
    213   std::string module_name_str = *v8::String::AsciiValue(module_name);
    214   v8::Handle<v8::Value> source(GetSource(module_name_str));
    215   if (source.IsEmpty() || source->IsUndefined()) {
    216     Fatal(context_, "No source for require(" + module_name_str + ")");
    217     return v8::Undefined();
    218   }
    219   v8::Handle<v8::String> wrapped_source(WrapSource(
    220       v8::Handle<v8::String>::Cast(source)));
    221   // Modules are wrapped in (function(){...}) so they always return functions.
    222   v8::Handle<v8::Value> func_as_value = RunString(wrapped_source, module_name);
    223   if (func_as_value.IsEmpty() || func_as_value->IsUndefined()) {
    224     Fatal(context_, "Bad source for require(" + module_name_str + ")");
    225     return v8::Undefined();
    226   }
    227 
    228   v8::Handle<v8::Function> func = v8::Handle<v8::Function>::Cast(func_as_value);
    229 
    230   exports = v8::Object::New();
    231   v8::Handle<v8::Object> natives(NewInstance());
    232   CHECK(!natives.IsEmpty());  // this can happen if v8 has issues
    233 
    234   // These must match the argument order in WrapSource.
    235   v8::Handle<v8::Value> args[] = {
    236     // CommonJS.
    237     natives->Get(v8::String::NewSymbol("require")),
    238     natives->Get(v8::String::NewSymbol("requireNative")),
    239     exports,
    240     // Libraries that we magically expose to every module.
    241     console::AsV8Object(),
    242     // Each safe builtin. Keep in order with the arguments in WrapSource.
    243     context_->safe_builtins()->GetArray(),
    244     context_->safe_builtins()->GetFunction(),
    245     context_->safe_builtins()->GetJSON(),
    246     context_->safe_builtins()->GetObjekt(),
    247     context_->safe_builtins()->GetRegExp(),
    248     context_->safe_builtins()->GetString(),
    249   };
    250   {
    251     v8::TryCatch try_catch;
    252     try_catch.SetCaptureMessage(true);
    253     context_->CallFunction(func, arraysize(args), args);
    254     if (try_catch.HasCaught()) {
    255       HandleException(try_catch);
    256       return v8::Undefined();
    257     }
    258   }
    259   modules->Set(module_name, exports);
    260   return handle_scope.Close(exports);
    261 }
    262 
    263 v8::Local<v8::Value> ModuleSystem::CallModuleMethod(
    264     const std::string& module_name,
    265     const std::string& method_name) {
    266   v8::HandleScope handle_scope;
    267   v8::Handle<v8::Value> no_args;
    268   return CallModuleMethod(module_name, method_name, 0, &no_args);
    269 }
    270 
    271 v8::Local<v8::Value> ModuleSystem::CallModuleMethod(
    272     const std::string& module_name,
    273     const std::string& method_name,
    274     std::vector<v8::Handle<v8::Value> >* args) {
    275   return CallModuleMethod(
    276       module_name, method_name, args->size(), vector_as_array(args));
    277 }
    278 
    279 v8::Local<v8::Value> ModuleSystem::CallModuleMethod(
    280     const std::string& module_name,
    281     const std::string& method_name,
    282     int argc,
    283     v8::Handle<v8::Value> argv[]) {
    284   TRACE_EVENT2("v8", "v8.callModuleMethod",
    285                "module_name", module_name,
    286                "method_name", method_name);
    287 
    288   v8::HandleScope handle_scope;
    289   v8::Context::Scope context_scope(context()->v8_context());
    290 
    291   v8::Local<v8::Value> module;
    292   {
    293     NativesEnabledScope natives_enabled(this);
    294     module = v8::Local<v8::Value>::New(
    295         RequireForJsInner(v8::String::New(module_name.c_str())));
    296   }
    297 
    298   if (module.IsEmpty() || !module->IsObject()) {
    299     Fatal(context_,
    300           "Failed to get module " + module_name + " to call " + method_name);
    301     return handle_scope.Close(v8::Undefined());
    302   }
    303 
    304   v8::Local<v8::Value> value =
    305       v8::Handle<v8::Object>::Cast(module)->Get(
    306           v8::String::New(method_name.c_str()));
    307   if (value.IsEmpty() || !value->IsFunction()) {
    308     Fatal(context_, module_name + "." + method_name + " is not a function");
    309     return handle_scope.Close(v8::Undefined());
    310   }
    311 
    312   v8::Handle<v8::Function> func = v8::Handle<v8::Function>::Cast(value);
    313   v8::Local<v8::Value> result;
    314   {
    315     v8::TryCatch try_catch;
    316     try_catch.SetCaptureMessage(true);
    317     result = context_->CallFunction(func, argc, argv);
    318     if (try_catch.HasCaught())
    319       HandleException(try_catch);
    320   }
    321   return handle_scope.Close(result);
    322 }
    323 
    324 void ModuleSystem::RegisterNativeHandler(const std::string& name,
    325     scoped_ptr<NativeHandler> native_handler) {
    326   native_handler_map_[name] =
    327       linked_ptr<NativeHandler>(native_handler.release());
    328 }
    329 
    330 void ModuleSystem::OverrideNativeHandlerForTest(const std::string& name) {
    331   overridden_native_handlers_.insert(name);
    332 }
    333 
    334 void ModuleSystem::RunString(const std::string& code, const std::string& name) {
    335   v8::HandleScope handle_scope;
    336   RunString(v8::String::New(code.c_str()), v8::String::New(name.c_str()));
    337 }
    338 
    339 // static
    340 void ModuleSystem::NativeLazyFieldGetter(
    341     v8::Local<v8::String> property,
    342     const v8::PropertyCallbackInfo<v8::Value>& info) {
    343   LazyFieldGetterInner(property,
    344                        info,
    345                        &ModuleSystem::RequireNativeFromString);
    346 }
    347 
    348 // static
    349 void ModuleSystem::LazyFieldGetter(
    350     v8::Local<v8::String> property,
    351     const v8::PropertyCallbackInfo<v8::Value>& info) {
    352   LazyFieldGetterInner(property, info, &ModuleSystem::Require);
    353 }
    354 
    355 // static
    356 void ModuleSystem::LazyFieldGetterInner(
    357     v8::Local<v8::String> property,
    358     const v8::PropertyCallbackInfo<v8::Value>& info,
    359     RequireFunction require_function) {
    360   CHECK(!info.Data().IsEmpty());
    361   CHECK(info.Data()->IsObject());
    362   v8::HandleScope handle_scope;
    363   v8::Handle<v8::Object> parameters = v8::Handle<v8::Object>::Cast(info.Data());
    364   // This context should be the same as context()->v8_context().
    365   v8::Handle<v8::Context> context = parameters->CreationContext();
    366   v8::Handle<v8::Object> global(context->Global());
    367   v8::Handle<v8::Value> module_system_value =
    368       global->GetHiddenValue(v8::String::New(kModuleSystem));
    369   if (module_system_value.IsEmpty() || !module_system_value->IsExternal()) {
    370     // ModuleSystem has been deleted.
    371     // TODO(kalman): See comment in header file.
    372     Warn("Module system has been deleted, does extension view exist?");
    373     return;
    374   }
    375 
    376   ModuleSystem* module_system = static_cast<ModuleSystem*>(
    377       v8::Handle<v8::External>::Cast(module_system_value)->Value());
    378 
    379   std::string name = *v8::String::AsciiValue(
    380       parameters->Get(v8::String::New(kModuleName))->ToString());
    381 
    382   // Switch to our v8 context because we need functions created while running
    383   // the require()d module to belong to our context, not the current one.
    384   v8::Context::Scope context_scope(context);
    385   NativesEnabledScope natives_enabled_scope(module_system);
    386 
    387   v8::TryCatch try_catch;
    388   v8::Handle<v8::Value> module_value = (module_system->*require_function)(name);
    389   if (try_catch.HasCaught()) {
    390     module_system->HandleException(try_catch);
    391     return;
    392   }
    393   if (module_value.IsEmpty() || !module_value->IsObject()) {
    394     // require_function will have already logged this, we don't need to.
    395     return;
    396   }
    397 
    398   v8::Handle<v8::Object> module = v8::Handle<v8::Object>::Cast(module_value);
    399   v8::Handle<v8::String> field =
    400       parameters->Get(v8::String::New(kModuleField))->ToString();
    401 
    402   if (!module->Has(field)) {
    403     std::string field_str = *v8::String::AsciiValue(field);
    404     Fatal(module_system->context_,
    405           "Lazy require of " + name + "." + field_str + " did not set the " +
    406               field_str + " field");
    407     return;
    408   }
    409 
    410   v8::Local<v8::Value> new_field = module->Get(field);
    411   if (try_catch.HasCaught()) {
    412     module_system->HandleException(try_catch);
    413     return;
    414   }
    415 
    416   // Ok for it to be undefined, among other things it's how bindings signify
    417   // that the extension doesn't have permission to use them.
    418   CHECK(!new_field.IsEmpty());
    419 
    420   // Delete the getter and set this field to |new_field| so the same object is
    421   // returned every time a certain API is accessed.
    422   v8::Handle<v8::Object> object = info.This();
    423   object->Delete(property);
    424   object->Set(property, new_field);
    425   info.GetReturnValue().Set(new_field);
    426 }
    427 
    428 void ModuleSystem::SetLazyField(v8::Handle<v8::Object> object,
    429                                 const std::string& field,
    430                                 const std::string& module_name,
    431                                 const std::string& module_field) {
    432   SetLazyField(object, field, module_name, module_field,
    433       &ModuleSystem::LazyFieldGetter);
    434 }
    435 
    436 void ModuleSystem::SetLazyField(v8::Handle<v8::Object> object,
    437                                 const std::string& field,
    438                                 const std::string& module_name,
    439                                 const std::string& module_field,
    440                                 v8::AccessorGetterCallback getter) {
    441   v8::HandleScope handle_scope;
    442   v8::Handle<v8::Object> parameters = v8::Object::New();
    443   parameters->Set(v8::String::New(kModuleName),
    444                   v8::String::New(module_name.c_str()));
    445   parameters->Set(v8::String::New(kModuleField),
    446                   v8::String::New(module_field.c_str()));
    447   object->SetAccessor(v8::String::New(field.c_str()),
    448                       getter,
    449                       NULL,
    450                       parameters);
    451 }
    452 
    453 void ModuleSystem::SetNativeLazyField(v8::Handle<v8::Object> object,
    454                                       const std::string& field,
    455                                       const std::string& module_name,
    456                                       const std::string& module_field) {
    457   SetLazyField(object, field, module_name, module_field,
    458       &ModuleSystem::NativeLazyFieldGetter);
    459 }
    460 
    461 v8::Handle<v8::Value> ModuleSystem::RunString(v8::Handle<v8::String> code,
    462                                               v8::Handle<v8::String> name) {
    463   v8::HandleScope handle_scope;
    464   v8::Context::Scope context_scope(context()->v8_context());
    465 
    466   WebKit::WebScopedMicrotaskSuppression suppression;
    467   v8::TryCatch try_catch;
    468   try_catch.SetCaptureMessage(true);
    469   v8::Handle<v8::Script> script(v8::Script::New(code, name));
    470   if (try_catch.HasCaught()) {
    471     HandleException(try_catch);
    472     return v8::Undefined();
    473   }
    474 
    475   v8::Handle<v8::Value> result = script->Run();
    476   if (try_catch.HasCaught()) {
    477     HandleException(try_catch);
    478     return v8::Undefined();
    479   }
    480 
    481   return handle_scope.Close(result);
    482 }
    483 
    484 v8::Handle<v8::Value> ModuleSystem::GetSource(const std::string& module_name) {
    485   v8::HandleScope handle_scope;
    486   if (!source_map_->Contains(module_name))
    487     return v8::Undefined();
    488   return handle_scope.Close(source_map_->GetSource(module_name));
    489 }
    490 
    491 void ModuleSystem::RequireNative(
    492     const v8::FunctionCallbackInfo<v8::Value>& args) {
    493   CHECK_EQ(1, args.Length());
    494   std::string native_name = *v8::String::AsciiValue(args[0]->ToString());
    495   args.GetReturnValue().Set(RequireNativeFromString(native_name));
    496 }
    497 
    498 v8::Handle<v8::Value> ModuleSystem::RequireNativeFromString(
    499     const std::string& native_name) {
    500   if (natives_enabled_ == 0) {
    501     // HACK: if in test throw exception so that we can test the natives-disabled
    502     // logic; however, under normal circumstances, this is programmer error so
    503     // we could crash.
    504     if (exception_handler_)
    505       return v8::ThrowException(v8::String::New("Natives disabled"));
    506     Fatal(context_, "Natives disabled for requireNative(" + native_name + ")");
    507     return v8::Undefined();
    508   }
    509 
    510   if (overridden_native_handlers_.count(native_name) > 0u)
    511     return RequireForJsInner(v8::String::New(native_name.c_str()));
    512 
    513   NativeHandlerMap::iterator i = native_handler_map_.find(native_name);
    514   if (i == native_handler_map_.end()) {
    515     Fatal(context_,
    516           "Couldn't find native for requireNative(" + native_name + ")");
    517     return v8::Undefined();
    518   }
    519   return i->second->NewInstance();
    520 }
    521 
    522 v8::Handle<v8::String> ModuleSystem::WrapSource(v8::Handle<v8::String> source) {
    523   v8::HandleScope handle_scope;
    524   // Keep in order with the arguments in RequireForJsInner.
    525   v8::Handle<v8::String> left = v8::String::New(
    526       "(function(require, requireNative, exports, "
    527                 "console, "
    528                 "$Array, $Function, $JSON, $Object, $RegExp, $String) {"
    529        "'use strict';");
    530   v8::Handle<v8::String> right = v8::String::New("\n})");
    531   return handle_scope.Close(
    532       v8::String::Concat(left, v8::String::Concat(source, right)));
    533 }
    534 
    535 }  // namespace extensions
    536