diff options
22 files changed, 440 insertions, 16 deletions
@@ -19,7 +19,7 @@ deps = { "http://googletest.googlecode.com/svn/trunk@214", "src/third_party/WebKit": - "/trunk/deps/third_party/WebKit@12324", + "/trunk/deps/third_party/WebKit@12346", "src/third_party/icu38": "/trunk/deps/third_party/icu38@11496", diff --git a/chrome/browser/browser.scons b/chrome/browser/browser.scons index 69a8ba7..fec32ef 100644 --- a/chrome/browser/browser.scons +++ b/chrome/browser/browser.scons @@ -506,6 +506,8 @@ input_files = ChromeFileList([ MSVSFilter('Extensions', [ 'extensions/extension.cc', 'extensions/extension.h', + 'extensions/extension_api_handler.cc', + 'extensions/extension_api_handler.h', 'extensions/extension_view.cc', 'extensions/extension_view.h', 'extensions/extension_error_reporter.cc', diff --git a/chrome/browser/browser.vcproj b/chrome/browser/browser.vcproj index 842f44c..ff76afb 100644 --- a/chrome/browser/browser.vcproj +++ b/chrome/browser/browser.vcproj @@ -1906,6 +1906,14 @@ > </File> <File + RelativePath=".\extensions\extension_api_handler.cc" + > + </File> + <File + RelativePath=".\extensions\extension_api_handler.h" + > + </File> + <File RelativePath=".\extensions\extension_error_reporter.cc" > </File> diff --git a/chrome/browser/extensions/extension_api_handler.cc b/chrome/browser/extensions/extension_api_handler.cc new file mode 100644 index 0000000..702917b --- /dev/null +++ b/chrome/browser/extensions/extension_api_handler.cc @@ -0,0 +1,45 @@ +// Copyright (c) 2009 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/browser/extensions/extension_api_handler.h" + +#include "base/json_reader.h" +#include "base/json_writer.h" +#include "base/values.h" +#include "chrome/browser/browser.h" +#include "chrome/browser/browser_list.h" +#include "chrome/browser/renderer_host/render_view_host.h" + +ExtensionAPIHandler::ExtensionAPIHandler(RenderViewHost* render_view_host) + : render_view_host_(render_view_host) {} + +void ExtensionAPIHandler::HandleRequest(const std::string& name, + const std::string& args, + int callback_id) { + scoped_ptr<Value> value; + if (!args.empty()) { + value.reset(JSONReader::Read(args, false)); + DCHECK(value.get()); + } + + // TODO(aa): This will probably dispatch to per-module specialized classes. + // Consider refactoring similar work in dom_ui to reuse. + if (name == "CreateTab") { + Browser* browser = BrowserList::GetLastActive(); + if (browser) { + DCHECK(value->IsType(Value::TYPE_DICTIONARY)); + std::string url; + static_cast<DictionaryValue*>(value.get())->GetString(L"url", &url); + browser->AddTabWithURL(GURL(url), GURL(), PageTransition::TYPED, true, + NULL); + + static int response_count = 0; + scoped_ptr<Value> response(Value::CreateIntegerValue(response_count++)); + std::string json; + JSONWriter::Write(response.get(), false, &json); + + render_view_host_->SendExtensionResponse(callback_id, json); + } + } +} diff --git a/chrome/browser/extensions/extension_api_handler.h b/chrome/browser/extensions/extension_api_handler.h new file mode 100644 index 0000000..3cd6efd --- /dev/null +++ b/chrome/browser/extensions/extension_api_handler.h @@ -0,0 +1,29 @@ +// Copyright (c) 2009 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. + +#ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_APIS_H_ +#define CHROME_BROWSER_EXTENSIONS_EXTENSION_APIS_H_ + +#include <string> + +class RenderViewHost; + +// ExtensionAPIHandler is the top-level entry point for extension callbacks +// in the browser process. It lives on the UI thread. +class ExtensionAPIHandler { + public: + ExtensionAPIHandler(RenderViewHost* render_view_host); + + // Handle a request to perform some synchronous API. + // TODO(aa): args should be a Value object. + void HandleRequest(const std::string& name, const std::string& args, + int callback_id); + + private: + // TODO(aa): Once there can be APIs that are asynchronous wrt the browser's UI + // thread, we may have to have to do something about this raw pointer. + RenderViewHost* render_view_host_; +}; + +#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_APIS_H_ diff --git a/chrome/browser/renderer_host/render_view_host.cc b/chrome/browser/renderer_host/render_view_host.cc index cb6110b..a02f15c 100644 --- a/chrome/browser/renderer_host/render_view_host.cc +++ b/chrome/browser/renderer_host/render_view_host.cc @@ -98,7 +98,8 @@ RenderViewHost::RenderViewHost(SiteInstance* instance, run_modal_reply_msg_(NULL), has_unload_listener_(false), is_waiting_for_unload_ack_(false), - are_javascript_messages_suppressed_(false) { + are_javascript_messages_suppressed_(false), + ALLOW_THIS_IN_INITIALIZER_LIST(extension_api_handler_(this)) { DCHECK(instance_); DCHECK(delegate_); if (modal_dialog_event == NULL) @@ -761,6 +762,7 @@ void RenderViewHost::OnMessageReceived(const IPC::Message& msg) { IPC_MESSAGE_HANDLER(ViewHostMsg_RemoveAutofillEntry, OnRemoveAutofillEntry) IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateFeedList, OnMsgUpdateFeedList) + IPC_MESSAGE_HANDLER(ViewHostMsg_ExtensionRequest, OnExtensionRequest) // Have the super handle all other messages. IPC_MESSAGE_UNHANDLED(RenderWidgetHost::OnMessageReceived(msg)) IPC_END_MESSAGE_MAP_EX() @@ -1344,3 +1346,16 @@ void RenderViewHost::ForwardMessageFromExternalHost(const std::string& message, Send(new ViewMsg_HandleMessageFromExternalHost(routing_id(), message, origin, target)); } + +void RenderViewHost::OnExtensionRequest(const std::string& name, + const std::string& args, + int callback_id) { + // TODO(aa): Here is where we can check that this renderer was supposed to be + // able to call extension APIs. + extension_api_handler_.HandleRequest(name, args, callback_id); +} + +void RenderViewHost::SendExtensionResponse(int callback_id, + const std::string& response) { + Send(new ViewMsg_ExtensionResponse(routing_id(), callback_id, response)); +} diff --git a/chrome/browser/renderer_host/render_view_host.h b/chrome/browser/renderer_host/render_view_host.h index fdaf769..1e61e7e 100644 --- a/chrome/browser/renderer_host/render_view_host.h +++ b/chrome/browser/renderer_host/render_view_host.h @@ -9,6 +9,7 @@ #include <vector> #include "base/scoped_ptr.h" +#include "chrome/browser/extensions/extension_api_handler.h" #include "chrome/browser/renderer_host/render_view_host_delegate.h" #include "chrome/browser/renderer_host/render_widget_host.h" #include "chrome/common/modal_dialog_event.h" @@ -429,6 +430,8 @@ class RenderViewHost : public RenderWidgetHost { // Creates a new RenderWidget with the given route id. void CreateNewWidget(int route_id, bool activatable); + void SendExtensionResponse(int callback_id, const std::string& response); + protected: // RenderWidgetHost protected overrides. virtual void UnhandledKeyboardEvent(const NativeWebKeyboardEvent& event); @@ -553,6 +556,9 @@ class RenderViewHost : public RenderWidgetHost { void OnRemoveAutofillEntry(const std::wstring& field_name, const std::wstring& value); + void OnExtensionRequest(const std::string& name, const std::string& args, + int callback_id); + // Helper function to send a navigation message. If a cross-site request is // in progress, we may be suspended while waiting for the onbeforeunload // handler, so this function might buffer the message rather than sending it. @@ -631,6 +637,10 @@ class RenderViewHost : public RenderWidgetHost { bool are_javascript_messages_suppressed_; + // Handler for extension API requests. + // Handles processing IPC messages related to the extension system. + ExtensionAPIHandler extension_api_handler_; + DISALLOW_EVIL_CONSTRUCTORS(RenderViewHost); }; diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp index 0475f20..d62f58e 100644 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp @@ -612,6 +612,8 @@ 'browser/encoding_menu_controller_delegate.h', 'browser/extensions/extension.cc', 'browser/extensions/extension.h', + 'browser/extensions/extension_api_handler.cc', + 'browser/extensions/extension_api_handler.h', 'browser/extensions/extension_error_reporter.cc', 'browser/extensions/extension_error_reporter.h', 'browser/extensions/extension_message_service.cc', @@ -1357,6 +1359,8 @@ # All .cc, .h, and .mm files under renderer except tests and mocks. 'renderer/automation/dom_automation_controller.cc', 'renderer/automation/dom_automation_controller.h', + 'renderer/extensions/extension_process_bindings.cc', + 'renderer/extensions/extension_process_bindings.h', 'renderer/extensions/renderer_extension_bindings.cc', 'renderer/extensions/renderer_extension_bindings.h', 'renderer/media/audio_renderer_impl.cc', diff --git a/chrome/common/render_messages_internal.h b/chrome/common/render_messages_internal.h index 577bde2..150bdbe 100644 --- a/chrome/common/render_messages_internal.h +++ b/chrome/common/render_messages_internal.h @@ -512,8 +512,13 @@ IPC_BEGIN_MESSAGES(View) // started. IPC_MESSAGE_ROUTED0(ViewMsg_MoveOrResizeStarted) - // Send a message to an extension process. channel_id is a handle that can - // be used for sending a response. + // The browser sends this message when an extension API has a response. + IPC_MESSAGE_ROUTED2(ViewMsg_ExtensionResponse, + int /* callback id */, + std::string /* response */) + + // Relay a message sent from a renderer to an extension process. channel_id + // is a handle that can be used for sending a response. IPC_MESSAGE_ROUTED2(ViewMsg_HandleExtensionMessage, std::string /* message */, int /* channel_id */) @@ -1197,6 +1202,13 @@ IPC_BEGIN_MESSAGES(ViewHost) double /* left_channel */, double /* right_channel */) + // A renderer sends this message when an extension process starts an API + // request. If callback id is -1, no response will be sent. + IPC_MESSAGE_ROUTED3(ViewHostMsg_ExtensionRequest, + std::string /* name */, + std::string /* argument */, + int /* callback id */) + #if defined(OS_MACOSX) // On OSX, we cannot allocated shared memory from within the sandbox, so // this call exists for the renderer to ask the browser to allocate memory diff --git a/chrome/renderer/extensions/extension_process_bindings.cc b/chrome/renderer/extensions/extension_process_bindings.cc new file mode 100644 index 0000000..63c0562 --- /dev/null +++ b/chrome/renderer/extensions/extension_process_bindings.cc @@ -0,0 +1,105 @@ +// Copyright (c) 2009 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/extension_process_bindings.h" + +#include "chrome/common/render_messages.h" +#include "chrome/common/resource_bundle.h" +#include "chrome/renderer/render_view.h" +#include "grit/renderer_resources.h" +#include "webkit/glue/webframe.h" + +namespace extensions_v8 { + +const char kExtensionProcessExtensionName[] = "v8/ExtensionProcess"; + +class ExtensionProcessBindingsWrapper : public v8::Extension { + public: + ExtensionProcessBindingsWrapper() + : v8::Extension(kExtensionProcessExtensionName, GetSource()) {} + + virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( + v8::Handle<v8::String> name) { + if (name->Equals(v8::String::New("GetNextCallbackId"))) + return v8::FunctionTemplate::New(GetNextCallbackId); + else if (name->Equals(v8::String::New("CreateTab"))) + return v8::FunctionTemplate::New(StartRequest, name); + + return v8::Handle<v8::FunctionTemplate>(); + } + + private: + static const char* GetSource() { + // This is weird. The v8::Extension constructor expects a null-terminated + // string which it doesn't own (all current uses are constant). The value + // returned by GetDataResource is *not* null-terminated, and simply + // converting it to a string, then using that string's c_str() in this + // class's constructor doesn't work because the resulting string is stack- + // allocated. + if (!source_) + source_ = new std::string( + ResourceBundle::GetSharedInstance().GetRawDataResource( + IDR_EXTENSION_PROCESS_BINDINGS_JS).as_string()); + + return source_->c_str(); + } + + static v8::Handle<v8::Value> GetNextCallbackId(const v8::Arguments& args) { + static int next_callback_id = 0; + return v8::Integer::New(next_callback_id++); + } + + static v8::Handle<v8::Value> StartRequest(const v8::Arguments& args) { + WebFrame* webframe = WebFrame::RetrieveActiveFrame(); + DCHECK(webframe) << "There should be an active frame since we just got " + "an API called."; + if (!webframe) return v8::Undefined(); + + WebView* webview = webframe->GetView(); + if (!webview) return v8::Undefined(); // can happen during closing + + RenderView* renderview = static_cast<RenderView*>(webview->GetDelegate()); + DCHECK(renderview) << "Encountered a WebView without a WebViewDelegate"; + if (!renderview) return v8::Undefined(); + + if (args.Length() != 2 || !args[0]->IsString() || !args[1]->IsInt32()) + return v8::Undefined(); + + int callback_id = args[1]->Int32Value(); + renderview->SendExtensionRequest( + std::string(*v8::String::AsciiValue(args.Data())), + std::string(*v8::String::Utf8Value(args[0])), + callback_id, webframe); + + return v8::Undefined(); + } + + static std::string* source_; +}; + +std::string* ExtensionProcessBindingsWrapper::source_; + + +// static +v8::Extension* ExtensionProcessBindings::Get() { + return new ExtensionProcessBindingsWrapper(); +} + +// static +void ExtensionProcessBindings::ExecuteCallbackInFrame( + WebFrame* frame, int callback_id, const std::string& response) { + std::string code = "chromium._dispatchCallback("; + code += IntToString(callback_id); + code += ", '"; + + size_t offset = code.length(); + code += response; + ReplaceSubstringsAfterOffset(&code, offset, "\\", "\\\\"); + ReplaceSubstringsAfterOffset(&code, offset, "'", "\\'"); + code += "')"; + + frame->ExecuteScript(webkit_glue::WebScriptSource(code)); +} + +} // namespace extensions_v8 diff --git a/chrome/renderer/extensions/extension_process_bindings.h b/chrome/renderer/extensions/extension_process_bindings.h new file mode 100644 index 0000000..cbdd0bd --- /dev/null +++ b/chrome/renderer/extensions/extension_process_bindings.h @@ -0,0 +1,27 @@ +// Copyright (c) 2009 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.
+
+// Exposes extension APIs into the extension process.
+
+#ifndef CHROME_RENDERER_EXTENSIONS_EXTENSION_PROCESS_BINDINGS_H_
+#define CHROME_RENDERER_EXTENSIONS_EXTENSION_PROCESS_BINDINGS_H_
+
+#include <string>
+
+#include "v8/include/v8.h"
+
+class WebFrame;
+
+namespace extensions_v8 {
+
+class ExtensionProcessBindings {
+ public:
+ static v8::Extension* Get();
+ static void ExecuteCallbackInFrame(WebFrame* frame, int callback_id,
+ const std::string& response);
+};
+
+} // namespace extensions_v8
+
+#endif // CHROME_RENDERER_EXTENSIONS_EXTENSION_PROCESS_BINDINGS_H_
diff --git a/chrome/renderer/render_thread.cc b/chrome/renderer/render_thread.cc index 96e239c..165af40 100644 --- a/chrome/renderer/render_thread.cc +++ b/chrome/renderer/render_thread.cc @@ -23,6 +23,7 @@ #include "chrome/plugin/plugin_channel_base.h" #include "webkit/glue/weburlrequest.h" #endif +#include "chrome/renderer/extensions/extension_process_bindings.h" #include "chrome/renderer/extensions/renderer_extension_bindings.h" #include "chrome/renderer/net/render_dns_master.h" #include "chrome/renderer/render_process.h" @@ -250,6 +251,9 @@ void RenderThread::EnsureWebKitInitialized() { WebKit::registerExtension(extensions_v8::IntervalExtension::Get()); WebKit::registerExtension(extensions_v8::RendererExtensionBindings::Get()); + WebKit::registerExtension(extensions_v8::ExtensionProcessBindings::Get(), + WebKit::WebString::fromUTF8(chrome::kExtensionScheme)); + const CommandLine& command_line = *CommandLine::ForCurrentProcess(); if (command_line.HasSwitch(switches::kPlaybackMode) || command_line.HasSwitch(switches::kRecordMode)) { diff --git a/chrome/renderer/render_view.cc b/chrome/renderer/render_view.cc index e6899dc..dfd2897 100644 --- a/chrome/renderer/render_view.cc +++ b/chrome/renderer/render_view.cc @@ -29,6 +29,7 @@ #include "chrome/renderer/debug_message_handler.h" #include "chrome/renderer/devtools_agent.h" #include "chrome/renderer/devtools_client.h" +#include "chrome/renderer/extensions/extension_process_bindings.h" #include "chrome/renderer/extensions/renderer_extension_bindings.h" #include "chrome/renderer/localized_error.h" #include "chrome/renderer/media/audio_renderer_impl.h" @@ -428,6 +429,7 @@ void RenderView::OnMessageReceived(const IPC::Message& message) { IPC_MESSAGE_HANDLER(ViewMsg_MoveOrResizeStarted, OnMoveOrResizeStarted) IPC_MESSAGE_HANDLER(ViewMsg_HandleExtensionMessage, OnHandleExtensionMessage) + IPC_MESSAGE_HANDLER(ViewMsg_ExtensionResponse, OnExtensionResponse) // Have the super handle all other messages. IPC_MESSAGE_UNHANDLED(RenderWidget::OnMessageReceived(message)) @@ -1450,6 +1452,25 @@ void RenderView::DidCancelClientRedirect(WebView* webview, WebFrame* frame) { } +void RenderView::WillCloseFrame(WebView* view, WebFrame* frame) { + // Remove all the pending extension callbacks for this frame. + if (pending_extension_callbacks_.IsEmpty()) + return; + + std::vector<int> orphaned_callbacks; + for (IDMap<WebFrame>::const_iterator iter = + pending_extension_callbacks_.begin(); + iter != pending_extension_callbacks_.end(); ++iter) { + if (iter->second == frame) + orphaned_callbacks.push_back(iter->first); + } + + for (std::vector<int>::const_iterator iter = orphaned_callbacks.begin(); + iter != orphaned_callbacks.end(); ++iter) { + pending_extension_callbacks_.Remove(*iter); + } +} + void RenderView::DidCompleteClientRedirect(WebView* webview, WebFrame* frame, const GURL& source) { @@ -2944,3 +2965,28 @@ void RenderView::OnHandleExtensionMessage(const std::string& message, extensions_v8::RendererExtensionBindings::HandleExtensionMessage( webview()->GetMainFrame(), message, channel_id); } + +void RenderView::SendExtensionRequest(const std::string& name, + const std::string& args, + int callback_id, + WebFrame* callback_frame) { + DCHECK(RenderThread::current()->message_loop() == MessageLoop::current()); + + if (callback_id != -1) { + DCHECK(callback_frame) << "Callback specified without frame"; + pending_extension_callbacks_.AddWithID(callback_frame, callback_id); + } + + Send(new ViewHostMsg_ExtensionRequest(routing_id_, name, args, callback_id)); +} + +void RenderView::OnExtensionResponse(int callback_id, + const std::string& response) { + WebFrame* web_frame = pending_extension_callbacks_.Lookup(callback_id); + if (!web_frame) + return; // The frame went away. + + extensions_v8::ExtensionProcessBindings::ExecuteCallbackInFrame( + web_frame, callback_id, response); + pending_extension_callbacks_.Remove(callback_id); +} diff --git a/chrome/renderer/render_view.h b/chrome/renderer/render_view.h index da75f93..83612d8 100644 --- a/chrome/renderer/render_view.h +++ b/chrome/renderer/render_view.h @@ -218,6 +218,7 @@ class RenderView : public RenderWidget, virtual void DidCompleteClientRedirect(WebView* webview, WebFrame* frame, const GURL& source); + virtual void WillCloseFrame(WebView* webview, WebFrame* frame); virtual void WillSendRequest(WebView* webview, uint32 identifier, WebRequest* request); @@ -367,6 +368,10 @@ class RenderView : public RenderWidget, void GetAudioVolume(int stream_id); void SetAudioVolume(int stream_id, double left, double right); + void SendExtensionRequest(const std::string& name, const std::string& args, + int callback_id, WebFrame* web_frame); + void OnExtensionResponse(int callback_id, const std::string& response); + protected: // RenderWidget override. virtual void OnResize(const gfx::Size& new_size, @@ -797,6 +802,9 @@ class RenderView : public RenderWidget, // A set of audio renderers registered to use IPC for audio output. IDMap<AudioRendererImpl> audio_renderers_; + // Maps pending callback IDs to their frames. + IDMap<WebFrame> pending_extension_callbacks_; + DISALLOW_COPY_AND_ASSIGN(RenderView); }; diff --git a/chrome/renderer/renderer.scons b/chrome/renderer/renderer.scons index 9cfa1585..25d309f 100644 --- a/chrome/renderer/renderer.scons +++ b/chrome/renderer/renderer.scons @@ -58,6 +58,8 @@ input_files = ChromeFileList([ 'net/render_dns_queue.h', ]), MSVSFilter('extensions', [ + 'extensions/extension_process_bindings.cc', + 'extensions/extension_process_bindings.h', 'extensions/renderer_extension_bindings.cc', 'extensions/renderer_extension_bindings.h', ]), diff --git a/chrome/renderer/renderer.vcproj b/chrome/renderer/renderer.vcproj index db4ec9c..e9e6a52 100644 --- a/chrome/renderer/renderer.vcproj +++ b/chrome/renderer/renderer.vcproj @@ -192,6 +192,14 @@ RelativePath=".\extensions\renderer_extension_bindings.h" > </File> + <File + RelativePath=".\extensions\extension_process_bindings.cc" + > + </File> + <File + RelativePath=".\extensions\extension_process_bindings.h" + > + </File> </Filter> <File RelativePath=".\about_handler.cc" diff --git a/chrome/renderer/renderer_resources.grd b/chrome/renderer/renderer_resources.grd index 6db6ffa..48bd7b4 100755 --- a/chrome/renderer/renderer_resources.grd +++ b/chrome/renderer/renderer_resources.grd @@ -13,6 +13,7 @@ <include name="IDR_INSECURE_CONTENT_STAMP" file="resources\insecure_content_stamp.png" type="BINDATA" /> <include name="IDR_ERROR_NO_DETAILS_HTML" file="resources\error_no_details.html" type="BINDATA" /> <include name="IDR_GREASEMONKEY_API_JS" file="resources\greasemonkey_api.js" type="BINDATA" /> + <include name="IDR_EXTENSION_PROCESS_BINDINGS_JS" file="resources\extension_process_bindings.js" type="BINDATA" /> </includes> </release> </grit>
\ No newline at end of file diff --git a/chrome/renderer/resources/extension_process_bindings.js b/chrome/renderer/resources/extension_process_bindings.js new file mode 100644 index 0000000..3031d85 --- /dev/null +++ b/chrome/renderer/resources/extension_process_bindings.js @@ -0,0 +1,71 @@ +var chromium; +(function() { + if (!chromium) + chromium = {}; + + // callback handling + var callbacks = []; + chromium._dispatchCallback = function(callbackId, str) { + // We shouldn't be receiving evil JSON unless the browser is owned, but just + // to be safe, we sanitize it. This regex mania was borrowed from json2, + // from json.org. + if (!/^[\],:{}\s]*$/.test(
+ str.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
+ replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
+ replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) + throw new Error("Unexpected characters in incoming JSON response."); + + // This is lame. V8 disallows direct access to eval() in extensions (see: + // v8::internal::Parser::ParseLeftHandSideExpression()). So we must use + // this supa-jank hack instead. We really need native JSON. + str = 'return ' + str; + callbacks[callbackId](new Function(str)()); + delete callbacks[callbackId]; + }; + + // Quick and dirty json serialization. + // TODO(aa): Did I mention we need native JSON? + function serialize(thing) { + switch (typeof thing) { + case 'string': + return '\"' + thing.replace('\\', '\\\\').replace('\"', '\\\"') + '\"'; + case 'boolean': + case 'number': + return String(thing); + case 'object': + if (thing === null) + return String(thing) + var items = []; + if (thing.constructor == Array) { + for (var i = 0; i < thing.length; i++) + items.push(serialize(thing[i])); + return '[' + items.join(',') + ']'; + } else { + for (var p in thing) + items.push(serialize(p) + ':' + serialize(thing[p])); + return '{' + items.join(',') + '}'; + } + default: + return ''; + } + } + + // Send an API request and optionally register a callback. + function sendRequest(request, args, callback) { + var sargs = serialize(args); + var callbackId = -1; + if (callback) { + native function GetNextCallbackId(); + callbackId = GetNextCallbackId(); + callbacks[callbackId] = callback; + } + request(sargs, callbackId); + } + + // Tabs + chromium.tabs = {}; + chromium.tabs.createTab = function(tab, callback) { + native function CreateTab(); + sendRequest(CreateTab, tab, callback); + }; +})(); diff --git a/webkit/glue/webframe.h b/webkit/glue/webframe.h index 76b50c6..8f38362 100644 --- a/webkit/glue/webframe.h +++ b/webkit/glue/webframe.h @@ -32,6 +32,8 @@ class WebFrame { public: WebFrame() {} + static WebFrame* RetrieveActiveFrame(); + // Binds a C++ class to a JavaScript property of the window object. This // should generally be used via CppBoundClass::BindToJavascript() instead of // calling it directly. diff --git a/webkit/glue/webframe_impl.cc b/webkit/glue/webframe_impl.cc index 49a6a08..818d272 100644 --- a/webkit/glue/webframe_impl.cc +++ b/webkit/glue/webframe_impl.cc @@ -321,6 +321,15 @@ class ChromePrintContext : public WebCore::PrintContext { int WebFrameImpl::live_object_count_ = 0; +// static +WebFrame* WebFrame::RetrieveActiveFrame() { + WebCore::Frame* frame = WebCore::ScriptController::retrieveActiveFrame(); + if (frame) + return WebFrameImpl::FromFrame(frame); + else + return NULL; +} + WebFrameImpl::WebFrameImpl() // Don't complain about using "this" in initializer list. MSVC_PUSH_DISABLE_WARNING(4355) diff --git a/webkit/port/bindings/v8/v8_proxy.cpp b/webkit/port/bindings/v8/v8_proxy.cpp index f36f836..91733b3 100644 --- a/webkit/port/bindings/v8/v8_proxy.cpp +++ b/webkit/port/bindings/v8/v8_proxy.cpp @@ -2346,12 +2346,15 @@ v8::Persistent<v8::Context> V8Proxy::createNewContext( // Dynamically tell v8 about our extensions now. const char** extensionNames = new const char*[m_extensions.size()]; int index = 0; - V8ExtensionList::iterator it = m_extensions.begin(); - while (it != m_extensions.end()) { - extensionNames[index++] = (*it)->name(); - ++it; + for (V8ExtensionList::iterator it = m_extensions.begin(); + it != m_extensions.end(); ++it) { + if (it->scheme.length() > 0 && + it->scheme != m_frame->document()->url().protocol()) + continue; + + extensionNames[index++] = it->extension->name(); } - v8::ExtensionConfiguration extensions(m_extensions.size(), extensionNames); + v8::ExtensionConfiguration extensions(index, extensionNames); result = v8::Context::New(&extensions, globalTemplate, global); delete [] extensionNames; extensionNames = 0; @@ -3604,9 +3607,11 @@ String V8Proxy::GetSourceName() { return ToWebCoreString(v8::Debug::Call(frame_source_name)); } -void V8Proxy::RegisterExtension(v8::Extension* extension) { +void V8Proxy::RegisterExtension(v8::Extension* extension, + const String& schemeRestriction) { v8::RegisterExtension(extension); - m_extensions.push_back(extension); + V8ExtensionInfo info = {schemeRestriction, extension}; + m_extensions.push_back(info); } } // namespace WebCore diff --git a/webkit/port/bindings/v8/v8_proxy.h b/webkit/port/bindings/v8/v8_proxy.h index 9d0336a..e403751 100644 --- a/webkit/port/bindings/v8/v8_proxy.h +++ b/webkit/port/bindings/v8/v8_proxy.h @@ -74,7 +74,6 @@ class SVGElementInstance; class V8EventListener; class V8ObjectEventListener; typedef std::list<V8EventListener*> V8EventListenerList; -typedef std::list<v8::Extension*> V8ExtensionList; // TODO(fqian): use standard logging facilities in WebCore. void log_info(Frame* frame, const String& msg, const String& url); @@ -132,7 +131,7 @@ void BatchConfigureAttributes(v8::Handle<v8::ObjectTemplate> inst, const BatchedAttribute* attrs, size_t num_attrs); -// BhatchedConstant translates into calls to Set() for setting up an object's +// BatchedConstant translates into calls to Set() for setting up an object's // constants. It sets the constant on both the FunctionTemplate |desc| and the // ObjectTemplate |proto|. PropertyAttributes is always ReadOnly. struct BatchedConstant { @@ -149,6 +148,15 @@ DOMWrapperMap<void>& GetDOMObjectMap(); const int kMaxRecursionDepth = 20; +// Information about an extension that is registered for use with V8. If scheme +// is non-empty, it contains the URL scheme the extension should be used with. +// Otherwise, the extension is used with all schemes. +struct V8ExtensionInfo { + String scheme; + v8::Extension* extension; +}; +typedef std::list<V8ExtensionInfo> V8ExtensionList; + class V8Proxy { public: // The types of javascript errors that can be thrown. @@ -452,9 +460,12 @@ class V8Proxy { return v8::Local<v8::Context>::New(m_context); } - // Register extensions before initializing the context. Once the context - // is initialized, extensions cannot be registered. - static void RegisterExtension(v8::Extension* extension); + // Registers an extension to be available on webpages with a particular scheme + // If the scheme argument is empty, the extension is available on all pages. + // Will only affect v8 contexts initialized after this call. Takes ownership + // of the v8::Extension object passed. + static void RegisterExtension(v8::Extension* extension, + const String& schemeRestriction); private: v8::Persistent<v8::Context> createNewContext(v8::Handle<v8::Object> global); |