diff options
author | rdevlin.cronin@chromium.org <rdevlin.cronin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-05-20 01:59:46 +0000 |
---|---|---|
committer | rdevlin.cronin@chromium.org <rdevlin.cronin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-05-20 01:59:46 +0000 |
commit | 3c22078b68b1a7ff6e291ae542970a9f4f2e5e70 (patch) | |
tree | 790e84782337111950ca9d43ab8190e384bab456 /extensions/renderer | |
parent | 1b1e9effe9fa3b66dd1bcfff4b78455460f66c61 (diff) | |
download | chromium_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.cc | 202 | ||||
-rw-r--r-- | extensions/renderer/script_injection.h | 107 | ||||
-rw-r--r-- | extensions/renderer/user_script_scheduler.cc | 6 | ||||
-rw-r--r-- | extensions/renderer/user_script_slave.cc | 249 | ||||
-rw-r--r-- | extensions/renderer/user_script_slave.h | 37 |
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_; |