Move extension_messages.h to extensions/common.
[chromium-blink-merge.git] / chrome / renderer / extensions / user_script_slave.cc
blob8c7ca5e16b2b59fb062e57c9807766f8bd01b6af
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"
7 #include <map>
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/url_constants.h"
17 #include "chrome/renderer/chrome_render_process_observer.h"
18 #include "chrome/renderer/extensions/dom_activity_logger.h"
19 #include "chrome/renderer/extensions/extension_groups.h"
20 #include "chrome/renderer/isolated_world_ids.h"
21 #include "content/public/renderer/render_thread.h"
22 #include "content/public/renderer/render_view.h"
23 #include "extensions/common/extension.h"
24 #include "extensions/common/extension_messages.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"
38 #include "url/gurl.h"
40 using blink::WebFrame;
41 using blink::WebSecurityOrigin;
42 using blink::WebSecurityPolicy;
43 using blink::WebString;
44 using blink::WebVector;
45 using blink::WebView;
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,
56 WebFrame* frame) {
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(
65 iter->second,
66 WebSecurityOrigin::create(extension->url()));
67 frame->setIsolatedWorldContentSecurityPolicy(
68 iter->second,
69 WebString::fromUTF8(CSPInfo::GetContentSecurityPolicy(extension)));
70 return iter->second;
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 frame->setIsolatedWorldSecurityOrigin(
80 new_id,
81 WebSecurityOrigin::create(extension->url()));
82 frame->setIsolatedWorldContentSecurityPolicy(
83 new_id,
84 WebString::fromUTF8(CSPInfo::GetContentSecurityPolicy(extension)));
85 return new_id;
88 std::string UserScriptSlave::GetExtensionIdForIsolatedWorld(
89 int isolated_world_id) {
90 for (IsolatedWorldMap::iterator iter = isolated_world_ids_.begin();
91 iter != isolated_world_ids_.end(); ++iter) {
92 if (iter->second == isolated_world_id)
93 return iter->first;
95 return std::string();
98 void UserScriptSlave::RemoveIsolatedWorld(const std::string& extension_id) {
99 isolated_world_ids_.erase(extension_id);
102 UserScriptSlave::UserScriptSlave(const ExtensionSet* extensions)
103 : script_deleter_(&scripts_), extensions_(extensions) {
104 api_js_ = ResourceBundle::GetSharedInstance().GetRawDataResource(
105 IDR_GREASEMONKEY_API_JS);
108 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) {
119 scripts_.clear();
121 bool only_inject_incognito =
122 ChromeRenderProcessObserver::is_incognito_process();
124 // Create the shared memory object (read only).
125 shared_memory_.reset(new base::SharedMemory(shared_memory, true));
126 if (!shared_memory_.get())
127 return false;
129 // First get the size of the memory block.
130 if (!shared_memory_->Map(sizeof(Pickle::Header)))
131 return false;
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))
139 return false;
141 // Unpickle scripts.
142 uint64 num_scripts = 0;
143 Pickle pickle(reinterpret_cast<char*>(shared_memory_->memory()),
144 pickle_size);
145 PickleIterator iter(pickle);
146 CHECK(pickle.ReadUInt64(&iter, &num_scripts));
148 scripts_.reserve(num_scripts);
149 for (uint64 i = 0; i < num_scripts; ++i) {
150 scripts_.push_back(new UserScript());
151 UserScript* script = scripts_.back();
152 script->Unpickle(pickle, &iter);
154 // Note that this is a pointer into shared memory. We don't own it. It gets
155 // cleared up when the last renderer or browser process drops their
156 // reference to the shared memory.
157 for (size_t j = 0; j < script->js_scripts().size(); ++j) {
158 const char* body = NULL;
159 int body_length = 0;
160 CHECK(pickle.ReadData(&iter, &body, &body_length));
161 script->js_scripts()[j].set_external_content(
162 base::StringPiece(body, body_length));
164 for (size_t j = 0; j < script->css_scripts().size(); ++j) {
165 const char* body = NULL;
166 int body_length = 0;
167 CHECK(pickle.ReadData(&iter, &body, &body_length));
168 script->css_scripts()[j].set_external_content(
169 base::StringPiece(body, body_length));
172 if (only_inject_incognito && !script->is_incognito_enabled()) {
173 // This script shouldn't run in an incognito tab.
174 delete script;
175 scripts_.pop_back();
179 // Push user styles down into WebCore
180 RenderThread::Get()->EnsureWebKitInitialized();
181 WebView::removeInjectedStyleSheets();
182 for (size_t i = 0; i < scripts_.size(); ++i) {
183 UserScript* script = scripts_[i];
184 if (script->css_scripts().empty())
185 continue;
187 WebVector<WebString> patterns;
188 std::vector<WebString> temp_patterns;
189 const URLPatternSet& url_patterns = script->url_patterns();
190 for (URLPatternSet::const_iterator k = url_patterns.begin();
191 k != url_patterns.end(); ++k) {
192 URLPatternList explicit_patterns = k->ConvertToExplicitSchemes();
193 for (size_t m = 0; m < explicit_patterns.size(); ++m) {
194 temp_patterns.push_back(WebString::fromUTF8(
195 explicit_patterns[m].GetAsString()));
198 patterns.assign(temp_patterns);
200 for (size_t j = 0; j < script->css_scripts().size(); ++j) {
201 const UserScript::File& file = scripts_[i]->css_scripts()[j];
202 std::string content = file.GetContent().as_string();
204 WebView::injectStyleSheet(
205 WebString::fromUTF8(content),
206 patterns,
207 script->match_all_frames() ?
208 WebView::InjectStyleInAllFrames :
209 WebView::InjectStyleInTopFrameOnly);
213 return true;
216 GURL UserScriptSlave::GetDataSourceURLForFrame(const WebFrame* frame) {
217 // Normally we would use frame->document().url() to determine the document's
218 // URL, but to decide whether to inject a content script, we use the URL from
219 // the data source. This "quirk" helps prevents content scripts from
220 // inadvertently adding DOM elements to the compose iframe in Gmail because
221 // the compose iframe's dataSource URL is about:blank, but the document URL
222 // changes to match the parent document after Gmail document.writes into
223 // it to create the editor.
224 // http://code.google.com/p/chromium/issues/detail?id=86742
225 blink::WebDataSource* data_source = frame->provisionalDataSource() ?
226 frame->provisionalDataSource() : frame->dataSource();
227 CHECK(data_source);
228 return GURL(data_source->request().url());
231 void UserScriptSlave::InjectScripts(WebFrame* frame,
232 UserScript::RunLocation location) {
233 GURL data_source_url = GetDataSourceURLForFrame(frame);
234 if (data_source_url.is_empty())
235 return;
237 if (frame->isViewSourceModeEnabled())
238 data_source_url = GURL(content::kViewSourceScheme + std::string(":") +
239 data_source_url.spec());
241 base::ElapsedTimer timer;
242 int num_css = 0;
243 int num_scripts = 0;
245 ExecutingScriptsMap extensions_executing_scripts;
247 for (size_t i = 0; i < scripts_.size(); ++i) {
248 std::vector<WebScriptSource> sources;
249 UserScript* script = scripts_[i];
251 if (frame->parent() && !script->match_all_frames())
252 continue; // Only match subframes if the script declared it wanted to.
254 const Extension* extension = extensions_->GetByID(script->extension_id());
256 // Since extension info is sent separately from user script info, they can
257 // be out of sync. We just ignore this situation.
258 if (!extension)
259 continue;
261 // Content scripts are not tab-specific.
262 const int kNoTabId = -1;
263 // We don't have a process id in this context.
264 const int kNoProcessId = -1;
265 if (!PermissionsData::CanExecuteScriptOnPage(extension,
266 data_source_url,
267 frame->top()->document().url(),
268 kNoTabId,
269 script,
270 kNoProcessId,
271 NULL)) {
272 continue;
275 // We rely on WebCore for CSS injection, but it's still useful to know how
276 // many css files there are.
277 if (location == UserScript::DOCUMENT_START)
278 num_css += script->css_scripts().size();
280 if (script->run_location() == location) {
281 num_scripts += script->js_scripts().size();
282 for (size_t j = 0; j < script->js_scripts().size(); ++j) {
283 UserScript::File &file = script->js_scripts()[j];
284 std::string content = file.GetContent().as_string();
286 // We add this dumb function wrapper for standalone user script to
287 // emulate what Greasemonkey does.
288 // TODO(aa): I think that maybe "is_standalone" scripts don't exist
289 // anymore. Investigate.
290 if (script->is_standalone() || script->emulate_greasemonkey()) {
291 content.insert(0, kUserScriptHead);
292 content += kUserScriptTail;
294 sources.push_back(
295 WebScriptSource(WebString::fromUTF8(content), file.url()));
299 if (!sources.empty()) {
300 // Emulate Greasemonkey API for scripts that were converted to extensions
301 // and "standalone" user scripts.
302 if (script->is_standalone() || script->emulate_greasemonkey()) {
303 sources.insert(sources.begin(),
304 WebScriptSource(WebString::fromUTF8(api_js_.as_string())));
307 int isolated_world_id = GetIsolatedWorldIdForExtension(extension, frame);
309 base::ElapsedTimer exec_timer;
310 DOMActivityLogger::AttachToWorld(isolated_world_id, extension->id());
311 frame->executeScriptInIsolatedWorld(
312 isolated_world_id, &sources.front(), sources.size(),
313 EXTENSION_GROUP_CONTENT_SCRIPTS);
314 UMA_HISTOGRAM_TIMES("Extensions.InjectScriptTime", exec_timer.Elapsed());
316 for (std::vector<WebScriptSource>::const_iterator iter = sources.begin();
317 iter != sources.end(); ++iter) {
318 extensions_executing_scripts[extension->id()].insert(
319 GURL(iter->url).path());
324 // Notify the browser if any extensions are now executing scripts.
325 if (!extensions_executing_scripts.empty()) {
326 blink::WebFrame* top_frame = frame->top();
327 content::RenderView* render_view =
328 content::RenderView::FromWebView(top_frame->view());
329 render_view->Send(new ExtensionHostMsg_ContentScriptsExecuting(
330 render_view->GetRoutingID(),
331 extensions_executing_scripts,
332 render_view->GetPageId(),
333 GetDataSourceURLForFrame(top_frame)));
336 // Log debug info.
337 if (location == UserScript::DOCUMENT_START) {
338 UMA_HISTOGRAM_COUNTS_100("Extensions.InjectStart_CssCount", num_css);
339 UMA_HISTOGRAM_COUNTS_100("Extensions.InjectStart_ScriptCount", num_scripts);
340 if (num_css || num_scripts)
341 UMA_HISTOGRAM_TIMES("Extensions.InjectStart_Time", timer.Elapsed());
342 } else if (location == UserScript::DOCUMENT_END) {
343 UMA_HISTOGRAM_COUNTS_100("Extensions.InjectEnd_ScriptCount", num_scripts);
344 if (num_scripts)
345 UMA_HISTOGRAM_TIMES("Extensions.InjectEnd_Time", timer.Elapsed());
346 } else if (location == UserScript::DOCUMENT_IDLE) {
347 UMA_HISTOGRAM_COUNTS_100("Extensions.InjectIdle_ScriptCount", num_scripts);
348 if (num_scripts)
349 UMA_HISTOGRAM_TIMES("Extensions.InjectIdle_Time", timer.Elapsed());
350 } else {
351 NOTREACHED();
355 } // namespace extensions