Index: chrome/renderer/extensions/extension_dispatcher.cc |
diff --git a/chrome/renderer/extensions/extension_dispatcher.cc b/chrome/renderer/extensions/extension_dispatcher.cc |
index 88ba39b0dfaf6e1455252b19273b875896d5bf31..5bbadbbe601ef8fc12c439fd2c471a2cb4278ace 100644 |
--- a/chrome/renderer/extensions/extension_dispatcher.cc |
+++ b/chrome/renderer/extensions/extension_dispatcher.cc |
@@ -34,11 +34,103 @@ |
#include "ui/base/resource/resource_bundle.h" |
#include "v8/include/v8.h" |
+#include <iostream> |
+using namespace std; |
+ |
namespace { |
+ |
static const int64 kInitialExtensionIdleHandlerDelayMs = 5*1000; |
static const int64 kMaxExtensionIdleHandlerDelayMs = 5*60*1000; |
+ |
+v8::Local<v8::Value> Run(v8::Handle<v8::String> code) { |
+ return v8::Script::New(code)->Run(); |
+} |
+ |
+const char* GetResource(int resourceId) { |
+ const ResourceBundle& resource_bundle = ResourceBundle::GetSharedInstance(); |
+ return resource_bundle.GetRawDataResource(resourceId).as_string().c_str(); |
+} |
+ |
+v8::Local<v8::Value> Run(int resourceId) { |
+ return Run(v8::String::New(GetResource(resourceId))); |
} |
+v8::Handle<v8::Value> Execute(const v8::Arguments& args) { |
+ v8::Handle<v8::String> code = v8::Handle<v8::String>::Cast(args[0]); |
+ return Run(code); |
+} |
+ |
+v8::Handle<v8::Value> GetSource(const v8::Arguments& args) { |
+ static std::map<std::string, std::string>* js_source_map = NULL; |
+ if (js_source_map == NULL) { |
+ js_source_map = new std::map<std::string, std::string>(); |
+ |
+ (*js_source_map)["app"] = GetResource(IDR_APP_BINDINGS_JS); |
+ (*js_source_map)["webstore"] = GetResource(IDR_WEBSTORE_BINDINGS_JS); |
+ |
+ (*js_source_map)["json_schema"] = GetResource(IDR_JSON_SCHEMA_JS); |
+ (*js_source_map)["event_bindings"] = GetResource(IDR_EVENT_BINDINGS_JS); |
+ (*js_source_map)["miscellaneous_bindings"] = |
+ GetResource(IDR_MISCELLANEOUS_BINDINGS_JS); |
+ (*js_source_map)["schema_generated_bindings"] = |
+ GetResource(IDR_SCHEMA_GENERATED_BINDINGS_JS); |
+ (*js_source_map)["apitest"] = |
+ GetResource(IDR_EXTENSION_APITEST_JS); |
+ |
+ // Custom hooks. |
+ (*js_source_map)["custom/browserAction"] = |
+ GetResource(IDR_BROWSER_ACTION_CUSTOM_BINDINGS_JS); |
+ (*js_source_map)["custom/chromePrivate"] = |
+ GetResource(IDR_CHROME_PRIVATE_CUSTOM_BINDINGS_JS); |
+ (*js_source_map)["custom/contentSettings"] = |
+ GetResource(IDR_CONTENT_SETTINGS_CUSTOM_BINDINGS_JS); |
+ (*js_source_map)["custom/contextMenus"] = |
+ GetResource(IDR_CONTEXT_MENUS_CUSTOM_BINDINGS_JS); |
+ (*js_source_map)["custom/devtools"] = |
+ GetResource(IDR_DEVTOOLS_CUSTOM_BINDINGS_JS); |
+ (*js_source_map)["custom/extension"] = |
+ GetResource(IDR_EXTENSION_CUSTOM_BINDINGS_JS); |
+ (*js_source_map)["custom/fileBrowserHandler"] = |
+ GetResource(IDR_FILE_BROWSER_HANDLER_CUSTOM_BINDINGS_JS); |
+ (*js_source_map)["custom/fileBrowserPrivateHandler"] = |
+ GetResource(IDR_FILE_BROWSER_PRIVATE_CUSTOM_BINDINGS_JS); |
+ (*js_source_map)["custom/input.ime"] = |
+ GetResource(IDR_INPUT_IME_CUSTOM_BINDINGS_JS); |
+ (*js_source_map)["custom/omnibox"] = |
+ GetResource(IDR_OMNIBOX_CUSTOM_BINDINGS_JS); |
+ (*js_source_map)["custom/pageAction"] = |
+ GetResource(IDR_PAGE_ACTION_CUSTOM_BINDINGS_JS); |
+ (*js_source_map)["custom/pageActions"] = |
+ GetResource(IDR_PAGE_ACTIONS_CUSTOM_BINDINGS_JS); |
+ (*js_source_map)["custom/pageCapture"] = |
+ GetResource(IDR_PAGE_CAPTURE_CUSTOM_BINDINGS_JS); |
+ (*js_source_map)["custom/socket"] = |
+ GetResource(IDR_EXPERIMENTAL_SOCKET_CUSTOM_BINDINGS_JS); |
+ (*js_source_map)["custom/storage"] = |
+ GetResource(IDR_STORAGE_CUSTOM_BINDINGS_JS); |
+ (*js_source_map)["custom/tabs"] = |
+ GetResource(IDR_TABS_CUSTOM_BINDINGS_JS); |
+ (*js_source_map)["custom/tts"] = |
+ GetResource(IDR_TTS_CUSTOM_BINDINGS_JS); |
+ (*js_source_map)["custom/ttsEngine"] = |
+ GetResource(IDR_TTS_ENGINE_CUSTOM_BINDINGS_JS); |
+ (*js_source_map)["custom/types"] = |
+ GetResource(IDR_TYPES_CUSTOM_BINDINGS_JS); |
+ (*js_source_map)["custom/webRequest"] = |
+ GetResource(IDR_WEB_REQUEST_CUSTOM_BINDINGS_JS); |
+ (*js_source_map)["custom/windows"] = |
+ GetResource(IDR_WINDOWS_CUSTOM_BINDINGS_JS); |
+ } |
+ std::string module_name = *v8::String::Utf8Value(args[0]->ToString()); |
+ if (js_source_map->count(module_name) == 0) { |
+ return v8::Undefined(); |
+ } |
+ v8::HandleScope scope; |
+ return scope.Close(v8::String::New((*js_source_map)[module_name].c_str())); |
+} |
+ |
+} // namespace |
+ |
using namespace extensions; |
using WebKit::WebDataSource; |
@@ -102,25 +194,6 @@ void ExtensionDispatcher::WebKitInitialized() { |
RenderThread::Get(), &RenderThread::IdleHandler); |
} |
- RegisterExtension(new AppBindings(this), false); |
- RegisterExtension(ChromeWebstoreExtension::Get(), false); |
- |
- // Add v8 extensions related to chrome extensions. |
- RegisterExtension(new ChromeV8Extension( |
- "extensions/json_schema.js", IDR_JSON_SCHEMA_JS, NULL), true); |
- RegisterExtension(EventBindings::Get(this), true); |
- RegisterExtension(MiscellaneousBindings::Get(this), true); |
- RegisterExtension(SchemaGeneratedBindings::Get(this), true); |
- RegisterExtension(new ChromeV8Extension( |
- "extensions/apitest.js", IDR_EXTENSION_APITEST_JS, NULL), true); |
- |
- std::vector<v8::Extension*> custom_bindings = |
- custom_bindings_util::GetAll(this); |
- for (std::vector<v8::Extension*>::iterator it = custom_bindings.begin(); |
- it != custom_bindings.end(); ++it) { |
- RegisterExtension(*it, true); |
- } |
- |
// Initialize host permissions for any extensions that were activated before |
// WebKit was initialized. |
for (std::set<std::string>::iterator iter = active_extension_ids_.begin(); |
@@ -277,18 +350,17 @@ bool ExtensionDispatcher::AllowScriptExtension( |
const std::string& v8_extension_name, |
int extension_group, |
int world_id) { |
- // NULL in unit tests. |
- if (!RenderThread::Get()) |
- return true; |
- |
- // If we don't know about it, it was added by WebCore, so we should allow it. |
- if (!RenderThread::Get()->IsRegisteredExtension(v8_extension_name)) |
- return true; |
- |
- // If the V8 extension is not restricted, allow it to run anywhere. |
- if (!restricted_v8_extensions_.count(v8_extension_name)) |
- return true; |
+ // We haven't registered any extensions, so this must have been added by |
+ // WebCore. |
+ // TODO(koz): Have WebCore not ask us and delete this function. |
+ return true; |
+} |
+bool ExtensionDispatcher::AllowAPI( |
+ WebFrame* frame, |
+ const std::string& module_name, |
+ int extension_group, |
+ int world_id) { |
// Extension-only bindings should be restricted to content scripts and |
// extension-blessed URLs. |
if (extension_group == EXTENSION_GROUP_CONTENT_SCRIPTS || |
@@ -297,50 +369,162 @@ bool ExtensionDispatcher::AllowScriptExtension( |
UserScriptSlave::GetDataSourceURLForFrame(frame)))) { |
// If the extension is a custom API binding, only allow if the extension |
// has permission to use the API. |
- std::string custom_binding_api_name = |
- custom_bindings_util::GetAPIName(v8_extension_name); |
- if (!custom_binding_api_name.empty()) { |
- std::string extension_id = GetExtensionID(frame, world_id); |
- const Extension* extension = extensions_.GetByID(extension_id); |
- if (!extension) { |
- // This can happen when a resource is blocked due to CSP; a valid |
- // chrome-extension:// URL is navigated to, so it passes the initial |
- // checks, but the URL gets changed to "chrome-extension://invalid" |
- // afterwards (see chrome_content_renderer_client.cc). An extension |
- // page still gets loaded, just for the extension with ID "invalid", |
- // which of course isn't found so GetById extension will be NULL. |
- // |
- // Reference: http://crbug.com/111614. |
- CHECK_EQ("invalid", extension_id); |
- return false; |
- } |
- return custom_bindings_util::AllowAPIInjection( |
- custom_binding_api_name, *extension); |
+ std::string extension_id = GetExtensionID(frame, world_id); |
+ const Extension* extension = extensions_.GetByID(extension_id); |
+ if (!extension) { |
+ // This can happen when a resource is blocked due to CSP; a valid |
+ // chrome-extension:// URL is navigated to, so it passes the initial |
+ // checks, but the URL gets changed to "chrome-extension://invalid" |
+ // afterwards (see chrome_content_renderer_client.cc). An extension |
+ // page still gets loaded, just for the extension with ID "invalid", |
+ // which of course isn't found so GetById extension will be NULL. |
+ // |
+ // Reference: http://crbug.com/111614. |
+ CHECK_EQ("invalid", extension_id); |
+ return false; |
} |
- |
- return true; |
+ return custom_bindings_util::AllowAPIInjection( |
+ module_name, *extension); |
} |
return false; |
} |
+class GlobalFunctions : public ChromeV8Extension { |
+ public: |
+ GlobalFunctions(ExtensionDispatcher* dispatcher, |
+ WebFrame* frame, |
+ int extension_group, |
+ int world_id) |
+ : ChromeV8Extension("blah", 0, dispatcher), |
+ frame_(frame), |
+ extension_group_(extension_group), |
+ world_id_(world_id) {} |
+ |
+ // ChromeV8Extension |
+ virtual void SetNativeFunctions(v8::Handle<v8::Object> object) OVERRIDE { |
+ RouteFunctionHere("AllowAPI", object); |
+ RouteFunctionToStatic("GetChromeHidden", GetChromeHidden, object); |
+ RouteFunctionToStatic("Print", Print, object); |
+ } |
+ |
+ v8::Handle<v8::Value> HandleNativeFunction(const std::string& name, |
+ const v8::Arguments& args) OVERRIDE { |
+ CHECK(!name.empty()); |
+ if (name == "AllowAPI") { |
+ return AllowAPI(args); |
+ } else { |
+ CHECK(false) << "Unknown native function: '" << name << "'" << endl; |
+ } |
+ return v8::Undefined(); |
+ } |
+ |
+ v8::Handle<v8::Value> AllowAPI(const v8::Arguments& args) { |
+ std::string api_name = *v8::String::AsciiValue(args[0]); |
+ return v8::Boolean::New(extension_dispatcher_->AllowAPI(frame_, |
+ api_name, extension_group_, world_id_)); |
+ } |
+ |
+ static v8::Handle<v8::Value> GetChromeHidden(const v8::Arguments& args) { |
+ return ChromeV8Context::GetOrCreateChromeHidden(v8::Context::GetCurrent()); |
+ } |
+ |
+ static v8::Handle<v8::Value> Print(const v8::Arguments& args) { |
+ if (args.Length() < 1) |
+ return v8::Undefined(); |
+ |
+ std::vector<std::string> components; |
+ for (int i = 0; i < args.Length(); ++i) |
+ components.push_back(*v8::String::Utf8Value(args[i]->ToString())); |
+ |
+ LOG(ERROR) << JoinString(components, ','); |
+ return v8::Undefined(); |
+ } |
+ |
+ private: |
+ WebFrame* frame_; |
+ int extension_group_; |
+ int world_id_; |
+}; |
+ |
+v8::Handle<v8::Object> ExtensionDispatcher::BuildBrowserObject( |
+ ChromeV8Context* context) { |
+ v8::Persistent<v8::Object> browser = |
+ v8::Persistent<v8::Object>::New(v8::Object::New()); |
+ |
+ browser->Set(v8::String::NewSymbol("Execute"), |
+ v8::FunctionTemplate::New(Execute)->GetFunction()); |
+ browser->Set(v8::String::NewSymbol("GetSource"), |
+ v8::FunctionTemplate::New(GetSource)->GetFunction()); |
+ browser->Set(v8::String::NewSymbol("natives"), context->natives()); |
+ RegisterExtensions(context); |
+ return browser; |
+} |
+ |
+void ExtensionDispatcher::RegisterExtensions(ChromeV8Context* context) { |
+ context->RegisterExtension(new AppBindings(this, context)); |
+ context->RegisterExtension(ChromeWebstoreExtension::Get(this)); |
+ context->RegisterExtension(EventBindings::Get(this)); |
+ context->RegisterExtension(MiscellaneousBindings::Get(this)); |
+ context->RegisterExtension(SchemaGeneratedBindings::Get(this)); |
+ |
+ std::vector<ChromeV8Extension*> extensions = |
+ custom_bindings_util::GetAll(this); |
+ for (std::vector<ChromeV8Extension*>::iterator i = extensions.begin(); |
+ i != extensions.end(); i++) { |
+ context->RegisterExtension(*i); |
+ } |
+} |
+ |
void ExtensionDispatcher::DidCreateScriptContext( |
- WebFrame* frame, v8::Handle<v8::Context> v8_context, int world_id) { |
+ WebFrame* frame, v8::Handle<v8::Context> v8_context, int extension_group, |
+ int world_id) { |
+ ExtensionURLInfo thing( |
+ frame->document().securityOrigin(), |
+ UserScriptSlave::GetDataSourceURLForFrame(frame)); |
+ std::string extension_id = GetExtensionID(frame, world_id); |
ChromeV8Context* context = |
- new ChromeV8Context(v8_context, frame, GetExtensionID(frame, world_id)); |
+ new ChromeV8Context(v8_context, frame, extension_id); |
v8_context_set_.Add(context); |
+ SetupAPIBindings(context, frame, extension_group, world_id); |
+ |
+ VLOG(1) << "Num tracked contexts: " << v8_context_set_.size(); |
+} |
+ |
+void ExtensionDispatcher::SetupAPIBindings(ChromeV8Context* context, |
+ WebFrame* frame, |
+ int extension_group, |
+ int world_id) { |
+ context->RegisterExtension(new GlobalFunctions(this, context->web_frame(), |
+ extension_group, world_id)); |
const Extension* extension = extensions_.GetByID(context->extension_id()); |
int manifest_version = 1; |
if (extension) |
manifest_version = extension->manifest_version(); |
- context->DispatchOnLoadEvent( |
- is_extension_process_, |
- ChromeRenderProcessObserver::is_incognito_process(), |
- manifest_version); |
+ Run(v8::String::New("var chrome = {};")); |
+ v8::Local<v8::Value> result = Run(IDR_BOOTSTRAP_JS); |
+ v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(result); |
- VLOG(1) << "Num tracked contexts: " << v8_context_set_.size(); |
+ v8::Handle<v8::Object> browser = BuildBrowserObject(context); |
+ |
+ bool allow_extension_apis = |
+ extension_group == EXTENSION_GROUP_CONTENT_SCRIPTS || |
+ extensions_.ExtensionBindingsAllowed(ExtensionURLInfo( |
+ frame->document().securityOrigin(), |
+ UserScriptSlave::GetDataSourceURLForFrame(frame))); |
+ |
+ v8::Handle<v8::Value> argv[] = { |
+ v8::Local<v8::Value>::New(browser), |
+ v8::String::New(context->extension_id().c_str()), |
+ v8::Boolean::New(is_extension_process_), |
+ v8::Boolean::New(ChromeRenderProcessObserver::is_incognito_process()), |
+ v8::Integer::New(manifest_version), |
+ v8::Boolean::New(allow_extension_apis) |
+ }; |
+ |
+ f->Call(v8::Context::GetCurrent()->Global(), arraysize(argv), argv); |
} |
std::string ExtensionDispatcher::GetExtensionID(WebFrame* frame, int world_id) { |