| Index: chrome/renderer/extensions/module_system.cc
|
| diff --git a/chrome/renderer/extensions/module_system.cc b/chrome/renderer/extensions/module_system.cc
|
| index 24982ccdabe2ed832a806f7c25a451e69f8d9164..5615c9375d52324b8e59ab77ddafb24a8c9a96ed 100644
|
| --- a/chrome/renderer/extensions/module_system.cc
|
| +++ b/chrome/renderer/extensions/module_system.cc
|
| @@ -5,7 +5,13 @@
|
| #include "chrome/renderer/extensions/module_system.h"
|
|
|
| #include "base/bind.h"
|
| +#include "base/debug/alias.h"
|
| #include "base/stl_util.h"
|
| +#include "base/string_util.h"
|
| +#include "base/stringprintf.h"
|
| +#include "chrome/common/extensions/extension_messages.h"
|
| +#include "chrome/renderer/extensions/chrome_v8_context.h"
|
| +#include "content/public/renderer/render_view.h"
|
| #include "third_party/WebKit/Source/WebKit/chromium/public/WebScopedMicrotaskSuppression.h"
|
|
|
| namespace {
|
| @@ -21,26 +27,32 @@ namespace extensions {
|
|
|
| ModuleSystem::ModuleSystem(v8::Handle<v8::Context> context,
|
| SourceMap* source_map)
|
| - : NativeHandler(context->GetIsolate()),
|
| - context_(context),
|
| + : ObjectBackedNativeHandler(context),
|
| source_map_(source_map),
|
| natives_enabled_(0) {
|
| RouteFunction("require",
|
| base::Bind(&ModuleSystem::RequireForJs, base::Unretained(this)));
|
| RouteFunction("requireNative",
|
| - base::Bind(&ModuleSystem::GetNative, base::Unretained(this)));
|
| + base::Bind(&ModuleSystem::RequireNative, base::Unretained(this)));
|
|
|
| - v8::Handle<v8::Object> global(context_->Global());
|
| + v8::Handle<v8::Object> global(context->Global());
|
| global->SetHiddenValue(v8::String::New(kModulesField), v8::Object::New());
|
| global->SetHiddenValue(v8::String::New(kModuleSystem),
|
| v8::External::New(this));
|
| }
|
|
|
| ModuleSystem::~ModuleSystem() {
|
| - v8::HandleScope handle_scope;
|
| - // Deleting this value here prevents future lazy field accesses from
|
| - // referencing ModuleSystem after it has been freed.
|
| - context_->Global()->DeleteHiddenValue(v8::String::New(kModuleSystem));
|
| + Invalidate();
|
| +}
|
| +
|
| +void ModuleSystem::Invalidate() {
|
| + if (!is_valid())
|
| + return;
|
| + for (NativeHandlerMap::iterator it = native_handler_map_.begin();
|
| + it != native_handler_map_.end(); ++it) {
|
| + it->second->Invalidate();
|
| + }
|
| + ObjectBackedNativeHandler::Invalidate();
|
| }
|
|
|
| ModuleSystem::NativesEnabledScope::NativesEnabledScope(
|
| @@ -54,16 +66,6 @@ ModuleSystem::NativesEnabledScope::~NativesEnabledScope() {
|
| CHECK_GE(module_system_->natives_enabled_, 0);
|
| }
|
|
|
| -// static
|
| -bool ModuleSystem::IsPresentInCurrentContext() {
|
| - v8::Handle<v8::Object> global(v8::Context::GetCurrent()->Global());
|
| - if (global.IsEmpty())
|
| - return false;
|
| - v8::Handle<v8::Value> module_system =
|
| - global->GetHiddenValue(v8::String::New(kModuleSystem));
|
| - return !module_system.IsEmpty() && !module_system->IsUndefined();
|
| -}
|
| -
|
| void ModuleSystem::HandleException(const v8::TryCatch& try_catch) {
|
| DumpException(try_catch);
|
| if (exception_handler_.get())
|
| @@ -71,13 +73,10 @@ void ModuleSystem::HandleException(const v8::TryCatch& try_catch) {
|
| }
|
|
|
| // static
|
| -void ModuleSystem::DumpException(const v8::TryCatch& try_catch) {
|
| - v8::HandleScope handle_scope;
|
| -
|
| +std::string ModuleSystem::CreateExceptionString(const v8::TryCatch& try_catch) {
|
| v8::Handle<v8::Message> message(try_catch.Message());
|
| if (message.IsEmpty()) {
|
| - LOG(ERROR) << "try_catch has no message";
|
| - return;
|
| + return "try_catch has no message";
|
| }
|
|
|
| std::string resource_name = "<unknown resource>";
|
| @@ -90,6 +89,16 @@ void ModuleSystem::DumpException(const v8::TryCatch& try_catch) {
|
| if (!message->Get().IsEmpty())
|
| error_message = *v8::String::Utf8Value(message->Get());
|
|
|
| + return base::StringPrintf("%s:%d: %s",
|
| + resource_name.c_str(),
|
| + message->GetLineNumber(),
|
| + error_message.c_str());
|
| +}
|
| +
|
| +// static
|
| +void ModuleSystem::DumpException(const v8::TryCatch& try_catch) {
|
| + v8::HandleScope handle_scope;
|
| +
|
| std::string stack_trace = "<stack trace unavailable>";
|
| if (!try_catch.StackTrace().IsEmpty()) {
|
| v8::String::Utf8Value stack_value(try_catch.StackTrace());
|
| @@ -99,14 +108,13 @@ void ModuleSystem::DumpException(const v8::TryCatch& try_catch) {
|
| stack_trace = "<could not convert stack trace to string>";
|
| }
|
|
|
| - LOG(ERROR) << "[" << resource_name << "(" << message->GetLineNumber() << ")] "
|
| - << error_message
|
| - << "{" << stack_trace << "}";
|
| + LOG(ERROR) << CreateExceptionString(try_catch) << "{" << stack_trace << "}";
|
| }
|
|
|
| -void ModuleSystem::Require(const std::string& module_name) {
|
| +v8::Handle<v8::Value> ModuleSystem::Require(const std::string& module_name) {
|
| v8::HandleScope handle_scope;
|
| - RequireForJsInner(v8::String::New(module_name.c_str()));
|
| + return handle_scope.Close(
|
| + RequireForJsInner(v8::String::New(module_name.c_str())));
|
| }
|
|
|
| v8::Handle<v8::Value> ModuleSystem::RequireForJs(const v8::Arguments& args) {
|
| @@ -118,7 +126,7 @@ v8::Handle<v8::Value> ModuleSystem::RequireForJs(const v8::Arguments& args) {
|
| v8::Handle<v8::Value> ModuleSystem::RequireForJsInner(
|
| v8::Handle<v8::String> module_name) {
|
| v8::HandleScope handle_scope;
|
| - v8::Handle<v8::Object> global(v8::Context::GetCurrent()->Global());
|
| + v8::Handle<v8::Object> global(v8_context()->Global());
|
| v8::Handle<v8::Object> modules(v8::Handle<v8::Object>::Cast(
|
| global->GetHiddenValue(v8::String::New(kModulesField))));
|
| v8::Handle<v8::Value> exports(modules->Get(module_name));
|
| @@ -158,10 +166,11 @@ v8::Handle<v8::Value> ModuleSystem::RequireForJsInner(
|
| return handle_scope.Close(exports);
|
| }
|
|
|
| -void ModuleSystem::CallModuleMethod(const std::string& module_name,
|
| - const std::string& method_name) {
|
| +v8::Local<v8::Value> ModuleSystem::CallModuleMethod(
|
| + const std::string& module_name,
|
| + const std::string& method_name) {
|
| std::vector<v8::Handle<v8::Value> > args;
|
| - CallModuleMethod(module_name, method_name, &args);
|
| + return CallModuleMethod(module_name, method_name, &args);
|
| }
|
|
|
| v8::Local<v8::Value> ModuleSystem::CallModuleMethod(
|
| @@ -181,8 +190,7 @@ v8::Local<v8::Value> ModuleSystem::CallModuleMethod(
|
| return v8::Local<v8::Value>();
|
| v8::Handle<v8::Function> func =
|
| v8::Handle<v8::Function>::Cast(value);
|
| - // TODO(jeremya/koz): refer to context_ here, not the current context.
|
| - v8::Handle<v8::Object> global(v8::Context::GetCurrent()->Global());
|
| + v8::Handle<v8::Object> global(v8_context()->Global());
|
| v8::Local<v8::Value> result;
|
| {
|
| WebKit::WebScopedMicrotaskSuppression suppression;
|
| @@ -195,6 +203,10 @@ v8::Local<v8::Value> ModuleSystem::CallModuleMethod(
|
| return handle_scope.Close(result);
|
| }
|
|
|
| +bool ModuleSystem::HasNativeHandler(const std::string& name) {
|
| + return native_handler_map_.find(name) != native_handler_map_.end();
|
| +}
|
| +
|
| void ModuleSystem::RegisterNativeHandler(const std::string& name,
|
| scoped_ptr<NativeHandler> native_handler) {
|
| native_handler_map_[name] =
|
| @@ -211,13 +223,31 @@ void ModuleSystem::RunString(const std::string& code, const std::string& name) {
|
| }
|
|
|
| // static
|
| +v8::Handle<v8::Value> ModuleSystem::NativeLazyFieldGetter(
|
| + v8::Local<v8::String> property, const v8::AccessorInfo& info) {
|
| + return LazyFieldGetterInner(property,
|
| + info,
|
| + &ModuleSystem::RequireNativeFromString);
|
| +}
|
| +
|
| +// static
|
| v8::Handle<v8::Value> ModuleSystem::LazyFieldGetter(
|
| v8::Local<v8::String> property, const v8::AccessorInfo& info) {
|
| + return LazyFieldGetterInner(property, info, &ModuleSystem::Require);
|
| +}
|
| +
|
| +// static
|
| +v8::Handle<v8::Value> ModuleSystem::LazyFieldGetterInner(
|
| + v8::Local<v8::String> property,
|
| + const v8::AccessorInfo& info,
|
| + RequireFunction require_function) {
|
| CHECK(!info.Data().IsEmpty());
|
| CHECK(info.Data()->IsObject());
|
| v8::HandleScope handle_scope;
|
| v8::Handle<v8::Object> parameters = v8::Handle<v8::Object>::Cast(info.Data());
|
| - v8::Handle<v8::Object> global(v8::Context::GetCurrent()->Global());
|
| + // This context should be the same as v8_context_, but this method is static.
|
| + v8::Handle<v8::Context> context = parameters->CreationContext();
|
| + v8::Handle<v8::Object> global(context->Global());
|
| v8::Handle<v8::Value> module_system_value =
|
| global->GetHiddenValue(v8::String::New(kModuleSystem));
|
| if (module_system_value.IsEmpty() || module_system_value->IsUndefined()) {
|
| @@ -227,38 +257,81 @@ v8::Handle<v8::Value> ModuleSystem::LazyFieldGetter(
|
| ModuleSystem* module_system = static_cast<ModuleSystem*>(
|
| v8::Handle<v8::External>::Cast(module_system_value)->Value());
|
|
|
| - v8::Handle<v8::Object> module;
|
| - {
|
| - NativesEnabledScope scope(module_system);
|
| - module = v8::Handle<v8::Object>::Cast(module_system->RequireForJsInner(
|
| - parameters->Get(v8::String::New(kModuleName))->ToString()));
|
| + std::string name = *v8::String::AsciiValue(
|
| + parameters->Get(v8::String::New(kModuleName))->ToString());
|
| +
|
| + // Switch to our v8 context because we need functions created while running
|
| + // the require()d module to belong to our context, not the current one.
|
| + v8::Context::Scope context_scope(context);
|
| + NativesEnabledScope natives_enabled_scope(module_system);
|
| +
|
| + v8::TryCatch try_catch;
|
| + v8::Handle<v8::Object> module = v8::Handle<v8::Object>::Cast(
|
| + (module_system->*require_function)(name));
|
| + if (try_catch.HasCaught()) {
|
| + module_system->HandleException(try_catch);
|
| + return handle_scope.Close(v8::Handle<v8::Value>());
|
| }
|
| +
|
| if (module.IsEmpty())
|
| return handle_scope.Close(v8::Handle<v8::Value>());
|
|
|
| v8::Handle<v8::String> field =
|
| parameters->Get(v8::String::New(kModuleField))->ToString();
|
|
|
| - return handle_scope.Close(module->Get(field));
|
| + // http://crbug.com/179741.
|
| + std::string field_name = *v8::String::AsciiValue(field);
|
| + char stack_debug[64];
|
| + base::debug::Alias(&stack_debug);
|
| + base::snprintf(stack_debug, arraysize(stack_debug),
|
| + "%s.%s", name.c_str(), field_name.c_str());
|
| +
|
| + v8::Local<v8::Value> new_field = module->Get(field);
|
| + v8::Handle<v8::Object> object = info.This();
|
| + // Delete the getter and set this field to |new_field| so the same object is
|
| + // returned every time a certain API is accessed.
|
| + // CHECK is for http://crbug.com/179741.
|
| + CHECK(!new_field.IsEmpty()) << "Empty require " << name << "." << field_name;
|
| + if (!new_field->IsUndefined()) {
|
| + object->Delete(property);
|
| + object->Set(property, new_field);
|
| + }
|
| + return handle_scope.Close(new_field);
|
| }
|
|
|
| void ModuleSystem::SetLazyField(v8::Handle<v8::Object> object,
|
| const std::string& field,
|
| const std::string& module_name,
|
| const std::string& module_field) {
|
| + SetLazyField(object, field, module_name, module_field,
|
| + &ModuleSystem::LazyFieldGetter);
|
| +}
|
| +
|
| +void ModuleSystem::SetLazyField(v8::Handle<v8::Object> object,
|
| + const std::string& field,
|
| + const std::string& module_name,
|
| + const std::string& module_field,
|
| + v8::AccessorGetter getter) {
|
| v8::HandleScope handle_scope;
|
| v8::Handle<v8::Object> parameters = v8::Object::New();
|
| parameters->Set(v8::String::New(kModuleName),
|
| v8::String::New(module_name.c_str()));
|
| parameters->Set(v8::String::New(kModuleField),
|
| v8::String::New(module_field.c_str()));
|
| -
|
| object->SetAccessor(v8::String::New(field.c_str()),
|
| - &ModuleSystem::LazyFieldGetter,
|
| + getter,
|
| NULL,
|
| parameters);
|
| }
|
|
|
| +void ModuleSystem::SetNativeLazyField(v8::Handle<v8::Object> object,
|
| + const std::string& field,
|
| + const std::string& module_name,
|
| + const std::string& module_field) {
|
| + SetLazyField(object, field, module_name, module_field,
|
| + &ModuleSystem::NativeLazyFieldGetter);
|
| +}
|
| +
|
| v8::Handle<v8::Value> ModuleSystem::RunString(v8::Handle<v8::String> code,
|
| v8::Handle<v8::String> name) {
|
| v8::HandleScope handle_scope;
|
| @@ -288,13 +361,18 @@ v8::Handle<v8::Value> ModuleSystem::GetSource(
|
| return handle_scope.Close(source_map_->GetSource(module_name));
|
| }
|
|
|
| -v8::Handle<v8::Value> ModuleSystem::GetNative(const v8::Arguments& args) {
|
| +v8::Handle<v8::Value> ModuleSystem::RequireNative(const v8::Arguments& args) {
|
| CHECK_EQ(1, args.Length());
|
| + std::string native_name = *v8::String::AsciiValue(args[0]->ToString());
|
| + return RequireNativeFromString(native_name);
|
| +}
|
| +
|
| +v8::Handle<v8::Value> ModuleSystem::RequireNativeFromString(
|
| + const std::string& native_name) {
|
| if (natives_enabled_ == 0)
|
| return ThrowException("Natives disabled");
|
| - std::string native_name = *v8::String::AsciiValue(args[0]->ToString());
|
| if (overridden_native_handlers_.count(native_name) > 0u)
|
| - return RequireForJs(args);
|
| + return RequireForJsInner(v8::String::New(native_name.c_str()));
|
| NativeHandlerMap::iterator i = native_handler_map_.find(native_name);
|
| if (i == native_handler_map_.end())
|
| return v8::Undefined();
|
|
|