Chromium Code Reviews| Index: extensions/renderer/user_script_injection.cc |
| diff --git a/extensions/renderer/user_script_injection.cc b/extensions/renderer/user_script_injection.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..ba30dd8c7564c41a16115fc3d587b00202a7223d |
| --- /dev/null |
| +++ b/extensions/renderer/user_script_injection.cc |
| @@ -0,0 +1,175 @@ |
| +// 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/user_script_injection.h" |
| + |
| +#include <vector> |
| + |
| +#include "base/lazy_instance.h" |
| +#include "base/metrics/histogram.h" |
| +#include "content/public/common/url_constants.h" |
| +#include "content/public/renderer/render_view.h" |
| +#include "extensions/common/extension.h" |
| +#include "extensions/common/permissions/permissions_data.h" |
| +#include "extensions/renderer/dom_activity_logger.h" |
| +#include "extensions/renderer/extension_groups.h" |
| +#include "grit/extensions_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 "third_party/WebKit/public/web/WebView.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 |
| + |
| +UserScriptInjection::UserScriptInjection( |
| + blink::WebFrame* web_frame, |
| + const std::string& extension_id, |
| + UserScript::RunLocation run_location, |
| + int tab_id, |
| + UserScriptInjectionList* script_list, |
| + const UserScript* script, |
| + bool inject_js, |
| + bool inject_css) |
| + : ScriptInjection(web_frame, |
| + extension_id, |
| + run_location, |
| + tab_id), |
| + script_(script), |
| + script_id_(script_->id()), |
| + inject_js_(inject_js), |
| + inject_css_(inject_css), |
|
not at google - send to devlin
2014/06/15 23:53:38
see comment in user_script_injection_list; I'd ski
Devlin
2014/06/16 18:11:16
Done.
|
| + user_script_injection_list_observer_(this) { |
| + user_script_injection_list_observer_.Add(script_list); |
| +} |
| + |
| +UserScriptInjection::~UserScriptInjection() { |
| +} |
| + |
| +void UserScriptInjection::InjectJS(const Extension* extension, |
| + ScriptsRunInfo* scripts_run_info) { |
| + const UserScript::FileList& js_scripts = script_->js_scripts(); |
| + std::vector<blink::WebScriptSource> sources; |
| + scripts_run_info->num_js += js_scripts.size(); |
| + |
| + bool is_standalone_or_emulate_greasemonkey = |
| + script_->is_standalone() || script_->emulate_greasemonkey(); |
| + 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 = |
| + GetIsolatedWorldIdForExtension(extension, web_frame()); |
| + base::ElapsedTimer exec_timer; |
| + DOMActivityLogger::AttachToWorld(isolated_world_id, script_->extension_id()); |
| + web_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[script_->extension_id()].insert( |
| + GURL(iter->url).path()); |
| + } |
| +} |
| + |
| +void UserScriptInjection::InjectCSS(ScriptsRunInfo* scripts_run_info) { |
| + 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) { |
| + web_frame()->document().insertStyleSheet( |
| + blink::WebString::fromUTF8(iter->GetContent().as_string())); |
| + } |
| +} |
| + |
| +bool UserScriptInjection::Allowed(const Extension* extension) { |
| + // If we don't have a tab id, we have no UI surface to ask for user consent. |
| + // For now, we treat this as an automatic allow. |
| + if (tab_id() == -1) |
| + return true; |
| + |
| + return !extension->permissions_data()->RequiresActionForScriptExecution( |
| + extension, tab_id(), web_frame()->top()->document().url()); |
| +} |
| + |
| +void UserScriptInjection::Inject(ScriptsRunInfo* scripts_run_info, |
| + const Extension* extension) { |
| + if (inject_js_) |
| + InjectJS(extension, scripts_run_info); |
| + if (inject_css_) |
| + InjectCSS(scripts_run_info); |
| +} |
| + |
| +void UserScriptInjection::OnUserScriptsUpdated( |
| + const std::set<std::string>& changed_extensions, |
| + const std::vector<UserScript*>& scripts) { |
| + // If the extension causing this injection changed, then this injection |
| + // will be removed, and there's no guarantee the backing script still even |
|
not at google - send to devlin
2014/06/15 23:53:38
s/even //
Devlin
2014/06/16 18:11:16
Done.
|
| + // exists. |
| + if (changed_extensions.count(extension_id()) > 0) |
| + return; |
| + |
| + for (std::vector<UserScript*>::const_iterator iter = scripts.begin(); |
| + iter != scripts.end(); |
| + ++iter) { |
| + if ((*iter)->id() == script_id_) { |
| + script_ = *iter; |
| + break; |
| + } |
| + } |
| +} |
| + |
| +} // namespace extensions |