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(); |
} |