OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "chrome/renderer/extensions/extension_dispatcher.h" | |
6 | |
7 #include "base/callback.h" | |
8 #include "base/command_line.h" | |
9 #include "base/memory/scoped_ptr.h" | |
10 #include "base/string_piece.h" | |
11 #include "chrome/common/child_process_logging.h" | |
12 #include "chrome/common/chrome_switches.h" | |
13 #include "chrome/common/chrome_version_info.h" | |
14 #include "chrome/common/extensions/api/extension_api.h" | |
15 #include "chrome/common/extensions/extension.h" | |
16 #include "chrome/common/extensions/extension_messages.h" | |
17 #include "chrome/common/extensions/permissions/permission_set.h" | |
18 #include "chrome/common/url_constants.h" | |
19 #include "chrome/common/view_type.h" | |
20 #include "chrome/renderer/chrome_render_process_observer.h" | |
21 #include "chrome/renderer/extensions/api_definitions_natives.h" | |
22 #include "chrome/renderer/extensions/app_bindings.h" | |
23 #include "chrome/renderer/extensions/app_window_custom_bindings.h" | |
24 #include "chrome/renderer/extensions/chrome_v8_context.h" | |
25 #include "chrome/renderer/extensions/chrome_v8_extension.h" | |
26 #include "chrome/renderer/extensions/context_menus_custom_bindings.h" | |
27 #include "chrome/renderer/extensions/event_bindings.h" | |
28 #include "chrome/renderer/extensions/experimental.app_custom_bindings.h" | |
29 #include "chrome/renderer/extensions/experimental.usb_custom_bindings.h" | |
30 #include "chrome/renderer/extensions/extension_custom_bindings.h" | |
31 #include "chrome/renderer/extensions/extension_groups.h" | |
32 #include "chrome/renderer/extensions/extension_helper.h" | |
33 #include "chrome/renderer/extensions/extension_request_sender.h" | |
34 #include "chrome/renderer/extensions/file_browser_handler_custom_bindings.h" | |
35 #include "chrome/renderer/extensions/file_browser_private_custom_bindings.h" | |
36 #include "chrome/renderer/extensions/file_system_natives.h" | |
37 #include "chrome/renderer/extensions/i18n_custom_bindings.h" | |
38 #include "chrome/renderer/extensions/media_gallery_custom_bindings.h" | |
39 #include "chrome/renderer/extensions/miscellaneous_bindings.h" | |
40 #include "chrome/renderer/extensions/page_actions_custom_bindings.h" | |
41 #include "chrome/renderer/extensions/page_capture_custom_bindings.h" | |
42 #include "chrome/renderer/extensions/runtime_custom_bindings.h" | |
43 #include "chrome/renderer/extensions/send_request_natives.h" | |
44 #include "chrome/renderer/extensions/set_icon_natives.h" | |
45 #include "chrome/renderer/extensions/tab_finder.h" | |
46 #include "chrome/renderer/extensions/tabs_custom_bindings.h" | |
47 #include "chrome/renderer/extensions/tts_custom_bindings.h" | |
48 #include "chrome/renderer/extensions/user_script_slave.h" | |
49 #include "chrome/renderer/extensions/web_request_custom_bindings.h" | |
50 #include "chrome/renderer/extensions/webstore_bindings.h" | |
51 #include "chrome/renderer/module_system.h" | |
52 #include "chrome/renderer/native_handler.h" | |
53 #include "chrome/renderer/resource_bundle_source_map.h" | |
54 #include "content/public/renderer/render_thread.h" | |
55 #include "content/public/renderer/render_view.h" | |
56 #include "grit/renderer_resources.h" | |
57 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDataSource.h" | |
58 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" | |
59 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" | |
60 #include "third_party/WebKit/Source/WebKit/chromium/public/WebScopedUserGesture.
h" | |
61 #include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityPolicy.h" | |
62 #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" | |
63 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h" | |
64 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebURLReques
t.h" | |
65 #include "ui/base/layout.h" | |
66 #include "ui/base/resource/resource_bundle.h" | |
67 #include "v8/include/v8.h" | |
68 | |
69 using WebKit::WebDataSource; | |
70 using WebKit::WebDocument; | |
71 using WebKit::WebFrame; | |
72 using WebKit::WebScopedUserGesture; | |
73 using WebKit::WebSecurityPolicy; | |
74 using WebKit::WebString; | |
75 using WebKit::WebVector; | |
76 using WebKit::WebView; | |
77 using content::RenderThread; | |
78 using content::RenderView; | |
79 using extensions::APIPermission; | |
80 using extensions::APIPermissionSet; | |
81 using extensions::ApiDefinitionsNatives; | |
82 using extensions::AppWindowCustomBindings; | |
83 using extensions::ContextMenusCustomBindings; | |
84 using extensions::Extension; | |
85 using extensions::ExperimentalAppCustomBindings; | |
86 using extensions::ExperimentalUsbCustomBindings; | |
87 using extensions::ExtensionAPI; | |
88 using extensions::ExtensionCustomBindings; | |
89 using extensions::Feature; | |
90 using extensions::FileBrowserHandlerCustomBindings; | |
91 using extensions::FileBrowserPrivateCustomBindings; | |
92 using extensions::FileSystemNatives; | |
93 using extensions::I18NCustomBindings; | |
94 using extensions::MiscellaneousBindings; | |
95 using extensions::MediaGalleryCustomBindings; | |
96 using extensions::PageActionsCustomBindings; | |
97 using extensions::PageCaptureCustomBindings; | |
98 using extensions::PermissionSet; | |
99 using extensions::RuntimeCustomBindings; | |
100 using extensions::SendRequestNatives; | |
101 using extensions::SetIconNatives; | |
102 using extensions::TTSCustomBindings; | |
103 using extensions::TabFinder; | |
104 using extensions::TabsCustomBindings; | |
105 using extensions::UpdatedExtensionPermissionsInfo; | |
106 using extensions::WebRequestCustomBindings; | |
107 | |
108 namespace { | |
109 | |
110 static const int64 kInitialExtensionIdleHandlerDelayMs = 5*1000; | |
111 static const int64 kMaxExtensionIdleHandlerDelayMs = 5*60*1000; | |
112 static const char kEventDispatchFunction[] = "Event.dispatchJSON"; | |
113 static const char kOnUnloadEvent[] = "runtime.onSuspend"; | |
114 static const char kOnSuspendCanceledEvent[] = "runtime.onSuspendCanceled"; | |
115 | |
116 class ChromeHiddenNativeHandler : public NativeHandler { | |
117 public: | |
118 ChromeHiddenNativeHandler() { | |
119 RouteFunction("GetChromeHidden", | |
120 base::Bind(&ChromeHiddenNativeHandler::GetChromeHidden, | |
121 base::Unretained(this))); | |
122 } | |
123 | |
124 v8::Handle<v8::Value> GetChromeHidden(const v8::Arguments& args) { | |
125 return ChromeV8Context::GetOrCreateChromeHidden(v8::Context::GetCurrent()); | |
126 } | |
127 }; | |
128 | |
129 class PrintNativeHandler : public NativeHandler { | |
130 public: | |
131 PrintNativeHandler() { | |
132 RouteFunction("Print", | |
133 base::Bind(&PrintNativeHandler::Print, | |
134 base::Unretained(this))); | |
135 } | |
136 | |
137 v8::Handle<v8::Value> Print(const v8::Arguments& args) { | |
138 if (args.Length() < 1) | |
139 return v8::Undefined(); | |
140 | |
141 std::vector<std::string> components; | |
142 for (int i = 0; i < args.Length(); ++i) | |
143 components.push_back(*v8::String::Utf8Value(args[i]->ToString())); | |
144 | |
145 LOG(ERROR) << JoinString(components, ','); | |
146 return v8::Undefined(); | |
147 } | |
148 }; | |
149 | |
150 class LazyBackgroundPageNativeHandler : public ChromeV8Extension { | |
151 public: | |
152 explicit LazyBackgroundPageNativeHandler(ExtensionDispatcher* dispatcher) | |
153 : ChromeV8Extension(dispatcher) { | |
154 RouteFunction("IncrementKeepaliveCount", | |
155 base::Bind(&LazyBackgroundPageNativeHandler::IncrementKeepaliveCount, | |
156 base::Unretained(this))); | |
157 RouteFunction("DecrementKeepaliveCount", | |
158 base::Bind(&LazyBackgroundPageNativeHandler::DecrementKeepaliveCount, | |
159 base::Unretained(this))); | |
160 } | |
161 | |
162 v8::Handle<v8::Value> IncrementKeepaliveCount(const v8::Arguments& args) { | |
163 ChromeV8Context* context = | |
164 extension_dispatcher()->v8_context_set().GetCurrent(); | |
165 if (!context) | |
166 return v8::Undefined(); | |
167 RenderView* render_view = context->GetRenderView(); | |
168 if (IsContextLazyBackgroundPage(render_view, context->extension())) { | |
169 render_view->Send(new ExtensionHostMsg_IncrementLazyKeepaliveCount( | |
170 render_view->GetRoutingID())); | |
171 } | |
172 return v8::Undefined(); | |
173 } | |
174 | |
175 v8::Handle<v8::Value> DecrementKeepaliveCount(const v8::Arguments& args) { | |
176 ChromeV8Context* context = | |
177 extension_dispatcher()->v8_context_set().GetCurrent(); | |
178 if (!context) | |
179 return v8::Undefined(); | |
180 RenderView* render_view = context->GetRenderView(); | |
181 if (IsContextLazyBackgroundPage(render_view, context->extension())) { | |
182 render_view->Send(new ExtensionHostMsg_DecrementLazyKeepaliveCount( | |
183 render_view->GetRoutingID())); | |
184 } | |
185 return v8::Undefined(); | |
186 } | |
187 | |
188 private: | |
189 bool IsContextLazyBackgroundPage(RenderView* render_view, | |
190 const Extension* extension) { | |
191 if (!render_view) | |
192 return false; | |
193 | |
194 ExtensionHelper* helper = ExtensionHelper::Get(render_view); | |
195 return (extension && extension->has_lazy_background_page() && | |
196 helper->view_type() == chrome::VIEW_TYPE_EXTENSION_BACKGROUND_PAGE); | |
197 } | |
198 }; | |
199 | |
200 class ProcessInfoNativeHandler : public ChromeV8Extension { | |
201 public: | |
202 explicit ProcessInfoNativeHandler( | |
203 ExtensionDispatcher* dispatcher, | |
204 const std::string& extension_id, | |
205 const std::string& context_type, | |
206 bool is_incognito_context, | |
207 int manifest_version) | |
208 : ChromeV8Extension(dispatcher), | |
209 extension_id_(extension_id), | |
210 context_type_(context_type), | |
211 is_incognito_context_(is_incognito_context), | |
212 manifest_version_(manifest_version) { | |
213 RouteFunction("GetExtensionId", | |
214 base::Bind(&ProcessInfoNativeHandler::GetExtensionId, | |
215 base::Unretained(this))); | |
216 RouteFunction("GetContextType", | |
217 base::Bind(&ProcessInfoNativeHandler::GetContextType, | |
218 base::Unretained(this))); | |
219 RouteFunction("InIncognitoContext", | |
220 base::Bind(&ProcessInfoNativeHandler::InIncognitoContext, | |
221 base::Unretained(this))); | |
222 RouteFunction("GetManifestVersion", | |
223 base::Bind(&ProcessInfoNativeHandler::GetManifestVersion, | |
224 base::Unretained(this))); | |
225 } | |
226 | |
227 v8::Handle<v8::Value> GetExtensionId(const v8::Arguments& args) { | |
228 return v8::String::New(extension_id_.c_str()); | |
229 } | |
230 | |
231 v8::Handle<v8::Value> GetContextType(const v8::Arguments& args) { | |
232 return v8::String::New(context_type_.c_str()); | |
233 } | |
234 | |
235 v8::Handle<v8::Value> InIncognitoContext(const v8::Arguments& args) { | |
236 return v8::Boolean::New(is_incognito_context_); | |
237 } | |
238 | |
239 v8::Handle<v8::Value> GetManifestVersion(const v8::Arguments& args) { | |
240 return v8::Integer::New(manifest_version_); | |
241 } | |
242 | |
243 private: | |
244 std::string extension_id_; | |
245 std::string context_type_; | |
246 bool is_incognito_context_; | |
247 int manifest_version_; | |
248 }; | |
249 | |
250 class ChannelNativeHandler : public NativeHandler { | |
251 public: | |
252 explicit ChannelNativeHandler(chrome::VersionInfo::Channel channel) | |
253 : channel_(channel) { | |
254 RouteFunction("IsDevChannel", | |
255 base::Bind(&ChannelNativeHandler::IsDevChannel, | |
256 base::Unretained(this))); | |
257 } | |
258 | |
259 v8::Handle<v8::Value> IsDevChannel(const v8::Arguments& args) { | |
260 return v8::Boolean::New(channel_ <= chrome::VersionInfo::CHANNEL_DEV); | |
261 } | |
262 | |
263 chrome::VersionInfo::Channel channel_; | |
264 }; | |
265 | |
266 class LoggingNativeHandler : public NativeHandler { | |
267 public: | |
268 LoggingNativeHandler() { | |
269 RouteFunction("DCHECK", | |
270 base::Bind(&LoggingNativeHandler::Dcheck, | |
271 base::Unretained(this))); | |
272 } | |
273 | |
274 v8::Handle<v8::Value> Dcheck(const v8::Arguments& args) { | |
275 CHECK_LE(args.Length(), 2); | |
276 bool check_value = args[0]->BooleanValue(); | |
277 std::string error_message; | |
278 if (args.Length() == 2) | |
279 error_message += "Error: " + std::string(*v8::String::AsciiValue(args[1])) | |
280 + "\n"; | |
281 | |
282 v8::Handle<v8::Array> stack_trace( | |
283 v8::StackTrace::CurrentStackTrace(10)->AsArray()); | |
284 error_message += "Stack trace: {\n"; | |
285 for (size_t i = 0; i < stack_trace->Length(); i++) { | |
286 error_message += " " | |
287 + std::string(*v8::String::AsciiValue(stack_trace->Get(i))) + "\n"; | |
288 } | |
289 error_message += "}"; | |
290 DCHECK(check_value) << error_message; | |
291 return v8::Undefined(); | |
292 } | |
293 }; | |
294 | |
295 void InstallAppBindings(ModuleSystem* module_system, | |
296 v8::Handle<v8::Object> chrome, | |
297 v8::Handle<v8::Object> chrome_hidden) { | |
298 module_system->SetLazyField(chrome, "app", "app", "chromeApp"); | |
299 module_system->SetLazyField(chrome, "appNotifications", "app", | |
300 "chromeAppNotifications"); | |
301 module_system->SetLazyField(chrome_hidden, "app", "app", | |
302 "chromeHiddenApp"); | |
303 } | |
304 | |
305 void InstallWebstoreBindings(ModuleSystem* module_system, | |
306 v8::Handle<v8::Object> chrome, | |
307 v8::Handle<v8::Object> chrome_hidden) { | |
308 module_system->SetLazyField(chrome, "webstore", "webstore", "chromeWebstore"); | |
309 module_system->SetLazyField(chrome_hidden, "webstore", "webstore", | |
310 "chromeHiddenWebstore"); | |
311 } | |
312 | |
313 static v8::Handle<v8::Object> GetOrCreateChrome( | |
314 v8::Handle<v8::Context> context) { | |
315 v8::Handle<v8::String> chrome_string(v8::String::New("chrome")); | |
316 v8::Handle<v8::Object> global(context->Global()); | |
317 v8::Handle<v8::Value> chrome(global->Get(chrome_string)); | |
318 if (chrome.IsEmpty() || chrome->IsUndefined()) { | |
319 v8::Handle<v8::Object> chrome_object(v8::Object::New()); | |
320 global->Set(chrome_string, chrome_object); | |
321 return chrome_object; | |
322 } | |
323 CHECK(chrome->IsObject()); | |
324 return chrome->ToObject(); | |
325 } | |
326 | |
327 } // namespace | |
328 | |
329 ExtensionDispatcher::ExtensionDispatcher() | |
330 : is_webkit_initialized_(false), | |
331 webrequest_adblock_(false), | |
332 webrequest_adblock_plus_(false), | |
333 webrequest_other_(false), | |
334 source_map_(&ResourceBundle::GetSharedInstance()), | |
335 chrome_channel_(chrome::VersionInfo::CHANNEL_UNKNOWN) { | |
336 const CommandLine& command_line = *(CommandLine::ForCurrentProcess()); | |
337 is_extension_process_ = | |
338 command_line.HasSwitch(switches::kExtensionProcess) || | |
339 command_line.HasSwitch(switches::kSingleProcess); | |
340 | |
341 if (is_extension_process_) { | |
342 RenderThread::Get()->SetIdleNotificationDelayInMs( | |
343 kInitialExtensionIdleHandlerDelayMs); | |
344 } | |
345 | |
346 user_script_slave_.reset(new extensions::UserScriptSlave(&extensions_)); | |
347 request_sender_.reset(new ExtensionRequestSender(this, &v8_context_set_)); | |
348 PopulateSourceMap(); | |
349 PopulateLazyBindingsMap(); | |
350 } | |
351 | |
352 ExtensionDispatcher::~ExtensionDispatcher() { | |
353 } | |
354 | |
355 bool ExtensionDispatcher::OnControlMessageReceived( | |
356 const IPC::Message& message) { | |
357 bool handled = true; | |
358 IPC_BEGIN_MESSAGE_MAP(ExtensionDispatcher, message) | |
359 IPC_MESSAGE_HANDLER(ExtensionMsg_SetChannel, OnSetChannel) | |
360 IPC_MESSAGE_HANDLER(ExtensionMsg_MessageInvoke, OnMessageInvoke) | |
361 IPC_MESSAGE_HANDLER(ExtensionMsg_DispatchOnConnect, OnDispatchOnConnect) | |
362 IPC_MESSAGE_HANDLER(ExtensionMsg_DeliverMessage, OnDeliverMessage) | |
363 IPC_MESSAGE_HANDLER(ExtensionMsg_DispatchOnDisconnect, | |
364 OnDispatchOnDisconnect) | |
365 IPC_MESSAGE_HANDLER(ExtensionMsg_SetFunctionNames, OnSetFunctionNames) | |
366 IPC_MESSAGE_HANDLER(ExtensionMsg_Loaded, OnLoaded) | |
367 IPC_MESSAGE_HANDLER(ExtensionMsg_Unloaded, OnUnloaded) | |
368 IPC_MESSAGE_HANDLER(ExtensionMsg_SetScriptingWhitelist, | |
369 OnSetScriptingWhitelist) | |
370 IPC_MESSAGE_HANDLER(ExtensionMsg_ActivateExtension, OnActivateExtension) | |
371 IPC_MESSAGE_HANDLER(ExtensionMsg_UpdatePermissions, OnUpdatePermissions) | |
372 IPC_MESSAGE_HANDLER(ExtensionMsg_UpdateTabSpecificPermissions, | |
373 OnUpdateTabSpecificPermissions) | |
374 IPC_MESSAGE_HANDLER(ExtensionMsg_ClearTabSpecificPermissions, | |
375 OnClearTabSpecificPermissions) | |
376 IPC_MESSAGE_HANDLER(ExtensionMsg_UpdateUserScripts, OnUpdateUserScripts) | |
377 IPC_MESSAGE_HANDLER(ExtensionMsg_UsingWebRequestAPI, OnUsingWebRequestAPI) | |
378 IPC_MESSAGE_HANDLER(ExtensionMsg_ShouldUnload, OnShouldUnload) | |
379 IPC_MESSAGE_HANDLER(ExtensionMsg_Unload, OnUnload) | |
380 IPC_MESSAGE_HANDLER(ExtensionMsg_CancelUnload, OnCancelUnload) | |
381 IPC_MESSAGE_UNHANDLED(handled = false) | |
382 IPC_END_MESSAGE_MAP() | |
383 | |
384 return handled; | |
385 } | |
386 | |
387 void ExtensionDispatcher::WebKitInitialized() { | |
388 // For extensions, we want to ensure we call the IdleHandler every so often, | |
389 // even if the extension keeps up activity. | |
390 if (is_extension_process_) { | |
391 forced_idle_timer_.Start(FROM_HERE, | |
392 base::TimeDelta::FromMilliseconds(kMaxExtensionIdleHandlerDelayMs), | |
393 RenderThread::Get(), &RenderThread::IdleHandler); | |
394 } | |
395 | |
396 // Initialize host permissions for any extensions that were activated before | |
397 // WebKit was initialized. | |
398 for (std::set<std::string>::iterator iter = active_extension_ids_.begin(); | |
399 iter != active_extension_ids_.end(); ++iter) { | |
400 const Extension* extension = extensions_.GetByID(*iter); | |
401 CHECK(extension); | |
402 InitOriginPermissions(extension); | |
403 } | |
404 | |
405 is_webkit_initialized_ = true; | |
406 } | |
407 | |
408 void ExtensionDispatcher::IdleNotification() { | |
409 if (is_extension_process_) { | |
410 // Dampen the forced delay as well if the extension stays idle for long | |
411 // periods of time. | |
412 int64 forced_delay_ms = std::max( | |
413 RenderThread::Get()->GetIdleNotificationDelayInMs(), | |
414 kMaxExtensionIdleHandlerDelayMs); | |
415 forced_idle_timer_.Stop(); | |
416 forced_idle_timer_.Start(FROM_HERE, | |
417 base::TimeDelta::FromMilliseconds(forced_delay_ms), | |
418 RenderThread::Get(), &RenderThread::IdleHandler); | |
419 } | |
420 } | |
421 | |
422 void ExtensionDispatcher::OnSetFunctionNames( | |
423 const std::vector<std::string>& names) { | |
424 function_names_.clear(); | |
425 for (size_t i = 0; i < names.size(); ++i) | |
426 function_names_.insert(names[i]); | |
427 } | |
428 | |
429 void ExtensionDispatcher::OnSetChannel(int channel) { | |
430 chrome_channel_ = channel; | |
431 } | |
432 | |
433 void ExtensionDispatcher::OnMessageInvoke(const std::string& extension_id, | |
434 const std::string& function_name, | |
435 const ListValue& args, | |
436 const GURL& event_url, | |
437 bool user_gesture) { | |
438 scoped_ptr<WebScopedUserGesture> web_user_gesture; | |
439 if (user_gesture) { | |
440 web_user_gesture.reset(new WebScopedUserGesture); | |
441 } | |
442 | |
443 v8_context_set_.DispatchChromeHiddenMethod( | |
444 extension_id, function_name, args, NULL, event_url); | |
445 | |
446 // Reset the idle handler each time there's any activity like event or message | |
447 // dispatch, for which Invoke is the chokepoint. | |
448 if (is_extension_process_) { | |
449 RenderThread::Get()->ScheduleIdleHandler( | |
450 kInitialExtensionIdleHandlerDelayMs); | |
451 } | |
452 | |
453 // Tell the browser process when an event has been dispatched with a lazy | |
454 // background page active. | |
455 const Extension* extension = extensions_.GetByID(extension_id); | |
456 if (extension && extension->has_lazy_background_page() && | |
457 function_name == kEventDispatchFunction) { | |
458 RenderView* background_view = | |
459 ExtensionHelper::GetBackgroundPage(extension_id); | |
460 if (background_view) { | |
461 background_view->Send(new ExtensionHostMsg_EventAck( | |
462 background_view->GetRoutingID())); | |
463 } | |
464 } | |
465 } | |
466 | |
467 void ExtensionDispatcher::OnDispatchOnConnect( | |
468 int target_port_id, | |
469 const std::string& channel_name, | |
470 const std::string& tab_json, | |
471 const std::string& source_extension_id, | |
472 const std::string& target_extension_id) { | |
473 MiscellaneousBindings::DispatchOnConnect( | |
474 v8_context_set_.GetAll(), | |
475 target_port_id, channel_name, tab_json, | |
476 source_extension_id, target_extension_id, | |
477 NULL); // All render views. | |
478 } | |
479 | |
480 void ExtensionDispatcher::OnDeliverMessage(int target_port_id, | |
481 const std::string& message) { | |
482 MiscellaneousBindings::DeliverMessage( | |
483 v8_context_set_.GetAll(), | |
484 target_port_id, | |
485 message, | |
486 NULL); // All render views. | |
487 } | |
488 | |
489 void ExtensionDispatcher::OnDispatchOnDisconnect(int port_id, | |
490 bool connection_error) { | |
491 MiscellaneousBindings::DispatchOnDisconnect( | |
492 v8_context_set_.GetAll(), | |
493 port_id, connection_error, | |
494 NULL); // All render views. | |
495 } | |
496 | |
497 void ExtensionDispatcher::OnLoaded( | |
498 const std::vector<ExtensionMsg_Loaded_Params>& loaded_extensions) { | |
499 std::vector<WebString> platform_app_patterns; | |
500 | |
501 std::vector<ExtensionMsg_Loaded_Params>::const_iterator i; | |
502 for (i = loaded_extensions.begin(); i != loaded_extensions.end(); ++i) { | |
503 scoped_refptr<const Extension> extension(i->ConvertToExtension()); | |
504 if (!extension) { | |
505 // This can happen if extension parsing fails for any reason. One reason | |
506 // this can legitimately happen is if the | |
507 // --enable-experimental-extension-apis changes at runtime, which happens | |
508 // during browser tests. Existing renderers won't know about the change. | |
509 continue; | |
510 } | |
511 | |
512 extensions_.Insert(extension); | |
513 | |
514 if (extension->is_platform_app()) { | |
515 platform_app_patterns.push_back( | |
516 WebString::fromUTF8(extension->url().spec() + "*")); | |
517 } | |
518 } | |
519 | |
520 if (!platform_app_patterns.empty()) { | |
521 // We have collected a set of platform-app extensions, so let's tell WebKit | |
522 // about them so that it can provide a default stylesheet for them. | |
523 // | |
524 // TODO(miket): consider enhancing WebView to allow removing | |
525 // single stylesheets, or else to edit the pattern set associated | |
526 // with one. | |
527 WebVector<WebString> patterns; | |
528 patterns.assign(platform_app_patterns); | |
529 WebView::addUserStyleSheet( | |
530 WebString::fromUTF8(ResourceBundle::GetSharedInstance(). | |
531 GetRawDataResource(IDR_PLATFORM_APP_CSS, | |
532 ui::SCALE_FACTOR_NONE)), | |
533 patterns, | |
534 WebView::UserContentInjectInAllFrames, | |
535 WebView::UserStyleInjectInExistingDocuments); | |
536 } | |
537 } | |
538 | |
539 void ExtensionDispatcher::OnUnloaded(const std::string& id) { | |
540 extensions_.Remove(id); | |
541 active_extension_ids_.erase(id); | |
542 | |
543 // If the extension is later reloaded with a different set of permissions, | |
544 // we'd like it to get a new isolated world ID, so that it can pick up the | |
545 // changed origin whitelist. | |
546 user_script_slave_->RemoveIsolatedWorld(id); | |
547 | |
548 // We don't do anything with existing platform-app stylesheets. They will | |
549 // stay resident, but the URL pattern corresponding to the unloaded | |
550 // extension's URL just won't match anything anymore. | |
551 } | |
552 | |
553 void ExtensionDispatcher::OnSetScriptingWhitelist( | |
554 const Extension::ScriptingWhitelist& extension_ids) { | |
555 Extension::SetScriptingWhitelist(extension_ids); | |
556 } | |
557 | |
558 bool ExtensionDispatcher::IsExtensionActive( | |
559 const std::string& extension_id) const { | |
560 bool is_active = | |
561 active_extension_ids_.find(extension_id) != active_extension_ids_.end(); | |
562 if (is_active) | |
563 CHECK(extensions_.Contains(extension_id)); | |
564 return is_active; | |
565 } | |
566 | |
567 bool ExtensionDispatcher::AllowScriptExtension( | |
568 WebFrame* frame, | |
569 const std::string& v8_extension_name, | |
570 int extension_group) { | |
571 return AllowScriptExtension(frame, v8_extension_name, extension_group, 0); | |
572 } | |
573 | |
574 namespace { | |
575 | |
576 // This is what the extension_group variable will be when DidCreateScriptContext | |
577 // is called. We know because it's the same as what AllowScriptExtension gets | |
578 // passed, and the two functions are called sequentially from WebKit. | |
579 // | |
580 // TODO(koz): Plumb extension_group through to AllowScriptExtension() from | |
581 // WebKit. | |
582 static int g_hack_extension_group = 0; | |
583 | |
584 } | |
585 | |
586 bool ExtensionDispatcher::AllowScriptExtension( | |
587 WebFrame* frame, | |
588 const std::string& v8_extension_name, | |
589 int extension_group, | |
590 int world_id) { | |
591 g_hack_extension_group = extension_group; | |
592 return true; | |
593 } | |
594 | |
595 void ExtensionDispatcher::RegisterNativeHandlers(ModuleSystem* module_system, | |
596 ChromeV8Context* context) { | |
597 module_system->RegisterNativeHandler("event_bindings", | |
598 scoped_ptr<NativeHandler>(EventBindings::Get(this))); | |
599 module_system->RegisterNativeHandler("miscellaneous_bindings", | |
600 scoped_ptr<NativeHandler>(MiscellaneousBindings::Get(this))); | |
601 module_system->RegisterNativeHandler("apiDefinitions", | |
602 scoped_ptr<NativeHandler>(new ApiDefinitionsNatives(this))); | |
603 module_system->RegisterNativeHandler("sendRequest", | |
604 scoped_ptr<NativeHandler>( | |
605 new SendRequestNatives(this, request_sender_.get()))); | |
606 module_system->RegisterNativeHandler("setIcon", | |
607 scoped_ptr<NativeHandler>( | |
608 new SetIconNatives(this, request_sender_.get()))); | |
609 | |
610 // Natives used by multiple APIs. | |
611 module_system->RegisterNativeHandler("file_system_natives", | |
612 scoped_ptr<NativeHandler>(new FileSystemNatives())); | |
613 | |
614 // Custom bindings. | |
615 module_system->RegisterNativeHandler("app", | |
616 scoped_ptr<NativeHandler>(new AppBindings(this, context))); | |
617 module_system->RegisterNativeHandler("app_window", | |
618 scoped_ptr<NativeHandler>(new AppWindowCustomBindings(this))); | |
619 module_system->RegisterNativeHandler("context_menus", | |
620 scoped_ptr<NativeHandler>(new ContextMenusCustomBindings())); | |
621 module_system->RegisterNativeHandler("extension", | |
622 scoped_ptr<NativeHandler>( | |
623 new ExtensionCustomBindings(this))); | |
624 module_system->RegisterNativeHandler("experimental_app", | |
625 scoped_ptr<NativeHandler>(new ExperimentalAppCustomBindings())); | |
626 module_system->RegisterNativeHandler("experimental_mediaGalleries", | |
627 scoped_ptr<NativeHandler>(new MediaGalleryCustomBindings())); | |
628 module_system->RegisterNativeHandler("experimental_usb", | |
629 scoped_ptr<NativeHandler>(new ExperimentalUsbCustomBindings())); | |
630 module_system->RegisterNativeHandler("file_browser_handler", | |
631 scoped_ptr<NativeHandler>(new FileBrowserHandlerCustomBindings())); | |
632 module_system->RegisterNativeHandler("file_browser_private", | |
633 scoped_ptr<NativeHandler>(new FileBrowserPrivateCustomBindings())); | |
634 module_system->RegisterNativeHandler("i18n", | |
635 scoped_ptr<NativeHandler>(new I18NCustomBindings())); | |
636 module_system->RegisterNativeHandler("page_actions", | |
637 scoped_ptr<NativeHandler>( | |
638 new PageActionsCustomBindings(this))); | |
639 module_system->RegisterNativeHandler("page_capture", | |
640 scoped_ptr<NativeHandler>(new PageCaptureCustomBindings())); | |
641 module_system->RegisterNativeHandler("runtime", | |
642 scoped_ptr<NativeHandler>(new RuntimeCustomBindings(context))); | |
643 module_system->RegisterNativeHandler("tabs", | |
644 scoped_ptr<NativeHandler>(new TabsCustomBindings())); | |
645 module_system->RegisterNativeHandler("tts", | |
646 scoped_ptr<NativeHandler>(new TTSCustomBindings())); | |
647 module_system->RegisterNativeHandler("web_request", | |
648 scoped_ptr<NativeHandler>(new WebRequestCustomBindings())); | |
649 module_system->RegisterNativeHandler("webstore", | |
650 scoped_ptr<NativeHandler>(new WebstoreBindings(this, context))); | |
651 } | |
652 | |
653 void ExtensionDispatcher::PopulateSourceMap() { | |
654 source_map_.RegisterSource("event_bindings", IDR_EVENT_BINDINGS_JS); | |
655 source_map_.RegisterSource("miscellaneous_bindings", | |
656 IDR_MISCELLANEOUS_BINDINGS_JS); | |
657 source_map_.RegisterSource("schema_generated_bindings", | |
658 IDR_SCHEMA_GENERATED_BINDINGS_JS); | |
659 source_map_.RegisterSource("json_schema", IDR_JSON_SCHEMA_JS); | |
660 source_map_.RegisterSource("apitest", IDR_EXTENSION_APITEST_JS); | |
661 | |
662 // Libraries. | |
663 source_map_.RegisterSource("lastError", IDR_LAST_ERROR_JS); | |
664 source_map_.RegisterSource("schemaUtils", IDR_SCHEMA_UTILS_JS); | |
665 source_map_.RegisterSource("sendRequest", IDR_SEND_REQUEST_JS); | |
666 source_map_.RegisterSource("setIcon", IDR_SET_ICON_JS); | |
667 source_map_.RegisterSource("utils", IDR_UTILS_JS); | |
668 | |
669 // Custom bindings. | |
670 source_map_.RegisterSource("app", IDR_APP_CUSTOM_BINDINGS_JS); | |
671 source_map_.RegisterSource("app.window", IDR_APP_WINDOW_CUSTOM_BINDINGS_JS); | |
672 source_map_.RegisterSource("browserAction", | |
673 IDR_BROWSER_ACTION_CUSTOM_BINDINGS_JS); | |
674 source_map_.RegisterSource("contentSettings", | |
675 IDR_CONTENT_SETTINGS_CUSTOM_BINDINGS_JS); | |
676 source_map_.RegisterSource("contextMenus", | |
677 IDR_CONTEXT_MENUS_CUSTOM_BINDINGS_JS); | |
678 source_map_.RegisterSource("declarativeWebRequest", | |
679 IDR_DECLARATIVE_WEBREQUEST_CUSTOM_BINDINGS_JS); | |
680 source_map_.RegisterSource("devtools", IDR_DEVTOOLS_CUSTOM_BINDINGS_JS); | |
681 source_map_.RegisterSource("experimental.app", | |
682 IDR_EXPERIMENTAL_APP_CUSTOM_BINDINGS_JS); | |
683 source_map_.RegisterSource("experimental.bluetooth", | |
684 IDR_EXPERIMENTAL_BLUETOOTH_CUSTOM_BINDINGS_JS); | |
685 source_map_.RegisterSource("experimental.mediaGalleries", | |
686 IDR_EXPERIMENTAL_MEDIA_GALLERY_CUSTOM_BINDINGS_JS); | |
687 source_map_.RegisterSource("experimental.offscreen", | |
688 IDR_EXPERIMENTAL_OFFSCREENTABS_CUSTOM_BINDINGS_JS); | |
689 source_map_.RegisterSource("experimental.usb", | |
690 IDR_EXPERIMENTAL_USB_CUSTOM_BINDINGS_JS); | |
691 source_map_.RegisterSource("extension", IDR_EXTENSION_CUSTOM_BINDINGS_JS); | |
692 source_map_.RegisterSource("fileBrowserHandler", | |
693 IDR_FILE_BROWSER_HANDLER_CUSTOM_BINDINGS_JS); | |
694 source_map_.RegisterSource("fileBrowserPrivate", | |
695 IDR_FILE_BROWSER_PRIVATE_CUSTOM_BINDINGS_JS); | |
696 source_map_.RegisterSource("fileSystem", | |
697 IDR_FILE_SYSTEM_CUSTOM_BINDINGS_JS); | |
698 source_map_.RegisterSource("i18n", IDR_I18N_CUSTOM_BINDINGS_JS); | |
699 source_map_.RegisterSource("input.ime", IDR_INPUT_IME_CUSTOM_BINDINGS_JS); | |
700 source_map_.RegisterSource("omnibox", IDR_OMNIBOX_CUSTOM_BINDINGS_JS); | |
701 source_map_.RegisterSource("pageActions", | |
702 IDR_PAGE_ACTIONS_CUSTOM_BINDINGS_JS); | |
703 source_map_.RegisterSource("pageAction", IDR_PAGE_ACTION_CUSTOM_BINDINGS_JS); | |
704 source_map_.RegisterSource("pageCapture", | |
705 IDR_PAGE_CAPTURE_CUSTOM_BINDINGS_JS); | |
706 source_map_.RegisterSource("runtime", IDR_RUNTIME_CUSTOM_BINDINGS_JS); | |
707 source_map_.RegisterSource("storage", IDR_STORAGE_CUSTOM_BINDINGS_JS); | |
708 source_map_.RegisterSource("tabs", IDR_TABS_CUSTOM_BINDINGS_JS); | |
709 source_map_.RegisterSource("tts", IDR_TTS_CUSTOM_BINDINGS_JS); | |
710 source_map_.RegisterSource("ttsEngine", IDR_TTS_ENGINE_CUSTOM_BINDINGS_JS); | |
711 source_map_.RegisterSource("types", IDR_TYPES_CUSTOM_BINDINGS_JS); | |
712 source_map_.RegisterSource("webRequest", IDR_WEB_REQUEST_CUSTOM_BINDINGS_JS); | |
713 source_map_.RegisterSource("webRequestInternal", | |
714 IDR_WEB_REQUEST_INTERNAL_CUSTOM_BINDINGS_JS); | |
715 source_map_.RegisterSource("webstore", IDR_WEBSTORE_CUSTOM_BINDINGS_JS); | |
716 | |
717 // Platform app sources that are not API-specific.. | |
718 source_map_.RegisterSource("browserTag", IDR_BROWSER_TAG_JS); | |
719 source_map_.RegisterSource("platformApp", IDR_PLATFORM_APP_JS); | |
720 } | |
721 | |
722 void ExtensionDispatcher::PopulateLazyBindingsMap() { | |
723 lazy_bindings_map_["app"] = InstallAppBindings; | |
724 lazy_bindings_map_["webstore"] = InstallWebstoreBindings; | |
725 } | |
726 | |
727 void ExtensionDispatcher::InstallBindings(ModuleSystem* module_system, | |
728 v8::Handle<v8::Context> v8_context, | |
729 const std::string& api) { | |
730 std::map<std::string, BindingInstaller>::const_iterator lazy_binding = | |
731 lazy_bindings_map_.find(api); | |
732 if (lazy_binding != lazy_bindings_map_.end()) { | |
733 v8::Handle<v8::Object> global(v8_context->Global()); | |
734 v8::Handle<v8::Object> chrome = | |
735 global->Get(v8::String::New("chrome"))->ToObject(); | |
736 v8::Handle<v8::Object> chrome_hidden = | |
737 ChromeV8Context::GetOrCreateChromeHidden(v8_context)->ToObject(); | |
738 (*lazy_binding->second)(module_system, chrome, chrome_hidden); | |
739 } else { | |
740 module_system->Require(api); | |
741 } | |
742 } | |
743 | |
744 void ExtensionDispatcher::DidCreateScriptContext( | |
745 WebFrame* frame, v8::Handle<v8::Context> v8_context, int extension_group, | |
746 int world_id) { | |
747 // TODO(koz): If the caller didn't pass extension_group, use the last value. | |
748 if (extension_group == -1) | |
749 extension_group = g_hack_extension_group; | |
750 | |
751 std::string extension_id = GetExtensionID(frame, world_id); | |
752 | |
753 const Extension* extension = extensions_.GetByID(extension_id); | |
754 if (!extension && !extension_id.empty()) { | |
755 // There are conditions where despite a context being associated with an | |
756 // extension, no extension actually gets found. Ignore "invalid" because | |
757 // CSP blocks extension page loading by switching the extension ID to | |
758 // "invalid". This isn't interesting. | |
759 if (extension_id != "invalid") { | |
760 LOG(ERROR) << "Extension \"" << extension_id << "\" not found"; | |
761 RenderThread::Get()->RecordUserMetrics("ExtensionNotFound_ED"); | |
762 } | |
763 | |
764 extension_id = ""; | |
765 } | |
766 | |
767 ExtensionURLInfo url_info(frame->document().securityOrigin(), | |
768 extensions::UserScriptSlave::GetDataSourceURLForFrame(frame)); | |
769 | |
770 Feature::Context context_type = | |
771 ClassifyJavaScriptContext(extension_id, extension_group, url_info); | |
772 | |
773 ChromeV8Context* context = | |
774 new ChromeV8Context(v8_context, frame, extension, context_type); | |
775 v8_context_set_.Add(context); | |
776 | |
777 scoped_ptr<ModuleSystem> module_system(new ModuleSystem(v8_context, | |
778 &source_map_)); | |
779 // Enable natives in startup. | |
780 ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system.get()); | |
781 | |
782 RegisterNativeHandlers(module_system.get(), context); | |
783 | |
784 module_system->RegisterNativeHandler("chrome_hidden", | |
785 scoped_ptr<NativeHandler>(new ChromeHiddenNativeHandler())); | |
786 module_system->RegisterNativeHandler("print", | |
787 scoped_ptr<NativeHandler>(new PrintNativeHandler())); | |
788 module_system->RegisterNativeHandler("lazy_background_page", | |
789 scoped_ptr<NativeHandler>(new LazyBackgroundPageNativeHandler(this))); | |
790 module_system->RegisterNativeHandler("channel", | |
791 scoped_ptr<NativeHandler>(new ChannelNativeHandler( | |
792 static_cast<chrome::VersionInfo::Channel>(chrome_channel_)))); | |
793 module_system->RegisterNativeHandler("logging", | |
794 scoped_ptr<NativeHandler>(new LoggingNativeHandler())); | |
795 | |
796 | |
797 int manifest_version = extension ? extension->manifest_version() : 1; | |
798 module_system->RegisterNativeHandler("process", | |
799 scoped_ptr<NativeHandler>(new ProcessInfoNativeHandler( | |
800 this, context->GetExtensionID(), | |
801 context->GetContextTypeDescription(), | |
802 ChromeRenderProcessObserver::is_incognito_process(), | |
803 manifest_version))); | |
804 | |
805 GetOrCreateChrome(v8_context); | |
806 | |
807 // Loading JavaScript is expensive, so only run the full API bindings | |
808 // generation mechanisms in extension pages (NOT all web pages). | |
809 switch (context_type) { | |
810 case Feature::UNSPECIFIED_CONTEXT: | |
811 case Feature::WEB_PAGE_CONTEXT: | |
812 // TODO(kalman): see comment below about ExtensionAPI. | |
813 InstallBindings(module_system.get(), v8_context, "app"); | |
814 InstallBindings(module_system.get(), v8_context, "webstore"); | |
815 break; | |
816 | |
817 case Feature::BLESSED_EXTENSION_CONTEXT: | |
818 case Feature::UNBLESSED_EXTENSION_CONTEXT: | |
819 case Feature::CONTENT_SCRIPT_CONTEXT: { | |
820 CHECK(extension); | |
821 if (!extension->is_platform_app()) | |
822 module_system->Require("miscellaneous_bindings"); | |
823 module_system->Require("schema_generated_bindings"); | |
824 module_system->Require("apitest"); | |
825 | |
826 // TODO(kalman): move this code back out of the switch and execute it | |
827 // regardless of |context_type|. ExtensionAPI knows how to return the | |
828 // correct APIs, however, until it doesn't have a 2MB overhead we can't | |
829 // load it in every process. | |
830 const std::set<std::string>& apis = context->GetAvailableExtensionAPIs(); | |
831 for (std::set<std::string>::const_iterator i = apis.begin(); | |
832 i != apis.end(); ++i) { | |
833 InstallBindings(module_system.get(), v8_context, *i); | |
834 } | |
835 | |
836 break; | |
837 } | |
838 } | |
839 | |
840 // Inject custom JS into the platform app context. | |
841 if (IsWithinPlatformApp(frame)) | |
842 module_system->Require("platformApp"); | |
843 | |
844 if (context_type == Feature::BLESSED_EXTENSION_CONTEXT && | |
845 extension->HasAPIPermission(APIPermission::kBrowserTag)) { | |
846 module_system->Require("browserTag"); | |
847 } | |
848 | |
849 context->set_module_system(module_system.Pass()); | |
850 | |
851 context->DispatchOnLoadEvent( | |
852 ChromeRenderProcessObserver::is_incognito_process(), | |
853 manifest_version); | |
854 | |
855 VLOG(1) << "Num tracked contexts: " << v8_context_set_.size(); | |
856 } | |
857 | |
858 std::string ExtensionDispatcher::GetExtensionID(const WebFrame* frame, | |
859 int world_id) { | |
860 if (world_id != 0) { | |
861 // Isolated worlds (content script). | |
862 return user_script_slave_->GetExtensionIdForIsolatedWorld(world_id); | |
863 } | |
864 | |
865 // Extension pages (chrome-extension:// URLs). | |
866 GURL frame_url = extensions::UserScriptSlave::GetDataSourceURLForFrame(frame); | |
867 return extensions_.GetExtensionOrAppIDByURL( | |
868 ExtensionURLInfo(frame->document().securityOrigin(), frame_url)); | |
869 } | |
870 | |
871 bool ExtensionDispatcher::IsWithinPlatformApp(const WebFrame* frame) { | |
872 const Extension* extension = | |
873 extensions_.GetByID(GetExtensionID(frame->top(), 0)); | |
874 return extension && extension->is_platform_app(); | |
875 } | |
876 | |
877 void ExtensionDispatcher::WillReleaseScriptContext( | |
878 WebFrame* frame, v8::Handle<v8::Context> v8_context, int world_id) { | |
879 ChromeV8Context* context = v8_context_set_.GetByV8Context(v8_context); | |
880 if (!context) | |
881 return; | |
882 | |
883 context->DispatchOnUnloadEvent(); | |
884 | |
885 v8_context_set_.Remove(context); | |
886 VLOG(1) << "Num tracked contexts: " << v8_context_set_.size(); | |
887 } | |
888 | |
889 void ExtensionDispatcher::OnActivateExtension( | |
890 const std::string& extension_id) { | |
891 active_extension_ids_.insert(extension_id); | |
892 const Extension* extension = extensions_.GetByID(extension_id); | |
893 CHECK(extension); | |
894 | |
895 // This is called when starting a new extension page, so start the idle | |
896 // handler ticking. | |
897 RenderThread::Get()->ScheduleIdleHandler(kInitialExtensionIdleHandlerDelayMs); | |
898 | |
899 UpdateActiveExtensions(); | |
900 | |
901 if (is_webkit_initialized_) | |
902 InitOriginPermissions(extension); | |
903 } | |
904 | |
905 void ExtensionDispatcher::InitOriginPermissions(const Extension* extension) { | |
906 // TODO(jstritar): We should try to remove this special case. Also, these | |
907 // whitelist entries need to be updated when the kManagement permission | |
908 // changes. | |
909 if (extension->HasAPIPermission(APIPermission::kManagement)) { | |
910 WebSecurityPolicy::addOriginAccessWhitelistEntry( | |
911 extension->url(), | |
912 WebString::fromUTF8(chrome::kChromeUIScheme), | |
913 WebString::fromUTF8(chrome::kChromeUIExtensionIconHost), | |
914 false); | |
915 } | |
916 | |
917 AddOrRemoveOriginPermissions( | |
918 UpdatedExtensionPermissionsInfo::ADDED, | |
919 extension, | |
920 extension->GetActivePermissions()->explicit_hosts()); | |
921 } | |
922 | |
923 void ExtensionDispatcher::AddOrRemoveOriginPermissions( | |
924 UpdatedExtensionPermissionsInfo::Reason reason, | |
925 const Extension* extension, | |
926 const URLPatternSet& origins) { | |
927 for (URLPatternSet::const_iterator i = origins.begin(); | |
928 i != origins.end(); ++i) { | |
929 const char* schemes[] = { | |
930 chrome::kHttpScheme, | |
931 chrome::kHttpsScheme, | |
932 chrome::kFileScheme, | |
933 chrome::kChromeUIScheme, | |
934 }; | |
935 for (size_t j = 0; j < arraysize(schemes); ++j) { | |
936 if (i->MatchesScheme(schemes[j])) { | |
937 ((reason == UpdatedExtensionPermissionsInfo::REMOVED) ? | |
938 WebSecurityPolicy::removeOriginAccessWhitelistEntry : | |
939 WebSecurityPolicy::addOriginAccessWhitelistEntry)( | |
940 extension->url(), | |
941 WebString::fromUTF8(schemes[j]), | |
942 WebString::fromUTF8(i->host()), | |
943 i->match_subdomains()); | |
944 } | |
945 } | |
946 } | |
947 } | |
948 | |
949 void ExtensionDispatcher::OnUpdatePermissions( | |
950 int reason_id, | |
951 const std::string& extension_id, | |
952 const extensions::APIPermissionSet& apis, | |
953 const URLPatternSet& explicit_hosts, | |
954 const URLPatternSet& scriptable_hosts) { | |
955 const Extension* extension = extensions_.GetByID(extension_id); | |
956 if (!extension) | |
957 return; | |
958 | |
959 scoped_refptr<const PermissionSet> delta = | |
960 new PermissionSet(apis, explicit_hosts, scriptable_hosts); | |
961 scoped_refptr<const PermissionSet> old_active = | |
962 extension->GetActivePermissions(); | |
963 UpdatedExtensionPermissionsInfo::Reason reason = | |
964 static_cast<UpdatedExtensionPermissionsInfo::Reason>(reason_id); | |
965 | |
966 const PermissionSet* new_active = NULL; | |
967 switch (reason) { | |
968 case UpdatedExtensionPermissionsInfo::ADDED: | |
969 new_active = PermissionSet::CreateUnion(old_active, delta); | |
970 break; | |
971 case UpdatedExtensionPermissionsInfo::REMOVED: | |
972 new_active = PermissionSet::CreateDifference(old_active, delta); | |
973 break; | |
974 } | |
975 | |
976 extension->SetActivePermissions(new_active); | |
977 AddOrRemoveOriginPermissions(reason, extension, explicit_hosts); | |
978 } | |
979 | |
980 void ExtensionDispatcher::OnUpdateTabSpecificPermissions( | |
981 int page_id, | |
982 int tab_id, | |
983 const std::string& extension_id, | |
984 const URLPatternSet& origin_set) { | |
985 RenderView* view = TabFinder::Find(tab_id); | |
986 | |
987 // For now, the message should only be sent to the render view that contains | |
988 // the target tab. This may change. Either way, if this is the target tab it | |
989 // gives us the chance to check against the page ID to avoid races. | |
990 DCHECK(view); | |
991 if (view && view->GetPageId() != page_id) | |
992 return; | |
993 | |
994 const Extension* extension = extensions_.GetByID(extension_id); | |
995 if (!extension) | |
996 return; | |
997 | |
998 extension->UpdateTabSpecificPermissions( | |
999 tab_id, | |
1000 new PermissionSet(APIPermissionSet(), origin_set, URLPatternSet())); | |
1001 } | |
1002 | |
1003 void ExtensionDispatcher::OnClearTabSpecificPermissions( | |
1004 int tab_id, | |
1005 const std::vector<std::string>& extension_ids) { | |
1006 for (std::vector<std::string>::const_iterator it = extension_ids.begin(); | |
1007 it != extension_ids.end(); ++it) { | |
1008 const Extension* extension = extensions_.GetByID(*it); | |
1009 if (extension) | |
1010 extension->ClearTabSpecificPermissions(tab_id); | |
1011 } | |
1012 } | |
1013 | |
1014 void ExtensionDispatcher::OnUpdateUserScripts( | |
1015 base::SharedMemoryHandle scripts) { | |
1016 DCHECK(base::SharedMemory::IsHandleValid(scripts)) << "Bad scripts handle"; | |
1017 user_script_slave_->UpdateScripts(scripts); | |
1018 UpdateActiveExtensions(); | |
1019 } | |
1020 | |
1021 void ExtensionDispatcher::UpdateActiveExtensions() { | |
1022 // In single-process mode, the browser process reports the active extensions. | |
1023 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess)) | |
1024 return; | |
1025 | |
1026 std::set<std::string> active_extensions = active_extension_ids_; | |
1027 user_script_slave_->GetActiveExtensions(&active_extensions); | |
1028 child_process_logging::SetActiveExtensions(active_extensions); | |
1029 } | |
1030 | |
1031 void ExtensionDispatcher::RegisterExtension(v8::Extension* extension, | |
1032 bool restrict_to_extensions) { | |
1033 if (restrict_to_extensions) | |
1034 restricted_v8_extensions_.insert(extension->name()); | |
1035 | |
1036 RenderThread::Get()->RegisterExtension(extension); | |
1037 } | |
1038 | |
1039 void ExtensionDispatcher::OnUsingWebRequestAPI( | |
1040 bool adblock, bool adblock_plus, bool other) { | |
1041 webrequest_adblock_ = adblock; | |
1042 webrequest_adblock_plus_ = adblock_plus; | |
1043 webrequest_other_ = other; | |
1044 } | |
1045 | |
1046 void ExtensionDispatcher::OnShouldUnload(const std::string& extension_id, | |
1047 int sequence_id) { | |
1048 RenderThread::Get()->Send( | |
1049 new ExtensionHostMsg_ShouldUnloadAck(extension_id, sequence_id)); | |
1050 } | |
1051 | |
1052 void ExtensionDispatcher::OnUnload(const std::string& extension_id) { | |
1053 // Dispatch the unload event. This doesn't go through the standard event | |
1054 // dispatch machinery because it requires special handling. We need to let | |
1055 // the browser know when we are starting and stopping the event dispatch, so | |
1056 // that it still considers the extension idle despite any activity the unload | |
1057 // event creates. | |
1058 ListValue args; | |
1059 args.Set(0, Value::CreateStringValue(kOnUnloadEvent)); | |
1060 args.Set(1, Value::CreateStringValue("[]")); | |
1061 v8_context_set_.DispatchChromeHiddenMethod( | |
1062 extension_id, kEventDispatchFunction, args, NULL, GURL()); | |
1063 | |
1064 RenderThread::Get()->Send(new ExtensionHostMsg_UnloadAck(extension_id)); | |
1065 } | |
1066 | |
1067 void ExtensionDispatcher::OnCancelUnload(const std::string& extension_id) { | |
1068 ListValue args; | |
1069 args.Set(0, Value::CreateStringValue(kOnSuspendCanceledEvent)); | |
1070 args.Set(1, Value::CreateStringValue("[]")); | |
1071 v8_context_set_.DispatchChromeHiddenMethod( | |
1072 extension_id, kEventDispatchFunction, args, NULL, GURL()); | |
1073 } | |
1074 | |
1075 Feature::Context ExtensionDispatcher::ClassifyJavaScriptContext( | |
1076 const std::string& extension_id, | |
1077 int extension_group, | |
1078 const ExtensionURLInfo& url_info) { | |
1079 if (extension_group == EXTENSION_GROUP_CONTENT_SCRIPTS) { | |
1080 return extensions_.Contains(extension_id) ? | |
1081 Feature::CONTENT_SCRIPT_CONTEXT : Feature::UNSPECIFIED_CONTEXT; | |
1082 } | |
1083 | |
1084 // We have an explicit check for sandboxed pages first since: | |
1085 // 1. Sandboxed pages run in the same process as regular extension pages, so | |
1086 // the extension is considered active. | |
1087 // 2. ScriptContext creation (which triggers bindings injection) happens | |
1088 // before the SecurityContext is updated with the sandbox flags (after | |
1089 // reading the CSP header), so url_info.url().securityOrigin() is not | |
1090 // unique yet. | |
1091 if (extensions_.IsSandboxedPage(url_info)) | |
1092 return Feature::WEB_PAGE_CONTEXT; | |
1093 | |
1094 if (IsExtensionActive(extension_id)) | |
1095 return Feature::BLESSED_EXTENSION_CONTEXT; | |
1096 | |
1097 if (extensions_.ExtensionBindingsAllowed(url_info)) | |
1098 return Feature::UNBLESSED_EXTENSION_CONTEXT; | |
1099 | |
1100 if (url_info.url().is_valid()) | |
1101 return Feature::WEB_PAGE_CONTEXT; | |
1102 | |
1103 return Feature::UNSPECIFIED_CONTEXT; | |
1104 } | |
1105 | |
1106 void ExtensionDispatcher::OnExtensionResponse(int request_id, | |
1107 bool success, | |
1108 const base::ListValue& response, | |
1109 const std::string& error) { | |
1110 request_sender_->HandleResponse(request_id, success, response, error); | |
1111 } | |
1112 | |
1113 bool ExtensionDispatcher::CheckCurrentContextAccessToExtensionAPI( | |
1114 const std::string& function_name) const { | |
1115 ChromeV8Context* context = v8_context_set().GetCurrent(); | |
1116 if (!context) { | |
1117 DLOG(ERROR) << "Not in a v8::Context"; | |
1118 return false; | |
1119 } | |
1120 | |
1121 if (!context->extension()) { | |
1122 v8::ThrowException( | |
1123 v8::Exception::Error(v8::String::New("Not in an extension."))); | |
1124 return false; | |
1125 } | |
1126 | |
1127 // We need to whitelist tabs.executeScript and tabs.insertCSS because they | |
1128 // are granted under special circumstances with the activeTab permission | |
1129 // (note that the browser checks too, so this isn't a security problem). | |
1130 // | |
1131 // Only the browser knows which tab this call will be sent to... sometimes we | |
1132 // *could* figure it out (if the extension gives an explicit tab ID in the | |
1133 // call), but the expected case will be the extension passing through -1, | |
1134 // meaning the active tab, and only the browser safely knows what this is. | |
1135 bool skip_permission_check = (function_name == "tabs.executeScript") || | |
1136 (function_name == "tabs.insertCSS"); | |
1137 | |
1138 if (!skip_permission_check && | |
1139 !context->extension()->HasAPIPermission(function_name)) { | |
1140 static const char kMessage[] = | |
1141 "You do not have permission to use '%s'. Be sure to declare" | |
1142 " in your manifest what permissions you need."; | |
1143 std::string error_msg = base::StringPrintf(kMessage, function_name.c_str()); | |
1144 v8::ThrowException( | |
1145 v8::Exception::Error(v8::String::New(error_msg.c_str()))); | |
1146 return false; | |
1147 } | |
1148 | |
1149 if (ExtensionAPI::GetSharedInstance()->IsPrivileged(function_name) && | |
1150 context->context_type() != Feature::BLESSED_EXTENSION_CONTEXT) { | |
1151 static const char kMessage[] = | |
1152 "%s can only be used in an extension process."; | |
1153 std::string error_msg = base::StringPrintf(kMessage, function_name.c_str()); | |
1154 v8::ThrowException( | |
1155 v8::Exception::Error(v8::String::New(error_msg.c_str()))); | |
1156 return false; | |
1157 } | |
1158 | |
1159 // We should never end up with sandboxed contexts trying to invoke extension | |
1160 // APIs, they don't get extension bindings injected. If we end up here it | |
1161 // means that a sandboxed page somehow managed to invoke an API anyway, so | |
1162 // we should abort. | |
1163 WebKit::WebFrame* frame = context->web_frame(); | |
1164 ExtensionURLInfo url_info(frame->document().securityOrigin(), | |
1165 extensions::UserScriptSlave::GetDataSourceURLForFrame(frame)); | |
1166 CHECK(!extensions_.IsSandboxedPage(url_info)); | |
1167 | |
1168 return true; | |
1169 } | |
OLD | NEW |