summaryrefslogtreecommitdiffstats
path: root/extensions/renderer
diff options
context:
space:
mode:
authorrdevlin.cronin@chromium.org <rdevlin.cronin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-20 01:59:46 +0000
committerrdevlin.cronin@chromium.org <rdevlin.cronin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-20 01:59:46 +0000
commit3c22078b68b1a7ff6e291ae542970a9f4f2e5e70 (patch)
tree790e84782337111950ca9d43ab8190e384bab456 /extensions/renderer
parent1b1e9effe9fa3b66dd1bcfff4b78455460f66c61 (diff)
downloadchromium_src-3c22078b68b1a7ff6e291ae542970a9f4f2e5e70.zip
chromium_src-3c22078b68b1a7ff6e291ae542970a9f4f2e5e70.tar.gz
chromium_src-3c22078b68b1a7ff6e291ae542970a9f4f2e5e70.tar.bz2
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
Diffstat (limited to 'extensions/renderer')
-rw-r--r--extensions/renderer/script_injection.cc202
-rw-r--r--extensions/renderer/script_injection.h107
-rw-r--r--extensions/renderer/user_script_scheduler.cc6
-rw-r--r--extensions/renderer/user_script_slave.cc249
-rw-r--r--extensions/renderer/user_script_slave.h37
5 files changed, 415 insertions, 186 deletions
diff --git a/extensions/renderer/script_injection.cc b/extensions/renderer/script_injection.cc
new file mode 100644
index 0000000..4db534a
--- /dev/null
+++ b/extensions/renderer/script_injection.cc
@@ -0,0 +1,202 @@
+// 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
diff --git a/extensions/renderer/script_injection.h b/extensions/renderer/script_injection.h
new file mode 100644
index 0000000..813fbb4
--- /dev/null
+++ b/extensions/renderer/script_injection.h
@@ -0,0 +1,107 @@
+// 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_
diff --git a/extensions/renderer/user_script_scheduler.cc b/extensions/renderer/user_script_scheduler.cc
index 92918d0..cc0d84b 100644
--- a/extensions/renderer/user_script_scheduler.cc
+++ b/extensions/renderer/user_script_scheduler.cc
@@ -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(
diff --git a/extensions/renderer/user_script_slave.cc b/extensions/renderer/user_script_slave.cc
index 8e176ef..036581b 100644
--- a/extensions/renderer/user_script_slave.cc
+++ b/extensions/renderer/user_script_slave.cc
@@ -6,14 +6,11 @@
#include <map>
-#include "base/command_line.h"
#include "base/logging.h"
#include "base/memory/shared_memory.h"
#include "base/metrics/histogram.h"
#include "base/pickle.h"
-#include "base/strings/stringprintf.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_view.h"
#include "extensions/common/extension.h"
@@ -21,66 +18,48 @@
#include "extensions/common/extension_set.h"
#include "extensions/common/manifest_handlers/csp_info.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/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/WebSecurityOrigin.h"
#include "third_party/WebKit/public/web/WebSecurityPolicy.h"
#include "third_party/WebKit/public/web/WebView.h"
-#include "ui/base/resource/resource_bundle.h"
#include "url/gurl.h"
using blink::WebFrame;
using blink::WebSecurityOrigin;
using blink::WebSecurityPolicy;
using blink::WebString;
-using blink::WebVector;
using blink::WebView;
using content::RenderThread;
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,
WebFrame* frame) {
static int g_next_isolated_world_id =
ExtensionsRendererClient::Get()->GetLowestIsolatedWorldId();
+ int id = 0;
IsolatedWorldMap::iterator iter = isolated_world_ids_.find(extension->id());
if (iter != isolated_world_ids_.end()) {
- // We need to set the isolated world origin and CSP even if it's not a new
- // world since these are stored per frame, and we might not have used this
- // isolated world in this frame before.
- frame->setIsolatedWorldSecurityOrigin(
- iter->second, WebSecurityOrigin::create(extension->url()));
- frame->setIsolatedWorldContentSecurityPolicy(
- iter->second,
- WebString::fromUTF8(CSPInfo::GetContentSecurityPolicy(extension)));
- return iter->second;
+ id = iter->second;
+ } else {
+ id = g_next_isolated_world_id++;
+ // 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()] = id;
}
- int new_id = g_next_isolated_world_id;
- ++g_next_isolated_world_id;
-
- // 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;
+ // We need to set the isolated world origin and CSP even if it's not a new
+ // world since these are stored per frame, and we might not have used this
+ // isolated world in this frame before.
frame->setIsolatedWorldSecurityOrigin(
- new_id, WebSecurityOrigin::create(extension->url()));
+ id, WebSecurityOrigin::create(extension->url()));
frame->setIsolatedWorldContentSecurityPolicy(
- new_id,
- WebString::fromUTF8(CSPInfo::GetContentSecurityPolicy(extension)));
- return new_id;
+ id, WebString::fromUTF8(CSPInfo::GetContentSecurityPolicy(extension)));
+
+ return id;
}
std::string UserScriptSlave::GetExtensionIdForIsolatedWorld(
@@ -99,9 +78,7 @@ void UserScriptSlave::RemoveIsolatedWorld(const std::string& extension_id) {
}
UserScriptSlave::UserScriptSlave(const ExtensionSet* extensions)
- : script_deleter_(&scripts_), extensions_(extensions) {
- api_js_ = ResourceBundle::GetSharedInstance().GetRawDataResource(
- IDR_GREASEMONKEY_API_JS);
+ : extensions_(extensions) {
}
UserScriptSlave::~UserScriptSlave() {
@@ -109,14 +86,23 @@ UserScriptSlave::~UserScriptSlave() {
void UserScriptSlave::GetActiveExtensions(
std::set<std::string>* extension_ids) {
- for (size_t i = 0; i < scripts_.size(); ++i) {
- DCHECK(!scripts_[i]->extension_id().empty());
- extension_ids->insert(scripts_[i]->extension_id());
+ DCHECK(extension_ids);
+ for (ScopedVector<ScriptInjection>::const_iterator iter =
+ 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) {
- scripts_.clear();
+ script_injections_.clear();
bool only_inject_incognito =
ExtensionsRendererClient::Get()->IsIncognitoProcess();
@@ -144,10 +130,9 @@ bool UserScriptSlave::UpdateScripts(base::SharedMemoryHandle shared_memory) {
PickleIterator iter(pickle);
CHECK(pickle.ReadUInt64(&iter, &num_scripts));
- scripts_.reserve(num_scripts);
+ script_injections_.reserve(num_scripts);
for (uint64 i = 0; i < num_scripts; ++i) {
- scripts_.push_back(new UserScript());
- UserScript* script = scripts_.back();
+ scoped_ptr<UserScript> script(new UserScript());
script->Unpickle(pickle, &iter);
// 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) {
base::StringPiece(body, body_length));
}
- if (only_inject_incognito && !script->is_incognito_enabled()) {
- // This script shouldn't run in an incognito tab.
- delete script;
- scripts_.pop_back();
- }
+ if (only_inject_incognito && !script->is_incognito_enabled())
+ continue; // This script shouldn't run in an incognito tab.
+
+ script_injections_.push_back(new ScriptInjection(script.Pass(), this));
}
return true;
@@ -180,145 +164,70 @@ bool UserScriptSlave::UpdateScripts(base::SharedMemoryHandle shared_memory) {
void UserScriptSlave::InjectScripts(WebFrame* frame,
UserScript::RunLocation location) {
- GURL data_source_url = ScriptContext::GetDataSourceURLForFrame(frame);
- if (data_source_url.is_empty())
+ GURL document_url = ScriptInjection::GetDocumentUrlForFrame(frame);
+ if (document_url.is_empty())
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::FromWebView(top_frame->view());
+ content::RenderView::FromWebView(frame->top()->view());
- for (size_t i = 0; i < scripts_.size(); ++i) {
- std::vector<WebScriptSource> sources;
- UserScript* script = scripts_[i];
-
- if (frame->parent() && !script->match_all_frames())
- continue; // Only match subframes if the script declared it wanted to.
-
- 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)
+ ScriptInjection::ScriptsRunInfo scripts_run_info;
+ for (ScopedVector<ScriptInjection>::const_iterator iter =
+ script_injections_.begin();
+ iter != script_injections_.end();
+ ++iter) {
+ ScriptInjection* injection = *iter;
+ if (!injection->WantsToRun(frame, location, document_url))
continue;
- const GURL& document_url = ScriptContext::GetEffectiveDocumentURL(
- frame, data_source_url, script->match_about_blank());
-
- // 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;
- }
+ const Extension* extension = GetExtension(injection->extension_id());
+ DCHECK(extension);
- if (location == UserScript::DOCUMENT_START) {
- 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) {
+ if (PermissionsData::RequiresActionForScriptExecution(extension)) {
// TODO(rdevlin.cronin): Right now, this is just a notification, but soon
// we should block without user consent.
- if (PermissionsData::RequiresActionForScriptExecution(extension)) {
- top_render_view->Send(
- new ExtensionHostMsg_NotifyExtensionScriptExecution(
- top_render_view->GetRoutingID(),
- extension->id(),
- 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()));
- }
+ top_render_view->Send(
+ new ExtensionHostMsg_NotifyExtensionScriptExecution(
+ top_render_view->GetRoutingID(),
+ extension->id(),
+ top_render_view->GetPageId()));
}
- if (!sources.empty()) {
- // 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());
- }
- }
+ injection->Inject(frame, location, &scripts_run_info);
}
+ 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.
- if (!extensions_executing_scripts.empty()) {
- top_render_view->Send(new ExtensionHostMsg_ContentScriptsExecuting(
- top_render_view->GetRoutingID(),
- extensions_executing_scripts,
- top_render_view->GetPageId(),
- ScriptContext::GetDataSourceURLForFrame(top_frame)));
+ if (!info.executing_scripts.empty()) {
+ content::RenderView* render_view =
+ content::RenderView::FromWebView(frame->view());
+ render_view->Send(new ExtensionHostMsg_ContentScriptsExecuting(
+ render_view->GetRoutingID(),
+ info.executing_scripts,
+ render_view->GetPageId(),
+ ScriptContext::GetDataSourceURLForFrame(frame)));
}
- // Log debug info.
if (location == UserScript::DOCUMENT_START) {
- UMA_HISTOGRAM_COUNTS_100("Extensions.InjectStart_CssCount", num_css);
- UMA_HISTOGRAM_COUNTS_100("Extensions.InjectStart_ScriptCount", num_scripts);
- if (num_css || num_scripts)
- UMA_HISTOGRAM_TIMES("Extensions.InjectStart_Time", timer.Elapsed());
+ UMA_HISTOGRAM_COUNTS_100("Extensions.InjectStart_CssCount",
+ info.num_css);
+ UMA_HISTOGRAM_COUNTS_100("Extensions.InjectStart_ScriptCount", info.num_js);
+ if (info.num_css || info.num_js)
+ UMA_HISTOGRAM_TIMES("Extensions.InjectStart_Time", info.timer.Elapsed());
} else if (location == UserScript::DOCUMENT_END) {
- UMA_HISTOGRAM_COUNTS_100("Extensions.InjectEnd_ScriptCount", num_scripts);
- if (num_scripts)
- UMA_HISTOGRAM_TIMES("Extensions.InjectEnd_Time", timer.Elapsed());
+ UMA_HISTOGRAM_COUNTS_100("Extensions.InjectEnd_ScriptCount", info.num_js);
+ if (info.num_js)
+ UMA_HISTOGRAM_TIMES("Extensions.InjectEnd_Time", info.timer.Elapsed());
} else if (location == UserScript::DOCUMENT_IDLE) {
- UMA_HISTOGRAM_COUNTS_100("Extensions.InjectIdle_ScriptCount", num_scripts);
- if (num_scripts)
- UMA_HISTOGRAM_TIMES("Extensions.InjectIdle_Time", timer.Elapsed());
+ UMA_HISTOGRAM_COUNTS_100("Extensions.InjectIdle_ScriptCount", info.num_js);
+ if (info.num_js)
+ UMA_HISTOGRAM_TIMES("Extensions.InjectIdle_Time", info.timer.Elapsed());
} else {
NOTREACHED();
}
diff --git a/extensions/renderer/user_script_slave.h b/extensions/renderer/user_script_slave.h
index fc3da79..4dd1029 100644
--- a/extensions/renderer/user_script_slave.h
+++ b/extensions/renderer/user_script_slave.h
@@ -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_;