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 @@
'renderer/script_context.h',
'renderer/script_context_set.cc',
'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.h',
'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 @@
#include "third_party/WebKit/public/web/WebDocument.h"
#include "third_party/WebKit/public/web/WebFrame.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 "v8/include/v8.h"
......@@ -210,7 +211,8 @@ void UserScriptScheduler::ExecuteCodeImpl(
}
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());
scoped_ptr<content::V8ValueConverter> v8_converter(
......@@ -223,7 +225,7 @@ void UserScriptScheduler::ExecuteCodeImpl(
script_value = child_frame->executeScriptAndReturnValue(source);
} else {
blink::WebVector<v8::Local<v8::Value> > results;
std::vector<WebScriptSource> sources;
std::vector<blink::WebScriptSource> sources;
sources.push_back(source);
int isolated_world_id =
dispatcher_->user_script_slave()->GetIsolatedWorldIdForExtension(
......
This diff is collapsed.
......@@ -10,12 +10,14 @@
#include <string>
#include <vector>
#include "base/memory/linked_ptr.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
#include "base/memory/shared_memory.h"
#include "base/stl_util.h"
#include "base/strings/string_piece.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;
......@@ -23,7 +25,9 @@ namespace blink {
class WebFrame;
}
using blink::WebScriptSource;
namespace content {
class RenderView;
}
namespace extensions {
class Extension;
......@@ -38,14 +42,12 @@ class UserScriptSlave {
// Returns the unique set of extension IDs this UserScriptSlave knows about.
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.
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
// |frame|. If no isolated world has been created for that extension,
// one will be created and initialized.
......@@ -59,16 +61,23 @@ class UserScriptSlave {
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:
// 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.
scoped_ptr<base::SharedMemory> shared_memory_;
// Parsed script data.
std::vector<UserScript*> scripts_;
STLElementDeleter<std::vector<UserScript*> > script_deleter_;
// Greasemonkey API source that is injected with the scripts.
base::StringPiece api_js_;
// Parsed script data, ready to inject.
ScopedVector<ScriptInjection> script_injections_;
// Extension metadata.
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