Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(101)

Unified Diff: extensions/renderer/user_script_injection.cc

Issue 321993003: Refactor renderer-side script injection (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Latest master Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « extensions/renderer/user_script_injection.h ('k') | extensions/renderer/user_script_scheduler.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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..0978deb2665219060b64667730e7e65a569c3e66
--- /dev/null
+++ b/extensions/renderer/user_script_injection.cc
@@ -0,0 +1,239 @@
+// 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/extension_messages.h"
+#include "extensions/common/feature_switch.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 {
+
+const int kInvalidRequestId = -1;
+
+// The id of the next pending injection.
+int64 g_next_pending_id = 0;
+
+// 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,
+ UserScriptSet* script_list,
+ const UserScript* script)
+ : ScriptInjection(web_frame,
+ extension_id,
+ run_location,
+ tab_id),
+ script_(script),
+ script_id_(script_->id()),
+ user_script_set_observer_(this) {
+ user_script_set_observer_.Add(script_list);
+}
+
+UserScriptInjection::~UserScriptInjection() {
+}
+
+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 exists.
+ if (changed_extensions.count(extension_id()) > 0)
+ return;
+
+ for (std::vector<UserScript*>::const_iterator iter = scripts.begin();
+ iter != scripts.end();
+ ++iter) {
+ // We need to compare to |script_id_| (and not to script_->id()) because the
+ // old |script_| may be deleted by now.
+ if ((*iter)->id() == script_id_) {
+ script_ = *iter;
+ break;
+ }
+ }
+}
+
+bool UserScriptInjection::TryToInject(UserScript::RunLocation current_location,
+ const Extension* extension,
+ ScriptsRunInfo* scripts_run_info) {
+ // If the extension is removed, we return immediately and never inject.
+ if (!extension)
+ return true;
+
+ // If we are already waiting for permission, wait.
+ if (request_id() != -1)
+ return false;
+
+ // If the page is not to the proper load point, wait.
+ if (current_location < run_location())
+ return false;
+
+ // If we are not allowed to inject, then request permission and wait.
+ if (!Allowed(extension) && !RequestPermission(extension))
+ return false;
+
+ // Otherwise, we're good to inject!
+ Inject(extension, scripts_run_info);
+
+ return true;
+}
+
+bool UserScriptInjection::OnPermissionGranted(
+ const Extension* extension,
+ ScriptsRunInfo* scripts_run_info) {
+ if (!extension)
+ return false;
+
+ Inject(extension, scripts_run_info);
+ return true;
+}
+
+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());
+}
+
+bool UserScriptInjection::RequestPermission(const Extension* extension) {
+ content::RenderView* render_view =
+ content::RenderView::FromWebView(web_frame()->top()->view());
+
+ // By default, we allow injection.
+ bool should_inject = true;
+ int64 request_id = kInvalidRequestId;
+ int page_id = render_view->GetPageId();
+
+ if (FeatureSwitch::scripts_require_action()->IsEnabled()) {
+ should_inject = false;
+ request_id = g_next_pending_id++;
+ set_request_id(request_id);
+ }
+
+ render_view->Send(new ExtensionHostMsg_RequestScriptInjectionPermission(
+ render_view->GetRoutingID(), extension->id(), page_id, request_id));
+
+ return should_inject;
+}
+
+void UserScriptInjection::Inject(const Extension* extension,
+ ScriptsRunInfo* scripts_run_info) {
+ if (!script_->css_scripts().empty() &&
+ run_location() == UserScript::DOCUMENT_START) {
+ InjectCSS(scripts_run_info);
+ }
+ if (!script_->js_scripts().empty() &&
+ script_->run_location() == run_location()) {
+ InjectJS(extension, scripts_run_info);
+ }
+}
+
+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()));
+ }
+}
+
+} // namespace extensions
« no previous file with comments | « extensions/renderer/user_script_injection.h ('k') | extensions/renderer/user_script_scheduler.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698