From 771baf0c6d0a97c3a10eb6448c2161daf351fb40 Mon Sep 17 00:00:00 2001
From: "eaugusti@chromium.org"
Date: Mon, 25 Jun 2012 23:34:31 +0000
Subject: 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
---
.../api/tabs/execute_code_in_tab_function.cc | 14 +++-
.../api/tabs/execute_code_in_tab_function.h | 12 ++-
chrome/browser/extensions/api/tabs/tabs.cc | 3 +-
chrome/browser/extensions/api/tabs/tabs.h | 3 +-
.../browser/extensions/execute_script_apitest.cc | 6 ++
.../browser/extensions/script_badge_controller.cc | 5 +-
.../browser/extensions/script_badge_controller.h | 7 +-
chrome/browser/extensions/script_executor.h | 11 ++-
chrome/browser/extensions/script_executor_impl.cc | 14 +++-
chrome/common/extensions/api/tabs.json | 10 ++-
chrome/common/extensions/docs/extensions/tabs.html | 33 +++++++-
chrome/common/extensions/extension_messages.h | 5 +-
chrome/renderer/extensions/extension_helper.cc | 5 +-
.../renderer/extensions/user_script_scheduler.cc | 44 +++++++++--
.../api_test/executescript/callback/manifest.json | 10 +++
.../api_test/executescript/callback/test.html | 1 +
.../api_test/executescript/callback/test.js | 91 ++++++++++++++++++++++
17 files changed, 246 insertions(+), 28 deletions(-)
create mode 100644 chrome/test/data/extensions/api_test/executescript/callback/manifest.json
create mode 100644 chrome/test/data/extensions/api_test/executescript/callback/test.html
create mode 100644 chrome/test/data/extensions/api_test/executescript/callback/test.js
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
+ // 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
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(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:
- function() {...};
+ function(array of any result) {...};
+
+
+
-
+ result
+
+
+
+ (
+ optional
+
+
+
+ array of
+
+ any
+
+
+
+
+
+ )
+
+
+
+
- The result of the script in every injected frame.
+
+
+
+
+
+
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 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 persistent_context = v8::Context::New();
+ v8::Local context =
+ v8::Local::New(persistent_context);
+ persistent_context.Dispose();
+ scoped_ptr v8_converter(
+ content::V8ValueConverter::create());
+ v8_converter->SetUndefinedAllowed(true);
+ v8::Handle script_value;
if (params.in_main_world) {
- frame->executeScript(source);
+ script_value = frame->executeScriptAndReturnValue(source);
} else {
+ WebKit::WebVector > results;
std::vector 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 @@
+
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 });
+});
--
cgit v1.1