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

Unified Diff: extensions/renderer/user_script_slave.cc

Issue 288053002: Block content scripts from executing until user grants permission (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 7 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
Index: extensions/renderer/user_script_slave.cc
diff --git a/extensions/renderer/user_script_slave.cc b/extensions/renderer/user_script_slave.cc
index f05d214799a264c3cefc665c90571b9351abf7ee..efa6691d2646621ad16c80d3d296eff00aedbf21 100644
--- a/extensions/renderer/user_script_slave.cc
+++ b/extensions/renderer/user_script_slave.cc
@@ -4,6 +4,7 @@
#include "extensions/renderer/user_script_slave.h"
+#include <algorithm> // std::max
not at google - send to devlin 2014/05/15 00:31:54 grepping through Chromium, the comment is almost n
Devlin 2014/05/20 16:48:09 Looks like you're right. I must have seen one of
#include <map>
#include "base/command_line.h"
@@ -46,10 +47,71 @@ using content::RenderThread;
namespace extensions {
+namespace {
+
// 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);";
+const char kUserScriptHead[] = "(function (unsafeWindow) {\n";
+const char kUserScriptTail[] = "\n})(window);";
+
+int64 g_next_pending_id = 0;
+
+} // namespace
+
+struct UserScriptSlave::ScriptsRunInfo {
+ ScriptsRunInfo();
+
+ size_t num_css;
+ size_t num_js;
+ ExecutingScriptsMap executing_scripts;
+ base::ElapsedTimer timer;
+
+ private:
+ // Technically, this is safe to copy, but it's slow because of
+ // |executing_scripts|. Disallow it until we need it.
+ DISALLOW_COPY_AND_ASSIGN(ScriptsRunInfo);
+};
+
+UserScriptSlave::ScriptsRunInfo::ScriptsRunInfo() : num_css(0u), num_js(0u) {
+}
+
+struct UserScriptSlave::PendingInjection {
+ PendingInjection(UserScript* user_script,
+ UserScript::RunLocation run_location,
+ const blink::WebString& web_frame_name);
+ ~PendingInjection();
+
+ // The user scripts to run. Weak, but guaranteed to be alive because they
+ // have the same parent (UserScriptSlave).
+ std::set<UserScript*> user_scripts;
+
+ // The extension id relating to these user scripts.
+ const std::string extension_id;
+
+ // The pending injection's latest run location. Scripts with any RunLocation
+ // before or equal to this are run when the injection is executed.
+ UserScript::RunLocation latest_run_location;
+
+ // The unique name of the web frame to inject into.
+ blink::WebString web_frame_name;
+
+ // The id of this PendingInjection, guaranteed to be unique.
+ int64 id;
+};
not at google - send to devlin 2014/05/15 00:31:54 this factoring into objects makes sense. could you
Devlin 2014/05/20 16:48:09 Semi-massive refactor done. Woohoo!
+
+UserScriptSlave::PendingInjection::PendingInjection(
+ UserScript* user_script,
+ UserScript::RunLocation run_location,
+ const blink::WebString& web_frame_name)
+ : extension_id(user_script->extension_id()),
+ latest_run_location(run_location),
+ web_frame_name(web_frame_name),
+ id(g_next_pending_id++) {
+ user_scripts.insert(user_script);
+}
+
+UserScriptSlave::PendingInjection::~PendingInjection() {
+}
int UserScriptSlave::GetIsolatedWorldIdForExtension(const Extension* extension,
WebFrame* frame) {
@@ -188,19 +250,16 @@ void UserScriptSlave::InjectScripts(WebFrame* frame,
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());
- for (size_t i = 0; i < scripts_.size(); ++i) {
- std::vector<WebScriptSource> sources;
- UserScript* script = scripts_[i];
+ UserScript* script = NULL;
not at google - send to devlin 2014/05/15 00:31:54 why declared outside scope?
Devlin 2014/05/20 16:48:09 Carryover from Uni days, when allocating 8 bytes o
+ ScriptsRunInfo scripts_run_info;
+ for (std::vector<UserScript*>::const_iterator iter = scripts_.begin();
+ iter != scripts_.end();
+ ++iter) {
+ script = *iter;
if (frame->parent() && !script->match_all_frames())
continue; // Only match subframes if the script declared it wanted to.
@@ -226,96 +285,203 @@ void UserScriptSlave::InjectScripts(WebFrame* frame,
continue;
}
- 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()));
- }
- }
+ bool run_css = !script->css_scripts().empty() &&
+ location == UserScript::DOCUMENT_START;
+ bool run_js = !script->js_scripts().empty() &&
+ script->run_location() == location;
+ if (!run_css && !run_js)
+ continue;
- if (script->run_location() == location) {
- // 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()));
- }
+ if (PermissionsData::RequiresActionForScriptExecution(extension)) {
+ int id = AddPendingInjection(script, location, frame->uniqueName());
+ top_render_view->Send(
+ new ExtensionHostMsg_RequestContentScriptPermission(
+ top_render_view->GetRoutingID(),
+ extension->id(),
+ top_render_view->GetPageId(),
+ id));
+ continue;
}
- 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());
- }
+ if (run_css)
+ InjectCSSScripts(frame, script, &scripts_run_info);
+ if (run_js)
+ InjectJSScripts(frame, script, extension, &scripts_run_info);
+ }
+
+ LogScriptsRun(frame, location, scripts_run_info);
+}
+
+void UserScriptSlave::OnContentScriptGrantedPermission(
+ content::RenderView* render_view, int request_id) {
+ if (!render_view)
+ return;
+
+ PendingInjectionList::iterator iter = pending_injections_.begin();
+ for (; iter != pending_injections_.end() && (*iter)->id != request_id; ++iter)
+ ; // Intentionally empty.
+ if (iter == pending_injections_.end())
+ return;
+
+ linked_ptr<PendingInjection> pending_injection = *iter;
+
+ blink::WebView* web_view = render_view->GetWebView();
+ if (!web_view)
+ return;
+
+ blink::WebFrame* web_frame =
+ web_view->findFrameByName(pending_injection->web_frame_name);
+ if (!web_frame)
+ return;
+
+ const Extension* extension =
+ extensions_->GetByID(pending_injection->extension_id);
+ if (!extension)
+ return;
+
+ ScriptsRunInfo scripts_run_info;
+ for (std::set<UserScript*>::const_iterator iter =
+ pending_injection->user_scripts.begin();
+ iter != pending_injection->user_scripts.end();
+ ++iter) {
+ UserScript* script = *iter;
+ if (!script->css_scripts().empty())
+ InjectCSSScripts(web_frame, script, &scripts_run_info);
+ if (!script->js_scripts().empty())
+ InjectJSScripts(web_frame, script, extension, &scripts_run_info);
+ }
+ LogScriptsRun(web_frame,
+ pending_injection->latest_run_location, // TODO: Add new
+ scripts_run_info);
+}
+
+int UserScriptSlave::AddPendingInjection(UserScript* script,
+ UserScript::RunLocation location,
+ const blink::WebString& frame_name) {
+ // Look for an existing entry with the same extension id and same web frame.
+ const std::string& extension_id = script->extension_id();
+ PendingInjectionList::iterator existing = pending_injections_.begin();
+ while (existing != pending_injections_.end() &&
+ ((*existing)->extension_id != extension_id ||
+ (*existing)->web_frame_name != frame_name)) {
+ ++existing;
+ }
+
+ // If there is an existing entry, append the script and latest run location.
+ // Otherwise, add a new entry.
+ if (existing != pending_injections_.end()) {
+ (*existing)->user_scripts.insert(script);
+ (*existing)->latest_run_location =
+ std::max(location, (*existing)->latest_run_location);
+ } else {
+ existing = pending_injections_.insert(
+ existing,
+ make_linked_ptr(new PendingInjection(script, location, frame_name)));
+ }
+ return (*existing)->id;
+}
+
+void UserScriptSlave::InjectCSSScripts(WebFrame* frame,
+ UserScript* script,
+ ScriptsRunInfo* scripts_run_info) {
+ DCHECK(frame);
+ DCHECK(frame->parent() || script->match_all_frames());
+ DCHECK(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) {
+ frame->document().insertStyleSheet(
+ WebString::fromUTF8(iter->GetContent().as_string()));
+ }
+}
+
+void UserScriptSlave::InjectJSScripts(WebFrame* frame,
+ UserScript* script,
+ const Extension* extension,
+ ScriptsRunInfo* scripts_run_info) {
+ DCHECK(frame);
+ DCHECK(!frame->parent() || script->match_all_frames());
+ DCHECK(extension);
+ DCHECK(scripts_run_info);
+
+ const UserScript::FileList& js_scripts = script->js_scripts();
+ std::vector<WebScriptSource> sources;
+ bool is_standalone_or_emulate_greasemonkey =
+ script->is_standalone() || script->emulate_greasemonkey();
+ 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(
+ WebScriptSource(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(),
+ 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) {
+ scripts_run_info->executing_scripts[extension->id()].insert(
+ GURL(iter->url).path());
+ }
+}
+
+void UserScriptSlave::LogScriptsRun(blink::WebFrame* frame,
+ UserScript::RunLocation location,
+ const 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();
}

Powered by Google App Engine
This is Rietveld 408576698