Introduce ScriptInjection in extensions/renderer to inject UserScripts

UserScriptSlave was responsible for too much, and the logic was getting
cluttered (which also made changes in, e.g.
https://codereview.chromium.org/288053002/ difficult to read, write, and
review). Refactor the logic to inject the scripts into a new class, so that
UserScriptSlave is pretty much just responsible for containing them.

BUG=373961

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

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@271532 0039d316-1c4b-4281-b951-d872f2087c98
parent 1b1e9eff
...@@ -533,6 +533,8 @@ ...@@ -533,6 +533,8 @@
'renderer/script_context.h', 'renderer/script_context.h',
'renderer/script_context_set.cc', 'renderer/script_context_set.cc',
'renderer/script_context_set.h', 'renderer/script_context_set.h',
'renderer/script_injection.cc',
'renderer/script_injection.h',
'renderer/static_v8_external_ascii_string_resource.cc', 'renderer/static_v8_external_ascii_string_resource.cc',
'renderer/static_v8_external_ascii_string_resource.h', 'renderer/static_v8_external_ascii_string_resource.h',
'renderer/test_features_native_handler.cc', 'renderer/test_features_native_handler.cc',
......
// Copyright 2014 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 "extensions/renderer/script_injection.h"
#include <vector>
#include "base/lazy_instance.h"
#include "base/metrics/histogram.h"
#include "content/public/common/url_constants.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_messages.h"
#include "extensions/common/permissions/permissions_data.h"
#include "extensions/renderer/dom_activity_logger.h"
#include "extensions/renderer/extension_groups.h"
#include "extensions/renderer/script_context.h"
#include "extensions/renderer/user_script_slave.h"
#include "grit/renderer_resources.h"
#include "third_party/WebKit/public/web/WebDocument.h"
#include "third_party/WebKit/public/web/WebFrame.h"
#include "third_party/WebKit/public/web/WebScriptSource.h"
#include "ui/base/resource/resource_bundle.h"
#include "url/gurl.h"
namespace extensions {
namespace {
// These two strings are injected before and after the Greasemonkey API and
// user script to wrap it in an anonymous scope.
const char kUserScriptHead[] = "(function (unsafeWindow) {\n";
const char kUserScriptTail[] = "\n})(window);";
// Greasemonkey API source that is injected with the scripts.
struct GreasemonkeyApiJsString {
GreasemonkeyApiJsString();
blink::WebScriptSource source;
};
// The below constructor, monstrous as it is, just makes a WebScriptSource from
// the GreasemonkeyApiJs resource.
GreasemonkeyApiJsString::GreasemonkeyApiJsString()
: source(blink::WebScriptSource(blink::WebString::fromUTF8(
ResourceBundle::GetSharedInstance().GetRawDataResource(
IDR_GREASEMONKEY_API_JS).as_string()))) {
}
base::LazyInstance<GreasemonkeyApiJsString> g_greasemonkey_api =
LAZY_INSTANCE_INITIALIZER;
} // namespace
ScriptInjection::ScriptsRunInfo::ScriptsRunInfo() : num_css(0u), num_js(0u) {
}
ScriptInjection::ScriptsRunInfo::~ScriptsRunInfo() {
}
// static
GURL ScriptInjection::GetDocumentUrlForFrame(blink::WebFrame* frame) {
GURL data_source_url = ScriptContext::GetDataSourceURLForFrame(frame);
if (!data_source_url.is_empty() && frame->isViewSourceModeEnabled()) {
data_source_url = GURL(content::kViewSourceScheme + std::string(":") +
data_source_url.spec());
}
return data_source_url;
}
ScriptInjection::ScriptInjection(
scoped_ptr<UserScript> script,
UserScriptSlave* user_script_slave)
: script_(script.Pass()),
extension_id_(script_->extension_id()),
user_script_slave_(user_script_slave),
is_standalone_or_emulate_greasemonkey_(
script_->is_standalone() || script_->emulate_greasemonkey()) {
}
ScriptInjection::~ScriptInjection() {
}
bool ScriptInjection::WantsToRun(blink::WebFrame* frame,
UserScript::RunLocation run_location,
const GURL& document_url) const {
if (frame->parent() && !script_->match_all_frames())
return false; // Only match subframes if the script declared it wanted to.
const Extension* extension = user_script_slave_->GetExtension(extension_id_);
// Since extension info is sent separately from user script info, they can
// be out of sync. We just ignore this situation.
if (!extension)
return false;
// Content scripts are not tab-specific.
static const int kNoTabId = -1;
// We don't have a process id in this context.
static const int kNoProcessId = -1;
GURL effective_document_url = ScriptContext::GetEffectiveDocumentURL(
frame, document_url, script_->match_about_blank());
if (!PermissionsData::CanExecuteScriptOnPage(extension,
effective_document_url,
frame->top()->document().url(),
kNoTabId,
script_.get(),
kNoProcessId,
NULL /* ignore error */)) {
return false;
}
return ShouldInjectCSS(run_location) || ShouldInjectJS(run_location);
}
void ScriptInjection::Inject(blink::WebFrame* frame,
UserScript::RunLocation run_location,
ScriptsRunInfo* scripts_run_info) const {
DCHECK(frame);
DCHECK(scripts_run_info);
DCHECK(WantsToRun(frame, run_location, GetDocumentUrlForFrame(frame)));
DCHECK(user_script_slave_->GetExtension(extension_id_));
if (ShouldInjectCSS(run_location))
InjectCSS(frame, scripts_run_info);
if (ShouldInjectJS(run_location))
InjectJS(frame, scripts_run_info);
}
bool ScriptInjection::ShouldInjectJS(UserScript::RunLocation run_location)
const {
return !script_->js_scripts().empty() &&
script_->run_location() == run_location;
}
bool ScriptInjection::ShouldInjectCSS(UserScript::RunLocation run_location)
const {
return !script_->css_scripts().empty() &&
run_location == UserScript::DOCUMENT_START;
}
void ScriptInjection::InjectJS(blink::WebFrame* frame,
ScriptsRunInfo* scripts_run_info) const {
const UserScript::FileList& js_scripts = script_->js_scripts();
std::vector<blink::WebScriptSource> sources;
scripts_run_info->num_js += js_scripts.size();
for (UserScript::FileList::const_iterator iter = js_scripts.begin();
iter != js_scripts.end();
++iter) {
std::string content = iter->GetContent().as_string();
// We add this dumb function wrapper for standalone user script to
// emulate what Greasemonkey does.
// TODO(aa): I think that maybe "is_standalone" scripts don't exist
// anymore. Investigate.
if (is_standalone_or_emulate_greasemonkey_) {
content.insert(0, kUserScriptHead);
content += kUserScriptTail;
}
sources.push_back(blink::WebScriptSource(
blink::WebString::fromUTF8(content), iter->url()));
}
// Emulate Greasemonkey API for scripts that were converted to extensions
// and "standalone" user scripts.
if (is_standalone_or_emulate_greasemonkey_)
sources.insert(sources.begin(), g_greasemonkey_api.Get().source);
int isolated_world_id =
user_script_slave_->GetIsolatedWorldIdForExtension(
user_script_slave_->GetExtension(extension_id_), frame);
base::ElapsedTimer exec_timer;
DOMActivityLogger::AttachToWorld(isolated_world_id, extension_id_);
frame->executeScriptInIsolatedWorld(isolated_world_id,
&sources.front(),
sources.size(),
EXTENSION_GROUP_CONTENT_SCRIPTS);
UMA_HISTOGRAM_TIMES("Extensions.InjectScriptTime", exec_timer.Elapsed());
for (std::vector<blink::WebScriptSource>::const_iterator iter =
sources.begin();
iter != sources.end();
++iter) {
scripts_run_info->executing_scripts[extension_id_].insert(
GURL(iter->url).path());
}
}
void ScriptInjection::InjectCSS(blink::WebFrame* frame,
ScriptsRunInfo* scripts_run_info) const {
const UserScript::FileList& css_scripts = script_->css_scripts();
scripts_run_info->num_css += css_scripts.size();
for (UserScript::FileList::const_iterator iter = css_scripts.begin();
iter != css_scripts.end();
++iter) {
frame->document().insertStyleSheet(
blink::WebString::fromUTF8(iter->GetContent().as_string()));
}
}
} // namespace extensions
// Copyright 2014 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_RENDERER_SCRIPT_INJECTION_H_
#define EXTENSIONS_RENDERER_SCRIPT_INJECTION_H_
#include <map>
#include <set>
#include <string>
#include "base/macros.h"
#include "base/memory/scoped_ptr.h"
#include "base/timer/elapsed_timer.h"
#include "extensions/common/user_script.h"
class GURL;
namespace blink {
class WebFrame;
}
namespace extensions {
class UserScriptSlave;
// This class is a wrapper around a UserScript that knows how to inject itself
// into a frame.
class ScriptInjection {
public:
// Map of extensions IDs to the executing script paths.
typedef std::map<std::string, std::set<std::string> > ExecutingScriptsMap;
// A struct containing information about a script run.
struct ScriptsRunInfo {
ScriptsRunInfo();
~ScriptsRunInfo();
// The number of CSS scripts injected.
size_t num_css;
// The number of JS scripts injected.
size_t num_js;
// A map of extension ids to executing script paths.
ExecutingScriptsMap executing_scripts;
// The elapsed time since the ScriptsRunInfo was constructed.
base::ElapsedTimer timer;
private:
DISALLOW_COPY_AND_ASSIGN(ScriptsRunInfo);
};
// Return the URL to use as the document url when checking permissions for
// script injection.
static GURL GetDocumentUrlForFrame(blink::WebFrame* frame);
ScriptInjection(scoped_ptr<UserScript> script,
UserScriptSlave* user_script_slave);
~ScriptInjection();
// Returns true if this ScriptInjection wants to run on the given |frame| at
// the given |run_location| (i.e., if this script would inject either JS or
// CSS).
bool WantsToRun(blink::WebFrame* frame,
UserScript::RunLocation run_location,
const GURL& document_url) const;
// Injects the script into the given |frame|, and updates |scripts_run_info|
// information about the run.
void Inject(blink::WebFrame* frame,
UserScript::RunLocation run_location,
ScriptsRunInfo* scripts_run_info) const;
const std::string& extension_id() { return extension_id_; }
private:
// Returns true if the script will inject [css|js] at the given
// |run_location|.
bool ShouldInjectJS(UserScript::RunLocation run_location) const;
bool ShouldInjectCSS(UserScript::RunLocation run_location) const;
// Injects the [css|js] scripts into the frame, and stores the results of
// the run in |scripts_run_info|.
void InjectJS(blink::WebFrame* frame, ScriptsRunInfo* scripts_run_info) const;
void InjectCSS(blink::WebFrame* frame, ScriptsRunInfo* scripts_run_info)
const;
// The UserScript this is injecting.
scoped_ptr<UserScript> script_;
// The associated extension's id. This is a safe const&, since it is owned by
// the |user_script_|.
const std::string& extension_id_;
// The associated UserScriptSlave.
// It's unfortunate that this is needed, but we use it to get the isolated
// world ids and the associated extensions.
// TODO(rdevlin.cronin): It would be nice to clean this up more.
UserScriptSlave* user_script_slave_;
// True if the script is a standalone script or emulates greasemonkey.
bool is_standalone_or_emulate_greasemonkey_;
DISALLOW_COPY_AND_ASSIGN(ScriptInjection);
};
} // namespace extensions
#endif // EXTENSIONS_RENDERER_SCRIPT_INJECTION_H_
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include "third_party/WebKit/public/web/WebDocument.h" #include "third_party/WebKit/public/web/WebDocument.h"
#include "third_party/WebKit/public/web/WebFrame.h" #include "third_party/WebKit/public/web/WebFrame.h"
#include "third_party/WebKit/public/web/WebScopedUserGesture.h" #include "third_party/WebKit/public/web/WebScopedUserGesture.h"
#include "third_party/WebKit/public/web/WebScriptSource.h"
#include "third_party/WebKit/public/web/WebView.h" #include "third_party/WebKit/public/web/WebView.h"
#include "v8/include/v8.h" #include "v8/include/v8.h"
...@@ -210,7 +211,8 @@ void UserScriptScheduler::ExecuteCodeImpl( ...@@ -210,7 +211,8 @@ void UserScriptScheduler::ExecuteCodeImpl(
} }
if (params.is_javascript) { if (params.is_javascript) {
WebScriptSource source(WebString::fromUTF8(params.code), params.file_url); blink::WebScriptSource source(
WebString::fromUTF8(params.code), params.file_url);
v8::HandleScope scope(v8::Isolate::GetCurrent()); v8::HandleScope scope(v8::Isolate::GetCurrent());
scoped_ptr<content::V8ValueConverter> v8_converter( scoped_ptr<content::V8ValueConverter> v8_converter(
...@@ -223,7 +225,7 @@ void UserScriptScheduler::ExecuteCodeImpl( ...@@ -223,7 +225,7 @@ void UserScriptScheduler::ExecuteCodeImpl(
script_value = child_frame->executeScriptAndReturnValue(source); script_value = child_frame->executeScriptAndReturnValue(source);
} else { } else {
blink::WebVector<v8::Local<v8::Value> > results; blink::WebVector<v8::Local<v8::Value> > results;
std::vector<WebScriptSource> sources; std::vector<blink::WebScriptSource> sources;
sources.push_back(source); sources.push_back(source);
int isolated_world_id = int isolated_world_id =
dispatcher_->user_script_slave()->GetIsolatedWorldIdForExtension( dispatcher_->user_script_slave()->GetIsolatedWorldIdForExtension(
......
...@@ -6,14 +6,11 @@ ...@@ -6,14 +6,11 @@
#include <map> #include <map>
#include "base/command_line.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/memory/shared_memory.h" #include "base/memory/shared_memory.h"
#include "base/metrics/histogram.h" #include "base/metrics/histogram.h"
#include "base/pickle.h" #include "base/pickle.h"
#include "base/strings/stringprintf.h"
#include "base/timer/elapsed_timer.h" #include "base/timer/elapsed_timer.h"
#include "content/public/common/url_constants.h"
#include "content/public/renderer/render_thread.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"
...@@ -21,66 +18,48 @@ ...@@ -21,66 +18,48 @@
#include "extensions/common/extension_set.h" #include "extensions/common/extension_set.h"
#include "extensions/common/manifest_handlers/csp_info.h" #include "extensions/common/manifest_handlers/csp_info.h"
#include "extensions/common/permissions/permissions_data.h" #include "extensions/common/permissions/permissions_data.h"
#include "extensions/renderer/dom_activity_logger.h"
#include "extensions/renderer/extension_groups.h"
#include "extensions/renderer/extensions_renderer_client.h" #include "extensions/renderer/extensions_renderer_client.h"
#include "extensions/renderer/script_context.h" #include "extensions/renderer/script_context.h"
#include "grit/renderer_resources.h"
#include "third_party/WebKit/public/platform/WebURLRequest.h"
#include "third_party/WebKit/public/platform/WebVector.h"
#include "third_party/WebKit/public/web/WebDocument.h"
#include "third_party/WebKit/public/web/WebFrame.h" #include "third_party/WebKit/public/web/WebFrame.h"
#include "third_party/WebKit/public/web/WebSecurityOrigin.h" #include "third_party/WebKit/public/web/WebSecurityOrigin.h"
#include "third_party/WebKit/public/web/WebSecurityPolicy.h" #include "third_party/WebKit/public/web/WebSecurityPolicy.h"
#include "third_party/WebKit/public/web/WebView.h" #include "third_party/WebKit/public/web/WebView.h"
#include "ui/base/resource/resource_bundle.h"
#include "url/gurl.h" #include "url/gurl.h"
using blink::WebFrame; using blink::WebFrame;
using blink::WebSecurityOrigin; using blink::WebSecurityOrigin;
using blink::WebSecurityPolicy; using blink::WebSecurityPolicy;
using blink::WebString; using blink::WebString;
using blink::WebVector;
using blink::WebView; using blink::WebView;
using content::RenderThread; using content::RenderThread;
namespace extensions { namespace extensions {
// These two strings are injected before and after the Greasemonkey API and
// user script to wrap it in an anonymous scope.
static const char kUserScriptHead[] = "(function (unsafeWindow) {\n";
static const char kUserScriptTail[] = "\n})(window);";
int UserScriptSlave::GetIsolatedWorldIdForExtension(const Extension* extension, int UserScriptSlave::GetIsolatedWorldIdForExtension(const Extension* extension,
WebFrame* frame) { WebFrame* frame) {
static int g_next_isolated_world_id = static int g_next_isolated_world_id =
ExtensionsRendererClient::Get()->GetLowestIsolatedWorldId(); ExtensionsRendererClient::Get()->GetLowestIsolatedWorldId();
int id = 0;
IsolatedWorldMap::iterator iter = isolated_world_ids_.find(extension->id()); IsolatedWorldMap::iterator iter = isolated_world_ids_.find(extension->id());
if (iter != isolated_world_ids_.end()) { if (iter != isolated_world_ids_.end()) {
// We need to set the isolated world origin and CSP even if it's not a new id = iter->second;
// world since these are stored per frame, and we might not have used this } else {
// isolated world in this frame before. id = g_next_isolated_world_id++;
frame->setIsolatedWorldSecurityOrigin( // This map will tend to pile up over time, but realistically, you're never
iter->second, WebSecurityOrigin::create(extension->url())); // going to have enough extensions for it to matter.
frame->setIsolatedWorldContentSecurityPolicy( isolated_world_ids_[extension->id()] = id;
iter->second,
WebString::fromUTF8(CSPInfo::GetContentSecurityPolicy(extension)));
return iter->second;
} }
int new_id = g_next_isolated_world_id; // We need to set the isolated world origin and CSP even if it's not a new
++g_next_isolated_world_id; // world since these are stored per frame, and we might not have used this
// isolated world in this frame before.
// This map will tend to pile up over time, but realistically, you're never
// going to have enough extensions for it to matter.
isolated_world_ids_[extension->id()] = new_id;
frame->setIsolatedWorldSecurityOrigin( frame->setIsolatedWorldSecurityOrigin(
new_id, WebSecurityOrigin::create(extension->url())); id, WebSecurityOrigin::create(extension->url()));
frame->setIsolatedWorldContentSecurityPolicy( frame->setIsolatedWorldContentSecurityPolicy(
new_id, id, WebString::fromUTF8(CSPInfo::GetContentSecurityPolicy(extension)));
WebString::fromUTF8(CSPInfo::GetContentSecurityPolicy(extension)));
return new_id; return id;
} }
std::string UserScriptSlave::GetExtensionIdForIsolatedWorld( std::string UserScriptSlave::GetExtensionIdForIsolatedWorld(
...@@ -99,9 +78,7 @@ void UserScriptSlave::RemoveIsolatedWorld(const std::string& extension_id) { ...@@ -99,9 +78,7 @@ void UserScriptSlave::RemoveIsolatedWorld(const std::string& extension_id) {
} }
UserScriptSlave::UserScriptSlave(const ExtensionSet* extensions) UserScriptSlave::UserScriptSlave(const ExtensionSet* extensions)
: script_deleter_(&scripts_), extensions_(extensions) { : extensions_(extensions) {
api_js_ = ResourceBundle::GetSharedInstance().GetRawDataResource(
IDR_GREASEMONKEY_API_JS);
} }
UserScriptSlave::~UserScriptSlave() { UserScriptSlave::~UserScriptSlave() {
...@@ -109,14 +86,23 @@ UserScriptSlave::~UserScriptSlave() { ...@@ -109,14 +86,23 @@ UserScriptSlave::~UserScriptSlave() {
void UserScriptSlave::GetActiveExtensions( void UserScriptSlave::GetActiveExtensions(
std::set<std::string>* extension_ids) { std::set<std::string>* extension_ids) {
for (size_t i = 0; i < scripts_.size(); ++i) { DCHECK(extension_ids);
DCHECK(!scripts_[i]->extension_id().empty()); for (ScopedVector<ScriptInjection>::const_iterator iter =
extension_ids->insert(scripts_[i]->extension_id()); script_injections_.begin();
iter != script_injections_.end();
++iter) {
DCHECK(!(*iter)->extension_id().empty());
extension_ids->insert((*iter)->extension_id());
} }
} }
const Extension* UserScriptSlave::GetExtension(
const std::string& extension_id) {
return extensions_->GetByID(extension_id);
}
bool UserScriptSlave::UpdateScripts(base::SharedMemoryHandle shared_memory) { bool UserScriptSlave::UpdateScripts(base::SharedMemoryHandle shared_memory) {
scripts_.clear(); script_injections_.clear();
bool only_inject_incognito = bool only_inject_incognito =
ExtensionsRendererClient::Get()->IsIncognitoProcess(); ExtensionsRendererClient::Get()->IsIncognitoProcess();
...@@ -144,10 +130,9 @@ bool UserScriptSlave::UpdateScripts(base::SharedMemoryHandle shared_memory) { ...@@ -144,10 +130,9 @@ bool UserScriptSlave::UpdateScripts(base::SharedMemoryHandle shared_memory) {
PickleIterator iter(pickle); PickleIterator iter(pickle);
CHECK(pickle.ReadUInt64(&iter, &num_scripts)); CHECK(pickle.ReadUInt64(&iter, &num_scripts));
scripts_.reserve(num_scripts); script_injections_.reserve(num_scripts);
for (uint64 i = 0; i < num_scripts; ++i) { for (uint64 i = 0; i < num_scripts; ++i) {
scripts_.push_back(new UserScript()); scoped_ptr<UserScript> script(new UserScript());
UserScript* script = scripts_.back();
script->Unpickle(pickle, &iter); script->Unpickle(pickle, &iter);
// Note that this is a pointer into shared memory. We don't own it. It gets // Note that this is a pointer into shared memory. We don't own it. It gets
...@@ -168,11 +153,10 @@ bool UserScriptSlave::UpdateScripts(base::SharedMemoryHandle shared_memory) { ...@@ -168,11 +153,10 @@ bool UserScriptSlave::UpdateScripts(base::SharedMemoryHandle shared_memory) {
base::StringPiece(body, body_length)); base::StringPiece(body, body_length));
} }
if (only_inject_incognito && !script->is_incognito_enabled()) { if (only_inject_incognito && !script->is_incognito_enabled())
// This script shouldn't run in an incognito tab. continue; // This script shouldn't run in an incognito tab.
delete script;
scripts_.pop_back(); script_injections_.push_back(new ScriptInjection(script.Pass(), this));
}
} }
return true; return true;
...@@ -180,145 +164,70 @@ bool UserScriptSlave::UpdateScripts(base::SharedMemoryHandle shared_memory) { ...@@ -180,145 +164,70 @@ bool UserScriptSlave::UpdateScripts(base::SharedMemoryHandle shared_memory) {
void UserScriptSlave::InjectScripts(WebFrame* frame, void UserScriptSlave::InjectScripts(WebFrame* frame,
UserScript::RunLocation location) { UserScript::RunLocation location) {
GURL data_source_url = ScriptContext::GetDataSourceURLForFrame(frame); GURL document_url = ScriptInjection::GetDocumentUrlForFrame(frame);
if (data_source_url.is_empty()) if (document_url.is_empty())
return; return;
if (frame->isViewSourceModeEnabled())
data_source_url = GURL(content::kViewSourceScheme + std::string(":") +
data_source_url.spec());
base::ElapsedTimer timer;
int num_css = 0;
int num_scripts = 0;
ExecutingScriptsMap extensions_executing_scripts;
blink::WebFrame* top_frame = frame->top();
content::RenderView* top_render_view = content::RenderView* top_render_view =
content::RenderView::FromWebView(top_frame->view()); content::RenderView::FromWebView(frame->top()->view());
for (size_t i = 0; i < scripts_.size(); ++i) { ScriptInjection::ScriptsRunInfo scripts_run_info;
std::vector<WebScriptSource> sources; for (ScopedVector<ScriptInjection>::const_iterator iter =
UserScript* script = scripts_[i]; script_injections_.begin();
iter != script_injections_.end();
if (frame->parent() && !script->match_all_frames()) ++iter) {
continue; // Only match subframes if the script declared it wanted to. ScriptInjection* injection = *iter;
if (!injection->WantsToRun(frame, location, document_url))
const Extension* extension = extensions_->GetByID(script->extension_id());
// Since extension info is sent separately from user script info, they can
// be out of sync. We just ignore this situation.
if (!extension)
continue; continue;
const GURL& document_url = ScriptContext::GetEffectiveDocumentURL( const Extension* extension = GetExtension(injection->extension_id());
frame, data_source_url, script->match_about_blank()); DCHECK(extension);
// Content scripts are not tab-specific.
const int kNoTabId = -1;
// We don't have a process id in this context.
const int kNoProcessId = -1;
if (!PermissionsData::CanExecuteScriptOnPage(extension,
document_url,
top_frame->document().url(),
kNoTabId,
script,
kNoProcessId,
NULL)) {
continue;
}
if (location == UserScript::DOCUMENT_START) { if (PermissionsData::RequiresActionForScriptExecution(extension)) {
num_css += script->css_scripts().size();
for (UserScript::FileList::const_iterator iter =
script->css_scripts().begin();
iter != script->css_scripts().end();
++iter) {
frame->document().insertStyleSheet(
WebString::fromUTF8(iter->GetContent().as_string()));
}
}
if (script->run_location() == location) {
// TODO(rdevlin.cronin): Right now, this is just a notification, but soon // TODO(rdevlin.cronin): Right now, this is just a notification, but soon
// we should block without user consent. // we should block without user consent.
if (PermissionsData::RequiresActionForScriptExecution(extension)) { top_render_view->Send(
top_render_view->Send( new ExtensionHostMsg_NotifyExtensionScriptExecution(
new ExtensionHostMsg_NotifyExtensionScriptExecution( top_render_view->GetRoutingID(),
top_render_view->GetRoutingID(), extension->id(),
extension->id(), top_render_view->GetPageId()));
top_render_view->GetPageId()));
}
num_scripts += script->js_scripts().size();
for (size_t j = 0; j < script->js_scripts().size(); ++j) {
UserScript::File& file = script->js_scripts()[j];
std::string content = file.GetContent().as_string();
// We add this dumb function wrapper for standalone user script to
// emulate what Greasemonkey does.
// TODO(aa): I think that maybe "is_standalone" scripts don't exist
// anymore. Investigate.
if (script->is_standalone() || script->emulate_greasemonkey()) {
content.insert(0, kUserScriptHead);
content += kUserScriptTail;
}
sources.push_back(
WebScriptSource(WebString::fromUTF8(content), file.url()));
}
} }
if (!sources.empty()) { injection->Inject(frame, location, &scripts_run_info);
// Emulate Greasemonkey API for scripts that were converted to extensions
// and "standalone" user scripts.
if (script->is_standalone() || script->emulate_greasemonkey()) {
sources.insert(
sources.begin(),
WebScriptSource(WebString::fromUTF8(api_js_.as_string())));
}
int isolated_world_id = GetIsolatedWorldIdForExtension(extension, frame);
base::ElapsedTimer exec_timer;
DOMActivityLogger::AttachToWorld(isolated_world_id, extension->id());
frame->executeScriptInIsolatedWorld(isolated_world_id,
&sources.front(),
sources.size(),
EXTENSION_GROUP_CONTENT_SCRIPTS);
UMA_HISTOGRAM_TIMES("Extensions.InjectScriptTime", exec_timer.Elapsed());
for (std::vector<WebScriptSource>::const_iterator iter = sources.begin();
iter != sources.end();
++iter) {
extensions_executing_scripts[extension->id()].insert(
GURL(iter->url).path());
}
}
} }
LogScriptsRun(frame, location, scripts_run_info);
}
void UserScriptSlave::LogScriptsRun(
blink::WebFrame* frame,
UserScript::RunLocation location,
const ScriptInjection::ScriptsRunInfo& info) {
// Notify the browser if any extensions are now executing scripts. // Notify the browser if any extensions are now executing scripts.
if (!extensions_executing_scripts.empty()) { if (!info.executing_scripts.empty()) {
top_render_view->Send(new ExtensionHostMsg_ContentScriptsExecuting( content::RenderView* render_view =
top_render_view->GetRoutingID(), content::RenderView::FromWebView(frame->view());
extensions_executing_scripts, render_view->Send(new ExtensionHostMsg_ContentScriptsExecuting(
top_render_view->GetPageId(), render_view->GetRoutingID(),
ScriptContext::GetDataSourceURLForFrame(top_frame))); info.executing_scripts,
render_view->GetPageId(),
ScriptContext::GetDataSourceURLForFrame(frame)));
} }
// Log debug info.
if (location == UserScript::DOCUMENT_START) { if (location == UserScript::DOCUMENT_START) {
UMA_HISTOGRAM_COUNTS_100("Extensions.InjectStart_CssCount", num_css); UMA_HISTOGRAM_COUNTS_100("Extensions.InjectStart_CssCount",
UMA_HISTOGRAM_COUNTS_100("Extensions.InjectStart_ScriptCount", num_scripts); info.num_css);
if (num_css || num_scripts) UMA_HISTOGRAM_COUNTS_100("Extensions.InjectStart_ScriptCount", info.num_js);
UMA_HISTOGRAM_TIMES("Extensions.InjectStart_Time", timer.Elapsed()); if (info.num_css || info.num_js)
UMA_HISTOGRAM_TIMES("Extensions.InjectStart_Time", info.timer.Elapsed());
} else if (location == UserScript::DOCUMENT_END) { } else if (location == UserScript::DOCUMENT_END) {
UMA_HISTOGRAM_COUNTS_100("Extensions.InjectEnd_ScriptCount", num_scripts); UMA_HISTOGRAM_COUNTS_100("Extensions.InjectEnd_ScriptCount", info.num_js);
if (num_scripts) if (info.num_js)
UMA_HISTOGRAM_TIMES("Extensions.InjectEnd_Time", timer.Elapsed()); UMA_HISTOGRAM_TIMES("Extensions.InjectEnd_Time", info.timer.Elapsed());
} else if (location == UserScript::DOCUMENT_IDLE) { } else if (location == UserScript::DOCUMENT_IDLE) {
UMA_HISTOGRAM_COUNTS_100("Extensions.InjectIdle_ScriptCount", num_scripts); UMA_HISTOGRAM_COUNTS_100("Extensions.InjectIdle_ScriptCount", info.num_js);
if (num_scripts) if (info.num_js)
UMA_HISTOGRAM_TIMES("Extensions.InjectIdle_Time", timer.Elapsed()); UMA_HISTOGRAM_TIMES("Extensions.InjectIdle_Time", info.timer.Elapsed());
} else { } else {
NOTREACHED(); NOTREACHED();
} }
......
...@@ -10,12 +10,14 @@ ...@@ -10,12 +10,14 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "base/memory/linked_ptr.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
#include "base/memory/shared_memory.h" #include "base/memory/shared_memory.h"
#include "base/stl_util.h"
#include "base/strings/string_piece.h" #include "base/strings/string_piece.h"
#include "extensions/common/user_script.h" #include "extensions/common/user_script.h"
#include "third_party/WebKit/public/web/WebScriptSource.h" #include "extensions/renderer/script_injection.h"
#include "third_party/WebKit/public/platform/WebString.h"
class GURL; class GURL;
...@@ -23,7 +25,9 @@ namespace blink { ...@@ -23,7 +25,9 @@ namespace blink {
class WebFrame; class WebFrame;
} }
using blink::WebScriptSource; namespace content {
class RenderView;
}
namespace extensions { namespace extensions {
class Extension; class Extension;
...@@ -38,14 +42,12 @@ class UserScriptSlave { ...@@ -38,14 +42,12 @@ class UserScriptSlave {
// Returns the unique set of extension IDs this UserScriptSlave knows about. // Returns the unique set of extension IDs this UserScriptSlave knows about.
void GetActiveExtensions(std::set<std::string>* extension_ids); void GetActiveExtensions(std::set<std::string>* extension_ids);
// Gets the extension with the given |id|, if one exists.
const Extension* GetExtension(const std::string& extension_id);
// Update the parsed scripts from shared memory. // Update the parsed scripts from shared memory.
bool UpdateScripts(base::SharedMemoryHandle shared_memory); bool UpdateScripts(base::SharedMemoryHandle shared_memory);
// Inject the appropriate scripts into a frame based on its URL.
// TODO(aa): Extract a UserScriptFrame interface out of this to improve
// testability.
void InjectScripts(blink::WebFrame* frame, UserScript::RunLocation location);
// Gets the isolated world ID to use for the given |extension| in the given // Gets the isolated world ID to use for the given |extension| in the given
// |frame|. If no isolated world has been created for that extension, // |frame|. If no isolated world has been created for that extension,
// one will be created and initialized. // one will be created and initialized.
...@@ -59,16 +61,23 @@ class UserScriptSlave { ...@@ -59,16 +61,23 @@ class UserScriptSlave {
void RemoveIsolatedWorld(const std::string& extension_id); void RemoveIsolatedWorld(const std::string& extension_id);
// Inject the appropriate scripts into a frame based on its URL.
// TODO(aa): Extract a UserScriptFrame interface out of this to improve
// testability.
void InjectScripts(blink::WebFrame* frame, UserScript::RunLocation location);
private: private:
// Log the data from scripts being run, including doing UMA and notifying the
// browser.
void LogScriptsRun(blink::WebFrame* frame,
UserScript::RunLocation location,
const ScriptInjection::ScriptsRunInfo& info);
// Shared memory containing raw script data. // Shared memory containing raw script data.
scoped_ptr<base::SharedMemory> shared_memory_; scoped_ptr<base::SharedMemory> shared_memory_;
// Parsed script data. // Parsed script data, ready to inject.
std::vector<UserScript*> scripts_; ScopedVector<ScriptInjection> script_injections_;
STLElementDeleter<std::vector<UserScript*> > script_deleter_;
// Greasemonkey API source that is injected with the scripts.
base::StringPiece api_js_;
// Extension metadata. // Extension metadata.
const ExtensionSet* extensions_; const ExtensionSet* extensions_;
......
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