diff options
author | eaugusti@chromium.org <eaugusti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-06-25 23:34:31 +0000 |
---|---|---|
committer | eaugusti@chromium.org <eaugusti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-06-25 23:34:31 +0000 |
commit | 771baf0c6d0a97c3a10eb6448c2161daf351fb40 (patch) | |
tree | e95f17214e578748cc5a99bfdae5a67d8b6052d1 | |
parent | f6f72fb8adde6f5c901fd887e275831df8de3b58 (diff) | |
download | chromium_src-771baf0c6d0a97c3a10eb6448c2161daf351fb40.zip chromium_src-771baf0c6d0a97c3a10eb6448c2161daf351fb40.tar.gz chromium_src-771baf0c6d0a97c3a10eb6448c2161daf351fb40.tar.bz2 |
Uses the result of injected js as an optional parameter to an optional callback.
This feature requires changes to WebKitt.
The diff for these changes are here: https://gist.github.com/1732015
BUG=22349
Review URL: https://chromiumcodereview.appspot.com/9325032
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@144055 0039d316-1c4b-4281-b951-d872f2087c98
17 files changed, 246 insertions, 28 deletions
diff --git a/chrome/browser/extensions/api/tabs/execute_code_in_tab_function.cc b/chrome/browser/extensions/api/tabs/execute_code_in_tab_function.cc index cdcbd0d..b967b10 100644 --- a/chrome/browser/extensions/api/tabs/execute_code_in_tab_function.cc +++ b/chrome/browser/extensions/api/tabs/execute_code_in_tab_function.cc @@ -143,7 +143,8 @@ bool ExecuteCodeInTabFunction::RunImpl() { void ExecuteCodeInTabFunction::OnExecuteCodeFinished(bool success, int32 page_id, - const std::string& error) { + const std::string& error, + const ListValue& result) { if (!error.empty()) { CHECK(!success); error_ = error; @@ -152,6 +153,17 @@ void ExecuteCodeInTabFunction::OnExecuteCodeFinished(bool success, SendResponse(success); } + +void TabsExecuteScriptFunction::OnExecuteCodeFinished(bool success, + int32 page_id, + const std::string& error, + const ListValue& result) { + if (error.empty()) + result_.reset(result.DeepCopy()); + ExecuteCodeInTabFunction::OnExecuteCodeFinished(success, page_id, error, + result); +} + void ExecuteCodeInTabFunction::DidLoadFile(bool success, const std::string& data) { std::string function_name = name(); diff --git a/chrome/browser/extensions/api/tabs/execute_code_in_tab_function.h b/chrome/browser/extensions/api/tabs/execute_code_in_tab_function.h index 5c97e3e..a954ede 100644 --- a/chrome/browser/extensions/api/tabs/execute_code_in_tab_function.h +++ b/chrome/browser/extensions/api/tabs/execute_code_in_tab_function.h @@ -23,12 +23,13 @@ class ExecuteCodeInTabFunction : public AsyncExtensionFunction { // ExtensionFunction: virtual bool RunImpl() OVERRIDE; - private: // Message handler. - void OnExecuteCodeFinished(bool success, - int32 page_id, - const std::string& error); + virtual void OnExecuteCodeFinished(bool success, + int32 page_id, + const std::string& error, + const ListValue& script_result); + private: // Called when contents from the file whose path is specified in JSON // arguments has been loaded. void DidLoadFile(bool success, const std::string& data); @@ -66,6 +67,9 @@ class ExecuteCodeInTabFunction : public AsyncExtensionFunction { class TabsExecuteScriptFunction : public ExecuteCodeInTabFunction { private: virtual ~TabsExecuteScriptFunction() {} + virtual void OnExecuteCodeFinished(bool success, int32 page_id, + const std::string& error, + const ListValue& script_result) OVERRIDE; DECLARE_EXTENSION_FUNCTION_NAME("tabs.executeScript") }; diff --git a/chrome/browser/extensions/api/tabs/tabs.cc b/chrome/browser/extensions/api/tabs/tabs.cc index 8b2ed1b..f245e17 100644 --- a/chrome/browser/extensions/api/tabs/tabs.cc +++ b/chrome/browser/extensions/api/tabs/tabs.cc @@ -1363,7 +1363,8 @@ void UpdateTabFunction::PopulateResult() { void UpdateTabFunction::OnExecuteCodeFinished(bool success, int32 page_id, - const std::string& error) { + const std::string& error, + const ListValue& script_result) { if (!error.empty()) { CHECK(!success); error_ = error; diff --git a/chrome/browser/extensions/api/tabs/tabs.h b/chrome/browser/extensions/api/tabs/tabs.h index 14276a0..f44e4e2 100644 --- a/chrome/browser/extensions/api/tabs/tabs.h +++ b/chrome/browser/extensions/api/tabs/tabs.h @@ -131,7 +131,8 @@ class UpdateTabFunction : public AsyncExtensionFunction { virtual bool RunImpl() OVERRIDE; void OnExecuteCodeFinished(bool success, int32 page_id, - const std::string& error); + const std::string& error, + const ListValue& script_result); DECLARE_EXTENSION_FUNCTION_NAME("tabs.update") }; diff --git a/chrome/browser/extensions/execute_script_apitest.cc b/chrome/browser/extensions/execute_script_apitest.cc index 0594c79..1edb440 100644 --- a/chrome/browser/extensions/execute_script_apitest.cc +++ b/chrome/browser/extensions/execute_script_apitest.cc @@ -78,3 +78,9 @@ IN_PROC_BROWSER_TEST_F(ExecuteScriptApiTest, ExecuteScriptRunAt) { ASSERT_TRUE(StartTestServer()); ASSERT_TRUE(RunExtensionTest("executescript/run_at")) << message_; } + +IN_PROC_BROWSER_TEST_F(ExecuteScriptApiTest, ExecuteScriptCallback) { + SetupDelayedHostResolver(); + ASSERT_TRUE(StartTestServer()); + ASSERT_TRUE(RunExtensionTest("executescript/callback")) << message_; +} diff --git a/chrome/browser/extensions/script_badge_controller.cc b/chrome/browser/extensions/script_badge_controller.cc index e15b259..bb655a6 100644 --- a/chrome/browser/extensions/script_badge_controller.cc +++ b/chrome/browser/extensions/script_badge_controller.cc @@ -88,13 +88,14 @@ void ScriptBadgeController::OnExecuteScriptFinished( const ExecuteScriptCallback& callback, bool success, int32 page_id, - const std::string& error) { + const std::string& error, + const base::ListValue& script_results) { if (success && page_id == GetPageID()) { if (InsertExtension(extension_id)) Notify(); } - callback.Run(success, page_id, error); + callback.Run(success, page_id, error, script_results); } ExtensionService* ScriptBadgeController::GetExtensionService() { diff --git a/chrome/browser/extensions/script_badge_controller.h b/chrome/browser/extensions/script_badge_controller.h index 420e666b..7ae4b6c 100644 --- a/chrome/browser/extensions/script_badge_controller.h +++ b/chrome/browser/extensions/script_badge_controller.h @@ -24,6 +24,10 @@ class ExtensionAction; class ExtensionService; class TabContents; +namespace base { +class ListValue; +} + namespace IPC { class Message; } @@ -79,7 +83,8 @@ class ScriptBadgeController const ExecuteScriptCallback& callback, bool success, int32 page_id, - const std::string& error); + const std::string& error, + const base::ListValue& script_result); // Gets the ExtensionService for |tab_contents_|. ExtensionService* GetExtensionService(); diff --git a/chrome/browser/extensions/script_executor.h b/chrome/browser/extensions/script_executor.h index 2119cc6..93c423e 100644 --- a/chrome/browser/extensions/script_executor.h +++ b/chrome/browser/extensions/script_executor.h @@ -11,6 +11,10 @@ #include "base/callback_forward.h" #include "chrome/common/extensions/user_script.h" +namespace base { + class ListValue; +} + namespace content { class WebContents; } @@ -42,9 +46,10 @@ class ScriptExecutor { ISOLATED_WORLD, }; - // Callback from ExecuteScript. The arguments are (success, page_id, error). - // page_id is only valid on success, error is only valid on !success. - typedef base::Callback<void(bool, int32, const std::string&)> + // Callback from ExecuteScript. The arguments are (success, page_id, error, + // result). page_id is only valid on success, error is only valid on !success. + typedef base::Callback<void(bool, int32, const std::string&, + const base::ListValue&)> ExecuteScriptCallback; // Executes a script. The arguments match ExtensionMsg_ExecuteCode_Params in diff --git a/chrome/browser/extensions/script_executor_impl.cc b/chrome/browser/extensions/script_executor_impl.cc index e8f6199..ae8c251 100644 --- a/chrome/browser/extensions/script_executor_impl.cc +++ b/chrome/browser/extensions/script_executor_impl.cc @@ -14,6 +14,10 @@ #include "ipc/ipc_message.h" #include "ipc/ipc_message_macros.h" +namespace base { +class ListValue; +} // namespace base + namespace extensions { namespace { @@ -58,7 +62,8 @@ class Handler : public content::WebContentsObserver { } virtual void WebContentsDestroyed(content::WebContents* tab) OVERRIDE { - callback_.Run(false, -1, kRendererDestroyed); + base::ListValue val; + callback_.Run(false, -1, kRendererDestroyed, val); delete this; } @@ -66,8 +71,9 @@ class Handler : public content::WebContentsObserver { void OnExecuteCodeFinished(int request_id, bool success, int32 page_id, - const std::string& error) { - callback_.Run(success, page_id, error); + const std::string& error, + const base::ListValue& script_result) { + callback_.Run(success, page_id, error, script_result); delete this; } @@ -98,7 +104,7 @@ void ScriptExecutorImpl::ExecuteScript( params.is_javascript = (script_type == JAVASCRIPT); params.code = code; params.all_frames = (frame_scope == ALL_FRAMES); - params.run_at = (int) run_at; + params.run_at = static_cast<int>(run_at); params.in_main_world = (world_type == MAIN_WORLD); // Handler handles IPCs and deletes itself on completion. diff --git a/chrome/common/extensions/api/tabs.json b/chrome/common/extensions/api/tabs.json index bb94fef..ccf137a 100644 --- a/chrome/common/extensions/api/tabs.json +++ b/chrome/common/extensions/api/tabs.json @@ -620,7 +620,15 @@ "name": "callback", "optional": true, "description": "Called after all the JavaScript has been executed.", - "parameters": [] + "parameters": [ + { + "name": "result", + "optional": true, + "type": "array", + "items": {"type": "any", "minimum": 0}, + "description": "The result of the script in every injected frame." + } + ] } ] }, diff --git a/chrome/common/extensions/docs/extensions/tabs.html b/chrome/common/extensions/docs/extensions/tabs.html index a94b5b2..89bde6f 100644 --- a/chrome/common/extensions/docs/extensions/tabs.html +++ b/chrome/common/extensions/docs/extensions/tabs.html @@ -1209,8 +1209,39 @@ For other examples and for help in viewing the source code, see specify a function that looks like this: </p> <!-- Note: intentionally longer 80 columns --> - <pre>function(<span></span>) <span class="subdued">{...}</span>;</pre> + <pre>function(<span>array of any result</span>) <span class="subdued">{...}</span>;</pre> <dl> + <div> + <div> + <dt> + <var>result</var> + <em> + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional">optional</span> + <span id="typeTemplate"> + <span> + <span> + array of <span><span> + <span> + <span>any</span> + </span> + </span></span> + </span> + </span> + </span> + ) + </div> + </em> + </dt> + <dd>The result of the script in every injected frame.</dd> + <!-- OBJECT PROPERTIES --> + <!-- OBJECT METHODS --> + <!-- OBJECT EVENT FIELDS --> + <!-- FUNCTION PARAMETERS --> + </div> + </div> </dl> </div> </div> diff --git a/chrome/common/extensions/extension_messages.h b/chrome/common/extensions/extension_messages.h index ae2b7f9..dfc8df9 100644 --- a/chrome/common/extensions/extension_messages.h +++ b/chrome/common/extensions/extension_messages.h @@ -408,11 +408,12 @@ IPC_SYNC_MESSAGE_CONTROL1_1(ExtensionHostMsg_GetMessageBundle, SubstitutionMap /* message bundle */) // Sent from the renderer to the browser to return the script running result. -IPC_MESSAGE_ROUTED4(ExtensionHostMsg_ExecuteCodeFinished, +IPC_MESSAGE_ROUTED5(ExtensionHostMsg_ExecuteCodeFinished, int /* request id */, bool /* whether the script ran successfully */, int32 /* page_id the code executed on, if successful */, - std::string /* error message, if unsuccessful */) + std::string /* error message, if unsuccessful */, + ListValue /* result of the script */) // Sent from the renderer to the browser to notify that content scripts are // running in the renderer that the IPC originated from. diff --git a/chrome/renderer/extensions/extension_helper.cc b/chrome/renderer/extensions/extension_helper.cc index 24d3dc2..67be4a62 100644 --- a/chrome/renderer/extensions/extension_helper.cc +++ b/chrome/renderer/extensions/extension_helper.cc @@ -11,6 +11,7 @@ #include "base/lazy_instance.h" #include "base/message_loop.h" #include "base/utf_string_conversions.h" +#include "base/values.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/extensions/extension_messages.h" #include "chrome/common/render_messages.h" @@ -111,7 +112,6 @@ class ExtensionViewAccumulator : public content::RenderViewVisitor { chrome::ViewType view_type_; std::vector<content::RenderView*> views_; }; - } // static @@ -339,8 +339,9 @@ void ExtensionHelper::OnExecuteCode( WebView* webview = render_view()->GetWebView(); WebFrame* main_frame = webview->mainFrame(); if (!main_frame) { + ListValue val; Send(new ExtensionHostMsg_ExecuteCodeFinished( - routing_id(), params.request_id, false, -1, "")); + routing_id(), params.request_id, false, -1, "", val)); return; } diff --git a/chrome/renderer/extensions/user_script_scheduler.cc b/chrome/renderer/extensions/user_script_scheduler.cc index dc4ebee..8fb5554 100644 --- a/chrome/renderer/extensions/user_script_scheduler.cc +++ b/chrome/renderer/extensions/user_script_scheduler.cc @@ -5,6 +5,7 @@ #include "chrome/renderer/extensions/user_script_scheduler.h" #include "base/bind.h" +#include "base/memory/scoped_ptr.h" #include "base/message_loop.h" #include "chrome/common/extensions/extension_error_utils.h" #include "chrome/common/extensions/extension_manifest_constants.h" @@ -14,10 +15,13 @@ #include "chrome/renderer/extensions/extension_helper.h" #include "chrome/renderer/extensions/user_script_slave.h" #include "content/public/renderer/render_view.h" +#include "content/public/renderer/v8_value_converter.h" #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebVector.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" +#include "v8/include/v8.h" namespace { // The length of time to wait after the DOM is complete to try and run user @@ -28,6 +32,7 @@ const int kUserScriptIdleTimeoutMs = 200; using WebKit::WebDocument; using WebKit::WebFrame; using WebKit::WebString; +using WebKit::WebVector; using WebKit::WebView; using extensions::Extension; @@ -134,12 +139,14 @@ void UserScriptScheduler::ExecuteCodeImpl( content::RenderView* render_view = content::RenderView::FromWebView(frame_->view()); ExtensionHelper* extension_helper = ExtensionHelper::Get(render_view); + base::ListValue execution_results; // Since extension info is sent separately from user script info, they can // be out of sync. We just ignore this situation. if (!extension) { render_view->Send(new ExtensionHostMsg_ExecuteCodeFinished( - render_view->GetRoutingID(), params.request_id, true, -1, "")); + render_view->GetRoutingID(), params.request_id, true, -1, "", + execution_results)); return; } @@ -176,21 +183,47 @@ void UserScriptScheduler::ExecuteCodeImpl( -1, ExtensionErrorUtils::FormatErrorMessage( extension_manifest_errors::kCannotAccessPage, - frame->document().url().spec()))); + frame->document().url().spec()), + execution_results)); return; } } WebScriptSource source(WebString::fromUTF8(params.code)); + v8::HandleScope scope; + v8::Persistent<v8::Context> persistent_context = v8::Context::New(); + v8::Local<v8::Context> context = + v8::Local<v8::Context>::New(persistent_context); + persistent_context.Dispose(); + scoped_ptr<content::V8ValueConverter> v8_converter( + content::V8ValueConverter::create()); + v8_converter->SetUndefinedAllowed(true); + v8::Handle<v8::Value> script_value; if (params.in_main_world) { - frame->executeScript(source); + script_value = frame->executeScriptAndReturnValue(source); } else { + WebKit::WebVector<v8::Local<v8::Value> > results; std::vector<WebScriptSource> sources; sources.push_back(source); frame->executeScriptInIsolatedWorld( extension_dispatcher_->user_script_slave()-> GetIsolatedWorldIdForExtension(extension, frame), - &sources.front(), sources.size(), EXTENSION_GROUP_CONTENT_SCRIPTS); + &sources.front(), sources.size(), EXTENSION_GROUP_CONTENT_SCRIPTS, + &results); + // We only expect one value back since we only pushed one source + if (results.size() == 1 && !results[0].IsEmpty()) + script_value = results[0]; + } + if (!script_value.IsEmpty()) { + base::Value* base_val = NULL; + // Don't try and convert non-pure JS objects. + if (!script_value->IsObject() || + script_value->ToObject()->InternalFieldCount() == 0) + base_val = v8_converter->FromV8Value(script_value, context); + if (!base_val) + base_val = base::Value::CreateNullValue(); + execution_results.Append(base_val); + script_value.Clear(); } } else { frame->document().insertUserStyleSheet( @@ -205,7 +238,8 @@ void UserScriptScheduler::ExecuteCodeImpl( params.request_id, true, render_view->GetPageId(), - "")); + "", + execution_results)); } bool UserScriptScheduler::GetAllChildFrames( diff --git a/chrome/test/data/extensions/api_test/executescript/callback/manifest.json b/chrome/test/data/extensions/api_test/executescript/callback/manifest.json new file mode 100644 index 0000000..b5162e7 --- /dev/null +++ b/chrome/test/data/extensions/api_test/executescript/callback/manifest.json @@ -0,0 +1,10 @@ +{ + "version": "1.0.0.0", + "manifest_version": 2, + "name": "callback test", + "description": "Test that executeScript uses the result of the evaluated script in the callback.", + "background": { + "scripts": ["test.js"] + }, + "permissions": ["tabs", "http://b.com/"] +} diff --git a/chrome/test/data/extensions/api_test/executescript/callback/test.html b/chrome/test/data/extensions/api_test/executescript/callback/test.html new file mode 100644 index 0000000..18ecdcb --- /dev/null +++ b/chrome/test/data/extensions/api_test/executescript/callback/test.html @@ -0,0 +1 @@ +<html></html> diff --git a/chrome/test/data/extensions/api_test/executescript/callback/test.js b/chrome/test/data/extensions/api_test/executescript/callback/test.js new file mode 100644 index 0000000..0474938 --- /dev/null +++ b/chrome/test/data/extensions/api_test/executescript/callback/test.js @@ -0,0 +1,91 @@ +// 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. + +var relativePath = + '/files/extensions/api_test/executescript/callback/test.html'; +var testUrl = 'http://b.com:PORT' + relativePath; + +chrome.test.getConfig(function(config) { + testUrl = testUrl.replace(/PORT/, config.testServer.port); + chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) { + if (changeInfo.status != 'complete') + return; + chrome.tabs.onUpdated.removeListener(arguments.callee); + chrome.test.runTests([ + + function executeCallbackIntShouldSucceed() { + var scriptDetails = {code: '3'}; + chrome.tabs.executeScript(tabId, scriptDetails, function(scriptVal) { + chrome.tabs.get(tabId, chrome.test.callbackPass(function(tab) { + chrome.test.assertEq(3, scriptVal[0]); + })); + }); + }, + + function executeCallbackDoubleShouldSucceed() { + var scriptDetails = {code: '1.4'}; + chrome.tabs.executeScript(tabId, scriptDetails, function(scriptVal) { + chrome.tabs.get(tabId, chrome.test.callbackPass(function(tab) { + chrome.test.assertEq(1.4, scriptVal[0]); + })); + }); + }, + + function executeCallbackStringShouldSucceed() { + var scriptDetails = {code: '"foobar"'}; + chrome.tabs.executeScript(tabId, scriptDetails, function(scriptVal) { + chrome.tabs.get(tabId, chrome.test.callbackPass(function(tab) { + chrome.test.assertEq('foobar', scriptVal[0]); + })); + }); + }, + + function executeCallbackTrueShouldSucceed() { + var scriptDetails = {code: 'true'}; + chrome.tabs.executeScript(tabId, scriptDetails, function(scriptVal) { + chrome.tabs.get(tabId, chrome.test.callbackPass(function(tab) { + chrome.test.assertEq(true, scriptVal[0]); + })); + }); + }, + + function executeCallbackFalseShouldSucceed() { + var scriptDetails = {code: 'false'}; + chrome.tabs.executeScript(tabId, scriptDetails, function(scriptVal) { + chrome.tabs.get(tabId, chrome.test.callbackPass(function(tab) { + chrome.test.assertEq(false, scriptVal[0]); + })); + }); + }, + + function executeCallbackNullShouldSucceed() { + var scriptDetails = {code: 'null'}; + chrome.tabs.executeScript(tabId, scriptDetails, function(scriptVal) { + chrome.tabs.get(tabId, chrome.test.callbackPass(function(tab) { + chrome.test.assertEq(null, scriptVal[0]); + })); + }); + }, + + function executeCallbackArrayShouldSucceed() { + var scriptDetails = {code: '[1, "5", false, null]'}; + chrome.tabs.executeScript(tabId, scriptDetails, function(scriptVal) { + chrome.tabs.get(tabId, chrome.test.callbackPass(function(tab) { + chrome.test.assertEq([1, "5", false, null], scriptVal[0]); + })); + }); + }, + + function executeCallbackObjShouldSucceed() { + var scriptDetails = {code: 'var obj = {"id": "foo", "bar": 9}; obj'}; + chrome.tabs.executeScript(tabId, scriptDetails, function(scriptVal) { + chrome.tabs.get(tabId, chrome.test.callbackPass(function(tab) { + chrome.test.assertEq({"id": "foo", "bar": 9}, scriptVal[0]); + })); + }); + } + ]); + }); + chrome.tabs.create({ url: testUrl }); +}); |