Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / extensions / renderer / user_script_slave.cc
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.
4
5 #include "extensions/renderer/user_script_slave.h"
6
7 #include <map>
8
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"
38
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;
46
47 namespace extensions {
48
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,
55                                                     WebFrame* frame) {
56   static int g_next_isolated_world_id =
57       ExtensionsRendererClient::Get()->GetLowestIsolatedWorldId();
58
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;
70   }
71
72   int new_id = g_next_isolated_world_id;
73   ++g_next_isolated_world_id;
74
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;
84 }
85
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;
93   }
94   return std::string();
95 }
96
97 void UserScriptSlave::RemoveIsolatedWorld(const std::string& extension_id) {
98   isolated_world_ids_.erase(extension_id);
99 }
100
101 UserScriptSlave::UserScriptSlave(const ExtensionSet* extensions)
102     : script_deleter_(&scripts_), extensions_(extensions) {
103   api_js_ = ResourceBundle::GetSharedInstance().GetRawDataResource(
104       IDR_GREASEMONKEY_API_JS);
105 }
106
107 UserScriptSlave::~UserScriptSlave() {
108 }
109
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());
115   }
116 }
117
118 bool UserScriptSlave::UpdateScripts(base::SharedMemoryHandle shared_memory) {
119   scripts_.clear();
120
121   bool only_inject_incognito =
122       ExtensionsRendererClient::Get()->IsIncognitoProcess();
123
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;
128
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());
134
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;
140
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));
146
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);
152
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));
162     }
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));
169     }
170
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();
175     }
176   }
177
178   return true;
179 }
180
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;
186
187   if (frame->isViewSourceModeEnabled())
188     data_source_url = GURL(content::kViewSourceScheme + std::string(":") +
189                            data_source_url.spec());
190
191   base::ElapsedTimer timer;
192   int num_css = 0;
193   int num_scripts = 0;
194
195   ExecutingScriptsMap extensions_executing_scripts;
196
197   for (size_t i = 0; i < scripts_.size(); ++i) {
198     std::vector<WebScriptSource> sources;
199     UserScript* script = scripts_[i];
200
201     if (frame->parent() && !script->match_all_frames())
202       continue;  // Only match subframes if the script declared it wanted to.
203
204     const Extension* extension = extensions_->GetByID(script->extension_id());
205
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;
210
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;
223     }
224
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()));
233       }
234     }
235
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();
241
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;
249         }
250         sources.push_back(
251             WebScriptSource(WebString::fromUTF8(content), file.url()));
252       }
253     }
254
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())));
262       }
263
264       int isolated_world_id = GetIsolatedWorldIdForExtension(extension, frame);
265
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());
273
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());
279       }
280     }
281   }
282
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)));
293   }
294
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();
311   }
312 }
313
314 }  // namespace extensions