Move ModuleSystem and NativeHandler to extensions/
[email protected]
Review URL: https://chromiumcodereview.appspot.com/10879107
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@153618 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/renderer/extensions/module_system.cc b/chrome/renderer/extensions/module_system.cc
new file mode 100644
index 0000000..6e4c5e3
--- /dev/null
+++ b/chrome/renderer/extensions/module_system.cc
@@ -0,0 +1,271 @@
+// Copyright (c) 2012 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 "chrome/renderer/extensions/module_system.h"
+
+#include "base/bind.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebScopedMicrotaskSuppression.h"
+
+namespace {
+
+const char* kModuleSystem = "module_system";
+const char* kModuleName = "module_name";
+const char* kModuleField = "module_field";
+const char* kModulesField = "modules";
+
+} // namespace
+
+namespace extensions {
+
+ModuleSystem::ModuleSystem(v8::Handle<v8::Context> context,
+ SourceMap* source_map)
+ : context_(v8::Persistent<v8::Context>::New(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)));
+
+ 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();
+}
+
+ModuleSystem::NativesEnabledScope::NativesEnabledScope(
+ ModuleSystem* module_system)
+ : module_system_(module_system) {
+ module_system_->natives_enabled_++;
+}
+
+ModuleSystem::NativesEnabledScope::~NativesEnabledScope() {
+ module_system_->natives_enabled_--;
+ CHECK_GE(module_system_->natives_enabled_, 0);
+}
+
+// static
+bool ModuleSystem::IsPresentInCurrentContext() {
+ v8::Handle<v8::Object> global(v8::Context::GetCurrent()->Global());
+ v8::Handle<v8::Value> module_system =
+ global->GetHiddenValue(v8::String::New(kModuleSystem));
+ return !module_system.IsEmpty() && !module_system->IsUndefined();
+}
+
+// static
+void ModuleSystem::DumpException(const v8::TryCatch& try_catch) {
+ v8::HandleScope handle_scope;
+
+ v8::Handle<v8::Message> message(try_catch.Message());
+ if (message.IsEmpty()) {
+ LOG(ERROR) << "try_catch has no message";
+ return;
+ }
+
+ std::string resource_name = "<unknown resource>";
+ if (!message->GetScriptResourceName().IsEmpty()) {
+ resource_name =
+ *v8::String::Utf8Value(message->GetScriptResourceName()->ToString());
+ }
+
+ std::string error_message = "<no error message>";
+ if (!message->Get().IsEmpty())
+ error_message = *v8::String::Utf8Value(message->Get());
+
+ std::string stack_trace = "<stack trace unavailable>";
+ if (!try_catch.StackTrace().IsEmpty()) {
+ v8::String::Utf8Value stack_value(try_catch.StackTrace());
+ if (*stack_value)
+ stack_trace.assign(*stack_value, stack_value.length());
+ else
+ stack_trace = "<could not convert stack trace to string>";
+ }
+
+ LOG(ERROR) << "[" << resource_name << "(" << message->GetLineNumber() << ")] "
+ << error_message
+ << "{" << stack_trace << "}";
+}
+
+void ModuleSystem::Require(const std::string& module_name) {
+ v8::HandleScope handle_scope;
+ RequireForJsInner(v8::String::New(module_name.c_str()));
+}
+
+v8::Handle<v8::Value> ModuleSystem::RequireForJs(const v8::Arguments& args) {
+ v8::HandleScope handle_scope;
+ v8::Handle<v8::String> module_name = args[0]->ToString();
+ return handle_scope.Close(RequireForJsInner(module_name));
+}
+
+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> modules(v8::Handle<v8::Object>::Cast(
+ global->GetHiddenValue(v8::String::New(kModulesField))));
+ v8::Handle<v8::Value> exports(modules->Get(module_name));
+ if (!exports->IsUndefined())
+ return handle_scope.Close(exports);
+
+ v8::Handle<v8::Value> source(GetSource(module_name));
+ if (source->IsUndefined())
+ return handle_scope.Close(v8::Undefined());
+ v8::Handle<v8::String> wrapped_source(WrapSource(
+ v8::Handle<v8::String>::Cast(source)));
+ v8::Handle<v8::Function> func =
+ v8::Handle<v8::Function>::Cast(RunString(wrapped_source, module_name));
+ if (func.IsEmpty()) {
+ return ThrowException(std::string(*v8::String::AsciiValue(module_name)) +
+ ": Bad source");
+ }
+
+ exports = v8::Object::New();
+ v8::Handle<v8::Object> natives(NewInstance());
+ v8::Handle<v8::Value> args[] = {
+ natives->Get(v8::String::NewSymbol("require")),
+ natives->Get(v8::String::NewSymbol("requireNative")),
+ exports,
+ };
+ {
+ WebKit::WebScopedMicrotaskSuppression suppression;
+ v8::TryCatch try_catch;
+ try_catch.SetCaptureMessage(true);
+ func->Call(global, 3, args);
+ if (try_catch.HasCaught()) {
+ DumpException(try_catch);
+ return v8::Undefined();
+ }
+ }
+ modules->Set(module_name, exports);
+ return handle_scope.Close(exports);
+}
+
+void ModuleSystem::RegisterNativeHandler(const std::string& name,
+ scoped_ptr<NativeHandler> native_handler) {
+ native_handler_map_[name] =
+ linked_ptr<NativeHandler>(native_handler.release());
+}
+
+void ModuleSystem::OverrideNativeHandler(const std::string& name) {
+ overridden_native_handlers_.insert(name);
+}
+
+void ModuleSystem::RunString(const std::string& code, const std::string& name) {
+ v8::HandleScope handle_scope;
+ RunString(v8::String::New(code.c_str()), v8::String::New(name.c_str()));
+}
+
+// static
+v8::Handle<v8::Value> ModuleSystem::LazyFieldGetter(
+ v8::Local<v8::String> property, const v8::AccessorInfo& info) {
+ 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());
+ v8::Handle<v8::Value> module_system_value =
+ global->GetHiddenValue(v8::String::New(kModuleSystem));
+ if (module_system_value->IsUndefined()) {
+ // ModuleSystem has been deleted.
+ return v8::Undefined();
+ }
+ 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()));
+ }
+ 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));
+}
+
+void ModuleSystem::SetLazyField(v8::Handle<v8::Object> object,
+ const std::string& field,
+ const std::string& module_name,
+ const std::string& module_field) {
+ 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,
+ NULL,
+ parameters);
+}
+
+v8::Handle<v8::Value> ModuleSystem::RunString(v8::Handle<v8::String> code,
+ v8::Handle<v8::String> name) {
+ v8::HandleScope handle_scope;
+ WebKit::WebScopedMicrotaskSuppression suppression;
+ v8::Handle<v8::Value> result;
+ v8::TryCatch try_catch;
+ try_catch.SetCaptureMessage(true);
+ v8::Handle<v8::Script> script(v8::Script::New(code, name));
+ if (try_catch.HasCaught()) {
+ DumpException(try_catch);
+ return handle_scope.Close(result);
+ }
+
+ result = script->Run();
+ if (try_catch.HasCaught())
+ DumpException(try_catch);
+
+ return handle_scope.Close(result);
+}
+
+v8::Handle<v8::Value> ModuleSystem::GetSource(
+ v8::Handle<v8::String> source_name) {
+ v8::HandleScope handle_scope;
+ std::string module_name = *v8::String::AsciiValue(source_name);
+ if (!source_map_->Contains(module_name))
+ return v8::Undefined();
+ return handle_scope.Close(source_map_->GetSource(module_name));
+}
+
+v8::Handle<v8::Value> ModuleSystem::GetNative(const v8::Arguments& args) {
+ CHECK_EQ(1, args.Length());
+ 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);
+ NativeHandlerMap::iterator i = native_handler_map_.find(native_name);
+ if (i == native_handler_map_.end())
+ return v8::Undefined();
+ return i->second->NewInstance();
+}
+
+v8::Handle<v8::String> ModuleSystem::WrapSource(v8::Handle<v8::String> source) {
+ v8::HandleScope handle_scope;
+ v8::Handle<v8::String> left =
+ v8::String::New("(function(require, requireNative, exports) {");
+ v8::Handle<v8::String> right = v8::String::New("\n})");
+ return handle_scope.Close(
+ v8::String::Concat(left, v8::String::Concat(source, right)));
+}
+
+v8::Handle<v8::Value> ModuleSystem::ThrowException(const std::string& message) {
+ return v8::ThrowException(v8::String::New(message.c_str()));
+}
+
+} // extensions