Commit 05a02505 authored by hanxi's avatar hanxi Committed by Commit bot

Implement <webview>.addContentScript/removeContentScript API [1].

This patch includes the changes that enables <webview>.addContentScript/removeContentScript API  work on extensions.

This is the first patch in a series of patches:
1) Implement <webview>.addContentScript/removeContentScript API [1] (https://codereview.chromium.org/959413003)
2) Implement <webview>.addContentScript/removeContentScript API [2] (https://codereview.chromium.org/1056533002)
3) Implement <webview>.addContentScript/removeContentScript API [3] (https://codereview.chromium.org/1058113002)

TBR=asvitkine@chromium.org
BUG=461052

Review URL: https://codereview.chromium.org/959413003

Cr-Commit-Position: refs/heads/master@{#325492}
parent b87cb277
...@@ -1114,6 +1114,53 @@ IN_PROC_BROWSER_TEST_F(WebViewTest, ...@@ -1114,6 +1114,53 @@ IN_PROC_BROWSER_TEST_F(WebViewTest,
NO_TEST_SERVER); NO_TEST_SERVER);
} }
IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestAddContentScript) {
TestHelper("testAddContentScript", "web_view/shim", NEEDS_TEST_SERVER);
}
IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestAddMultipleContentScripts) {
TestHelper("testAddMultipleContentScripts", "web_view/shim",
NEEDS_TEST_SERVER);
}
IN_PROC_BROWSER_TEST_F(
WebViewTest,
Shim_TestAddContentScriptWithSameNameShouldOverwriteTheExistingOne) {
TestHelper("testAddContentScriptWithSameNameShouldOverwriteTheExistingOne",
"web_view/shim", NEEDS_TEST_SERVER);
}
IN_PROC_BROWSER_TEST_F(
WebViewTest,
Shim_TestAddContentScriptToOneWebViewShouldNotInjectToTheOtherWebView) {
TestHelper("testAddContentScriptToOneWebViewShouldNotInjectToTheOtherWebView",
"web_view/shim", NEEDS_TEST_SERVER);
}
IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestAddAndRemoveContentScripts) {
TestHelper("testAddAndRemoveContentScripts", "web_view/shim",
NEEDS_TEST_SERVER);
}
IN_PROC_BROWSER_TEST_F(WebViewTest,
Shim_TestAddContentScriptsWithNewWindowAPI) {
TestHelper("testAddContentScriptsWithNewWindowAPI", "web_view/shim",
NEEDS_TEST_SERVER);
}
IN_PROC_BROWSER_TEST_F(
WebViewTest,
Shim_TestContentScriptIsInjectedAfterTerminateAndReloadWebView) {
TestHelper("testContentScriptIsInjectedAfterTerminateAndReloadWebView",
"web_view/shim", NEEDS_TEST_SERVER);
}
IN_PROC_BROWSER_TEST_F(WebViewTest,
Shim_TestContentScriptExistsAsLongAsWebViewTagExists) {
TestHelper("testContentScriptExistsAsLongAsWebViewTagExists", "web_view/shim",
NEEDS_TEST_SERVER);
}
IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestExecuteScriptFail) { IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestExecuteScriptFail) {
#if defined(OS_WIN) #if defined(OS_WIN)
// Flaky on XP bot http://crbug.com/266185 // Flaky on XP bot http://crbug.com/266185
......
<!doctype html>
<!--
* Copyright (c) 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.
-->
<html>
<head>
<title>A guest that opens a new window.</title>
<script type="text/javascript">
window.onload = function() {
window.open(document.documentURI);
};
</script>
</head>
<body style="padding: 0; margin: 0;">
</body>
</html>
// 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.
var embedder = null;
function reportConnected_request() {
window.console.log('Reporting connection to embedder.');
var msg = ['connected_response'];
embedder.postMessage(JSON.stringify(msg), '*');
}
window.addEventListener('message', function(e) {
embedder = e.source;
var data = JSON.parse(e.data);
switch (data[0]) {
case 'connect_request': {
reportConnected_request();
break;
}
}
});
window.console.log('Script has been successfully injected.');
// 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.
document.body.style.backgroundColor = "red";
...@@ -129,6 +129,40 @@ class WebViewInternalInsertCSSFunction ...@@ -129,6 +129,40 @@ class WebViewInternalInsertCSSFunction
DISALLOW_COPY_AND_ASSIGN(WebViewInternalInsertCSSFunction); DISALLOW_COPY_AND_ASSIGN(WebViewInternalInsertCSSFunction);
}; };
class WebViewInternalAddContentScriptsFunction
: public UIThreadExtensionFunction {
public:
DECLARE_EXTENSION_FUNCTION("webViewInternal.addContentScripts",
WEBVIEWINTERNAL_ADDCONTENTSCRIPTS);
WebViewInternalAddContentScriptsFunction();
protected:
~WebViewInternalAddContentScriptsFunction() override;
private:
ExecuteCodeFunction::ResponseAction Run() override;
DISALLOW_COPY_AND_ASSIGN(WebViewInternalAddContentScriptsFunction);
};
class WebViewInternalRemoveContentScriptsFunction
: public UIThreadExtensionFunction {
public:
DECLARE_EXTENSION_FUNCTION("webViewInternal.removeContentScripts",
WEBVIEWINTERNAL_REMOVECONTENTSCRIPTS);
WebViewInternalRemoveContentScriptsFunction();
protected:
~WebViewInternalRemoveContentScriptsFunction() override;
private:
ExecuteCodeFunction::ResponseAction Run() override;
DISALLOW_COPY_AND_ASSIGN(WebViewInternalRemoveContentScriptsFunction);
};
class WebViewInternalSetNameFunction : public WebViewInternalExtensionFunction { class WebViewInternalSetNameFunction : public WebViewInternalExtensionFunction {
public: public:
DECLARE_EXTENSION_FUNCTION("webViewInternal.setName", DECLARE_EXTENSION_FUNCTION("webViewInternal.setName",
......
...@@ -28,12 +28,22 @@ void DeclarativeUserScriptMaster::AddScript(const UserScript& script) { ...@@ -28,12 +28,22 @@ void DeclarativeUserScriptMaster::AddScript(const UserScript& script) {
loader_.AddScripts(set); loader_.AddScripts(set);
} }
void DeclarativeUserScriptMaster::AddScripts(
const std::set<UserScript>& scripts) {
loader_.AddScripts(scripts);
}
void DeclarativeUserScriptMaster::RemoveScript(const UserScript& script) { void DeclarativeUserScriptMaster::RemoveScript(const UserScript& script) {
std::set<UserScript> set; std::set<UserScript> set;
set.insert(script); set.insert(script);
loader_.RemoveScripts(set); loader_.RemoveScripts(set);
} }
void DeclarativeUserScriptMaster::RemoveScripts(
const std::set<UserScript>& scripts) {
loader_.RemoveScripts(scripts);
}
void DeclarativeUserScriptMaster::ClearScripts() { void DeclarativeUserScriptMaster::ClearScripts() {
loader_.ClearScripts(); loader_.ClearScripts();
} }
......
...@@ -30,10 +30,18 @@ class DeclarativeUserScriptMaster { ...@@ -30,10 +30,18 @@ class DeclarativeUserScriptMaster {
// script load is in progress. // script load is in progress.
void AddScript(const UserScript& script); void AddScript(const UserScript& script);
// Adds a set of scripts to shared memory region. This may not happen right
// away if a script load is in progress.
void AddScripts(const std::set<UserScript>& scripts);
// Removes script from shared memory region. This may not happen right away if // Removes script from shared memory region. This may not happen right away if
// a script load is in progress. // a script load is in progress.
void RemoveScript(const UserScript& script); void RemoveScript(const UserScript& script);
// Removes a set of scripts from shared memory region. This may not happen
// right away if a script load is in progress.
void RemoveScripts(const std::set<UserScript>& scripts);
// Removes all scripts from shared memory region. This may not happen right // Removes all scripts from shared memory region. This may not happen right
// away if a script load is in progress. // away if a script load is in progress.
void ClearScripts(); void ClearScripts();
......
...@@ -1062,6 +1062,8 @@ enum HistogramValue { ...@@ -1062,6 +1062,8 @@ enum HistogramValue {
DEVELOPERPRIVATE_UPDATEPROFILECONFIGURATION, DEVELOPERPRIVATE_UPDATEPROFILECONFIGURATION,
SOCKETS_UDP_SETBROADCAST, SOCKETS_UDP_SETBROADCAST,
FILEMANAGERPRIVATE_GETPROVIDINGEXTENSIONS, FILEMANAGERPRIVATE_GETPROVIDINGEXTENSIONS,
WEBVIEWINTERNAL_ADDCONTENTSCRIPTS,
WEBVIEWINTERNAL_REMOVECONTENTSCRIPTS,
// Last entry: Add new entries above and ensure to update // Last entry: Add new entries above and ensure to update
// tools/metrics/histograms/histograms.xml. // tools/metrics/histograms/histograms.xml.
ENUM_BOUNDARY ENUM_BOUNDARY
......
...@@ -6,11 +6,15 @@ ...@@ -6,11 +6,15 @@
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h" #include "content/public/browser/render_view_host.h"
#include "extensions/browser/guest_view/guest_view_base.h" #include "extensions/browser/guest_view/guest_view_base.h"
#include "extensions/browser/guest_view/guest_view_manager.h" #include "extensions/browser/guest_view/guest_view_manager.h"
#include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_constants.h" #include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_constants.h"
#include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h" #include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h"
#include "extensions/browser/guest_view/web_view/web_view_content_script_manager.h"
#include "extensions/browser/guest_view/web_view/web_view_guest.h"
#include "extensions/browser/guest_view/web_view/web_view_renderer_state.h"
#include "extensions/common/guest_view/guest_view_messages.h" #include "extensions/common/guest_view/guest_view_messages.h"
#include "ipc/ipc_message_macros.h" #include "ipc/ipc_message_macros.h"
...@@ -61,6 +65,8 @@ bool GuestViewMessageFilter::OnMessageReceived(const IPC::Message& message) { ...@@ -61,6 +65,8 @@ bool GuestViewMessageFilter::OnMessageReceived(const IPC::Message& message) {
IPC_MESSAGE_HANDLER(GuestViewHostMsg_CreateMimeHandlerViewGuest, IPC_MESSAGE_HANDLER(GuestViewHostMsg_CreateMimeHandlerViewGuest,
OnCreateMimeHandlerViewGuest) OnCreateMimeHandlerViewGuest)
IPC_MESSAGE_HANDLER(GuestViewHostMsg_ResizeGuest, OnResizeGuest) IPC_MESSAGE_HANDLER(GuestViewHostMsg_ResizeGuest, OnResizeGuest)
IPC_MESSAGE_HANDLER(GuestViewHostMsg_CanExecuteContentScriptSync,
OnCanExecuteContentScript)
IPC_MESSAGE_UNHANDLED(handled = false) IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP() IPC_END_MESSAGE_MAP()
return handled; return handled;
...@@ -133,6 +139,17 @@ void GuestViewMessageFilter::OnResizeGuest(int render_frame_id, ...@@ -133,6 +139,17 @@ void GuestViewMessageFilter::OnResizeGuest(int render_frame_id,
mhvg->SetSize(set_size_params); mhvg->SetSize(set_size_params);
} }
void GuestViewMessageFilter::OnCanExecuteContentScript(int render_view_id,
int script_id,
bool* allowed) {
WebViewRendererState::WebViewInfo info;
WebViewRendererState::GetInstance()->GetInfo(render_process_id_,
render_view_id, &info);
*allowed =
info.content_script_ids.find(script_id) != info.content_script_ids.end();
}
void GuestViewMessageFilter::MimeHandlerViewGuestCreatedCallback( void GuestViewMessageFilter::MimeHandlerViewGuestCreatedCallback(
int element_instance_id, int element_instance_id,
int embedder_render_process_id, int embedder_render_process_id,
......
...@@ -60,6 +60,10 @@ class GuestViewMessageFilter : public content::BrowserMessageFilter { ...@@ -60,6 +60,10 @@ class GuestViewMessageFilter : public content::BrowserMessageFilter {
int element_instance_id, int element_instance_id,
const gfx::Size& new_size); const gfx::Size& new_size);
void OnCanExecuteContentScript(int render_view_id,
int script_id,
bool* allowed);
// Runs on UI thread. // Runs on UI thread.
void MimeHandlerViewGuestCreatedCallback(int element_instance_id, void MimeHandlerViewGuestCreatedCallback(int element_instance_id,
int embedder_render_process_id, int embedder_render_process_id,
......
...@@ -126,4 +126,7 @@ const uint32 WEB_VIEW_REMOVE_DATA_MASK_INDEXEDDB = 1 << 4; ...@@ -126,4 +126,7 @@ const uint32 WEB_VIEW_REMOVE_DATA_MASK_INDEXEDDB = 1 << 4;
const uint32 WEB_VIEW_REMOVE_DATA_MASK_LOCAL_STORAGE = 1 << 5; const uint32 WEB_VIEW_REMOVE_DATA_MASK_LOCAL_STORAGE = 1 << 5;
const uint32 WEB_VIEW_REMOVE_DATA_MASK_WEBSQL = 1 << 6; const uint32 WEB_VIEW_REMOVE_DATA_MASK_WEBSQL = 1 << 6;
// Other.
const char kWebViewContentScriptManagerKeyName[] =
"web_view_content_script_manager";
} // namespace webview } // namespace webview
...@@ -136,6 +136,9 @@ extern const uint32 WEB_VIEW_REMOVE_DATA_MASK_INDEXEDDB; ...@@ -136,6 +136,9 @@ extern const uint32 WEB_VIEW_REMOVE_DATA_MASK_INDEXEDDB;
extern const uint32 WEB_VIEW_REMOVE_DATA_MASK_LOCAL_STORAGE; extern const uint32 WEB_VIEW_REMOVE_DATA_MASK_LOCAL_STORAGE;
extern const uint32 WEB_VIEW_REMOVE_DATA_MASK_WEBSQL; extern const uint32 WEB_VIEW_REMOVE_DATA_MASK_WEBSQL;
// Other.
extern const char kWebViewContentScriptManagerKeyName[];
} // namespace webview } // namespace webview
#endif // EXTENSIONS_BROWSER_GUEST_VIEW_WEB_VIEW_WEB_VIEW_CONSTANTS_H_ #endif // EXTENSIONS_BROWSER_GUEST_VIEW_WEB_VIEW_WEB_VIEW_CONSTANTS_H_
// 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.
#ifndef EXTENSIONS_BROWSER_GUEST_VIEW_WEB_VIEW_WEB_VIEW_CONTENT_SCRIPT_MANAGER_H
#define EXTENSIONS_BROWSER_GUEST_VIEW_WEB_VIEW_WEB_VIEW_CONTENT_SCRIPT_MANAGER_H
#include <map>
#include <set>
#include <string>
#include <vector>
#include "base/supports_user_data.h"
struct HostID;
namespace content {
class BrowserContext;
class WebContents;
}
namespace extensions {
class UserScript;
// WebViewContentScriptManager manages the content scripts that each webview
// guest adds and removes programmatically.
// TODO(hanxi): crbug.com/476938. Introduce a new class to manage the lifetime
// of <webview> and clean up WebViewContentScriptManager.
class WebViewContentScriptManager : public base::SupportsUserData::Data {
public:
explicit WebViewContentScriptManager(
content::BrowserContext* browser_context);
~WebViewContentScriptManager() override;
static WebViewContentScriptManager* Get(
content::BrowserContext* browser_context);
// Adds content scripts for the guest specified by the |embedder_web_contents,
// view_instance_id|.
void AddContentScripts(content::WebContents* embedder_web_contents,
int view_instance_id,
const HostID& host_id,
const std::set<UserScript>& user_scripts);
// Removes contents scipts whose names are in the |script_name_list| for the
// guest specified by |embedder_web_contents, view_instance_id|.
// If the |script_name_list| is empty, removes all the content scripts added
// for this guest.
void RemoveContentScripts(content::WebContents* embedder_web_contents,
int view_instance_id,
const HostID& host_id,
const std::vector<std::string>& script_name_list);
// Returns the content script IDs added by the guest specified by
// |embedder_process_id, view_instance_id|.
std::set<int> GetContentScriptIDSet(int embedder_process_id,
int view_instance_id);
private:
class OwnerWebContentsObserver;
using GuestMapKey = std::pair<int, int>;
using ContentScriptMap = std::map<std::string, extensions::UserScript>;
using GuestContentScriptMap = std::map<GuestMapKey, ContentScriptMap>;
using OwnerWebContentsObserverMap =
std::map<content::WebContents*, linked_ptr<OwnerWebContentsObserver>>;
// Called by OwnerWebContentsObserver when the observer sees a main frame
// navigation or sees the process has went away or the |embedder_web_contents|
// is being destroyed.
void RemoveObserver(content::WebContents* embedder_web_contents);
OwnerWebContentsObserverMap owner_web_contents_observer_map_;
GuestContentScriptMap guest_content_script_map_;
content::BrowserContext* browser_context_;
DISALLOW_COPY_AND_ASSIGN(WebViewContentScriptManager);
};
} // namespace extensions
#endif // EXTENSIONS_BROWSER_GUEST_VIEW_WEB_VIEW_WEB_VIEW_CONTENT_SCRIPT_MANAGER_H
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
#include "extensions/browser/guest_view/guest_view_event.h" #include "extensions/browser/guest_view/guest_view_event.h"
#include "extensions/browser/guest_view/guest_view_manager.h" #include "extensions/browser/guest_view/guest_view_manager.h"
#include "extensions/browser/guest_view/web_view/web_view_constants.h" #include "extensions/browser/guest_view/web_view/web_view_constants.h"
#include "extensions/browser/guest_view/web_view/web_view_content_script_manager.h"
#include "extensions/browser/guest_view/web_view/web_view_permission_helper.h" #include "extensions/browser/guest_view/web_view/web_view_permission_helper.h"
#include "extensions/browser/guest_view/web_view/web_view_permission_types.h" #include "extensions/browser/guest_view/web_view/web_view_permission_types.h"
#include "extensions/browser/guest_view/web_view/web_view_renderer_state.h" #include "extensions/browser/guest_view/web_view/web_view_renderer_state.h"
...@@ -873,6 +874,13 @@ void WebViewGuest::PushWebViewStateToIOThread() { ...@@ -873,6 +874,13 @@ void WebViewGuest::PushWebViewStateToIOThread() {
web_view_info.owner_extension_id = owner_extension_id(); web_view_info.owner_extension_id = owner_extension_id();
web_view_info.rules_registry_id = rules_registry_id_; web_view_info.rules_registry_id = rules_registry_id_;
// Get content scripts IDs added by the guest.
WebViewContentScriptManager* manager =
WebViewContentScriptManager::Get(browser_context());
DCHECK(manager);
web_view_info.content_script_ids = manager->GetContentScriptIDSet(
web_view_info.embedder_process_id, web_view_info.instance_id);
content::BrowserThread::PostTask( content::BrowserThread::PostTask(
content::BrowserThread::IO, content::BrowserThread::IO,
FROM_HERE, FROM_HERE,
......
...@@ -9,6 +9,12 @@ using content::BrowserThread; ...@@ -9,6 +9,12 @@ using content::BrowserThread;
namespace extensions { namespace extensions {
WebViewRendererState::WebViewInfo::WebViewInfo() {
}
WebViewRendererState::WebViewInfo::~WebViewInfo() {
}
// static // static
WebViewRendererState* WebViewRendererState::GetInstance() { WebViewRendererState* WebViewRendererState::GetInstance() {
return Singleton<WebViewRendererState>::get(); return Singleton<WebViewRendererState>::get();
...@@ -103,4 +109,38 @@ bool WebViewRendererState::GetPartitionID(int guest_process_id, ...@@ -103,4 +109,38 @@ bool WebViewRendererState::GetPartitionID(int guest_process_id,
return false; return false;
} }
void WebViewRendererState::AddContentScriptIDs(
int embedder_process_id,
int view_instance_id,
const std::set<int>& script_ids) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
for (auto& render_id_info : web_view_info_map_) {
WebViewInfo& info = render_id_info.second;
if (info.embedder_process_id == embedder_process_id &&
info.instance_id == view_instance_id) {
for (int id : script_ids)
info.content_script_ids.insert(id);
return;
}
}
}
void WebViewRendererState::RemoveContentScriptIDs(
int embedder_process_id,
int view_instance_id,
const std::set<int>& script_ids) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
for (auto& render_id_info : web_view_info_map_) {
WebViewInfo& info = render_id_info.second;
if (info.embedder_process_id == embedder_process_id &&
info.instance_id == view_instance_id) {
for (int id : script_ids)
info.content_script_ids.erase(id);
return;
}
}
}
} // namespace extensions } // namespace extensions
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#define EXTENSIONS_BROWSER_GUEST_VIEW_WEB_VIEW_WEB_VIEW_RENDERER_STATE_H_ #define EXTENSIONS_BROWSER_GUEST_VIEW_WEB_VIEW_WEB_VIEW_RENDERER_STATE_H_
#include <map> #include <map>
#include <set>
#include <string> #include <string>
#include <utility> #include <utility>
...@@ -25,6 +26,10 @@ class WebViewRendererState { ...@@ -25,6 +26,10 @@ class WebViewRendererState {
int rules_registry_id; int rules_registry_id;
std::string partition_id; std::string partition_id;
std::string owner_extension_id; std::string owner_extension_id;
std::set<int> content_script_ids;
WebViewInfo();
~WebViewInfo();
}; };
static WebViewRendererState* GetInstance(); static WebViewRendererState* GetInstance();
...@@ -47,6 +52,13 @@ class WebViewRendererState { ...@@ -47,6 +52,13 @@ class WebViewRendererState {
// Returns true if the given renderer is used by webviews. // Returns true if the given renderer is used by webviews.
bool IsGuest(int render_process_id); bool IsGuest(int render_process_id);
void AddContentScriptIDs(int embedder_process_id,
int view_instance_id,
const std::set<int>& script_ids);
void RemoveContentScriptIDs(int embedder_process_id,
int view_instance_id,
const std::set<int>& script_ids);
private: private:
friend class WebViewGuest; friend class WebViewGuest;
friend struct DefaultSingletonTraits<WebViewRendererState>; friend struct DefaultSingletonTraits<WebViewRendererState>;
......
...@@ -444,25 +444,10 @@ void UserScriptLoader::OnScriptsLoaded( ...@@ -444,25 +444,10 @@ void UserScriptLoader::OnScriptsLoaded(
// We've got scripts ready to go. // We've got scripts ready to go.
shared_memory_.reset(shared_memory.release()); shared_memory_.reset(shared_memory.release());
// If user scripts are coming from a <webview>, will only notify the for (content::RenderProcessHost::iterator i(
// RenderProcessHost of that <webview>; otherwise will notify all of the content::RenderProcessHost::AllHostsIterator());
// RenderProcessHosts. !i.IsAtEnd(); i.Advance()) {
if (user_scripts_ && !user_scripts_->empty() && SendUpdate(i.GetCurrentValue(), shared_memory_.get(), changed_hosts_);
(*user_scripts_)[0].consumer_instance_type() ==
UserScript::ConsumerInstanceType::WEBVIEW) {
DCHECK_EQ(1u, user_scripts_->size());
int render_process_id =
(*user_scripts_)[0].routing_info().render_process_id;
content::RenderProcessHost* host =
content::RenderProcessHost::FromID(render_process_id);
if (host)
SendUpdate(host, shared_memory_.get(), changed_hosts_);
} else {
for (content::RenderProcessHost::iterator i(
content::RenderProcessHost::AllHostsIterator());
!i.IsAtEnd(); i.Advance()) {
SendUpdate(i.GetCurrentValue(), shared_memory_.get(), changed_hosts_);
}
} }
changed_hosts_.clear(); changed_hosts_.clear();
...@@ -475,8 +460,8 @@ void UserScriptLoader::OnScriptsLoaded( ...@@ -475,8 +460,8 @@ void UserScriptLoader::OnScriptsLoaded(
void UserScriptLoader::SendUpdate(content::RenderProcessHost* process, void UserScriptLoader::SendUpdate(content::RenderProcessHost* process,
base::SharedMemory* shared_memory, base::SharedMemory* shared_memory,
const std::set<HostID>& changed_hosts) { const std::set<HostID>& changed_hosts) {
// Don't allow injection of content scripts into <webview>. // Don't allow injection of extensions' content scripts into <webview>.
if (process->IsIsolatedGuest()) if (process->IsIsolatedGuest() && host_id().id().empty())
return; return;
// Make sure we only send user scripts to processes in our browser_context. // Make sure we only send user scripts to processes in our browser_context.
......
...@@ -93,6 +93,74 @@ ...@@ -93,6 +93,74 @@
"id": "SetPermissionAction", "id": "SetPermissionAction",
"type": "string", "type": "string",
"enum": ["allow", "deny", "default"] "enum": ["allow", "deny", "default"]
},
{
"id": "RunLocation",
"type": "string",
"enum": ["document_start", "document_end", "document_idle"]
},
{
"id": "ContentScriptDetails",
"type": "object",
"description": "Details of the content script to inject.",
"properties": {
"name": {
"type": "string",
"description": "The name of the content script to inject."
},
"matches": {
"type": "array",
"items": { "type": "string"},
"description": "Specifies which pages this content script will be injected into."
},
"exclude_matches": {
"type": "array",
"items": { "type": "string"},
"optional": true,
"description": "Excludes pages that this content script would otherwise be injected into."
},
"match_about_blank": {
"type": "boolean",
"optional": true,
"description": "Whether to insert the content script on about:blank and about:srcdoc. Content scripts will only be injected on pages when their inherit URL is matched by one of the declared patterns in the matches field. The inherit URL is the URL of the document that created the frame or window. Content scripts cannot be inserted in sandboxed frames."
},
"css": {
"type": "array",
"items": { "type": "string"},
"optional": true,
"description": "The list of CSS files to be injected into matching pages. These are injected in the order they appear in this array, before any DOM is constructed or displayed for the page."
},
"js": {
"type": "array",
"items": { "type": "string"},
"optional": true,
"description": "The list of JavaScript files to be injected into matching pages. These are injected in the order they appear in this array."
},
"run_at": {
// TODO(hanxi): change the reference to extensionTypes.RunAt after http://crbug.com/477457 is fixed.
"$ref": "RunLocation",
"optional": true,
"description": "The soonest that the JavaScript or CSS will be injected into the tab. Defaults to \"document_idle\"."
},
"all_frames": {
"type": "boolean",
"optional": true,
"description": "If allFrames is <code>true</code>, implies that the JavaScript or CSS should be injected into all frames of current page. By default, it's <code>false</code> and is only injected into the top frame."
},
"include_globs": {
"type": "array",
"items": { "type": "string"},
"optional": true,
"description": "Applied after matches to include only those URLs that also match this glob. Intended to emulate the @include Greasemonkey keyword."
},
"exclude_globs": {
"type": "array",
"items": { "type": "string"},
"optional": true,
"description": "Applied after matches to exclude URLs that match this glob. Intended to emulate the @exclude Greasemonkey keyword."
}
},
"required": ["name", "matches"]
} }
], ],
"functions": [ "functions": [
...@@ -162,6 +230,65 @@ ...@@ -162,6 +230,65 @@
} }
] ]
}, },
{
"name": "addContentScripts",
"type": "function",
"description": "Adds content scripts into a <webview> page. For details, see the <a href='/extensions/content_scripts#pi'>programmatic injection</a> section of the content scripts doc.",
"parameters": [
{
"type": "integer",
"name": "instanceId",
"description": "The instance ID of the guest <webview> process."
},
{
"type": "array",
"name": "contentScriptList",
"items": {
"$ref": "ContentScriptDetails",
"name": "contentScriptDetails",
"minimum": 1
},
"description": "Details of the content scripts to add."
},
{
"type": "function",
"name": "callback",
"optional": true,
"description": "Called when all the content scripts has been added.",
"parameters": []
}
]
},
{
"name": "removeContentScripts",
"type": "function",
"description": "Removes specified content scripts from a <webview> page. For details, see the <a href='/extensions/content_scripts#pi'>programmatic injection</a> section of the content scripts doc.",
"parameters": [
{
"type": "integer",
"name": "instanceId",
"description": "The instance ID of the guest <webview> process."
},
{
"type": "array",
"name": "scriptNameList",
"items": {
"type": "string",
"minimum": 0,
"description": "The name of a content script that will be removed."
},
"optional": true,
"description": "A list of names of content scripts that will be removed. If the list is empty, all the content scripts added to the <webview> page will be removed."
},
{
"type": "function",
"name": "callback",
"optional": true,
"description": "Called when all the content scripts has been removed.",
"parameters": []
}
]
},
{ {
"name": "setZoom", "name": "setZoom",
"type": "function", "type": "function",
......
...@@ -59,3 +59,8 @@ IPC_MESSAGE_CONTROL3(GuestViewHostMsg_ResizeGuest, ...@@ -59,3 +59,8 @@ IPC_MESSAGE_CONTROL3(GuestViewHostMsg_ResizeGuest,
int /* routing_id */, int /* routing_id */,
int /* element_instance_id*/, int /* element_instance_id*/,
gfx::Size /* new_size */) gfx::Size /* new_size */)
IPC_SYNC_MESSAGE_CONTROL2_1(GuestViewHostMsg_CanExecuteContentScriptSync,
int /* routing_id */,
int /* script_id */,
bool /* allowed */)
...@@ -147,7 +147,6 @@ void UserScript::Pickle(::Pickle* pickle) const { ...@@ -147,7 +147,6 @@ void UserScript::Pickle(::Pickle* pickle) const {
PickleHostID(pickle, host_id_); PickleHostID(pickle, host_id_);
pickle->WriteInt(consumer_instance_type()); pickle->WriteInt(consumer_instance_type());
PickleRoutingInfo(pickle, routing_info_);
PickleGlobs(pickle, globs_); PickleGlobs(pickle, globs_);
PickleGlobs(pickle, exclude_globs_); PickleGlobs(pickle, exclude_globs_);
PickleURLPatternSet(pickle, url_set_); PickleURLPatternSet(pickle, url_set_);
...@@ -170,12 +169,6 @@ void UserScript::PickleHostID(::Pickle* pickle, const HostID& host_id) const { ...@@ -170,12 +169,6 @@ void UserScript::PickleHostID(::Pickle* pickle, const HostID& host_id) const {
pickle->WriteString(host_id.id()); pickle->WriteString(host_id.id());
} }
void UserScript::PickleRoutingInfo(::Pickle* pickle,
const RoutingInfo& routing_info) const {
pickle->WriteInt(routing_info.render_process_id);
pickle->WriteInt(routing_info.render_view_id);
}
void UserScript::PickleURLPatternSet(::Pickle* pickle, void UserScript::PickleURLPatternSet(::Pickle* pickle,
const URLPatternSet& pattern_list) const { const URLPatternSet& pattern_list) const {
pickle->WriteSizeT(pattern_list.patterns().size()); pickle->WriteSizeT(pattern_list.patterns().size());
...@@ -215,7 +208,6 @@ void UserScript::Unpickle(const ::Pickle& pickle, PickleIterator* iter) { ...@@ -215,7 +208,6 @@ void UserScript::Unpickle(const ::Pickle& pickle, PickleIterator* iter) {
consumer_instance_type_ = consumer_instance_type_ =
static_cast<ConsumerInstanceType>(consumer_instance_type); static_cast<ConsumerInstanceType>(consumer_instance_type);
UnpickleRoutingInfo(pickle, iter, &routing_info_);
UnpickleGlobs(pickle, iter, &globs_); UnpickleGlobs(pickle, iter, &globs_);
UnpickleGlobs(pickle, iter, &exclude_globs_); UnpickleGlobs(pickle, iter, &exclude_globs_);
UnpickleURLPatternSet(pickle, iter, &url_set_); UnpickleURLPatternSet(pickle, iter, &url_set_);
...@@ -246,13 +238,6 @@ void UserScript::UnpickleHostID(const ::Pickle& pickle, ...@@ -246,13 +238,6 @@ void UserScript::UnpickleHostID(const ::Pickle& pickle,
*host_id = HostID(static_cast<HostID::HostType>(type), id); *host_id = HostID(static_cast<HostID::HostType>(type), id);
} }
void UserScript::UnpickleRoutingInfo(const ::Pickle& pickle,
PickleIterator* iter,
RoutingInfo* routing_info) {
CHECK(iter->ReadInt(&routing_info->render_process_id));
CHECK(iter->ReadInt(&routing_info->render_view_id));
}
void UserScript::UnpickleURLPatternSet(const ::Pickle& pickle, void UserScript::UnpickleURLPatternSet(const ::Pickle& pickle,
PickleIterator* iter, PickleIterator* iter,
URLPatternSet* pattern_list) { URLPatternSet* pattern_list) {
......
...@@ -126,19 +126,6 @@ class UserScript { ...@@ -126,19 +126,6 @@ class UserScript {
typedef std::vector<File> FileList; typedef std::vector<File> FileList;
// Render's routing info of a <webview> that the user script will be injected
// on. Only user scripts from <webview>s have a custom routing info.
struct RoutingInfo {
RoutingInfo() : render_process_id(-1), render_view_id(-1) {}
RoutingInfo(int render_process_id, int render_view_id)
: render_process_id(render_process_id),
render_view_id(render_view_id) {}
~RoutingInfo() {}
int render_process_id;
int render_view_id;
};
// Type of a API consumer instance that user scripts will be injected on. // Type of a API consumer instance that user scripts will be injected on.
enum ConsumerInstanceType { TAB, WEBVIEW }; enum ConsumerInstanceType { TAB, WEBVIEW };
...@@ -224,11 +211,6 @@ class UserScript { ...@@ -224,11 +211,6 @@ class UserScript {
consumer_instance_type_ = consumer_instance_type; consumer_instance_type_ = consumer_instance_type;
} }
const RoutingInfo& routing_info() const { return routing_info_; }
void set_routing_info(const RoutingInfo& routing_info) {
routing_info_ = routing_info;
}
int id() const { return user_script_id_; } int id() const { return user_script_id_; }
void set_id(int id) { user_script_id_ = id; } void set_id(int id) { user_script_id_ = id; }
...@@ -255,8 +237,6 @@ class UserScript { ...@@ -255,8 +237,6 @@ class UserScript {
void PickleGlobs(::Pickle* pickle, void PickleGlobs(::Pickle* pickle,
const std::vector<std::string>& globs) const; const std::vector<std::string>& globs) const;
void PickleHostID(::Pickle* pickle, const HostID& host_id) const; void PickleHostID(::Pickle* pickle, const HostID& host_id) const;
void PickleRoutingInfo(::Pickle* pickle,
const RoutingInfo& routing_info) const;
void PickleURLPatternSet(::Pickle* pickle, void PickleURLPatternSet(::Pickle* pickle,
const URLPatternSet& pattern_list) const; const URLPatternSet& pattern_list) const;
void PickleScripts(::Pickle* pickle, const FileList& scripts) const; void PickleScripts(::Pickle* pickle, const FileList& scripts) const;
...@@ -267,9 +247,6 @@ class UserScript { ...@@ -267,9 +247,6 @@ class UserScript {
void UnpickleHostID(const ::Pickle& pickle, void UnpickleHostID(const ::Pickle& pickle,
PickleIterator* iter, PickleIterator* iter,
HostID* host_id); HostID* host_id);
void UnpickleRoutingInfo(const ::Pickle& pickle,
PickleIterator* iter,
RoutingInfo* routing_info);
void UnpickleURLPatternSet(const ::Pickle& pickle, PickleIterator* iter, void UnpickleURLPatternSet(const ::Pickle& pickle, PickleIterator* iter,
URLPatternSet* pattern_list); URLPatternSet* pattern_list);
void UnpickleScripts(const ::Pickle& pickle, PickleIterator* iter, void UnpickleScripts(const ::Pickle& pickle, PickleIterator* iter,
...@@ -315,9 +292,6 @@ class UserScript { ...@@ -315,9 +292,6 @@ class UserScript {
// The type of the consumer instance that the script will be injected. // The type of the consumer instance that the script will be injected.
ConsumerInstanceType consumer_instance_type_; ConsumerInstanceType consumer_instance_type_;
// The render side's rounting info for content_scripts of <webview>.
RoutingInfo routing_info_;
// The globally-unique id associated with this user script. Defaults to // The globally-unique id associated with this user script. Defaults to
// -1 for invalid. // -1 for invalid.
int user_script_id_; int user_script_id_;
......
...@@ -662,6 +662,8 @@ ...@@ -662,6 +662,8 @@
'browser/guest_view/web_view/javascript_dialog_helper.h', 'browser/guest_view/web_view/javascript_dialog_helper.h',
'browser/guest_view/web_view/web_view_constants.cc', 'browser/guest_view/web_view/web_view_constants.cc',
'browser/guest_view/web_view/web_view_constants.h', 'browser/guest_view/web_view/web_view_constants.h',
'browser/guest_view/web_view/web_view_content_script_manager.cc',
'browser/guest_view/web_view/web_view_content_script_manager.h',
'browser/guest_view/web_view/web_view_find_helper.cc', 'browser/guest_view/web_view/web_view_find_helper.cc',
'browser/guest_view/web_view/web_view_find_helper.h', 'browser/guest_view/web_view/web_view_find_helper.h',
'browser/guest_view/web_view/web_view_guest.cc', 'browser/guest_view/web_view/web_view_guest.cc',
......
...@@ -11,6 +11,9 @@ var WebViewImpl = require('webView').WebViewImpl; ...@@ -11,6 +11,9 @@ var WebViewImpl = require('webView').WebViewImpl;
// implementations will be given default implementations. Default // implementations will be given default implementations. Default
// implementations come from createDefaultApiMethod() in web_view.js. // implementations come from createDefaultApiMethod() in web_view.js.
var WEB_VIEW_API_METHODS = [ var WEB_VIEW_API_METHODS = [
// Add content scripts for the guest page.
'addContentScripts',
// Navigates to the previous history entry. // Navigates to the previous history entry.
'back', 'back',
...@@ -64,6 +67,9 @@ var WEB_VIEW_API_METHODS = [ ...@@ -64,6 +67,9 @@ var WEB_VIEW_API_METHODS = [
// Prints the contents of the webview. // Prints the contents of the webview.
'print', 'print',
// Removes content scripts for the guest page.
'removeContentScripts',
// Reloads the current top-level page. // Reloads the current top-level page.
'reload', 'reload',
...@@ -89,6 +95,11 @@ var WEB_VIEW_API_METHODS = [ ...@@ -89,6 +95,11 @@ var WEB_VIEW_API_METHODS = [
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Custom API method implementations. // Custom API method implementations.
WebViewImpl.prototype.addContentScripts = function() {
var args = $Array.concat([this.viewInstanceId], $Array.slice(arguments));
return $Function.apply(WebViewInternal.addContentScripts, null, args);
};
WebViewImpl.prototype.back = function(callback) { WebViewImpl.prototype.back = function(callback) {
return this.go(-1, callback); return this.go(-1, callback);
}; };
...@@ -148,6 +159,11 @@ WebViewImpl.prototype.print = function() { ...@@ -148,6 +159,11 @@ WebViewImpl.prototype.print = function() {
return this.executeScript({code: 'window.print();'}); return this.executeScript({code: 'window.print();'});
}; };
WebViewImpl.prototype.removeContentScripts = function(var_args) {
var args = $Array.concat([this.viewInstanceId], $Array.slice(arguments));
return $Function.apply(WebViewInternal.removeContentScripts, null, args);
};
WebViewImpl.prototype.setUserAgentOverride = function(userAgentOverride) { WebViewImpl.prototype.setUserAgentOverride = function(userAgentOverride) {
this.userAgentOverride = userAgentOverride; this.userAgentOverride = userAgentOverride;
if (!this.guest.getId()) { if (!this.guest.getId()) {
......
...@@ -8,8 +8,10 @@ ...@@ -8,8 +8,10 @@
#include "base/lazy_instance.h" #include "base/lazy_instance.h"
#include "content/public/common/url_constants.h" #include "content/public/common/url_constants.h"
#include "content/public/renderer/render_thread.h"
#include "content/public/renderer/render_view.h" #include "content/public/renderer/render_view.h"
#include "extensions/common/extension.h" #include "extensions/common/extension.h"
#include "extensions/common/guest_view/guest_view_messages.h"
#include "extensions/common/permissions/permissions_data.h" #include "extensions/common/permissions/permissions_data.h"
#include "extensions/renderer/injection_host.h" #include "extensions/renderer/injection_host.h"
#include "extensions/renderer/script_context.h" #include "extensions/renderer/script_context.h"
...@@ -25,11 +27,40 @@ namespace extensions { ...@@ -25,11 +27,40 @@ namespace extensions {
namespace { namespace {
struct RoutingInfoKey {
int routing_id;
int script_id;
RoutingInfoKey(int routing_id, int script_id)
: routing_id(routing_id), script_id(script_id) {}
bool operator<(const RoutingInfoKey& other) const {
if (routing_id != other.routing_id)
return routing_id < other.routing_id;
if (script_id != other.script_id)
return script_id < other.script_id;
return false; // keys are equal.
}
};
using RoutingInfoMap = std::map<RoutingInfoKey, bool>;
// These two strings are injected before and after the Greasemonkey API and // These two strings are injected before and after the Greasemonkey API and
// user script to wrap it in an anonymous scope. // user script to wrap it in an anonymous scope.
const char kUserScriptHead[] = "(function (unsafeWindow) {\n"; const char kUserScriptHead[] = "(function (unsafeWindow) {\n";
const char kUserScriptTail[] = "\n})(window);"; const char kUserScriptTail[] = "\n})(window);";
// A map records whether a given |script_id| from a webview-added user script
// is allowed to inject on the render of given |routing_id|.
// Once a script is added, the decision of whether or not allowed to inject
// won't be changed.
// After removed by the webview, the user scipt will also be removed
// from the render. Therefore, there won't be any query from the same
// |script_id| and |routing_id| pair.
base::LazyInstance<RoutingInfoMap> g_routing_info_map =
LAZY_INSTANCE_INITIALIZER;
// Greasemonkey API source that is injected with the scripts. // Greasemonkey API source that is injected with the scripts.
struct GreasemonkeyApiJsString { struct GreasemonkeyApiJsString {
GreasemonkeyApiJsString(); GreasemonkeyApiJsString();
...@@ -131,17 +162,35 @@ PermissionsData::AccessType UserScriptInjector::CanExecuteOnFrame( ...@@ -131,17 +162,35 @@ PermissionsData::AccessType UserScriptInjector::CanExecuteOnFrame(
web_frame, web_frame->document().url(), script_->match_about_blank()); web_frame, web_frame->document().url(), script_->match_about_blank());
PermissionsData::AccessType can_execute = injection_host->CanExecuteOnFrame( PermissionsData::AccessType can_execute = injection_host->CanExecuteOnFrame(
effective_document_url, top_url, tab_id, is_declarative_); effective_document_url, top_url, tab_id, is_declarative_);
if (script_->consumer_instance_type() != if (script_->consumer_instance_type() !=
UserScript::ConsumerInstanceType::WEBVIEW || UserScript::ConsumerInstanceType::WEBVIEW ||
can_execute == PermissionsData::ACCESS_DENIED) can_execute == PermissionsData::ACCESS_DENIED)
return can_execute; return can_execute;
int routing_id = content::RenderView::FromWebView(web_frame->top()->view()) int routing_id = content::RenderView::FromWebView(web_frame->top()->view())
->GetRoutingID(); ->GetRoutingID();
return script_->routing_info().render_view_id == routing_id
? PermissionsData::ACCESS_ALLOWED RoutingInfoKey key(routing_id, script_->id());
: PermissionsData::ACCESS_DENIED;
RoutingInfoMap& map = g_routing_info_map.Get();
auto iter = map.find(key);
bool allowed = false;
if (iter != map.end()) {
allowed = iter->second;
} else {
// Send a SYNC IPC message to the browser to check if this is allowed. This
// is not ideal, but is mitigated by the fact that this is only done for
// webviews, and then only once per host.
// TODO(hanxi): Find a more efficient way to do this.
content::RenderThread::Get()->Send(
new GuestViewHostMsg_CanExecuteContentScriptSync(
routing_id, script_->id(), &allowed));
map.insert(std::pair<RoutingInfoKey, bool>(key, allowed));
}
return allowed ? PermissionsData::ACCESS_ALLOWED
: PermissionsData::ACCESS_DENIED;
} }
std::vector<blink::WebScriptSource> UserScriptInjector::GetJsSources( std::vector<blink::WebScriptSource> UserScriptInjector::GetJsSources(
......
...@@ -17,7 +17,8 @@ ...@@ -17,7 +17,8 @@
namespace extensions { namespace extensions {
UserScriptSetManager::UserScriptSetManager(const ExtensionSet* extensions) UserScriptSetManager::UserScriptSetManager(const ExtensionSet* extensions)
: static_scripts_(extensions), extensions_(extensions) { : static_scripts_(extensions),
extensions_(extensions) {
content::RenderThread::Get()->AddObserver(this); content::RenderThread::Get()->AddObserver(this);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment