summaryrefslogtreecommitdiffstats
path: root/chrome/renderer
diff options
context:
space:
mode:
authoraa@chromium.org <aa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-03-24 09:18:27 +0000
committeraa@chromium.org <aa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-03-24 09:18:27 +0000
commit309d7a28aa6c938f60ac7a543ab4a73827d29562 (patch)
treea806e83ff1533242c190a736519dbd31d43fb1e6 /chrome/renderer
parentcb2c560e5c5761329e614aeeaeb6ccb5dcfc1987 (diff)
downloadchromium_src-309d7a28aa6c938f60ac7a543ab4a73827d29562.zip
chromium_src-309d7a28aa6c938f60ac7a543ab4a73827d29562.tar.gz
chromium_src-309d7a28aa6c938f60ac7a543ab4a73827d29562.tar.bz2
Add basic infrastructure for sending async browser API request and receiving results.
Review URL: http://codereview.chromium.org/42262 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@12347 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/renderer')
-rw-r--r--chrome/renderer/extensions/extension_process_bindings.cc105
-rw-r--r--chrome/renderer/extensions/extension_process_bindings.h27
-rw-r--r--chrome/renderer/render_thread.cc4
-rw-r--r--chrome/renderer/render_view.cc46
-rw-r--r--chrome/renderer/render_view.h8
-rw-r--r--chrome/renderer/renderer.scons2
-rw-r--r--chrome/renderer/renderer.vcproj8
-rwxr-xr-xchrome/renderer/renderer_resources.grd1
-rw-r--r--chrome/renderer/resources/extension_process_bindings.js71
9 files changed, 272 insertions, 0 deletions
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);
+ };
+})();