diff --git a/chrome/browser/chromeos/accessibility/accessibility_manager.cc b/chrome/browser/chromeos/accessibility/accessibility_manager.cc index 2fcc539c62fb04..338a86e270637b 100644 --- a/chrome/browser/chromeos/accessibility/accessibility_manager.cc +++ b/chrome/browser/chromeos/accessibility/accessibility_manager.cc @@ -62,6 +62,7 @@ #include "extensions/common/extension.h" #include "extensions/common/extension_messages.h" #include "extensions/common/extension_resource.h" +#include "extensions/common/host_id.h" #include "media/audio/sounds/sounds_manager.h" #include "ui/base/ime/chromeos/input_method_manager.h" #include "ui/base/resource/resource_bundle.h" @@ -135,7 +136,7 @@ class ContentScriptLoader { if (success) { ExtensionMsg_ExecuteCode_Params params; params.request_id = 0; - params.extension_id = extension_id_; + params.host_id = HostID(HostID::EXTENSIONS, extension_id_); params.is_javascript = true; params.code = data; params.run_at = extensions::UserScript::DOCUMENT_IDLE; @@ -204,7 +205,7 @@ void InjectChromeVoxContentScript( // so that it won't interrupt our speech feedback enabled message. ExtensionMsg_ExecuteCode_Params params; params.request_id = 0; - params.extension_id = extension->id(); + params.host_id = HostID(HostID::EXTENSIONS, extension->id()); params.is_javascript = true; params.code = "window.INJECTED_AFTER_LOAD = true;"; params.run_at = extensions::UserScript::DOCUMENT_IDLE; diff --git a/chrome/browser/extensions/api/tabs/tabs_api.cc b/chrome/browser/extensions/api/tabs/tabs_api.cc index 4816da7e0687d8..229d89aaed2445 100644 --- a/chrome/browser/extensions/api/tabs/tabs_api.cc +++ b/chrome/browser/extensions/api/tabs/tabs_api.cc @@ -75,6 +75,7 @@ #include "extensions/common/constants.h" #include "extensions/common/error_utils.h" #include "extensions/common/extension.h" +#include "extensions/common/host_id.h" #include "extensions/common/manifest_constants.h" #include "extensions/common/manifest_handlers/default_locale_handler.h" #include "extensions/common/message_bundle.h" @@ -1241,7 +1242,7 @@ bool TabsUpdateFunction::UpdateURL(const std::string &url_string, } TabHelper::FromWebContents(web_contents_)->script_executor()->ExecuteScript( - extension_id(), + HostID(HostID::EXTENSIONS, extension_id()), ScriptExecutor::JAVASCRIPT, url.GetContent(), ScriptExecutor::TOP_FRAME, @@ -1731,6 +1732,7 @@ bool ExecuteCodeInTabFunction::Init() { execute_tab_id_ = tab_id; details_ = details.Pass(); + set_host_id(HostID(HostID::EXTENSIONS, extension()->id())); return true; } diff --git a/chrome/browser/ui/webui/webui_webview_browsertest.cc b/chrome/browser/ui/webui/webui_webview_browsertest.cc new file mode 100644 index 00000000000000..9750dba172a341 --- /dev/null +++ b/chrome/browser/ui/webui/webui_webview_browsertest.cc @@ -0,0 +1,46 @@ +// Copyright 2015 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 "base/macros.h" +#include "base/path_service.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/url_constants.h" +#include "chrome/test/base/ui_test_utils.h" +#include "chrome/test/base/web_ui_browser_test.h" +#include "net/test/embedded_test_server/embedded_test_server.h" + +class WebUIWebViewBrowserTest : public WebUIBrowserTest { + public: + WebUIWebViewBrowserTest() {} + + void SetUpOnMainThread() override { + WebUIBrowserTest::SetUpOnMainThread(); + AddLibrary( + base::FilePath(FILE_PATH_LITERAL("webview_execute_script_test.js"))); + + base::FilePath test_data_dir; + PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir); + embedded_test_server()->ServeFilesFromDirectory(test_data_dir); + ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); + } + + GURL GetTestUrl(const std::string& path) const { + return embedded_test_server()->base_url().Resolve(path); + } + + GURL GetWebViewEnabledWebUIURL() const { + return GURL(chrome::kChromeUIChromeSigninURL); + } + + private: + DISALLOW_COPY_AND_ASSIGN(WebUIWebViewBrowserTest); +}; + +IN_PROC_BROWSER_TEST_F(WebUIWebViewBrowserTest, ExecuteScriptCode) { + ui_test_utils::NavigateToURL(browser(), GetWebViewEnabledWebUIURL()); + + ASSERT_TRUE(WebUIBrowserTest::RunJavascriptAsyncTest( + "testExecuteScriptCode", + new base::StringValue(GetTestUrl("empty.html").spec()))); +} diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index 5f164b3494f0ef..50a64b7c009094 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -527,6 +527,7 @@ 'browser/ui/webui/signin/user_manager_ui_browsertest.cc', 'browser/ui/webui/web_ui_test_handler.cc', 'browser/ui/webui/web_ui_test_handler.h', + 'browser/ui/webui/webui_webview_browsertest.cc', 'browser/ui/zoom/zoom_controller_browsertest.cc', 'browser/unload_browsertest.cc', 'common/mac/app_mode_chrome_locator_browsertest.mm', diff --git a/chrome/test/data/webui/webview_execute_script_test.js b/chrome/test/data/webui/webview_execute_script_test.js new file mode 100644 index 00000000000000..e0a4c80c948697 --- /dev/null +++ b/chrome/test/data/webui/webview_execute_script_test.js @@ -0,0 +1,32 @@ +// Copyright 2015 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. + +function createWebview() { + var webview = document.createElement('webview'); + document.body.appendChild(webview); + return webview; +} + +function testExecuteScriptCode(url) { + var webview = createWebview(); + + var onGetBackgroundExecuted = function(results) { + chrome.send('testResult', [results.length == 1 && results[0] == 'red']); + }; + + var onSetBackgroundExecuted = function() { + webview.executeScript({ + code: 'document.body.style.backgroundColor;' + }, onGetBackgroundExecuted); + }; + + var onLoadStop = function() { + webview.executeScript({ + code: 'document.body.style.backgroundColor = \'red\';' + }, onSetBackgroundExecuted); + }; + + webview.addEventListener('loadstop', onLoadStop); + webview.src = url; +} diff --git a/extensions/browser/api/execute_code_function.cc b/extensions/browser/api/execute_code_function.cc index 21845aa47ec48b..aebf61f46b4dd9 100644 --- a/extensions/browser/api/execute_code_function.cc +++ b/extensions/browser/api/execute_code_function.cc @@ -129,7 +129,7 @@ bool ExecuteCodeFunction::Execute(const std::string& code_string) { if (!executor) return false; - if (!extension()) + if (!extension() && !IsWebView()) return false; ScriptExecutor::ScriptType script_type = ScriptExecutor::JAVASCRIPT; @@ -162,7 +162,7 @@ bool ExecuteCodeFunction::Execute(const std::string& code_string) { CHECK_NE(UserScript::UNDEFINED, run_at); executor->ExecuteScript( - extension()->id(), + host_id_, script_type, code_string, frame_scope, @@ -204,6 +204,10 @@ bool ExecuteCodeFunction::RunAsync() { if (!details_->file.get()) return false; + + if (!extension()) + return false; + resource_ = extension()->GetResource(*details_->file); if (resource_.extension_root().empty() || resource_.relative_path().empty()) { diff --git a/extensions/browser/api/execute_code_function.h b/extensions/browser/api/execute_code_function.h index 9e1593a9d3ea3a..db0f441c8149ba 100644 --- a/extensions/browser/api/execute_code_function.h +++ b/extensions/browser/api/execute_code_function.h @@ -8,6 +8,7 @@ #include "extensions/browser/extension_function.h" #include "extensions/browser/script_executor.h" #include "extensions/common/api/extension_types.h" +#include "extensions/common/host_id.h" namespace extensions { @@ -39,6 +40,11 @@ class ExecuteCodeFunction : public AsyncExtensionFunction { // The injection details. scoped_ptr details_; + const HostID& host_id() const { return host_id_; } + void set_host_id(HostID host_id) { + host_id_ = host_id; + } + private: // Called when contents from the file whose path is specified in JSON // arguments has been loaded. @@ -65,6 +71,9 @@ class ExecuteCodeFunction : public AsyncExtensionFunction { // The URL of the file being injected into the page. GURL file_url_; + + // The ID of the injection host. + HostID host_id_; }; } // namespace extensions diff --git a/extensions/browser/api/web_view/web_view_internal_api.cc b/extensions/browser/api/web_view/web_view_internal_api.cc index 4b041e911764ef..dd9842ad698520 100644 --- a/extensions/browser/api/web_view/web_view_internal_api.cc +++ b/extensions/browser/api/web_view/web_view_internal_api.cc @@ -102,7 +102,19 @@ bool WebViewInternalExecuteCodeFunction::Init() { return false; details_ = details.Pass(); - return true; + + if (extension()) { + set_host_id(HostID(HostID::EXTENSIONS, extension()->id())); + return true; + } + + WebContents* web_contents = GetSenderWebContents(); + if (web_contents && web_contents->GetWebUI()) { + const GURL& url = render_view_host()->GetSiteInstance()->GetSiteURL(); + set_host_id(HostID(HostID::WEBUI, url.spec())); + return true; + } + return false; } bool WebViewInternalExecuteCodeFunction::ShouldInsertCSS() const { diff --git a/extensions/browser/script_executor.cc b/extensions/browser/script_executor.cc index ac3e671b9a48c0..bdd831abdccfcc 100644 --- a/extensions/browser/script_executor.cc +++ b/extensions/browser/script_executor.cc @@ -38,7 +38,7 @@ class Handler : public content::WebContentsObserver { const ScriptExecutor::ExecuteScriptCallback& callback) : content::WebContentsObserver(web_contents), script_observers_(AsWeakPtr(script_observers)), - extension_id_(params.extension_id), + host_id_(params.host_id), request_id_(params.request_id), callback_(callback) { content::RenderViewHost* rvh = web_contents->GetRenderViewHost(); @@ -78,9 +78,10 @@ class Handler : public content::WebContentsObserver { const std::string& error, const GURL& on_url, const base::ListValue& script_result) { - if (script_observers_.get() && error.empty()) { + if (script_observers_.get() && error.empty() && + host_id_.type() == HostID::EXTENSIONS) { ScriptExecutionObserver::ExecutingScriptsMap id_map; - id_map[extension_id_] = std::set(); + id_map[host_id_.id()] = std::set(); FOR_EACH_OBSERVER(ScriptExecutionObserver, *script_observers_, OnScriptsExecuted(web_contents(), id_map, on_url)); @@ -91,7 +92,7 @@ class Handler : public content::WebContentsObserver { } base::WeakPtr > script_observers_; - std::string extension_id_; + HostID host_id_; int request_id_; ScriptExecutor::ExecuteScriptCallback callback_; }; @@ -113,7 +114,7 @@ ScriptExecutor::ScriptExecutor( ScriptExecutor::~ScriptExecutor() { } -void ScriptExecutor::ExecuteScript(const std::string& extension_id, +void ScriptExecutor::ExecuteScript(const HostID& host_id, ScriptExecutor::ScriptType script_type, const std::string& code, ScriptExecutor::FrameScope frame_scope, @@ -126,16 +127,20 @@ void ScriptExecutor::ExecuteScript(const std::string& extension_id, bool user_gesture, ScriptExecutor::ResultType result_type, const ExecuteScriptCallback& callback) { - // Don't execute if the extension has been unloaded. - const Extension* extension = - ExtensionRegistry::Get(web_contents_->GetBrowserContext()) - ->enabled_extensions().GetByID(extension_id); - if (!extension) - return; + if (host_id.type() == HostID::EXTENSIONS) { + // Don't execute if the extension has been unloaded. + const Extension* extension = + ExtensionRegistry::Get(web_contents_->GetBrowserContext()) + ->enabled_extensions().GetByID(host_id.id()); + if (!extension) + return; + } else { + CHECK(process_type == WEB_VIEW_PROCESS); + } ExtensionMsg_ExecuteCode_Params params; params.request_id = next_request_id_++; - params.extension_id = extension_id; + params.host_id = host_id; params.is_javascript = (script_type == JAVASCRIPT); params.code = code; params.all_frames = (frame_scope == ALL_FRAMES); diff --git a/extensions/browser/script_executor.h b/extensions/browser/script_executor.h index c4ecb008d06efe..322e9bbb881ca0 100644 --- a/extensions/browser/script_executor.h +++ b/extensions/browser/script_executor.h @@ -85,7 +85,7 @@ class ScriptExecutor { // |callback| will always be called even if the IPC'd renderer is destroyed // before a response is received (in this case the callback will be with a // failure and appropriate error message). - void ExecuteScript(const std::string& extension_id, + void ExecuteScript(const HostID& host_id, ScriptType script_type, const std::string& code, FrameScope frame_scope, diff --git a/extensions/common/extension_messages.cc b/extensions/common/extension_messages.cc index 94a6bd0abcf7c9..41b8023423f8b4 100644 --- a/extensions/common/extension_messages.cc +++ b/extensions/common/extension_messages.cc @@ -249,6 +249,30 @@ void ParamTraits::Log( LogParam(p.map(), l); } +void ParamTraits::Write( + Message* m, const param_type& p) { + WriteParam(m, p.type()); + WriteParam(m, p.id()); +} + +bool ParamTraits::Read( + const Message* m, PickleIterator* iter, param_type* r) { + HostID::HostType type; + std::string id; + if (!ReadParam(m, iter, &type)) + return false; + if (!ReadParam(m, iter, &id)) + return false; + *r = HostID(type, id); + return true; +} + +void ParamTraits::Log( + const param_type& p, std::string* l) { + LogParam(p.type(), l); + LogParam(p.id(), l); +} + void ParamTraits::Write(Message* m, const param_type& p) { WriteParam(m, p.apis); diff --git a/extensions/common/extension_messages.h b/extensions/common/extension_messages.h index aee505c44a9d4b..232167c224f15e 100644 --- a/extensions/common/extension_messages.h +++ b/extensions/common/extension_messages.h @@ -16,6 +16,7 @@ #include "extensions/common/draggable_region.h" #include "extensions/common/extension.h" #include "extensions/common/extensions_client.h" +#include "extensions/common/host_id.h" #include "extensions/common/permissions/media_galleries_permission_data.h" #include "extensions/common/permissions/permission_set.h" #include "extensions/common/permissions/socket_permission_data.h" @@ -38,6 +39,8 @@ IPC_ENUM_TRAITS_MAX_VALUE(content::SocketPermissionRequest::OperationType, IPC_ENUM_TRAITS_MAX_VALUE(extensions::UserScript::InjectionType, extensions::UserScript::INJECTION_TYPE_LAST) +IPC_ENUM_TRAITS_MAX_VALUE(HostID::HostType, HostID::HOST_TYPE_LAST) + // Parameters structure for ExtensionHostMsg_AddAPIActionToActivityLog and // ExtensionHostMsg_AddEventToActivityLog. IPC_STRUCT_BEGIN(ExtensionHostMsg_APIActionOrEvent_Params) @@ -106,9 +109,8 @@ IPC_STRUCT_BEGIN(ExtensionMsg_ExecuteCode_Params) // The extension API request id, for responding. IPC_STRUCT_MEMBER(int, request_id) - // The ID of the requesting extension. To know which isolated world to - // execute the code inside of. - IPC_STRUCT_MEMBER(std::string, extension_id) + // The ID of the requesting injection host. + IPC_STRUCT_MEMBER(HostID, host_id) // Whether the code is JavaScript or CSS. IPC_STRUCT_MEMBER(bool, is_javascript) @@ -337,6 +339,14 @@ struct ParamTraits { static void Log(const param_type& p, std::string* l); }; +template <> +struct ParamTraits { + typedef HostID param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, PickleIterator* iter, param_type* r); + static void Log(const param_type& p, std::string* l); +}; + template <> struct ParamTraits { typedef ExtensionMsg_PermissionSetStruct param_type; diff --git a/extensions/common/host_id.h b/extensions/common/host_id.h index d977a44243d252..176ea49b6e50b3 100644 --- a/extensions/common/host_id.h +++ b/extensions/common/host_id.h @@ -10,7 +10,7 @@ // IDs of hosts who own user scripts. // A HostID is immutable after creation. struct HostID { - enum HostType { EXTENSIONS, WEBUI }; + enum HostType { EXTENSIONS, WEBUI, HOST_TYPE_LAST = WEBUI }; HostID(); HostID(HostType type, const std::string& id); diff --git a/extensions/renderer/extension_injection_host.cc b/extensions/renderer/extension_injection_host.cc index 7a27a08a778fcd..a97a7026a9c24b 100644 --- a/extensions/renderer/extension_injection_host.cc +++ b/extensions/renderer/extension_injection_host.cc @@ -18,8 +18,9 @@ ExtensionInjectionHost::~ExtensionInjectionHost() { } // static -scoped_ptr ExtensionInjectionHost::Create( - const std::string& extension_id, const ExtensionSet* extensions) { +scoped_ptr ExtensionInjectionHost::Create( + const std::string& extension_id, + const ExtensionSet* extensions) { const Extension* extension = extensions->GetByID(extension_id); if (!extension) return scoped_ptr(); diff --git a/extensions/renderer/extension_injection_host.h b/extensions/renderer/extension_injection_host.h index bdf6f41247e462..1b77cacfb206c8 100644 --- a/extensions/renderer/extension_injection_host.h +++ b/extensions/renderer/extension_injection_host.h @@ -12,6 +12,8 @@ namespace extensions { class ExtensionSet; +class ExtensionSet; + // A wrapper class that holds an extension and implements the InjectionHost // interface. class ExtensionInjectionHost : public InjectionHost { @@ -21,8 +23,8 @@ class ExtensionInjectionHost : public InjectionHost { // Create an ExtensionInjectionHost object. If the extension is gone, returns // a null scoped ptr. - static scoped_ptr Create( - const std::string& extension_id, const ExtensionSet* extensions); + static scoped_ptr Create(const std::string& extension_id, + const ExtensionSet* extensions); private: // InjectionHost: diff --git a/extensions/renderer/programmatic_script_injector.cc b/extensions/renderer/programmatic_script_injector.cc index 19259dff2c67a5..6e28bef510a3b5 100644 --- a/extensions/renderer/programmatic_script_injector.cc +++ b/extensions/renderer/programmatic_script_injector.cc @@ -82,6 +82,7 @@ PermissionsData::AccessType ProgrammaticScriptInjector::CanExecuteOnFrame( ? PermissionsData::ACCESS_ALLOWED : PermissionsData::ACCESS_DENIED; } + DCHECK_EQ(injection_host->id().type(), HostID::EXTENSIONS); return injection_host->CanExecuteOnFrame( effective_document_url, top_url, tab_id, true /* is_declarative */); diff --git a/extensions/renderer/script_injection_manager.cc b/extensions/renderer/script_injection_manager.cc index 5481a8e19240b4..c550cfd6aa9135 100644 --- a/extensions/renderer/script_injection_manager.cc +++ b/extensions/renderer/script_injection_manager.cc @@ -19,6 +19,7 @@ #include "extensions/renderer/programmatic_script_injector.h" #include "extensions/renderer/script_injection.h" #include "extensions/renderer/scripts_run_info.h" +#include "extensions/renderer/web_ui_injection_host.h" #include "ipc/ipc_message_macros.h" #include "third_party/WebKit/public/web/WebDocument.h" #include "third_party/WebKit/public/web/WebFrame.h" @@ -406,17 +407,22 @@ void ScriptInjectionManager::HandleExecuteCode( return; } - scoped_ptr extension_injection_host = - ExtensionInjectionHost::Create(params.extension_id, extensions_); - - if (!extension_injection_host) - return; + scoped_ptr injection_host; + if (params.host_id.type() == HostID::EXTENSIONS) { + injection_host = ExtensionInjectionHost::Create(params.host_id.id(), + extensions_); + if (!injection_host) + return; + } else if (params.host_id.type() == HostID::WEBUI) { + injection_host.reset( + new WebUIInjectionHost(params.host_id)); + } scoped_ptr injection(new ScriptInjection( scoped_ptr( new ProgrammaticScriptInjector(params, main_frame)), main_frame, - extension_injection_host.Pass(), + injection_host.Pass(), static_cast(params.run_at), ExtensionHelper::Get(render_view)->tab_id())); diff --git a/extensions/renderer/web_ui_injection_host.cc b/extensions/renderer/web_ui_injection_host.cc index 334358a03c1e02..5c6e1175c8810d 100644 --- a/extensions/renderer/web_ui_injection_host.cc +++ b/extensions/renderer/web_ui_injection_host.cc @@ -12,7 +12,6 @@ WebUIInjectionHost::WebUIInjectionHost(const HostID& host_id) WebUIInjectionHost::~WebUIInjectionHost() { } - std::string WebUIInjectionHost::GetContentSecurityPolicy() const { return std::string(); }