diff options
Diffstat (limited to 'chrome/renderer/extensions/module_system.cc')
-rw-r--r-- | chrome/renderer/extensions/module_system.cc | 172 |
1 files changed, 124 insertions, 48 deletions
diff --git a/chrome/renderer/extensions/module_system.cc b/chrome/renderer/extensions/module_system.cc index 31146c8..5615c93 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,28 +27,32 @@ namespace extensions { ModuleSystem::ModuleSystem(v8::Handle<v8::Context> context, SourceMap* source_map) - : NativeHandler(context->GetIsolate()), - context_(v8::Persistent<v8::Context>::New(context->GetIsolate(), - 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)); - context_.Dispose(context_->GetIsolate()); + 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( @@ -56,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()) @@ -73,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>"; @@ -92,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()); @@ -101,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) { @@ -120,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)); @@ -160,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( @@ -183,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; @@ -197,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] = @@ -213,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()) { @@ -229,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; @@ -290,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(); |