OLD | NEW |
---|---|
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "extensions/renderer/user_script_slave.h" | 5 #include "extensions/renderer/user_script_slave.h" |
6 | 6 |
7 #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
| |
7 #include <map> | 8 #include <map> |
8 | 9 |
9 #include "base/command_line.h" | 10 #include "base/command_line.h" |
10 #include "base/logging.h" | 11 #include "base/logging.h" |
11 #include "base/memory/shared_memory.h" | 12 #include "base/memory/shared_memory.h" |
12 #include "base/metrics/histogram.h" | 13 #include "base/metrics/histogram.h" |
13 #include "base/pickle.h" | 14 #include "base/pickle.h" |
14 #include "base/strings/stringprintf.h" | 15 #include "base/strings/stringprintf.h" |
15 #include "base/timer/elapsed_timer.h" | 16 #include "base/timer/elapsed_timer.h" |
16 #include "content/public/common/url_constants.h" | 17 #include "content/public/common/url_constants.h" |
(...skipping 22 matching lines...) Expand all Loading... | |
39 using blink::WebFrame; | 40 using blink::WebFrame; |
40 using blink::WebSecurityOrigin; | 41 using blink::WebSecurityOrigin; |
41 using blink::WebSecurityPolicy; | 42 using blink::WebSecurityPolicy; |
42 using blink::WebString; | 43 using blink::WebString; |
43 using blink::WebVector; | 44 using blink::WebVector; |
44 using blink::WebView; | 45 using blink::WebView; |
45 using content::RenderThread; | 46 using content::RenderThread; |
46 | 47 |
47 namespace extensions { | 48 namespace extensions { |
48 | 49 |
50 namespace { | |
51 | |
49 // These two strings are injected before and after the Greasemonkey API and | 52 // These two strings are injected before and after the Greasemonkey API and |
50 // user script to wrap it in an anonymous scope. | 53 // user script to wrap it in an anonymous scope. |
51 static const char kUserScriptHead[] = "(function (unsafeWindow) {\n"; | 54 const char kUserScriptHead[] = "(function (unsafeWindow) {\n"; |
52 static const char kUserScriptTail[] = "\n})(window);"; | 55 const char kUserScriptTail[] = "\n})(window);"; |
56 | |
57 int64 g_next_pending_id = 0; | |
58 | |
59 } // namespace | |
60 | |
61 struct UserScriptSlave::ScriptsRunInfo { | |
62 ScriptsRunInfo(); | |
63 | |
64 size_t num_css; | |
65 size_t num_js; | |
66 ExecutingScriptsMap executing_scripts; | |
67 base::ElapsedTimer timer; | |
68 | |
69 private: | |
70 // Technically, this is safe to copy, but it's slow because of | |
71 // |executing_scripts|. Disallow it until we need it. | |
72 DISALLOW_COPY_AND_ASSIGN(ScriptsRunInfo); | |
73 }; | |
74 | |
75 UserScriptSlave::ScriptsRunInfo::ScriptsRunInfo() : num_css(0u), num_js(0u) { | |
76 } | |
77 | |
78 struct UserScriptSlave::PendingInjection { | |
79 PendingInjection(UserScript* user_script, | |
80 UserScript::RunLocation run_location, | |
81 const blink::WebString& web_frame_name); | |
82 ~PendingInjection(); | |
83 | |
84 // The user scripts to run. Weak, but guaranteed to be alive because they | |
85 // have the same parent (UserScriptSlave). | |
86 std::set<UserScript*> user_scripts; | |
87 | |
88 // The extension id relating to these user scripts. | |
89 const std::string extension_id; | |
90 | |
91 // The pending injection's latest run location. Scripts with any RunLocation | |
92 // before or equal to this are run when the injection is executed. | |
93 UserScript::RunLocation latest_run_location; | |
94 | |
95 // The unique name of the web frame to inject into. | |
96 blink::WebString web_frame_name; | |
97 | |
98 // The id of this PendingInjection, guaranteed to be unique. | |
99 int64 id; | |
100 }; | |
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!
| |
101 | |
102 UserScriptSlave::PendingInjection::PendingInjection( | |
103 UserScript* user_script, | |
104 UserScript::RunLocation run_location, | |
105 const blink::WebString& web_frame_name) | |
106 : extension_id(user_script->extension_id()), | |
107 latest_run_location(run_location), | |
108 web_frame_name(web_frame_name), | |
109 id(g_next_pending_id++) { | |
110 user_scripts.insert(user_script); | |
111 } | |
112 | |
113 UserScriptSlave::PendingInjection::~PendingInjection() { | |
114 } | |
53 | 115 |
54 int UserScriptSlave::GetIsolatedWorldIdForExtension(const Extension* extension, | 116 int UserScriptSlave::GetIsolatedWorldIdForExtension(const Extension* extension, |
55 WebFrame* frame) { | 117 WebFrame* frame) { |
56 static int g_next_isolated_world_id = | 118 static int g_next_isolated_world_id = |
57 ExtensionsRendererClient::Get()->GetLowestIsolatedWorldId(); | 119 ExtensionsRendererClient::Get()->GetLowestIsolatedWorldId(); |
58 | 120 |
59 IsolatedWorldMap::iterator iter = isolated_world_ids_.find(extension->id()); | 121 IsolatedWorldMap::iterator iter = isolated_world_ids_.find(extension->id()); |
60 if (iter != isolated_world_ids_.end()) { | 122 if (iter != isolated_world_ids_.end()) { |
61 // We need to set the isolated world origin and CSP even if it's not a new | 123 // We need to set the isolated world origin and CSP even if it's not a new |
62 // world since these are stored per frame, and we might not have used this | 124 // world since these are stored per frame, and we might not have used this |
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
181 void UserScriptSlave::InjectScripts(WebFrame* frame, | 243 void UserScriptSlave::InjectScripts(WebFrame* frame, |
182 UserScript::RunLocation location) { | 244 UserScript::RunLocation location) { |
183 GURL data_source_url = ScriptContext::GetDataSourceURLForFrame(frame); | 245 GURL data_source_url = ScriptContext::GetDataSourceURLForFrame(frame); |
184 if (data_source_url.is_empty()) | 246 if (data_source_url.is_empty()) |
185 return; | 247 return; |
186 | 248 |
187 if (frame->isViewSourceModeEnabled()) | 249 if (frame->isViewSourceModeEnabled()) |
188 data_source_url = GURL(content::kViewSourceScheme + std::string(":") + | 250 data_source_url = GURL(content::kViewSourceScheme + std::string(":") + |
189 data_source_url.spec()); | 251 data_source_url.spec()); |
190 | 252 |
191 base::ElapsedTimer timer; | |
192 int num_css = 0; | |
193 int num_scripts = 0; | |
194 | |
195 ExecutingScriptsMap extensions_executing_scripts; | |
196 | |
197 blink::WebFrame* top_frame = frame->top(); | 253 blink::WebFrame* top_frame = frame->top(); |
198 content::RenderView* top_render_view = | 254 content::RenderView* top_render_view = |
199 content::RenderView::FromWebView(top_frame->view()); | 255 content::RenderView::FromWebView(top_frame->view()); |
200 | 256 |
201 for (size_t i = 0; i < scripts_.size(); ++i) { | 257 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
| |
202 std::vector<WebScriptSource> sources; | 258 ScriptsRunInfo scripts_run_info; |
203 UserScript* script = scripts_[i]; | 259 for (std::vector<UserScript*>::const_iterator iter = scripts_.begin(); |
260 iter != scripts_.end(); | |
261 ++iter) { | |
262 script = *iter; | |
204 | 263 |
205 if (frame->parent() && !script->match_all_frames()) | 264 if (frame->parent() && !script->match_all_frames()) |
206 continue; // Only match subframes if the script declared it wanted to. | 265 continue; // Only match subframes if the script declared it wanted to. |
207 | 266 |
208 const Extension* extension = extensions_->GetByID(script->extension_id()); | 267 const Extension* extension = extensions_->GetByID(script->extension_id()); |
209 | 268 |
210 // Since extension info is sent separately from user script info, they can | 269 // Since extension info is sent separately from user script info, they can |
211 // be out of sync. We just ignore this situation. | 270 // be out of sync. We just ignore this situation. |
212 if (!extension) | 271 if (!extension) |
213 continue; | 272 continue; |
214 | 273 |
215 // Content scripts are not tab-specific. | 274 // Content scripts are not tab-specific. |
216 const int kNoTabId = -1; | 275 const int kNoTabId = -1; |
217 // We don't have a process id in this context. | 276 // We don't have a process id in this context. |
218 const int kNoProcessId = -1; | 277 const int kNoProcessId = -1; |
219 if (!PermissionsData::CanExecuteScriptOnPage(extension, | 278 if (!PermissionsData::CanExecuteScriptOnPage(extension, |
220 data_source_url, | 279 data_source_url, |
221 top_frame->document().url(), | 280 top_frame->document().url(), |
222 kNoTabId, | 281 kNoTabId, |
223 script, | 282 script, |
224 kNoProcessId, | 283 kNoProcessId, |
225 NULL)) { | 284 NULL)) { |
226 continue; | 285 continue; |
227 } | 286 } |
228 | 287 |
229 if (location == UserScript::DOCUMENT_START) { | 288 bool run_css = !script->css_scripts().empty() && |
230 num_css += script->css_scripts().size(); | 289 location == UserScript::DOCUMENT_START; |
231 for (UserScript::FileList::const_iterator iter = | 290 bool run_js = !script->js_scripts().empty() && |
232 script->css_scripts().begin(); | 291 script->run_location() == location; |
233 iter != script->css_scripts().end(); | 292 if (!run_css && !run_js) |
234 ++iter) { | 293 continue; |
235 frame->document().insertStyleSheet( | 294 |
236 WebString::fromUTF8(iter->GetContent().as_string())); | 295 if (PermissionsData::RequiresActionForScriptExecution(extension)) { |
237 } | 296 int id = AddPendingInjection(script, location, frame->uniqueName()); |
297 top_render_view->Send( | |
298 new ExtensionHostMsg_RequestContentScriptPermission( | |
299 top_render_view->GetRoutingID(), | |
300 extension->id(), | |
301 top_render_view->GetPageId(), | |
302 id)); | |
303 continue; | |
238 } | 304 } |
239 | 305 |
240 if (script->run_location() == location) { | 306 if (run_css) |
241 // TODO(rdevlin.cronin): Right now, this is just a notification, but soon | 307 InjectCSSScripts(frame, script, &scripts_run_info); |
242 // we should block without user consent. | 308 if (run_js) |
243 if (PermissionsData::RequiresActionForScriptExecution(extension)) { | 309 InjectJSScripts(frame, script, extension, &scripts_run_info); |
244 top_render_view->Send( | 310 } |
245 new ExtensionHostMsg_NotifyExtensionScriptExecution( | 311 |
246 top_render_view->GetRoutingID(), | 312 LogScriptsRun(frame, location, scripts_run_info); |
247 extension->id(), | 313 } |
248 top_render_view->GetPageId())); | 314 |
249 } | 315 void UserScriptSlave::OnContentScriptGrantedPermission( |
250 num_scripts += script->js_scripts().size(); | 316 content::RenderView* render_view, int request_id) { |
251 for (size_t j = 0; j < script->js_scripts().size(); ++j) { | 317 if (!render_view) |
252 UserScript::File& file = script->js_scripts()[j]; | 318 return; |
253 std::string content = file.GetContent().as_string(); | 319 |
254 | 320 PendingInjectionList::iterator iter = pending_injections_.begin(); |
255 // We add this dumb function wrapper for standalone user script to | 321 for (; iter != pending_injections_.end() && (*iter)->id != request_id; ++iter) |
256 // emulate what Greasemonkey does. | 322 ; // Intentionally empty. |
257 // TODO(aa): I think that maybe "is_standalone" scripts don't exist | 323 if (iter == pending_injections_.end()) |
258 // anymore. Investigate. | 324 return; |
259 if (script->is_standalone() || script->emulate_greasemonkey()) { | 325 |
260 content.insert(0, kUserScriptHead); | 326 linked_ptr<PendingInjection> pending_injection = *iter; |
261 content += kUserScriptTail; | 327 |
262 } | 328 blink::WebView* web_view = render_view->GetWebView(); |
263 sources.push_back( | 329 if (!web_view) |
264 WebScriptSource(WebString::fromUTF8(content), file.url())); | 330 return; |
265 } | 331 |
332 blink::WebFrame* web_frame = | |
333 web_view->findFrameByName(pending_injection->web_frame_name); | |
334 if (!web_frame) | |
335 return; | |
336 | |
337 const Extension* extension = | |
338 extensions_->GetByID(pending_injection->extension_id); | |
339 if (!extension) | |
340 return; | |
341 | |
342 ScriptsRunInfo scripts_run_info; | |
343 for (std::set<UserScript*>::const_iterator iter = | |
344 pending_injection->user_scripts.begin(); | |
345 iter != pending_injection->user_scripts.end(); | |
346 ++iter) { | |
347 UserScript* script = *iter; | |
348 if (!script->css_scripts().empty()) | |
349 InjectCSSScripts(web_frame, script, &scripts_run_info); | |
350 if (!script->js_scripts().empty()) | |
351 InjectJSScripts(web_frame, script, extension, &scripts_run_info); | |
352 } | |
353 LogScriptsRun(web_frame, | |
354 pending_injection->latest_run_location, // TODO: Add new | |
355 scripts_run_info); | |
356 } | |
357 | |
358 int UserScriptSlave::AddPendingInjection(UserScript* script, | |
359 UserScript::RunLocation location, | |
360 const blink::WebString& frame_name) { | |
361 // Look for an existing entry with the same extension id and same web frame. | |
362 const std::string& extension_id = script->extension_id(); | |
363 PendingInjectionList::iterator existing = pending_injections_.begin(); | |
364 while (existing != pending_injections_.end() && | |
365 ((*existing)->extension_id != extension_id || | |
366 (*existing)->web_frame_name != frame_name)) { | |
367 ++existing; | |
368 } | |
369 | |
370 // If there is an existing entry, append the script and latest run location. | |
371 // Otherwise, add a new entry. | |
372 if (existing != pending_injections_.end()) { | |
373 (*existing)->user_scripts.insert(script); | |
374 (*existing)->latest_run_location = | |
375 std::max(location, (*existing)->latest_run_location); | |
376 } else { | |
377 existing = pending_injections_.insert( | |
378 existing, | |
379 make_linked_ptr(new PendingInjection(script, location, frame_name))); | |
380 } | |
381 return (*existing)->id; | |
382 } | |
383 | |
384 void UserScriptSlave::InjectCSSScripts(WebFrame* frame, | |
385 UserScript* script, | |
386 ScriptsRunInfo* scripts_run_info) { | |
387 DCHECK(frame); | |
388 DCHECK(frame->parent() || script->match_all_frames()); | |
389 DCHECK(scripts_run_info); | |
390 | |
391 const UserScript::FileList& css_scripts = script->css_scripts(); | |
392 scripts_run_info->num_css += css_scripts.size(); | |
393 for (UserScript::FileList::const_iterator iter = css_scripts.begin(); | |
394 iter != css_scripts.end(); | |
395 ++iter) { | |
396 frame->document().insertStyleSheet( | |
397 WebString::fromUTF8(iter->GetContent().as_string())); | |
398 } | |
399 } | |
400 | |
401 void UserScriptSlave::InjectJSScripts(WebFrame* frame, | |
402 UserScript* script, | |
403 const Extension* extension, | |
404 ScriptsRunInfo* scripts_run_info) { | |
405 DCHECK(frame); | |
406 DCHECK(!frame->parent() || script->match_all_frames()); | |
407 DCHECK(extension); | |
408 DCHECK(scripts_run_info); | |
409 | |
410 const UserScript::FileList& js_scripts = script->js_scripts(); | |
411 std::vector<WebScriptSource> sources; | |
412 bool is_standalone_or_emulate_greasemonkey = | |
413 script->is_standalone() || script->emulate_greasemonkey(); | |
414 scripts_run_info->num_js += js_scripts.size(); | |
415 for (UserScript::FileList::const_iterator iter = js_scripts.begin(); | |
416 iter != js_scripts.end(); | |
417 ++iter) { | |
418 std::string content = iter->GetContent().as_string(); | |
419 | |
420 // We add this dumb function wrapper for standalone user script to | |
421 // emulate what Greasemonkey does. | |
422 // TODO(aa): I think that maybe "is_standalone" scripts don't exist | |
423 // anymore. Investigate. | |
424 if (is_standalone_or_emulate_greasemonkey) { | |
425 content.insert(0, kUserScriptHead); | |
426 content += kUserScriptTail; | |
266 } | 427 } |
267 | 428 sources.push_back( |
268 if (!sources.empty()) { | 429 WebScriptSource(WebString::fromUTF8(content), iter->url())); |
269 // Emulate Greasemonkey API for scripts that were converted to extensions | 430 } |
270 // and "standalone" user scripts. | 431 |
271 if (script->is_standalone() || script->emulate_greasemonkey()) { | 432 // Emulate Greasemonkey API for scripts that were converted to extensions |
272 sources.insert( | 433 // and "standalone" user scripts. |
273 sources.begin(), | 434 if (is_standalone_or_emulate_greasemonkey) { |
274 WebScriptSource(WebString::fromUTF8(api_js_.as_string()))); | 435 sources.insert( |
275 } | 436 sources.begin(), |
276 | 437 WebScriptSource(WebString::fromUTF8(api_js_.as_string()))); |
277 int isolated_world_id = GetIsolatedWorldIdForExtension(extension, frame); | 438 } |
278 | 439 |
279 base::ElapsedTimer exec_timer; | 440 int isolated_world_id = GetIsolatedWorldIdForExtension(extension, frame); |
280 DOMActivityLogger::AttachToWorld(isolated_world_id, extension->id()); | 441 base::ElapsedTimer exec_timer; |
281 frame->executeScriptInIsolatedWorld(isolated_world_id, | 442 DOMActivityLogger::AttachToWorld(isolated_world_id, extension->id()); |
282 &sources.front(), | 443 frame->executeScriptInIsolatedWorld(isolated_world_id, |
283 sources.size(), | 444 &sources.front(), |
284 EXTENSION_GROUP_CONTENT_SCRIPTS); | 445 sources.size(), |
285 UMA_HISTOGRAM_TIMES("Extensions.InjectScriptTime", exec_timer.Elapsed()); | 446 EXTENSION_GROUP_CONTENT_SCRIPTS); |
286 | 447 UMA_HISTOGRAM_TIMES("Extensions.InjectScriptTime", exec_timer.Elapsed()); |
287 for (std::vector<WebScriptSource>::const_iterator iter = sources.begin(); | 448 |
288 iter != sources.end(); | 449 for (std::vector<WebScriptSource>::const_iterator iter = sources.begin(); |
289 ++iter) { | 450 iter != sources.end(); |
290 extensions_executing_scripts[extension->id()].insert( | 451 ++iter) { |
291 GURL(iter->url).path()); | 452 scripts_run_info->executing_scripts[extension->id()].insert( |
292 } | 453 GURL(iter->url).path()); |
293 } | 454 } |
294 } | 455 } |
295 | 456 |
457 void UserScriptSlave::LogScriptsRun(blink::WebFrame* frame, | |
458 UserScript::RunLocation location, | |
459 const ScriptsRunInfo& info) { | |
296 // Notify the browser if any extensions are now executing scripts. | 460 // Notify the browser if any extensions are now executing scripts. |
297 if (!extensions_executing_scripts.empty()) { | 461 if (!info.executing_scripts.empty()) { |
298 top_render_view->Send(new ExtensionHostMsg_ContentScriptsExecuting( | 462 content::RenderView* render_view = |
299 top_render_view->GetRoutingID(), | 463 content::RenderView::FromWebView(frame->view()); |
300 extensions_executing_scripts, | 464 render_view->Send(new ExtensionHostMsg_ContentScriptsExecuting( |
301 top_render_view->GetPageId(), | 465 render_view->GetRoutingID(), |
302 ScriptContext::GetDataSourceURLForFrame(top_frame))); | 466 info.executing_scripts, |
303 } | 467 render_view->GetPageId(), |
304 | 468 ScriptContext::GetDataSourceURLForFrame(frame))); |
305 // Log debug info. | 469 } |
470 | |
306 if (location == UserScript::DOCUMENT_START) { | 471 if (location == UserScript::DOCUMENT_START) { |
307 UMA_HISTOGRAM_COUNTS_100("Extensions.InjectStart_CssCount", num_css); | 472 UMA_HISTOGRAM_COUNTS_100("Extensions.InjectStart_CssCount", |
308 UMA_HISTOGRAM_COUNTS_100("Extensions.InjectStart_ScriptCount", num_scripts); | 473 info.num_css); |
309 if (num_css || num_scripts) | 474 UMA_HISTOGRAM_COUNTS_100("Extensions.InjectStart_ScriptCount", info.num_js); |
310 UMA_HISTOGRAM_TIMES("Extensions.InjectStart_Time", timer.Elapsed()); | 475 if (info.num_css || info.num_js) |
476 UMA_HISTOGRAM_TIMES("Extensions.InjectStart_Time", info.timer.Elapsed()); | |
311 } else if (location == UserScript::DOCUMENT_END) { | 477 } else if (location == UserScript::DOCUMENT_END) { |
312 UMA_HISTOGRAM_COUNTS_100("Extensions.InjectEnd_ScriptCount", num_scripts); | 478 UMA_HISTOGRAM_COUNTS_100("Extensions.InjectEnd_ScriptCount", info.num_js); |
313 if (num_scripts) | 479 if (info.num_js) |
314 UMA_HISTOGRAM_TIMES("Extensions.InjectEnd_Time", timer.Elapsed()); | 480 UMA_HISTOGRAM_TIMES("Extensions.InjectEnd_Time", info.timer.Elapsed()); |
315 } else if (location == UserScript::DOCUMENT_IDLE) { | 481 } else if (location == UserScript::DOCUMENT_IDLE) { |
316 UMA_HISTOGRAM_COUNTS_100("Extensions.InjectIdle_ScriptCount", num_scripts); | 482 UMA_HISTOGRAM_COUNTS_100("Extensions.InjectIdle_ScriptCount", info.num_js); |
317 if (num_scripts) | 483 if (info.num_js) |
318 UMA_HISTOGRAM_TIMES("Extensions.InjectIdle_Time", timer.Elapsed()); | 484 UMA_HISTOGRAM_TIMES("Extensions.InjectIdle_Time", info.timer.Elapsed()); |
319 } else { | 485 } else { |
320 NOTREACHED(); | 486 NOTREACHED(); |
321 } | 487 } |
322 } | 488 } |
323 | 489 |
324 } // namespace extensions | 490 } // namespace extensions |
OLD | NEW |