diff options
19 files changed, 241 insertions, 41 deletions
diff --git a/chrome/browser/extensions/execute_code_in_tab_function.cc b/chrome/browser/extensions/execute_code_in_tab_function.cc index 98bd3be..3fb0e86 100644 --- a/chrome/browser/extensions/execute_code_in_tab_function.cc +++ b/chrome/browser/extensions/execute_code_in_tab_function.cc @@ -17,6 +17,7 @@ namespace keys = extension_tabs_module_constants; const wchar_t* kCodeKey = L"code"; const wchar_t* kFileKey = L"file"; +const wchar_t* kAllFramesKey = L"allFrames"; bool ExecuteCodeInTabFunction::RunImpl() { EXTENSION_FUNCTION_VALIDATE(args_->IsType(Value::TYPE_LIST)); @@ -28,9 +29,16 @@ bool ExecuteCodeInTabFunction::RunImpl() { if (number_of_value == 0) { error_ = keys::kNoCodeOrFileToExecuteError; return false; - } else if (number_of_value > 1) { - error_ = keys::kMoreThanOneValuesError; - return false; + } else { + bool has_code = script_info->HasKey(kCodeKey); + bool has_file = script_info->HasKey(kFileKey); + if (has_code && has_file) { + error_ = keys::kMoreThanOneValuesError; + return false; + } else if (!has_code && !has_file) { + error_ = keys::kNoCodeOrFileToExecuteError; + return false; + } } execute_tab_id_ = -1; @@ -66,6 +74,11 @@ bool ExecuteCodeInTabFunction::RunImpl() { return false; } + if (script_info->HasKey(kAllFramesKey)) { + if (!script_info->GetBoolean(kAllFramesKey, &all_frames_)) + return false; + } + std::string code_string; if (script_info->HasKey(kCodeKey)) { if (!script_info->GetString(kCodeKey, &code_string)) @@ -133,7 +146,7 @@ void ExecuteCodeInTabFunction::Execute(const std::string& code_string) { NotificationService::AllSources()); AddRef(); // balanced in Observe() contents->ExecuteCode(request_id(), extension_id(), is_js_code, - code_string); + code_string, all_frames_); } void ExecuteCodeInTabFunction::Observe(NotificationType type, diff --git a/chrome/browser/extensions/execute_code_in_tab_function.h b/chrome/browser/extensions/execute_code_in_tab_function.h index 8215fd2..bd765f5 100644 --- a/chrome/browser/extensions/execute_code_in_tab_function.h +++ b/chrome/browser/extensions/execute_code_in_tab_function.h @@ -20,7 +20,8 @@ class MessageLoop; class ExecuteCodeInTabFunction : public AsyncExtensionFunction, public NotificationObserver { public: - ExecuteCodeInTabFunction() : execute_tab_id_(-1) {} + ExecuteCodeInTabFunction() : execute_tab_id_(-1), + all_frames_(false) {} private: virtual bool RunImpl(); @@ -44,6 +45,10 @@ class ExecuteCodeInTabFunction : public AsyncExtensionFunction, // Contains extension resource built from path of file which is // specified in JSON arguments. ExtensionResource resource_; + + // If all_frames_ is true, script or CSS text would be injected + // to all frames; Otherwise only injected to top main frame. + bool all_frames_; }; class TabsExecuteScriptFunction : public ExecuteCodeInTabFunction { diff --git a/chrome/browser/extensions/execute_script_apitest.cc b/chrome/browser/extensions/execute_script_apitest.cc index f519702..2ac734b 100644 --- a/chrome/browser/extensions/execute_script_apitest.cc +++ b/chrome/browser/extensions/execute_script_apitest.cc @@ -10,4 +10,5 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ExecuteScript) { StartHTTPServer(); ASSERT_TRUE(RunExtensionTest("executescript")) << message_; + ASSERT_TRUE(RunExtensionTest("executescript_in_frame")) << message_; } diff --git a/chrome/browser/extensions/extension_tabs_module_constants.cc b/chrome/browser/extensions/extension_tabs_module_constants.cc index dac399f3..a3fc48d 100644 --- a/chrome/browser/extensions/extension_tabs_module_constants.cc +++ b/chrome/browser/extensions/extension_tabs_module_constants.cc @@ -47,8 +47,8 @@ const char kCannotAccessPageError[] = "Cannot access contents of url \"*\". " const char kSupportedInWindowsOnlyError[] = "Supported in Windows only"; const char kNoCodeOrFileToExecuteError[] = "No source code or file specified."; -const char kMoreThanOneValuesError[] = "There should be only one value (either" - "code or file) in the second argument."; +const char kMoreThanOneValuesError[] = "Code and file should not be specified " + "at the same time in the second argument."; const char kLoadFileError[] = "Failed to load file: \"*\". "; } // namespace extension_tabs_module_constants diff --git a/chrome/browser/tab_contents/tab_contents.cc b/chrome/browser/tab_contents/tab_contents.cc index 7706c5b..0251f03 100644 --- a/chrome/browser/tab_contents/tab_contents.cc +++ b/chrome/browser/tab_contents/tab_contents.cc @@ -815,10 +815,12 @@ void TabContents::CloseAllSuppressedPopups() { } void TabContents::ExecuteCode(int request_id, const std::string& extension_id, - bool is_js_code, const std::string& code_string) { + bool is_js_code, const std::string& code_string, + bool all_frames) { render_view_host()->Send( new ViewMsg_ExecuteCode(render_view_host()->routing_id(), request_id, - extension_id, is_js_code, code_string)); + extension_id, is_js_code, code_string, + all_frames)); } void TabContents::PopupNotificationVisibilityChanged(bool visible) { diff --git a/chrome/browser/tab_contents/tab_contents.h b/chrome/browser/tab_contents/tab_contents.h index 1b1bd13..e440a53 100644 --- a/chrome/browser/tab_contents/tab_contents.h +++ b/chrome/browser/tab_contents/tab_contents.h @@ -363,7 +363,8 @@ class TabContents : public PageNavigator, // Execute code in this tab. void ExecuteCode(int request_id, const std::string& extension_id, - bool is_js_code, const std::string& code_string); + bool is_js_code, const std::string& code_string, + bool all_frames); // Called when the blocked popup notification is shown or hidden. virtual void PopupNotificationVisibilityChanged(bool visible); diff --git a/chrome/common/extensions/api/extension_api.json b/chrome/common/extensions/api/extension_api.json index 9a29dae..8797153 100755 --- a/chrome/common/extensions/api/extension_api.json +++ b/chrome/common/extensions/api/extension_api.json @@ -718,11 +718,12 @@ {"type": "integer", "name": "tabId", "optional": true, "description": "The id of tab which run the script, default to selected tab of current window."}, { "type": "object", - "name": "scriptDef", - "description": "Note:scripts are injected in the following definition order.", + "name": "details", + "description": "Details of the script to run. Either the code or file property must be set, but both may not be set at the same time.", "properties": { "code": {"type": "string", "optional": true, "description": "JavaScript code to execute."}, - "file": {"type": "string", "optional": true, "description": "JavaScript file to execute."} + "file": {"type": "string", "optional": true, "description": "JavaScript file to execute."}, + "allFrames": {"type": "boolean", "optional": true, "description": "If allFrames is true, this function injects script into all frames of current page. By default, it's false and only inject script into top main frame."} }, "optional": true }, @@ -743,11 +744,12 @@ {"type": "integer", "name": "tabId", "optional": true, "description": "The id of tab which run the script, default to selected tab of current window."}, { "type": "object", - "name": "scriptDef", - "description": "Note:css are injected in the following definition order.", + "name": "details", + "description": "Details of the css text to insert. Either the code or file property must be set, but both may not be set at the same time.", "properties": { "code": {"type": "string", "optional": true, "description": "CSS code to be injected."}, - "file": {"type": "string", "optional": true, "description": "CSS file to be injected."} + "file": {"type": "string", "optional": true, "description": "CSS file to be injected."}, + "allFrames": {"type": "boolean", "optional": true, "description": "If allFrames is true, this function injects css text into all frames of current page. By default, it's false and only inject css text into top main frame."} }, "optional": true }, diff --git a/chrome/common/render_messages_internal.h b/chrome/common/render_messages_internal.h index ec9e869..618b46d 100644 --- a/chrome/common/render_messages_internal.h +++ b/chrome/common/render_messages_internal.h @@ -736,12 +736,15 @@ IPC_BEGIN_MESSAGES(View) ViewType::Type /* view_type */) // Notification that renderer should run some JavaScript code. - IPC_MESSAGE_ROUTED4(ViewMsg_ExecuteCode, + IPC_MESSAGE_ROUTED5(ViewMsg_ExecuteCode, int, /* request id */ std::string, /* id of extension which runs the scripts */ bool, /* It's true if the code is JavaScript; Otherwise the code is CSS text. */ - std::string /* code would be executed */) + std::string, /* code would be executed */ + bool /* It's true if the code would be injected into all + frames; Otherwise the code is only injected into + top main frame */) // Returns a file handle IPC_MESSAGE_CONTROL2(ViewMsg_DatabaseOpenFileResponse, diff --git a/chrome/renderer/render_view.cc b/chrome/renderer/render_view.cc index 030222a..8a9df7a 100644 --- a/chrome/renderer/render_view.cc +++ b/chrome/renderer/render_view.cc @@ -2351,7 +2351,7 @@ void RenderView::OnUserScriptIdleTriggered(WebFrame* frame) { scoped_refptr<CodeExecutionInfo> info = pending_code_execution_queue_.front(); ExecuteCodeImpl(main_frame, info->request_id, info->extension_id, - info->is_js_code, info->code_string); + info->is_js_code, info->code_string, info->all_frames); pending_code_execution_queue_.pop(); } } @@ -2989,6 +2989,19 @@ void RenderView::OnResetPageEncodingToDefault() { webview()->setPageEncoding(no_encoding); } +bool RenderView::GetAllChildFrames( + WebFrame* parent_frame, + std::vector<WebFrame*>* frames_vector) const { + if (!parent_frame) + return false; + for (WebFrame* child_frame = parent_frame->firstChild(); child_frame; + child_frame = child_frame->nextSibling()) { + frames_vector->push_back(child_frame); + GetAllChildFrames(child_frame, frames_vector); + } + return true; +} + WebFrame* RenderView::GetChildFrame(const std::wstring& xpath) const { if (xpath.empty()) return webview()->mainFrame(); @@ -3770,7 +3783,8 @@ void RenderView::OnSetEditCommandsForNextKeyEvent( void RenderView::OnExecuteCode(int request_id, const std::string& extension_id, bool is_js_code, - const std::string& code_string) { + const std::string& code_string, + bool all_frames) { WebFrame* main_frame = webview() ? webview()->mainFrame() : NULL; if (!main_frame) { Send(new ViewMsg_ExecuteCodeFinished(routing_id_, request_id, false)); @@ -3781,30 +3795,40 @@ void RenderView::OnExecuteCode(int request_id, const std::string& extension_id, NavigationState* navigation_state = NavigationState::FromDataSource(ds); if (!navigation_state->user_script_idle_scheduler()->has_run()) { scoped_refptr<CodeExecutionInfo> info = new CodeExecutionInfo( - request_id, extension_id, is_js_code, code_string); + request_id, extension_id, is_js_code, code_string, all_frames); pending_code_execution_queue_.push(info); return; } ExecuteCodeImpl(main_frame, request_id, extension_id, is_js_code, - code_string); + code_string, all_frames); } void RenderView::ExecuteCodeImpl(WebFrame* frame, int request_id, const std::string& extension_id, bool is_js_code, - const std::string& code_string) { - if (is_js_code) { - std::vector<WebScriptSource> sources; - sources.push_back( - WebScriptSource(WebString::fromUTF8(code_string))); - UserScriptSlave::InsertInitExtensionCode(&sources, extension_id); - frame->executeScriptInIsolatedWorld( - UserScriptSlave::GetIsolatedWorldId(extension_id), - &sources.front(), sources.size(), EXTENSION_GROUP_CONTENT_SCRIPTS); - } else { - frame->insertStyleText(WebString::fromUTF8(code_string), WebString()); + const std::string& code_string, + bool all_frames) { + std::vector<WebFrame*> frame_vector; + frame_vector.push_back(frame); + if (all_frames) + GetAllChildFrames(frame, &frame_vector); + + for (std::vector<WebFrame*>::iterator frame_it = frame_vector.begin(); + frame_it != frame_vector.end(); ++frame_it) { + WebFrame* frame = *frame_it; + if (is_js_code) { + std::vector<WebScriptSource> sources; + sources.push_back( + WebScriptSource(WebString::fromUTF8(code_string))); + UserScriptSlave::InsertInitExtensionCode(&sources, extension_id); + frame->executeScriptInIsolatedWorld( + UserScriptSlave::GetIsolatedWorldId(extension_id), + &sources.front(), sources.size(), EXTENSION_GROUP_CONTENT_SCRIPTS); + } else { + frame->insertStyleText(WebString::fromUTF8(code_string), WebString()); + } } Send(new ViewMsg_ExecuteCodeFinished(routing_id_, request_id, true)); diff --git a/chrome/renderer/render_view.h b/chrome/renderer/render_view.h index 2cc7e7a..d53c998 100644 --- a/chrome/renderer/render_view.h +++ b/chrome/renderer/render_view.h @@ -610,12 +610,14 @@ class RenderView : public RenderWidget, void OnExecuteCode(int request_id, const std::string& extension_id, bool is_js_code, - const std::string& code_string); + const std::string& code_string, + bool all_frames); void ExecuteCodeImpl(WebKit::WebFrame* frame, int request_id, const std::string& extension_id, bool is_js_code, - const std::string& code_string); + const std::string& code_string, + bool all_frames); void OnUpdateBackForwardListCount(int back_list_count, int forward_list_count); void OnGetAccessibilityInfo( @@ -679,6 +681,10 @@ class RenderView : public RenderWidget, // Locates a sub frame with given xpath WebKit::WebFrame* GetChildFrame(const std::wstring& frame_xpath) const; + // Get all child frames of parent_frame, returned by frames_vector. + bool GetAllChildFrames(WebKit::WebFrame* parent_frame, + std::vector<WebKit::WebFrame* >* frames_vector) const; + // Requests to download an image. When done, the RenderView is // notified by way of DidDownloadImage. Returns true if the request was // successfully started, false otherwise. id is used to uniquely identify the @@ -953,11 +959,13 @@ class RenderView : public RenderWidget, // page is loaded. struct CodeExecutionInfo : public base::RefCounted<CodeExecutionInfo> { CodeExecutionInfo(int id_of_request, const std::string& id_of_extension, - bool is_js, const std::string& code) + bool is_js, const std::string& code, + bool inject_to_all_frames) : request_id(id_of_request), extension_id(id_of_extension), code_string(code), - is_js_code(is_js) {} + is_js_code(is_js), + all_frames(inject_to_all_frames) {} int request_id; // The id of extension who issues the pending executeScript API call. @@ -969,6 +977,8 @@ class RenderView : public RenderWidget, // It's true if |code_string| is JavaScript; otherwise |code_string| is // CSS text. bool is_js_code; + // It's true if the code_string would be injected into all frames. + bool all_frames; }; std::queue<scoped_refptr<CodeExecutionInfo> > pending_code_execution_queue_; diff --git a/chrome/test/data/extensions/api_test/executescript/test.html b/chrome/test/data/extensions/api_test/executescript/test.html index c4bbbe3..8a2db2d 100644 --- a/chrome/test/data/extensions/api_test/executescript/test.html +++ b/chrome/test/data/extensions/api_test/executescript/test.html @@ -8,14 +8,15 @@ var relativePath = '/files/extensions/api_test/executescript/test_executescript.html'; var testUrl = 'http://a.com:1337' + relativePath; var testFailureUrl = 'http://b.com:1337' + relativePath; -var testingFailure = false; +var firstEnter = true; chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) { - if (changeInfo.status == 'complete') + if (changeInfo.status != 'complete') return; - if (testingFailure) { + if (!firstEnter) { return; } + firstEnter = false; chrome.test.runTests([ @@ -68,7 +69,6 @@ chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) { }, function executeJavaScriptCodeShouldFail() { - testingFailure = true; chrome.tabs.update(tabId, { url: testFailureUrl }, function() { var script_file = {}; script_file.code = "document.title = 'executeScript';"; @@ -77,6 +77,21 @@ chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) { '". Extension manifest must request permission to access this ' + 'host.')); }); + }, + + function executeJavaScriptWithNoneValueShouldFail() { + var script_file = {}; + chrome.tabs.executeScript(tabId, script_file, fail( + 'No source code or file specified.')); + }, + + function executeJavaScriptWithTwoValuesShouldFail() { + var script_file = {}; + script_file.file = 'script1.js'; + script_file.code = 'var test = 1;'; + chrome.tabs.executeScript(tabId, script_file, fail( + 'Code and file should not be specified ' + + 'at the same time in the second argument.')); } ]); }); diff --git a/chrome/test/data/extensions/api_test/executescript_in_frame/frame0.html b/chrome/test/data/extensions/api_test/executescript_in_frame/frame0.html new file mode 100644 index 0000000..6bb02a0 --- /dev/null +++ b/chrome/test/data/extensions/api_test/executescript_in_frame/frame0.html @@ -0,0 +1,5 @@ +<html> + <title>test0</title> + <b id='test1'>text</b> + <p id='test2'>text</p> +</html>
\ No newline at end of file diff --git a/chrome/test/data/extensions/api_test/executescript_in_frame/frame1.html b/chrome/test/data/extensions/api_test/executescript_in_frame/frame1.html new file mode 100644 index 0000000..148114b --- /dev/null +++ b/chrome/test/data/extensions/api_test/executescript_in_frame/frame1.html @@ -0,0 +1,5 @@ +<html> + <title>test1</title> + <b id='test1'>text</b> + <p id='test2'>text</p> +</html>
\ No newline at end of file diff --git a/chrome/test/data/extensions/api_test/executescript_in_frame/frame2.html b/chrome/test/data/extensions/api_test/executescript_in_frame/frame2.html new file mode 100644 index 0000000..e8b5b3c --- /dev/null +++ b/chrome/test/data/extensions/api_test/executescript_in_frame/frame2.html @@ -0,0 +1,5 @@ +<html> + <title>test2</title> + <b id='test1'>text</b> + <p id='test2'>text</p> +</html>
\ No newline at end of file diff --git a/chrome/test/data/extensions/api_test/executescript_in_frame/frame3.html b/chrome/test/data/extensions/api_test/executescript_in_frame/frame3.html new file mode 100644 index 0000000..63f8807 --- /dev/null +++ b/chrome/test/data/extensions/api_test/executescript_in_frame/frame3.html @@ -0,0 +1,5 @@ +<html> + <title>test3</title> + <b id='test1'>text</b> + <p id='test2'>text</p> +</html>
\ No newline at end of file diff --git a/chrome/test/data/extensions/api_test/executescript_in_frame/manifest.json b/chrome/test/data/extensions/api_test/executescript_in_frame/manifest.json new file mode 100644 index 0000000..b2f3856 --- /dev/null +++ b/chrome/test/data/extensions/api_test/executescript_in_frame/manifest.json @@ -0,0 +1,7 @@ +{ + "version": "1.0.0.0", + "name": "executeScript and insertCSS test", + "description": "Test extension API: executeScript and insertCSS", + "background_page": "test.html", + "permissions": ["tabs", "http://a.com/"] +} diff --git a/chrome/test/data/extensions/api_test/executescript_in_frame/script.js b/chrome/test/data/extensions/api_test/executescript_in_frame/script.js new file mode 100644 index 0000000..a4efa64 --- /dev/null +++ b/chrome/test/data/extensions/api_test/executescript_in_frame/script.js @@ -0,0 +1,19 @@ +function getStyle(elem, name) { + if (document.defaultView && document.defaultView.getComputedStyle) { + name = name.toLowerCase(); + + try { + var s = document.defaultView.getComputedStyle(elem, ''); + return s && s.getPropertyValue(name); + } catch (ex) { + return null; + } + } else { + return null; + } +} + +var bElement = document.getElementById('test2'); +var display = getStyle(bElement, 'display').toLowerCase(); +var extensionPort = chrome.extension.connect(); +extensionPort.postMessage({message: display}); diff --git a/chrome/test/data/extensions/api_test/executescript_in_frame/test.html b/chrome/test/data/extensions/api_test/executescript_in_frame/test.html new file mode 100644 index 0000000..a8e2f06 --- /dev/null +++ b/chrome/test/data/extensions/api_test/executescript_in_frame/test.html @@ -0,0 +1,68 @@ +<script> + +var pass = chrome.test.callbackPass; +var fail = chrome.test.callbackFail; +var assertEq = chrome.test.assertEq; +var assertTrue = chrome.test.assertTrue; +var relativePath = + '/files/extensions/api_test/executescript_in_frame/test_executescript.html'; +var testUrl = 'http://a.com:1337' + relativePath; + +chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) { + if (changeInfo.status != 'complete') + return; + + chrome.test.runTests([ + function executeJavaScriptCodeInAllFramesShouldSucceed() { + var script_file = {}; + script_file.code = "var extensionPort = chrome.extension.connect();"; + script_file.code = script_file.code + + "extensionPort.postMessage({message: document.title});"; + script_file.allFrames = true; + var counter = 0; + var totalTitles = ''; + function eventListener(port) { + port.onMessage.addListener(function(data) { + counter++; + totalTitles += data.message; + }); + }; + chrome.self.onConnect.addListener(eventListener); + chrome.tabs.executeScript(tabId, script_file, pass(function() { + assertEq(counter, 5); + assertEq(totalTitles, 'frametest0test1test2test3'); + chrome.self.onConnect.removeListener(eventListener); + })); + }, + + function insertCSSTextInAllFramesShouldSucceed() { + var css_file = {}; + css_file.code = "p {display:none;}"; + css_file.allFrames = true; + var newStyle = ''; + var counter = 0; + function eventListener(port) { + port.onMessage.addListener(function(data) { + counter++; + newStyle += data.message; + }); + }; + chrome.self.onConnect.addListener(eventListener); + chrome.tabs.insertCSS(tabId, css_file, function() { + var script_file = {}; + script_file.file = 'script.js'; + script_file.allFrames = true; + chrome.tabs.executeScript(tabId, script_file, + pass(function() { + assertEq(newStyle, 'nonenonenonenone'); + assertEq(counter, 4); + chrome.self.onConnect.removeListener(eventListener); + })); + }); + } + ]); +}); + +chrome.tabs.create({ url: testUrl }); + +</script> diff --git a/chrome/test/data/extensions/api_test/executescript_in_frame/test_executescript.html b/chrome/test/data/extensions/api_test/executescript_in_frame/test_executescript.html new file mode 100644 index 0000000..b1f0b6c --- /dev/null +++ b/chrome/test/data/extensions/api_test/executescript_in_frame/test_executescript.html @@ -0,0 +1,10 @@ +<html> +<title>frame</title> +<frameset cols="25%,25%,25%,25%"> + <frame src="frame0.html" /> + <frame src="frame1.html" /> + <frame src="frame2.html" /> + <frame src="frame3.html" /> +</frameset> + +</html>
\ No newline at end of file |