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
3 // found in the LICENSE file.
5 #include "extensions/renderer/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 "content/public/common/url_constants.h"
17 #include "content/public/renderer/render_thread.h"
18 #include "content/public/renderer/render_view.h"
19 #include "extensions/common/extension.h"
20 #include "extensions/common/extension_messages.h"
21 #include "extensions/common/extension_set.h"
22 #include "extensions/common/manifest_handlers/csp_info.h"
23 #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"
27 #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"
33 #include "third_party/WebKit/public/web/WebSecurityOrigin.h"
34 #include "third_party/WebKit/public/web/WebSecurityPolicy.h"
35 #include "third_party/WebKit/public/web/WebView.h"
36 #include "ui/base/resource/resource_bundle.h"
39 using blink::WebFrame
;
40 using blink::WebSecurityOrigin
;
41 using blink::WebSecurityPolicy
;
42 using blink::WebString
;
43 using blink::WebVector
;
45 using content::RenderThread
;
47 namespace extensions
{
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);";
54 int UserScriptSlave::GetIsolatedWorldIdForExtension(const Extension
* extension
,
56 static int g_next_isolated_world_id
=
57 ExtensionsRendererClient::Get()->GetLowestIsolatedWorldId();
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(
65 iter
->second
, WebSecurityOrigin::create(extension
->url()));
66 frame
->setIsolatedWorldContentSecurityPolicy(
68 WebString::fromUTF8(CSPInfo::GetContentSecurityPolicy(extension
)));
72 int new_id
= g_next_isolated_world_id
;
73 ++g_next_isolated_world_id
;
75 // This map will tend to pile up over time, but realistically, you're never
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(
82 WebString::fromUTF8(CSPInfo::GetContentSecurityPolicy(extension
)));
86 std::string
UserScriptSlave::GetExtensionIdForIsolatedWorld(
87 int isolated_world_id
) {
88 for (IsolatedWorldMap::iterator iter
= isolated_world_ids_
.begin();
89 iter
!= isolated_world_ids_
.end();
91 if (iter
->second
== isolated_world_id
)
97 void UserScriptSlave::RemoveIsolatedWorld(const std::string
& extension_id
) {
98 isolated_world_ids_
.erase(extension_id
);
101 UserScriptSlave::UserScriptSlave(const ExtensionSet
* extensions
)
102 : script_deleter_(&scripts_
), extensions_(extensions
) {
103 api_js_
= ResourceBundle::GetSharedInstance().GetRawDataResource(
104 IDR_GREASEMONKEY_API_JS
);
107 UserScriptSlave::~UserScriptSlave() {
110 void UserScriptSlave::GetActiveExtensions(
111 std::set
<std::string
>* extension_ids
) {
112 for (size_t i
= 0; i
< scripts_
.size(); ++i
) {
113 DCHECK(!scripts_
[i
]->extension_id().empty());
114 extension_ids
->insert(scripts_
[i
]->extension_id());
118 bool UserScriptSlave::UpdateScripts(base::SharedMemoryHandle shared_memory
) {
121 bool only_inject_incognito
=
122 ExtensionsRendererClient::Get()->IsIncognitoProcess();
124 // Create the shared memory object (read only).
125 shared_memory_
.reset(new base::SharedMemory(shared_memory
, true));
126 if (!shared_memory_
.get())
129 // First get the size of the memory block.
130 if (!shared_memory_
->Map(sizeof(Pickle::Header
)))
132 Pickle::Header
* pickle_header
=
133 reinterpret_cast<Pickle::Header
*>(shared_memory_
->memory());
135 // Now map in the rest of the block.
136 int pickle_size
= sizeof(Pickle::Header
) + pickle_header
->payload_size
;
137 shared_memory_
->Unmap();
138 if (!shared_memory_
->Map(pickle_size
))
142 uint64 num_scripts
= 0;
143 Pickle
pickle(reinterpret_cast<char*>(shared_memory_
->memory()), pickle_size
);
144 PickleIterator
iter(pickle
);
145 CHECK(pickle
.ReadUInt64(&iter
, &num_scripts
));
147 scripts_
.reserve(num_scripts
);
148 for (uint64 i
= 0; i
< num_scripts
; ++i
) {
149 scripts_
.push_back(new UserScript());
150 UserScript
* script
= scripts_
.back();
151 script
->Unpickle(pickle
, &iter
);
153 // 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
155 // reference to the shared memory.
156 for (size_t j
= 0; j
< script
->js_scripts().size(); ++j
) {
157 const char* body
= NULL
;
159 CHECK(pickle
.ReadData(&iter
, &body
, &body_length
));
160 script
->js_scripts()[j
].set_external_content(
161 base::StringPiece(body
, body_length
));
163 for (size_t j
= 0; j
< script
->css_scripts().size(); ++j
) {
164 const char* body
= NULL
;
166 CHECK(pickle
.ReadData(&iter
, &body
, &body_length
));
167 script
->css_scripts()[j
].set_external_content(
168 base::StringPiece(body
, body_length
));
171 if (only_inject_incognito
&& !script
->is_incognito_enabled()) {
172 // This script shouldn't run in an incognito tab.
181 void UserScriptSlave::InjectScripts(WebFrame
* frame
,
182 UserScript::RunLocation location
) {
183 GURL data_source_url
= ScriptContext::GetDataSourceURLForFrame(frame
);
184 if (data_source_url
.is_empty())
187 if (frame
->isViewSourceModeEnabled())
188 data_source_url
= GURL(content::kViewSourceScheme
+ std::string(":") +
189 data_source_url
.spec());
191 base::ElapsedTimer timer
;
195 ExecutingScriptsMap extensions_executing_scripts
;
197 for (size_t i
= 0; i
< scripts_
.size(); ++i
) {
198 std::vector
<WebScriptSource
> sources
;
199 UserScript
* script
= scripts_
[i
];
201 if (frame
->parent() && !script
->match_all_frames())
202 continue; // Only match subframes if the script declared it wanted to.
204 const Extension
* extension
= extensions_
->GetByID(script
->extension_id());
206 // Since extension info is sent separately from user script info, they can
207 // be out of sync. We just ignore this situation.
211 // Content scripts are not tab-specific.
212 const int kNoTabId
= -1;
213 // We don't have a process id in this context.
214 const int kNoProcessId
= -1;
215 if (!PermissionsData::CanExecuteScriptOnPage(extension
,
217 frame
->top()->document().url(),
225 if (location
== UserScript::DOCUMENT_START
) {
226 num_css
+= script
->css_scripts().size();
227 for (UserScript::FileList::const_iterator iter
=
228 script
->css_scripts().begin();
229 iter
!= script
->css_scripts().end();
231 frame
->document().insertStyleSheet(
232 WebString::fromUTF8(iter
->GetContent().as_string()));
236 if (script
->run_location() == location
) {
237 num_scripts
+= script
->js_scripts().size();
238 for (size_t j
= 0; j
< script
->js_scripts().size(); ++j
) {
239 UserScript::File
& file
= script
->js_scripts()[j
];
240 std::string content
= file
.GetContent().as_string();
242 // We add this dumb function wrapper for standalone user script to
243 // emulate what Greasemonkey does.
244 // TODO(aa): I think that maybe "is_standalone" scripts don't exist
245 // anymore. Investigate.
246 if (script
->is_standalone() || script
->emulate_greasemonkey()) {
247 content
.insert(0, kUserScriptHead
);
248 content
+= kUserScriptTail
;
251 WebScriptSource(WebString::fromUTF8(content
), file
.url()));
255 if (!sources
.empty()) {
256 // Emulate Greasemonkey API for scripts that were converted to extensions
257 // and "standalone" user scripts.
258 if (script
->is_standalone() || script
->emulate_greasemonkey()) {
261 WebScriptSource(WebString::fromUTF8(api_js_
.as_string())));
264 int isolated_world_id
= GetIsolatedWorldIdForExtension(extension
, frame
);
266 base::ElapsedTimer exec_timer
;
267 DOMActivityLogger::AttachToWorld(isolated_world_id
, extension
->id());
268 frame
->executeScriptInIsolatedWorld(isolated_world_id
,
271 EXTENSION_GROUP_CONTENT_SCRIPTS
);
272 UMA_HISTOGRAM_TIMES("Extensions.InjectScriptTime", exec_timer
.Elapsed());
274 for (std::vector
<WebScriptSource
>::const_iterator iter
= sources
.begin();
275 iter
!= sources
.end();
277 extensions_executing_scripts
[extension
->id()].insert(
278 GURL(iter
->url
).path());
283 // Notify the browser if any extensions are now executing scripts.
284 if (!extensions_executing_scripts
.empty()) {
285 blink::WebFrame
* top_frame
= frame
->top();
286 content::RenderView
* render_view
=
287 content::RenderView::FromWebView(top_frame
->view());
288 render_view
->Send(new ExtensionHostMsg_ContentScriptsExecuting(
289 render_view
->GetRoutingID(),
290 extensions_executing_scripts
,
291 render_view
->GetPageId(),
292 ScriptContext::GetDataSourceURLForFrame(top_frame
)));
296 if (location
== UserScript::DOCUMENT_START
) {
297 UMA_HISTOGRAM_COUNTS_100("Extensions.InjectStart_CssCount", num_css
);
298 UMA_HISTOGRAM_COUNTS_100("Extensions.InjectStart_ScriptCount", num_scripts
);
299 if (num_css
|| num_scripts
)
300 UMA_HISTOGRAM_TIMES("Extensions.InjectStart_Time", timer
.Elapsed());
301 } else if (location
== UserScript::DOCUMENT_END
) {
302 UMA_HISTOGRAM_COUNTS_100("Extensions.InjectEnd_ScriptCount", num_scripts
);
304 UMA_HISTOGRAM_TIMES("Extensions.InjectEnd_Time", timer
.Elapsed());
305 } else if (location
== UserScript::DOCUMENT_IDLE
) {
306 UMA_HISTOGRAM_COUNTS_100("Extensions.InjectIdle_ScriptCount", num_scripts
);
308 UMA_HISTOGRAM_TIMES("Extensions.InjectIdle_Time", timer
.Elapsed());
314 } // namespace extensions