Cast: Stop logging kVideoFrameSentToEncoder and rename a couple events.
[chromium-blink-merge.git] / extensions / renderer / user_script_slave.cc
blob64cd3f65e1090cc47eb0fbda33e067c4c7c271cc
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"
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 "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"
37 #include "url/gurl.h"
39 using blink::WebFrame;
40 using blink::WebSecurityOrigin;
41 using blink::WebSecurityPolicy;
42 using blink::WebString;
43 using blink::WebVector;
44 using blink::WebView;
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,
55 WebFrame* frame) {
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(
67 iter->second,
68 WebString::fromUTF8(CSPInfo::GetContentSecurityPolicy(extension)));
69 return iter->second;
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(
81 new_id,
82 WebString::fromUTF8(CSPInfo::GetContentSecurityPolicy(extension)));
83 return new_id;
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();
90 ++iter) {
91 if (iter->second == isolated_world_id)
92 return iter->first;
94 return std::string();
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) {
119 scripts_.clear();
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())
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()), 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;
158 int body_length = 0;
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;
165 int body_length = 0;
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.
173 delete script;
174 scripts_.pop_back();
178 return true;
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())
185 return;
187 if (frame->isViewSourceModeEnabled())
188 data_source_url = GURL(content::kViewSourceScheme + std::string(":") +
189 data_source_url.spec());
191 base::ElapsedTimer timer;
192 int num_css = 0;
193 int num_scripts = 0;
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.
208 if (!extension)
209 continue;
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,
216 data_source_url,
217 frame->top()->document().url(),
218 kNoTabId,
219 script,
220 kNoProcessId,
221 NULL)) {
222 continue;
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();
230 ++iter) {
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;
250 sources.push_back(
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()) {
259 sources.insert(
260 sources.begin(),
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,
269 &sources.front(),
270 sources.size(),
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();
276 ++iter) {
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)));
295 // Log debug info.
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);
303 if (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);
307 if (num_scripts)
308 UMA_HISTOGRAM_TIMES("Extensions.InjectIdle_Time", timer.Elapsed());
309 } else {
310 NOTREACHED();
314 } // namespace extensions