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

Side by Side Diff: extensions/renderer/user_script_slave.cc

Issue 284153006: Introduce ScriptInjection in extensions/renderer to inject UserScripts (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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
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 <map> 7 #include <map>
8 8
9 #include "base/command_line.h"
10 #include "base/logging.h" 9 #include "base/logging.h"
11 #include "base/memory/shared_memory.h" 10 #include "base/memory/shared_memory.h"
12 #include "base/metrics/histogram.h" 11 #include "base/metrics/histogram.h"
13 #include "base/pickle.h" 12 #include "base/pickle.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/timer/elapsed_timer.h" 13 #include "base/timer/elapsed_timer.h"
16 #include "content/public/common/url_constants.h"
17 #include "content/public/renderer/render_thread.h" 14 #include "content/public/renderer/render_thread.h"
18 #include "content/public/renderer/render_view.h" 15 #include "content/public/renderer/render_view.h"
19 #include "extensions/common/extension.h" 16 #include "extensions/common/extension.h"
20 #include "extensions/common/extension_messages.h" 17 #include "extensions/common/extension_messages.h"
21 #include "extensions/common/extension_set.h" 18 #include "extensions/common/extension_set.h"
22 #include "extensions/common/manifest_handlers/csp_info.h" 19 #include "extensions/common/manifest_handlers/csp_info.h"
23 #include "extensions/common/permissions/permissions_data.h" 20 #include "extensions/common/permissions/permissions_data.h"
24 #include "extensions/renderer/dom_activity_logger.h"
25 #include "extensions/renderer/extension_groups.h"
26 #include "extensions/renderer/extensions_renderer_client.h" 21 #include "extensions/renderer/extensions_renderer_client.h"
27 #include "extensions/renderer/script_context.h" 22 #include "extensions/renderer/script_context.h"
28 #include "grit/renderer_resources.h"
29 #include "third_party/WebKit/public/platform/WebURLRequest.h"
30 #include "third_party/WebKit/public/platform/WebVector.h"
31 #include "third_party/WebKit/public/web/WebDocument.h"
32 #include "third_party/WebKit/public/web/WebFrame.h" 23 #include "third_party/WebKit/public/web/WebFrame.h"
33 #include "third_party/WebKit/public/web/WebSecurityOrigin.h" 24 #include "third_party/WebKit/public/web/WebSecurityOrigin.h"
34 #include "third_party/WebKit/public/web/WebSecurityPolicy.h" 25 #include "third_party/WebKit/public/web/WebSecurityPolicy.h"
35 #include "third_party/WebKit/public/web/WebView.h" 26 #include "third_party/WebKit/public/web/WebView.h"
36 #include "ui/base/resource/resource_bundle.h"
37 #include "url/gurl.h" 27 #include "url/gurl.h"
38 28
39 using blink::WebFrame; 29 using blink::WebFrame;
40 using blink::WebSecurityOrigin; 30 using blink::WebSecurityOrigin;
41 using blink::WebSecurityPolicy; 31 using blink::WebSecurityPolicy;
42 using blink::WebString; 32 using blink::WebString;
43 using blink::WebVector;
44 using blink::WebView; 33 using blink::WebView;
45 using content::RenderThread; 34 using content::RenderThread;
46 35
47 namespace extensions { 36 namespace extensions {
48 37
49 // These two strings are injected before and after the Greasemonkey API and
50 // user script to wrap it in an anonymous scope.
51 static const char kUserScriptHead[] = "(function (unsafeWindow) {\n";
52 static const char kUserScriptTail[] = "\n})(window);";
53
54 int UserScriptSlave::GetIsolatedWorldIdForExtension(const Extension* extension, 38 int UserScriptSlave::GetIsolatedWorldIdForExtension(const Extension* extension,
55 WebFrame* frame) { 39 WebFrame* frame) {
56 static int g_next_isolated_world_id = 40 static int g_next_isolated_world_id =
57 ExtensionsRendererClient::Get()->GetLowestIsolatedWorldId(); 41 ExtensionsRendererClient::Get()->GetLowestIsolatedWorldId();
58 42
43 int id = 0;
59 IsolatedWorldMap::iterator iter = isolated_world_ids_.find(extension->id()); 44 IsolatedWorldMap::iterator iter = isolated_world_ids_.find(extension->id());
60 if (iter != isolated_world_ids_.end()) { 45 if (iter != isolated_world_ids_.end()) {
61 // We need to set the isolated world origin and CSP even if it's not a new 46 id = iter->second;
62 // world since these are stored per frame, and we might not have used this 47 } else {
63 // isolated world in this frame before. 48 id = g_next_isolated_world_id++;
64 frame->setIsolatedWorldSecurityOrigin( 49 // This map will tend to pile up over time, but realistically, you're never
65 iter->second, WebSecurityOrigin::create(extension->url())); 50 // going to have enough extensions for it to matter.
66 frame->setIsolatedWorldContentSecurityPolicy( 51 isolated_world_ids_[extension->id()] = id;
67 iter->second,
68 WebString::fromUTF8(CSPInfo::GetContentSecurityPolicy(extension)));
69 return iter->second;
70 } 52 }
71 53
72 int new_id = g_next_isolated_world_id; 54 // We need to set the isolated world origin and CSP even if it's not a new
73 ++g_next_isolated_world_id; 55 // world since these are stored per frame, and we might not have used this
56 // isolated world in this frame before.
57 frame->setIsolatedWorldSecurityOrigin(
58 id, WebSecurityOrigin::create(extension->url()));
59 frame->setIsolatedWorldContentSecurityPolicy(
60 id, WebString::fromUTF8(CSPInfo::GetContentSecurityPolicy(extension)));
74 61
75 // This map will tend to pile up over time, but realistically, you're never 62 return id;
76 // going to have enough extensions for it to matter.
77 isolated_world_ids_[extension->id()] = new_id;
78 frame->setIsolatedWorldSecurityOrigin(
79 new_id, WebSecurityOrigin::create(extension->url()));
80 frame->setIsolatedWorldContentSecurityPolicy(
81 new_id,
82 WebString::fromUTF8(CSPInfo::GetContentSecurityPolicy(extension)));
83 return new_id;
84 } 63 }
85 64
86 std::string UserScriptSlave::GetExtensionIdForIsolatedWorld( 65 std::string UserScriptSlave::GetExtensionIdForIsolatedWorld(
87 int isolated_world_id) { 66 int isolated_world_id) {
88 for (IsolatedWorldMap::iterator iter = isolated_world_ids_.begin(); 67 for (IsolatedWorldMap::iterator iter = isolated_world_ids_.begin();
89 iter != isolated_world_ids_.end(); 68 iter != isolated_world_ids_.end();
90 ++iter) { 69 ++iter) {
91 if (iter->second == isolated_world_id) 70 if (iter->second == isolated_world_id)
92 return iter->first; 71 return iter->first;
93 } 72 }
94 return std::string(); 73 return std::string();
95 } 74 }
96 75
97 void UserScriptSlave::RemoveIsolatedWorld(const std::string& extension_id) { 76 void UserScriptSlave::RemoveIsolatedWorld(const std::string& extension_id) {
98 isolated_world_ids_.erase(extension_id); 77 isolated_world_ids_.erase(extension_id);
99 } 78 }
100 79
101 UserScriptSlave::UserScriptSlave(const ExtensionSet* extensions) 80 UserScriptSlave::UserScriptSlave(const ExtensionSet* extensions)
102 : script_deleter_(&scripts_), extensions_(extensions) { 81 : script_deleter_(&script_injections_), extensions_(extensions) {
103 api_js_ = ResourceBundle::GetSharedInstance().GetRawDataResource(
104 IDR_GREASEMONKEY_API_JS);
105 } 82 }
106 83
107 UserScriptSlave::~UserScriptSlave() { 84 UserScriptSlave::~UserScriptSlave() {
108 } 85 }
109 86
110 void UserScriptSlave::GetActiveExtensions( 87 void UserScriptSlave::GetActiveExtensions(
111 std::set<std::string>* extension_ids) { 88 std::set<std::string>* extension_ids) {
112 for (size_t i = 0; i < scripts_.size(); ++i) { 89 DCHECK(extension_ids);
113 DCHECK(!scripts_[i]->extension_id().empty()); 90 for (std::vector<ScriptInjection*>::const_iterator iter =
114 extension_ids->insert(scripts_[i]->extension_id()); 91 script_injections_.begin();
92 iter != script_injections_.end();
93 ++iter) {
94 DCHECK(!(*iter)->extension_id().empty());
95 extension_ids->insert((*iter)->extension_id());
115 } 96 }
116 } 97 }
117 98
99 const Extension* UserScriptSlave::GetExtension(
100 const std::string& extension_id) {
101 return extensions_->GetByID(extension_id);
102 }
103
118 bool UserScriptSlave::UpdateScripts(base::SharedMemoryHandle shared_memory) { 104 bool UserScriptSlave::UpdateScripts(base::SharedMemoryHandle shared_memory) {
119 scripts_.clear(); 105 STLDeleteElements(&script_injections_);
120 106
121 bool only_inject_incognito = 107 bool only_inject_incognito =
122 ExtensionsRendererClient::Get()->IsIncognitoProcess(); 108 ExtensionsRendererClient::Get()->IsIncognitoProcess();
123 109
124 // Create the shared memory object (read only). 110 // Create the shared memory object (read only).
125 shared_memory_.reset(new base::SharedMemory(shared_memory, true)); 111 shared_memory_.reset(new base::SharedMemory(shared_memory, true));
126 if (!shared_memory_.get()) 112 if (!shared_memory_.get())
127 return false; 113 return false;
128 114
129 // First get the size of the memory block. 115 // First get the size of the memory block.
130 if (!shared_memory_->Map(sizeof(Pickle::Header))) 116 if (!shared_memory_->Map(sizeof(Pickle::Header)))
131 return false; 117 return false;
132 Pickle::Header* pickle_header = 118 Pickle::Header* pickle_header =
133 reinterpret_cast<Pickle::Header*>(shared_memory_->memory()); 119 reinterpret_cast<Pickle::Header*>(shared_memory_->memory());
134 120
135 // Now map in the rest of the block. 121 // Now map in the rest of the block.
136 int pickle_size = sizeof(Pickle::Header) + pickle_header->payload_size; 122 int pickle_size = sizeof(Pickle::Header) + pickle_header->payload_size;
137 shared_memory_->Unmap(); 123 shared_memory_->Unmap();
138 if (!shared_memory_->Map(pickle_size)) 124 if (!shared_memory_->Map(pickle_size))
139 return false; 125 return false;
140 126
141 // Unpickle scripts. 127 // Unpickle scripts.
142 uint64 num_scripts = 0; 128 uint64 num_scripts = 0;
143 Pickle pickle(reinterpret_cast<char*>(shared_memory_->memory()), pickle_size); 129 Pickle pickle(reinterpret_cast<char*>(shared_memory_->memory()), pickle_size);
144 PickleIterator iter(pickle); 130 PickleIterator iter(pickle);
145 CHECK(pickle.ReadUInt64(&iter, &num_scripts)); 131 CHECK(pickle.ReadUInt64(&iter, &num_scripts));
146 132
147 scripts_.reserve(num_scripts); 133 script_injections_.reserve(num_scripts);
148 for (uint64 i = 0; i < num_scripts; ++i) { 134 for (uint64 i = 0; i < num_scripts; ++i) {
149 scripts_.push_back(new UserScript()); 135 scoped_ptr<UserScript> script(new UserScript());
150 UserScript* script = scripts_.back();
151 script->Unpickle(pickle, &iter); 136 script->Unpickle(pickle, &iter);
152 137
153 // Note that this is a pointer into shared memory. We don't own it. It gets 138 // Note that this is a pointer into shared memory. We don't own it. It gets
154 // cleared up when the last renderer or browser process drops their 139 // cleared up when the last renderer or browser process drops their
155 // reference to the shared memory. 140 // reference to the shared memory.
156 for (size_t j = 0; j < script->js_scripts().size(); ++j) { 141 for (size_t j = 0; j < script->js_scripts().size(); ++j) {
157 const char* body = NULL; 142 const char* body = NULL;
158 int body_length = 0; 143 int body_length = 0;
159 CHECK(pickle.ReadData(&iter, &body, &body_length)); 144 CHECK(pickle.ReadData(&iter, &body, &body_length));
160 script->js_scripts()[j].set_external_content( 145 script->js_scripts()[j].set_external_content(
161 base::StringPiece(body, body_length)); 146 base::StringPiece(body, body_length));
162 } 147 }
163 for (size_t j = 0; j < script->css_scripts().size(); ++j) { 148 for (size_t j = 0; j < script->css_scripts().size(); ++j) {
164 const char* body = NULL; 149 const char* body = NULL;
165 int body_length = 0; 150 int body_length = 0;
166 CHECK(pickle.ReadData(&iter, &body, &body_length)); 151 CHECK(pickle.ReadData(&iter, &body, &body_length));
167 script->css_scripts()[j].set_external_content( 152 script->css_scripts()[j].set_external_content(
168 base::StringPiece(body, body_length)); 153 base::StringPiece(body, body_length));
169 } 154 }
170 155
171 if (only_inject_incognito && !script->is_incognito_enabled()) { 156 if (only_inject_incognito && !script->is_incognito_enabled())
172 // This script shouldn't run in an incognito tab. 157 continue; // This script shouldn't run in an incognito tab.
173 delete script; 158
174 scripts_.pop_back(); 159 script_injections_.push_back(new ScriptInjection(script.Pass(), this));
175 }
176 } 160 }
177 161
178 return true; 162 return true;
179 } 163 }
180 164
181 void UserScriptSlave::InjectScripts(WebFrame* frame, 165 void UserScriptSlave::InjectScripts(WebFrame* frame,
182 UserScript::RunLocation location) { 166 UserScript::RunLocation location) {
183 GURL data_source_url = ScriptContext::GetDataSourceURLForFrame(frame); 167 GURL document_url = ScriptInjection::GetDocumentUrlForFrame(frame);
184 if (data_source_url.is_empty()) 168 if (document_url.is_empty())
185 return; 169 return;
186 170
187 if (frame->isViewSourceModeEnabled()) 171 content::RenderView* top_render_view =
188 data_source_url = GURL(content::kViewSourceScheme + std::string(":") + 172 content::RenderView::FromWebView(frame->top()->view());
189 data_source_url.spec());
190 173
191 base::ElapsedTimer timer; 174 ScriptInjection::ScriptsRunInfo scripts_run_info;
192 int num_css = 0; 175 for (std::vector<ScriptInjection*>::const_iterator iter =
193 int num_scripts = 0; 176 script_injections_.begin();
194 177 iter != script_injections_.end();
195 ExecutingScriptsMap extensions_executing_scripts; 178 ++iter) {
196 179 ScriptInjection* injection = *iter;
197 blink::WebFrame* top_frame = frame->top(); 180 if (!injection->WantsToRun(frame, location, document_url))
198 content::RenderView* top_render_view =
199 content::RenderView::FromWebView(top_frame->view());
200
201 for (size_t i = 0; i < scripts_.size(); ++i) {
202 std::vector<WebScriptSource> sources;
203 UserScript* script = scripts_[i];
204
205 if (frame->parent() && !script->match_all_frames())
206 continue; // Only match subframes if the script declared it wanted to.
207
208 const Extension* extension = extensions_->GetByID(script->extension_id());
209
210 // Since extension info is sent separately from user script info, they can
211 // be out of sync. We just ignore this situation.
212 if (!extension)
213 continue; 181 continue;
214 182
215 // Content scripts are not tab-specific. 183 const Extension* extension = GetExtension(injection->extension_id());
216 const int kNoTabId = -1; 184 DCHECK(extension);
217 // We don't have a process id in this context. 185
not at google - send to devlin 2014/05/15 23:23:21 I note you removed your TODO here :)
Devlin 2014/05/16 22:32:20 Whoops! That was actually just because this was b
218 const int kNoProcessId = -1; 186 if (PermissionsData::RequiresActionForScriptExecution(extension)) {
219 if (!PermissionsData::CanExecuteScriptOnPage(extension, 187 top_render_view->Send(
220 data_source_url, 188 new ExtensionHostMsg_NotifyExtensionScriptExecution(
221 top_frame->document().url(), 189 top_render_view->GetRoutingID(),
222 kNoTabId, 190 extension->id(),
223 script, 191 top_render_view->GetPageId()));
224 kNoProcessId,
225 NULL)) {
226 continue;
227 } 192 }
228 193
229 if (location == UserScript::DOCUMENT_START) { 194 injection->Inject(frame, location, &scripts_run_info);
230 num_css += script->css_scripts().size();
231 for (UserScript::FileList::const_iterator iter =
232 script->css_scripts().begin();
233 iter != script->css_scripts().end();
234 ++iter) {
235 frame->document().insertStyleSheet(
236 WebString::fromUTF8(iter->GetContent().as_string()));
237 }
238 }
239
240 if (script->run_location() == location) {
241 // TODO(rdevlin.cronin): Right now, this is just a notification, but soon
242 // we should block without user consent.
243 if (PermissionsData::RequiresActionForScriptExecution(extension)) {
244 top_render_view->Send(
245 new ExtensionHostMsg_NotifyExtensionScriptExecution(
246 top_render_view->GetRoutingID(),
247 extension->id(),
248 top_render_view->GetPageId()));
249 }
250 num_scripts += script->js_scripts().size();
251 for (size_t j = 0; j < script->js_scripts().size(); ++j) {
252 UserScript::File& file = script->js_scripts()[j];
253 std::string content = file.GetContent().as_string();
254
255 // We add this dumb function wrapper for standalone user script to
256 // emulate what Greasemonkey does.
257 // TODO(aa): I think that maybe "is_standalone" scripts don't exist
258 // anymore. Investigate.
259 if (script->is_standalone() || script->emulate_greasemonkey()) {
260 content.insert(0, kUserScriptHead);
261 content += kUserScriptTail;
262 }
263 sources.push_back(
264 WebScriptSource(WebString::fromUTF8(content), file.url()));
265 }
266 }
267
268 if (!sources.empty()) {
269 // Emulate Greasemonkey API for scripts that were converted to extensions
270 // and "standalone" user scripts.
271 if (script->is_standalone() || script->emulate_greasemonkey()) {
272 sources.insert(
273 sources.begin(),
274 WebScriptSource(WebString::fromUTF8(api_js_.as_string())));
275 }
276
277 int isolated_world_id = GetIsolatedWorldIdForExtension(extension, frame);
278
279 base::ElapsedTimer exec_timer;
280 DOMActivityLogger::AttachToWorld(isolated_world_id, extension->id());
281 frame->executeScriptInIsolatedWorld(isolated_world_id,
282 &sources.front(),
283 sources.size(),
284 EXTENSION_GROUP_CONTENT_SCRIPTS);
285 UMA_HISTOGRAM_TIMES("Extensions.InjectScriptTime", exec_timer.Elapsed());
286
287 for (std::vector<WebScriptSource>::const_iterator iter = sources.begin();
288 iter != sources.end();
289 ++iter) {
290 extensions_executing_scripts[extension->id()].insert(
291 GURL(iter->url).path());
292 }
293 }
294 } 195 }
295 196
197 LogScriptsRun(frame, location, scripts_run_info);
198 }
199
200 void UserScriptSlave::LogScriptsRun(
201 blink::WebFrame* frame,
202 UserScript::RunLocation location,
203 const ScriptInjection::ScriptsRunInfo& info) {
296 // Notify the browser if any extensions are now executing scripts. 204 // Notify the browser if any extensions are now executing scripts.
297 if (!extensions_executing_scripts.empty()) { 205 if (!info.executing_scripts.empty()) {
298 top_render_view->Send(new ExtensionHostMsg_ContentScriptsExecuting( 206 content::RenderView* render_view =
299 top_render_view->GetRoutingID(), 207 content::RenderView::FromWebView(frame->view());
300 extensions_executing_scripts, 208 render_view->Send(new ExtensionHostMsg_ContentScriptsExecuting(
301 top_render_view->GetPageId(), 209 render_view->GetRoutingID(),
302 ScriptContext::GetDataSourceURLForFrame(top_frame))); 210 info.executing_scripts,
211 render_view->GetPageId(),
212 ScriptContext::GetDataSourceURLForFrame(frame)));
303 } 213 }
304 214
305 // Log debug info.
306 if (location == UserScript::DOCUMENT_START) { 215 if (location == UserScript::DOCUMENT_START) {
307 UMA_HISTOGRAM_COUNTS_100("Extensions.InjectStart_CssCount", num_css); 216 UMA_HISTOGRAM_COUNTS_100("Extensions.InjectStart_CssCount",
308 UMA_HISTOGRAM_COUNTS_100("Extensions.InjectStart_ScriptCount", num_scripts); 217 info.num_css);
309 if (num_css || num_scripts) 218 UMA_HISTOGRAM_COUNTS_100("Extensions.InjectStart_ScriptCount", info.num_js);
310 UMA_HISTOGRAM_TIMES("Extensions.InjectStart_Time", timer.Elapsed()); 219 if (info.num_css || info.num_js)
220 UMA_HISTOGRAM_TIMES("Extensions.InjectStart_Time", info.timer.Elapsed());
311 } else if (location == UserScript::DOCUMENT_END) { 221 } else if (location == UserScript::DOCUMENT_END) {
312 UMA_HISTOGRAM_COUNTS_100("Extensions.InjectEnd_ScriptCount", num_scripts); 222 UMA_HISTOGRAM_COUNTS_100("Extensions.InjectEnd_ScriptCount", info.num_js);
313 if (num_scripts) 223 if (info.num_js)
314 UMA_HISTOGRAM_TIMES("Extensions.InjectEnd_Time", timer.Elapsed()); 224 UMA_HISTOGRAM_TIMES("Extensions.InjectEnd_Time", info.timer.Elapsed());
315 } else if (location == UserScript::DOCUMENT_IDLE) { 225 } else if (location == UserScript::DOCUMENT_IDLE) {
316 UMA_HISTOGRAM_COUNTS_100("Extensions.InjectIdle_ScriptCount", num_scripts); 226 UMA_HISTOGRAM_COUNTS_100("Extensions.InjectIdle_ScriptCount", info.num_js);
317 if (num_scripts) 227 if (info.num_js)
318 UMA_HISTOGRAM_TIMES("Extensions.InjectIdle_Time", timer.Elapsed()); 228 UMA_HISTOGRAM_TIMES("Extensions.InjectIdle_Time", info.timer.Elapsed());
319 } else { 229 } else {
320 NOTREACHED(); 230 NOTREACHED();
321 } 231 }
322 } 232 }
323 233
324 } // namespace extensions 234 } // namespace extensions
OLDNEW
« extensions/renderer/user_script_slave.h ('K') | « extensions/renderer/user_script_slave.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698