1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/renderer/extensions/user_script_slave.h"
9 #include "base/command_line.h"
10 #include "base/logging.h"
11 #include "base/memory/shared_memory.h"
12 #include "base/metrics/histogram.h"
13 #include "base/pickle.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/timer/elapsed_timer.h"
16 #include "chrome/common/extensions/extension_messages.h"
17 #include "chrome/common/url_constants.h"
18 #include "chrome/renderer/chrome_render_process_observer.h"
19 #include "chrome/renderer/extensions/dom_activity_logger.h"
20 #include "chrome/renderer/extensions/extension_groups.h"
21 #include "chrome/renderer/isolated_world_ids.h"
22 #include "content/public/renderer/render_thread.h"
23 #include "content/public/renderer/render_view.h"
24 #include "extensions/common/extension.h"
25 #include "extensions/common/extension_set.h"
26 #include "extensions/common/manifest_handlers/csp_info.h"
27 #include "extensions/common/permissions/permissions_data.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/WebDataSource.h"
32 #include "third_party/WebKit/public/web/WebDocument.h"
33 #include "third_party/WebKit/public/web/WebFrame.h"
34 #include "third_party/WebKit/public/web/WebSecurityOrigin.h"
35 #include "third_party/WebKit/public/web/WebSecurityPolicy.h"
36 #include "third_party/WebKit/public/web/WebView.h"
37 #include "ui/base/resource/resource_bundle.h"
40 using blink::WebFrame
;
41 using blink::WebSecurityOrigin
;
42 using blink::WebSecurityPolicy
;
43 using blink::WebString
;
44 using blink::WebVector
;
46 using content::RenderThread
;
48 namespace extensions
{
50 // These two strings are injected before and after the Greasemonkey API and
51 // user script to wrap it in an anonymous scope.
52 static const char kUserScriptHead
[] = "(function (unsafeWindow) {\n";
53 static const char kUserScriptTail
[] = "\n})(window);";
55 int UserScriptSlave::GetIsolatedWorldIdForExtension(const Extension
* extension
,
57 static int g_next_isolated_world_id
= chrome::ISOLATED_WORLD_ID_EXTENSIONS
;
59 IsolatedWorldMap::iterator iter
= isolated_world_ids_
.find(extension
->id());
60 if (iter
!= isolated_world_ids_
.end()) {
61 // 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
63 // isolated world in this frame before.
64 frame
->setIsolatedWorldSecurityOrigin(
66 WebSecurityOrigin::create(extension
->url()));
67 frame
->setIsolatedWorldContentSecurityPolicy(
69 WebString::fromUTF8(CSPInfo::GetContentSecurityPolicy(extension
)));
73 int new_id
= g_next_isolated_world_id
;
74 ++g_next_isolated_world_id
;
76 // This map will tend to pile up over time, but realistically, you're never
77 // going to have enough extensions for it to matter.
78 isolated_world_ids_
[extension
->id()] = new_id
;
79 InitializeIsolatedWorld(new_id
, extension
);
80 frame
->setIsolatedWorldSecurityOrigin(
82 WebSecurityOrigin::create(extension
->url()));
83 frame
->setIsolatedWorldContentSecurityPolicy(
85 WebString::fromUTF8(CSPInfo::GetContentSecurityPolicy(extension
)));
89 std::string
UserScriptSlave::GetExtensionIdForIsolatedWorld(
90 int isolated_world_id
) {
91 for (IsolatedWorldMap::iterator iter
= isolated_world_ids_
.begin();
92 iter
!= isolated_world_ids_
.end(); ++iter
) {
93 if (iter
->second
== isolated_world_id
)
100 void UserScriptSlave::InitializeIsolatedWorld(int isolated_world_id
,
101 const Extension
* extension
) {
102 const URLPatternSet
& permissions
=
103 PermissionsData::GetEffectiveHostPermissions(extension
);
104 for (URLPatternSet::const_iterator i
= permissions
.begin();
105 i
!= permissions
.end(); ++i
) {
106 const char* schemes
[] = {
107 content::kHttpScheme
,
108 content::kHttpsScheme
,
109 content::kFileScheme
,
110 chrome::kChromeUIScheme
,
112 for (size_t j
= 0; j
< arraysize(schemes
); ++j
) {
113 if (i
->MatchesScheme(schemes
[j
])) {
114 WebSecurityPolicy::addOriginAccessWhitelistEntry(
116 WebString::fromUTF8(schemes
[j
]),
117 WebString::fromUTF8(i
->host()),
118 i
->match_subdomains());
124 void UserScriptSlave::RemoveIsolatedWorld(const std::string
& extension_id
) {
125 isolated_world_ids_
.erase(extension_id
);
128 UserScriptSlave::UserScriptSlave(const ExtensionSet
* extensions
)
129 : script_deleter_(&scripts_
), extensions_(extensions
) {
130 api_js_
= ResourceBundle::GetSharedInstance().GetRawDataResource(
131 IDR_GREASEMONKEY_API_JS
);
134 UserScriptSlave::~UserScriptSlave() {}
136 void UserScriptSlave::GetActiveExtensions(
137 std::set
<std::string
>* extension_ids
) {
138 for (size_t i
= 0; i
< scripts_
.size(); ++i
) {
139 DCHECK(!scripts_
[i
]->extension_id().empty());
140 extension_ids
->insert(scripts_
[i
]->extension_id());
144 bool UserScriptSlave::UpdateScripts(base::SharedMemoryHandle shared_memory
) {
147 bool only_inject_incognito
=
148 ChromeRenderProcessObserver::is_incognito_process();
150 // Create the shared memory object (read only).
151 shared_memory_
.reset(new base::SharedMemory(shared_memory
, true));
152 if (!shared_memory_
.get())
155 // First get the size of the memory block.
156 if (!shared_memory_
->Map(sizeof(Pickle::Header
)))
158 Pickle::Header
* pickle_header
=
159 reinterpret_cast<Pickle::Header
*>(shared_memory_
->memory());
161 // Now map in the rest of the block.
162 int pickle_size
= sizeof(Pickle::Header
) + pickle_header
->payload_size
;
163 shared_memory_
->Unmap();
164 if (!shared_memory_
->Map(pickle_size
))
168 uint64 num_scripts
= 0;
169 Pickle
pickle(reinterpret_cast<char*>(shared_memory_
->memory()),
171 PickleIterator
iter(pickle
);
172 CHECK(pickle
.ReadUInt64(&iter
, &num_scripts
));
174 scripts_
.reserve(num_scripts
);
175 for (uint64 i
= 0; i
< num_scripts
; ++i
) {
176 scripts_
.push_back(new UserScript());
177 UserScript
* script
= scripts_
.back();
178 script
->Unpickle(pickle
, &iter
);
180 // Note that this is a pointer into shared memory. We don't own it. It gets
181 // cleared up when the last renderer or browser process drops their
182 // reference to the shared memory.
183 for (size_t j
= 0; j
< script
->js_scripts().size(); ++j
) {
184 const char* body
= NULL
;
186 CHECK(pickle
.ReadData(&iter
, &body
, &body_length
));
187 script
->js_scripts()[j
].set_external_content(
188 base::StringPiece(body
, body_length
));
190 for (size_t j
= 0; j
< script
->css_scripts().size(); ++j
) {
191 const char* body
= NULL
;
193 CHECK(pickle
.ReadData(&iter
, &body
, &body_length
));
194 script
->css_scripts()[j
].set_external_content(
195 base::StringPiece(body
, body_length
));
198 if (only_inject_incognito
&& !script
->is_incognito_enabled()) {
199 // This script shouldn't run in an incognito tab.
205 // Push user styles down into WebCore
206 RenderThread::Get()->EnsureWebKitInitialized();
207 WebView::removeInjectedStyleSheets();
208 for (size_t i
= 0; i
< scripts_
.size(); ++i
) {
209 UserScript
* script
= scripts_
[i
];
210 if (script
->css_scripts().empty())
213 WebVector
<WebString
> patterns
;
214 std::vector
<WebString
> temp_patterns
;
215 const URLPatternSet
& url_patterns
= script
->url_patterns();
216 for (URLPatternSet::const_iterator k
= url_patterns
.begin();
217 k
!= url_patterns
.end(); ++k
) {
218 URLPatternList explicit_patterns
= k
->ConvertToExplicitSchemes();
219 for (size_t m
= 0; m
< explicit_patterns
.size(); ++m
) {
220 temp_patterns
.push_back(WebString::fromUTF8(
221 explicit_patterns
[m
].GetAsString()));
224 patterns
.assign(temp_patterns
);
226 for (size_t j
= 0; j
< script
->css_scripts().size(); ++j
) {
227 const UserScript::File
& file
= scripts_
[i
]->css_scripts()[j
];
228 std::string content
= file
.GetContent().as_string();
230 WebView::injectStyleSheet(
231 WebString::fromUTF8(content
),
233 script
->match_all_frames() ?
234 WebView::InjectStyleInAllFrames
:
235 WebView::InjectStyleInTopFrameOnly
);
242 GURL
UserScriptSlave::GetDataSourceURLForFrame(const WebFrame
* frame
) {
243 // Normally we would use frame->document().url() to determine the document's
244 // URL, but to decide whether to inject a content script, we use the URL from
245 // the data source. This "quirk" helps prevents content scripts from
246 // inadvertently adding DOM elements to the compose iframe in Gmail because
247 // the compose iframe's dataSource URL is about:blank, but the document URL
248 // changes to match the parent document after Gmail document.writes into
249 // it to create the editor.
250 // http://code.google.com/p/chromium/issues/detail?id=86742
251 blink::WebDataSource
* data_source
= frame
->provisionalDataSource() ?
252 frame
->provisionalDataSource() : frame
->dataSource();
254 return GURL(data_source
->request().url());
257 void UserScriptSlave::InjectScripts(WebFrame
* frame
,
258 UserScript::RunLocation location
) {
259 GURL data_source_url
= GetDataSourceURLForFrame(frame
);
260 if (data_source_url
.is_empty())
263 if (frame
->isViewSourceModeEnabled())
264 data_source_url
= GURL(content::kViewSourceScheme
+ std::string(":") +
265 data_source_url
.spec());
267 base::ElapsedTimer timer
;
271 ExecutingScriptsMap extensions_executing_scripts
;
273 for (size_t i
= 0; i
< scripts_
.size(); ++i
) {
274 std::vector
<WebScriptSource
> sources
;
275 UserScript
* script
= scripts_
[i
];
277 if (frame
->parent() && !script
->match_all_frames())
278 continue; // Only match subframes if the script declared it wanted to.
280 const Extension
* extension
= extensions_
->GetByID(script
->extension_id());
282 // Since extension info is sent separately from user script info, they can
283 // be out of sync. We just ignore this situation.
287 // Content scripts are not tab-specific.
288 const int kNoTabId
= -1;
289 // We don't have a process id in this context.
290 const int kNoProcessId
= -1;
291 if (!PermissionsData::CanExecuteScriptOnPage(extension
,
293 frame
->top()->document().url(),
301 // We rely on WebCore for CSS injection, but it's still useful to know how
302 // many css files there are.
303 if (location
== UserScript::DOCUMENT_START
)
304 num_css
+= script
->css_scripts().size();
306 if (script
->run_location() == location
) {
307 num_scripts
+= script
->js_scripts().size();
308 for (size_t j
= 0; j
< script
->js_scripts().size(); ++j
) {
309 UserScript::File
&file
= script
->js_scripts()[j
];
310 std::string content
= file
.GetContent().as_string();
312 // We add this dumb function wrapper for standalone user script to
313 // emulate what Greasemonkey does.
314 // TODO(aa): I think that maybe "is_standalone" scripts don't exist
315 // anymore. Investigate.
316 if (script
->is_standalone() || script
->emulate_greasemonkey()) {
317 content
.insert(0, kUserScriptHead
);
318 content
+= kUserScriptTail
;
321 WebScriptSource(WebString::fromUTF8(content
), file
.url()));
325 if (!sources
.empty()) {
326 // Emulate Greasemonkey API for scripts that were converted to extensions
327 // and "standalone" user scripts.
328 if (script
->is_standalone() || script
->emulate_greasemonkey()) {
329 sources
.insert(sources
.begin(),
330 WebScriptSource(WebString::fromUTF8(api_js_
.as_string())));
333 int isolated_world_id
= GetIsolatedWorldIdForExtension(extension
, frame
);
335 base::ElapsedTimer exec_timer
;
336 DOMActivityLogger::AttachToWorld(isolated_world_id
, extension
->id());
337 frame
->executeScriptInIsolatedWorld(
338 isolated_world_id
, &sources
.front(), sources
.size(),
339 EXTENSION_GROUP_CONTENT_SCRIPTS
);
340 UMA_HISTOGRAM_TIMES("Extensions.InjectScriptTime", exec_timer
.Elapsed());
342 for (std::vector
<WebScriptSource
>::const_iterator iter
= sources
.begin();
343 iter
!= sources
.end(); ++iter
) {
344 extensions_executing_scripts
[extension
->id()].insert(
345 GURL(iter
->url
).path());
350 // Notify the browser if any extensions are now executing scripts.
351 if (!extensions_executing_scripts
.empty()) {
352 blink::WebFrame
* top_frame
= frame
->top();
353 content::RenderView
* render_view
=
354 content::RenderView::FromWebView(top_frame
->view());
355 render_view
->Send(new ExtensionHostMsg_ContentScriptsExecuting(
356 render_view
->GetRoutingID(),
357 extensions_executing_scripts
,
358 render_view
->GetPageId(),
359 GetDataSourceURLForFrame(top_frame
)));
363 if (location
== UserScript::DOCUMENT_START
) {
364 UMA_HISTOGRAM_COUNTS_100("Extensions.InjectStart_CssCount", num_css
);
365 UMA_HISTOGRAM_COUNTS_100("Extensions.InjectStart_ScriptCount", num_scripts
);
366 if (num_css
|| num_scripts
)
367 UMA_HISTOGRAM_TIMES("Extensions.InjectStart_Time", timer
.Elapsed());
368 } else if (location
== UserScript::DOCUMENT_END
) {
369 UMA_HISTOGRAM_COUNTS_100("Extensions.InjectEnd_ScriptCount", num_scripts
);
371 UMA_HISTOGRAM_TIMES("Extensions.InjectEnd_Time", timer
.Elapsed());
372 } else if (location
== UserScript::DOCUMENT_IDLE
) {
373 UMA_HISTOGRAM_COUNTS_100("Extensions.InjectIdle_ScriptCount", num_scripts
);
375 UMA_HISTOGRAM_TIMES("Extensions.InjectIdle_Time", timer
.Elapsed());
381 } // namespace extensions