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/dispatcher.h" 6 7 #include "base/callback.h" 8 #include "base/command_line.h" 9 #include "base/debug/alias.h" 10 #include "base/json/json_reader.h" 11 #include "base/memory/scoped_ptr.h" 12 #include "base/sha1.h" 13 #include "base/strings/string_number_conversions.h" 14 #include "base/strings/string_piece.h" 15 #include "base/strings/string_split.h" 16 #include "base/strings/string_util.h" 17 #include "chrome/common/child_process_logging.h" 18 #include "chrome/common/chrome_switches.h" 19 #include "chrome/common/chrome_version_info.h" 20 #include "chrome/common/extensions/api/extension_api.h" 21 #include "chrome/common/extensions/background_info.h" 22 #include "chrome/common/extensions/extension.h" 23 #include "chrome/common/extensions/extension_constants.h" 24 #include "chrome/common/extensions/extension_manifest_constants.h" 25 #include "chrome/common/extensions/extension_messages.h" 26 #include "chrome/common/extensions/features/feature.h" 27 #include "chrome/common/extensions/features/feature_channel.h" 28 #include "chrome/common/extensions/manifest.h" 29 #include "chrome/common/extensions/manifest_handlers/externally_connectable.h" 30 #include "chrome/common/extensions/manifest_handlers/sandboxed_page_info.h" 31 #include "chrome/common/extensions/message_bundle.h" 32 #include "chrome/common/extensions/permissions/permission_set.h" 33 #include "chrome/common/extensions/permissions/permissions_data.h" 34 #include "chrome/common/url_constants.h" 35 #include "chrome/renderer/chrome_render_process_observer.h" 36 #include "chrome/renderer/extensions/api_activity_logger.h" 37 #include "chrome/renderer/extensions/api_definitions_natives.h" 38 #include "chrome/renderer/extensions/app_bindings.h" 39 #include "chrome/renderer/extensions/app_runtime_custom_bindings.h" 40 #include "chrome/renderer/extensions/app_window_custom_bindings.h" 41 #include "chrome/renderer/extensions/binding_generating_native_handler.h" 42 #include "chrome/renderer/extensions/chrome_v8_context.h" 43 #include "chrome/renderer/extensions/chrome_v8_extension.h" 44 #include "chrome/renderer/extensions/content_watcher.h" 45 #include "chrome/renderer/extensions/context_menus_custom_bindings.h" 46 #include "chrome/renderer/extensions/dom_activity_logger.h" 47 #include "chrome/renderer/extensions/event_bindings.h" 48 #include "chrome/renderer/extensions/extension_custom_bindings.h" 49 #include "chrome/renderer/extensions/extension_groups.h" 50 #include "chrome/renderer/extensions/extension_helper.h" 51 #include "chrome/renderer/extensions/feedback_private_custom_bindings.h" 52 #include "chrome/renderer/extensions/file_browser_handler_custom_bindings.h" 53 #include "chrome/renderer/extensions/file_browser_private_custom_bindings.h" 54 #include "chrome/renderer/extensions/file_system_natives.h" 55 #include "chrome/renderer/extensions/i18n_custom_bindings.h" 56 #include "chrome/renderer/extensions/logging_native_handler.h" 57 #include "chrome/renderer/extensions/media_galleries_custom_bindings.h" 58 #include "chrome/renderer/extensions/messaging_bindings.h" 59 #include "chrome/renderer/extensions/module_system.h" 60 #include "chrome/renderer/extensions/object_backed_native_handler.h" 61 #include "chrome/renderer/extensions/page_actions_custom_bindings.h" 62 #include "chrome/renderer/extensions/page_capture_custom_bindings.h" 63 #include "chrome/renderer/extensions/render_view_observer_natives.h" 64 #include "chrome/renderer/extensions/request_sender.h" 65 #include "chrome/renderer/extensions/runtime_custom_bindings.h" 66 #include "chrome/renderer/extensions/safe_builtins.h" 67 #include "chrome/renderer/extensions/send_request_natives.h" 68 #include "chrome/renderer/extensions/set_icon_natives.h" 69 #include "chrome/renderer/extensions/sync_file_system_custom_bindings.h" 70 #include "chrome/renderer/extensions/tab_finder.h" 71 #include "chrome/renderer/extensions/tabs_custom_bindings.h" 72 #include "chrome/renderer/extensions/tts_custom_bindings.h" 73 #include "chrome/renderer/extensions/user_script_slave.h" 74 #include "chrome/renderer/extensions/web_request_custom_bindings.h" 75 #include "chrome/renderer/extensions/webstore_bindings.h" 76 #include "chrome/renderer/resource_bundle_source_map.h" 77 #include "content/public/renderer/render_thread.h" 78 #include "content/public/renderer/render_view.h" 79 #include "content/public/renderer/v8_value_converter.h" 80 #include "extensions/common/constants.h" 81 #include "extensions/common/features/feature_provider.h" 82 #include "extensions/common/view_type.h" 83 #include "grit/common_resources.h" 84 #include "grit/renderer_resources.h" 85 #include "third_party/WebKit/public/platform/WebString.h" 86 #include "third_party/WebKit/public/platform/WebURLRequest.h" 87 #include "third_party/WebKit/public/web/WebCustomElement.h" 88 #include "third_party/WebKit/public/web/WebDataSource.h" 89 #include "third_party/WebKit/public/web/WebDocument.h" 90 #include "third_party/WebKit/public/web/WebFrame.h" 91 #include "third_party/WebKit/public/web/WebRuntimeFeatures.h" 92 #include "third_party/WebKit/public/web/WebScopedUserGesture.h" 93 #include "third_party/WebKit/public/web/WebSecurityPolicy.h" 94 #include "third_party/WebKit/public/web/WebView.h" 95 #include "ui/base/layout.h" 96 #include "ui/base/resource/resource_bundle.h" 97 #include "v8/include/v8.h" 98 99 using WebKit::WebDataSource; 100 using WebKit::WebDocument; 101 using WebKit::WebFrame; 102 using WebKit::WebScopedUserGesture; 103 using WebKit::WebSecurityPolicy; 104 using WebKit::WebString; 105 using WebKit::WebVector; 106 using WebKit::WebView; 107 using content::RenderThread; 108 using content::RenderView; 109 110 namespace extensions { 111 112 namespace { 113 114 static const int64 kInitialExtensionIdleHandlerDelayMs = 5*1000; 115 static const int64 kMaxExtensionIdleHandlerDelayMs = 5*60*1000; 116 static const char kEventModule[] = "event_bindings"; 117 static const char kEventDispatchFunction[] = "dispatchEvent"; 118 static const char kOnSuspendEvent[] = "runtime.onSuspend"; 119 static const char kOnSuspendCanceledEvent[] = "runtime.onSuspendCanceled"; 120 121 // Returns the global value for "chrome" from |context|. If one doesn't exist 122 // creates a new object for it. 123 // 124 // Note that this isn't necessarily an object, since webpages can write, for 125 // example, "window.chrome = true". 126 v8::Handle<v8::Value> GetOrCreateChrome(ChromeV8Context* context) { 127 v8::Handle<v8::String> chrome_string(v8::String::New("chrome")); 128 v8::Handle<v8::Object> global(context->v8_context()->Global()); 129 v8::Handle<v8::Value> chrome(global->Get(chrome_string)); 130 if (chrome->IsUndefined()) { 131 chrome = v8::Object::New(); 132 global->Set(chrome_string, chrome); 133 } 134 return chrome; 135 } 136 137 // Returns |value| cast to an object if possible, else an empty handle. 138 v8::Handle<v8::Object> AsObjectOrEmpty(v8::Handle<v8::Value> value) { 139 return value->IsObject() ? value.As<v8::Object>() : v8::Handle<v8::Object>(); 140 } 141 142 class TestFeaturesNativeHandler : public ObjectBackedNativeHandler { 143 public: 144 explicit TestFeaturesNativeHandler(ChromeV8Context* context) 145 : ObjectBackedNativeHandler(context) { 146 RouteFunction("GetAPIFeatures", 147 base::Bind(&TestFeaturesNativeHandler::GetAPIFeatures, 148 base::Unretained(this))); 149 } 150 151 private: 152 void GetAPIFeatures(const v8::FunctionCallbackInfo<v8::Value>& args) { 153 base::Value* value = base::JSONReader::Read( 154 ResourceBundle::GetSharedInstance().GetRawDataResource( 155 IDR_EXTENSION_API_FEATURES).as_string()); 156 scoped_ptr<content::V8ValueConverter> converter( 157 content::V8ValueConverter::create()); 158 args.GetReturnValue().Set( 159 converter->ToV8Value(value, context()->v8_context())); 160 } 161 }; 162 163 class V8ContextNativeHandler : public ObjectBackedNativeHandler { 164 public: 165 V8ContextNativeHandler(ChromeV8Context* context, Dispatcher* dispatcher) 166 : ObjectBackedNativeHandler(context), 167 context_(context), 168 dispatcher_(dispatcher) { 169 RouteFunction("GetAvailability", 170 base::Bind(&V8ContextNativeHandler::GetAvailability, 171 base::Unretained(this))); 172 RouteFunction("GetModuleSystem", 173 base::Bind(&V8ContextNativeHandler::GetModuleSystem, 174 base::Unretained(this))); 175 } 176 177 private: 178 void GetAvailability(const v8::FunctionCallbackInfo<v8::Value>& args) { 179 CHECK_EQ(args.Length(), 1); 180 std::string api_name = *v8::String::AsciiValue(args[0]->ToString()); 181 Feature::Availability availability = context_->GetAvailability(api_name); 182 183 v8::Handle<v8::Object> ret = v8::Object::New(); 184 ret->Set(v8::String::New("is_available"), 185 v8::Boolean::New(availability.is_available())); 186 ret->Set(v8::String::New("message"), 187 v8::String::New(availability.message().c_str())); 188 ret->Set(v8::String::New("result"), 189 v8::Integer::New(availability.result())); 190 args.GetReturnValue().Set(ret); 191 } 192 193 void GetModuleSystem(const v8::FunctionCallbackInfo<v8::Value>& args) { 194 CHECK_EQ(args.Length(), 1); 195 CHECK(args[0]->IsObject()); 196 v8::Handle<v8::Context> v8_context = 197 v8::Handle<v8::Object>::Cast(args[0])->CreationContext(); 198 ChromeV8Context* context = dispatcher_->v8_context_set().GetByV8Context( 199 v8_context); 200 args.GetReturnValue().Set(context->module_system()->NewInstance()); 201 } 202 203 ChromeV8Context* context_; 204 Dispatcher* dispatcher_; 205 }; 206 207 class ChromeNativeHandler : public ObjectBackedNativeHandler { 208 public: 209 explicit ChromeNativeHandler(ChromeV8Context* context) 210 : ObjectBackedNativeHandler(context) { 211 RouteFunction("GetChrome", 212 base::Bind(&ChromeNativeHandler::GetChrome, base::Unretained(this))); 213 } 214 215 void GetChrome(const v8::FunctionCallbackInfo<v8::Value>& args) { 216 args.GetReturnValue().Set(GetOrCreateChrome(context())); 217 } 218 }; 219 220 class PrintNativeHandler : public ObjectBackedNativeHandler { 221 public: 222 explicit PrintNativeHandler(ChromeV8Context* context) 223 : ObjectBackedNativeHandler(context) { 224 RouteFunction("Print", 225 base::Bind(&PrintNativeHandler::Print, 226 base::Unretained(this))); 227 } 228 229 void Print(const v8::FunctionCallbackInfo<v8::Value>& args) { 230 if (args.Length() < 1) 231 return; 232 233 std::vector<std::string> components; 234 for (int i = 0; i < args.Length(); ++i) 235 components.push_back(*v8::String::Utf8Value(args[i]->ToString())); 236 237 LOG(ERROR) << JoinString(components, ','); 238 } 239 }; 240 241 class LazyBackgroundPageNativeHandler : public ChromeV8Extension { 242 public: 243 LazyBackgroundPageNativeHandler(Dispatcher* dispatcher, 244 ChromeV8Context* context) 245 : ChromeV8Extension(dispatcher, context) { 246 RouteFunction("IncrementKeepaliveCount", 247 base::Bind(&LazyBackgroundPageNativeHandler::IncrementKeepaliveCount, 248 base::Unretained(this))); 249 RouteFunction("DecrementKeepaliveCount", 250 base::Bind(&LazyBackgroundPageNativeHandler::DecrementKeepaliveCount, 251 base::Unretained(this))); 252 } 253 254 void IncrementKeepaliveCount( 255 const v8::FunctionCallbackInfo<v8::Value>& args) { 256 if (!context()) 257 return; 258 RenderView* render_view = context()->GetRenderView(); 259 if (IsContextLazyBackgroundPage(render_view, context()->extension())) { 260 render_view->Send(new ExtensionHostMsg_IncrementLazyKeepaliveCount( 261 render_view->GetRoutingID())); 262 } 263 } 264 265 void DecrementKeepaliveCount( 266 const v8::FunctionCallbackInfo<v8::Value>& args) { 267 if (!context()) 268 return; 269 RenderView* render_view = context()->GetRenderView(); 270 if (IsContextLazyBackgroundPage(render_view, context()->extension())) { 271 render_view->Send(new ExtensionHostMsg_DecrementLazyKeepaliveCount( 272 render_view->GetRoutingID())); 273 } 274 } 275 276 private: 277 bool IsContextLazyBackgroundPage(RenderView* render_view, 278 const Extension* extension) { 279 if (!render_view) 280 return false; 281 282 ExtensionHelper* helper = ExtensionHelper::Get(render_view); 283 return (extension && BackgroundInfo::HasLazyBackgroundPage(extension) && 284 helper->view_type() == VIEW_TYPE_EXTENSION_BACKGROUND_PAGE); 285 } 286 }; 287 288 class ProcessInfoNativeHandler : public ChromeV8Extension { 289 public: 290 ProcessInfoNativeHandler(Dispatcher* dispatcher, 291 ChromeV8Context* context, 292 const std::string& extension_id, 293 const std::string& context_type, 294 bool is_incognito_context, 295 int manifest_version, 296 bool send_request_disabled) 297 : ChromeV8Extension(dispatcher, context), 298 extension_id_(extension_id), 299 context_type_(context_type), 300 is_incognito_context_(is_incognito_context), 301 manifest_version_(manifest_version), 302 send_request_disabled_(send_request_disabled) { 303 RouteFunction("GetExtensionId", 304 base::Bind(&ProcessInfoNativeHandler::GetExtensionId, 305 base::Unretained(this))); 306 RouteFunction("GetContextType", 307 base::Bind(&ProcessInfoNativeHandler::GetContextType, 308 base::Unretained(this))); 309 RouteFunction("InIncognitoContext", 310 base::Bind(&ProcessInfoNativeHandler::InIncognitoContext, 311 base::Unretained(this))); 312 RouteFunction("GetManifestVersion", 313 base::Bind(&ProcessInfoNativeHandler::GetManifestVersion, 314 base::Unretained(this))); 315 RouteFunction("IsSendRequestDisabled", 316 base::Bind(&ProcessInfoNativeHandler::IsSendRequestDisabled, 317 base::Unretained(this))); 318 RouteFunction("HasSwitch", 319 base::Bind(&ProcessInfoNativeHandler::HasSwitch, 320 base::Unretained(this))); 321 } 322 323 private: 324 void GetExtensionId(const v8::FunctionCallbackInfo<v8::Value>& args) { 325 args.GetReturnValue().Set(v8::String::New(extension_id_.c_str())); 326 } 327 328 void GetContextType(const v8::FunctionCallbackInfo<v8::Value>& args) { 329 args.GetReturnValue().Set(v8::String::New(context_type_.c_str())); 330 } 331 332 void InIncognitoContext(const v8::FunctionCallbackInfo<v8::Value>& args) { 333 args.GetReturnValue().Set(is_incognito_context_); 334 } 335 336 void GetManifestVersion(const v8::FunctionCallbackInfo<v8::Value>& args) { 337 args.GetReturnValue().Set(static_cast<int32_t>(manifest_version_)); 338 } 339 340 void IsSendRequestDisabled(const v8::FunctionCallbackInfo<v8::Value>& args) { 341 if (send_request_disabled_) { 342 args.GetReturnValue().Set(v8::String::New( 343 "sendRequest and onRequest are obsolete." 344 " Please use sendMessage and onMessage instead.")); 345 } 346 } 347 348 void HasSwitch(const v8::FunctionCallbackInfo<v8::Value>& args) { 349 CHECK(args.Length() == 1 && args[0]->IsString()); 350 bool has_switch = CommandLine::ForCurrentProcess()->HasSwitch( 351 *v8::String::AsciiValue(args[0])); 352 args.GetReturnValue().Set(v8::Boolean::New(has_switch)); 353 } 354 355 std::string extension_id_; 356 std::string context_type_; 357 bool is_incognito_context_; 358 int manifest_version_; 359 bool send_request_disabled_; 360 }; 361 362 void InstallAppBindings(ModuleSystem* module_system, 363 v8::Handle<v8::Object> chrome) { 364 module_system->SetLazyField(chrome, "app", "app", "chromeApp"); 365 } 366 367 void InstallWebstoreBindings(ModuleSystem* module_system, 368 v8::Handle<v8::Object> chrome) { 369 module_system->SetLazyField(chrome, "webstore", "webstore", "chromeWebstore"); 370 } 371 372 // Calls a method |method_name| in a module |module_name| belonging to the 373 // module system from |context|. Intended as a callback target from 374 // ChromeV8ContextSet::ForEach. 375 void CallModuleMethod(const std::string& module_name, 376 const std::string& method_name, 377 const base::ListValue* args, 378 ChromeV8Context* context) { 379 v8::HandleScope handle_scope; 380 v8::Context::Scope context_scope(context->v8_context()); 381 382 scoped_ptr<content::V8ValueConverter> converter( 383 content::V8ValueConverter::create()); 384 385 std::vector<v8::Handle<v8::Value> > arguments; 386 for (base::ListValue::const_iterator it = args->begin(); it != args->end(); 387 ++it) { 388 arguments.push_back(converter->ToV8Value(*it, context->v8_context())); 389 } 390 391 context->module_system()->CallModuleMethod( 392 module_name, method_name, &arguments); 393 } 394 395 } // namespace 396 397 Dispatcher::Dispatcher() 398 : content_watcher_(new ContentWatcher(this)), 399 is_webkit_initialized_(false), 400 webrequest_adblock_(false), 401 webrequest_adblock_plus_(false), 402 webrequest_other_(false), 403 source_map_(&ResourceBundle::GetSharedInstance()), 404 v8_schema_registry_(new V8SchemaRegistry) { 405 const CommandLine& command_line = *(CommandLine::ForCurrentProcess()); 406 is_extension_process_ = 407 command_line.HasSwitch(switches::kExtensionProcess) || 408 command_line.HasSwitch(switches::kSingleProcess); 409 410 if (is_extension_process_) { 411 RenderThread::Get()->SetIdleNotificationDelayInMs( 412 kInitialExtensionIdleHandlerDelayMs); 413 } 414 415 RenderThread::Get()->RegisterExtension(SafeBuiltins::CreateV8Extension()); 416 417 user_script_slave_.reset(new UserScriptSlave(&extensions_)); 418 request_sender_.reset(new RequestSender(this)); 419 PopulateSourceMap(); 420 PopulateLazyBindingsMap(); 421 } 422 423 Dispatcher::~Dispatcher() { 424 } 425 426 bool Dispatcher::OnControlMessageReceived(const IPC::Message& message) { 427 bool handled = true; 428 IPC_BEGIN_MESSAGE_MAP(Dispatcher, message) 429 IPC_MESSAGE_HANDLER(ExtensionMsg_SetChannel, OnSetChannel) 430 IPC_MESSAGE_HANDLER(ExtensionMsg_MessageInvoke, OnMessageInvoke) 431 IPC_MESSAGE_HANDLER(ExtensionMsg_DispatchOnConnect, OnDispatchOnConnect) 432 IPC_MESSAGE_HANDLER(ExtensionMsg_DeliverMessage, OnDeliverMessage) 433 IPC_MESSAGE_HANDLER(ExtensionMsg_DispatchOnDisconnect, 434 OnDispatchOnDisconnect) 435 IPC_MESSAGE_HANDLER(ExtensionMsg_SetFunctionNames, OnSetFunctionNames) 436 IPC_MESSAGE_HANDLER(ExtensionMsg_SetSystemFont, OnSetSystemFont) 437 IPC_MESSAGE_HANDLER(ExtensionMsg_Loaded, OnLoaded) 438 IPC_MESSAGE_HANDLER(ExtensionMsg_Unloaded, OnUnloaded) 439 IPC_MESSAGE_HANDLER(ExtensionMsg_SetScriptingWhitelist, 440 OnSetScriptingWhitelist) 441 IPC_MESSAGE_HANDLER(ExtensionMsg_ActivateExtension, OnActivateExtension) 442 IPC_MESSAGE_HANDLER(ExtensionMsg_UpdatePermissions, OnUpdatePermissions) 443 IPC_MESSAGE_HANDLER(ExtensionMsg_UpdateTabSpecificPermissions, 444 OnUpdateTabSpecificPermissions) 445 IPC_MESSAGE_HANDLER(ExtensionMsg_ClearTabSpecificPermissions, 446 OnClearTabSpecificPermissions) 447 IPC_MESSAGE_HANDLER(ExtensionMsg_UpdateUserScripts, OnUpdateUserScripts) 448 IPC_MESSAGE_HANDLER(ExtensionMsg_UsingWebRequestAPI, OnUsingWebRequestAPI) 449 IPC_MESSAGE_HANDLER(ExtensionMsg_ShouldSuspend, OnShouldSuspend) 450 IPC_MESSAGE_HANDLER(ExtensionMsg_Suspend, OnSuspend) 451 IPC_MESSAGE_HANDLER(ExtensionMsg_CancelSuspend, OnCancelSuspend) 452 IPC_MESSAGE_FORWARD(ExtensionMsg_WatchPages, 453 content_watcher_.get(), ContentWatcher::OnWatchPages) 454 IPC_MESSAGE_UNHANDLED(handled = false) 455 IPC_END_MESSAGE_MAP() 456 457 return handled; 458 } 459 460 void Dispatcher::WebKitInitialized() { 461 // For extensions, we want to ensure we call the IdleHandler every so often, 462 // even if the extension keeps up activity. 463 if (is_extension_process_) { 464 forced_idle_timer_.Start(FROM_HERE, 465 base::TimeDelta::FromMilliseconds(kMaxExtensionIdleHandlerDelayMs), 466 RenderThread::Get(), &RenderThread::IdleHandler); 467 } 468 469 // Initialize host permissions for any extensions that were activated before 470 // WebKit was initialized. 471 for (std::set<std::string>::iterator iter = active_extension_ids_.begin(); 472 iter != active_extension_ids_.end(); ++iter) { 473 const Extension* extension = extensions_.GetByID(*iter); 474 CHECK(extension); 475 InitOriginPermissions(extension); 476 } 477 478 if (IsWithinPlatformApp()) 479 EnableCustomElementWhiteList(); 480 481 is_webkit_initialized_ = true; 482 } 483 484 void Dispatcher::IdleNotification() { 485 if (is_extension_process_) { 486 // Dampen the forced delay as well if the extension stays idle for long 487 // periods of time. 488 int64 forced_delay_ms = std::max( 489 RenderThread::Get()->GetIdleNotificationDelayInMs(), 490 kMaxExtensionIdleHandlerDelayMs); 491 forced_idle_timer_.Stop(); 492 forced_idle_timer_.Start(FROM_HERE, 493 base::TimeDelta::FromMilliseconds(forced_delay_ms), 494 RenderThread::Get(), &RenderThread::IdleHandler); 495 } 496 } 497 498 void Dispatcher::OnRenderProcessShutdown() { 499 v8_schema_registry_.reset(); 500 } 501 502 void Dispatcher::OnSetFunctionNames( 503 const std::vector<std::string>& names) { 504 function_names_.clear(); 505 for (size_t i = 0; i < names.size(); ++i) 506 function_names_.insert(names[i]); 507 } 508 509 void Dispatcher::OnSetSystemFont(const std::string& font_family, 510 const std::string& font_size) { 511 system_font_family_ = font_family; 512 system_font_size_ = font_size; 513 } 514 515 void Dispatcher::OnSetChannel(int channel) { 516 extensions::SetCurrentChannel( 517 static_cast<chrome::VersionInfo::Channel>(channel)); 518 } 519 520 void Dispatcher::OnMessageInvoke(const std::string& extension_id, 521 const std::string& module_name, 522 const std::string& function_name, 523 const base::ListValue& args, 524 bool user_gesture) { 525 InvokeModuleSystemMethod( 526 NULL, extension_id, module_name, function_name, args, user_gesture); 527 } 528 529 void Dispatcher::OnDispatchOnConnect( 530 int target_port_id, 531 const std::string& channel_name, 532 const base::DictionaryValue& source_tab, 533 const ExtensionMsg_ExternalConnectionInfo& info) { 534 MessagingBindings::DispatchOnConnect( 535 v8_context_set_.GetAll(), 536 target_port_id, channel_name, source_tab, 537 info.source_id, info.target_id, info.source_url, 538 NULL); // All render views. 539 } 540 541 void Dispatcher::OnDeliverMessage(int target_port_id, 542 const std::string& message) { 543 MessagingBindings::DeliverMessage( 544 v8_context_set_.GetAll(), 545 target_port_id, 546 message, 547 NULL); // All render views. 548 } 549 550 void Dispatcher::OnDispatchOnDisconnect(int port_id, 551 const std::string& error_message) { 552 MessagingBindings::DispatchOnDisconnect( 553 v8_context_set_.GetAll(), 554 port_id, error_message, 555 NULL); // All render views. 556 } 557 558 void Dispatcher::OnLoaded( 559 const std::vector<ExtensionMsg_Loaded_Params>& loaded_extensions) { 560 std::vector<ExtensionMsg_Loaded_Params>::const_iterator i; 561 for (i = loaded_extensions.begin(); i != loaded_extensions.end(); ++i) { 562 std::string error; 563 scoped_refptr<const Extension> extension = i->ConvertToExtension(&error); 564 if (!extension.get()) { 565 extension_load_errors_[i->id] = error; 566 continue; 567 } 568 OnLoadedInternal(extension); 569 } 570 // Update the available bindings for all contexts. These may have changed if 571 // an externally_connectable extension was loaded that can connect to an 572 // open webpage. 573 AddOrRemoveBindings(""); 574 } 575 576 void Dispatcher::OnLoadedInternal(scoped_refptr<const Extension> extension) { 577 extensions_.Insert(extension); 578 } 579 580 void Dispatcher::OnUnloaded(const std::string& id) { 581 extensions_.Remove(id); 582 active_extension_ids_.erase(id); 583 584 // If the extension is later reloaded with a different set of permissions, 585 // we'd like it to get a new isolated world ID, so that it can pick up the 586 // changed origin whitelist. 587 user_script_slave_->RemoveIsolatedWorld(id); 588 589 // Invalidate all of the contexts that were removed. 590 // TODO(kalman): add an invalidation observer interface to ChromeV8Context. 591 ChromeV8ContextSet::ContextSet removed_contexts = 592 v8_context_set_.OnExtensionUnloaded(id); 593 for (ChromeV8ContextSet::ContextSet::iterator it = removed_contexts.begin(); 594 it != removed_contexts.end(); ++it) { 595 request_sender_->InvalidateSource(*it); 596 } 597 598 // Update the available bindings for the remaining contexts. These may have 599 // changed if an externally_connectable extension is unloaded and a webpage 600 // is no longer accessible. 601 AddOrRemoveBindings(""); 602 603 // Invalidates the messages map for the extension in case the extension is 604 // reloaded with a new messages map. 605 EraseL10nMessagesMap(id); 606 607 // We don't do anything with existing platform-app stylesheets. They will 608 // stay resident, but the URL pattern corresponding to the unloaded 609 // extension's URL just won't match anything anymore. 610 } 611 612 void Dispatcher::OnSetScriptingWhitelist( 613 const Extension::ScriptingWhitelist& extension_ids) { 614 Extension::SetScriptingWhitelist(extension_ids); 615 } 616 617 bool Dispatcher::IsExtensionActive( 618 const std::string& extension_id) const { 619 bool is_active = 620 active_extension_ids_.find(extension_id) != active_extension_ids_.end(); 621 if (is_active) 622 CHECK(extensions_.Contains(extension_id)); 623 return is_active; 624 } 625 626 v8::Handle<v8::Object> Dispatcher::GetOrCreateObject( 627 v8::Handle<v8::Object> object, 628 const std::string& field) { 629 v8::Handle<v8::String> key = v8::String::New(field.c_str()); 630 // If the object has a callback property, it is assumed it is an unavailable 631 // API, so it is safe to delete. This is checked before GetOrCreateObject is 632 // called. 633 if (object->HasRealNamedCallbackProperty(key)) { 634 object->Delete(key); 635 } else if (object->HasRealNamedProperty(key)) { 636 v8::Handle<v8::Value> value = object->Get(key); 637 CHECK(value->IsObject()); 638 return v8::Handle<v8::Object>::Cast(value); 639 } 640 641 v8::Handle<v8::Object> new_object = v8::Object::New(); 642 object->Set(key, new_object); 643 return new_object; 644 } 645 646 void Dispatcher::AddOrRemoveBindingsForContext(ChromeV8Context* context) { 647 v8::HandleScope handle_scope; 648 v8::Context::Scope context_scope(context->v8_context()); 649 650 // TODO(kalman): Make the bindings registration have zero overhead then run 651 // the same code regardless of context type. 652 switch (context->context_type()) { 653 case Feature::UNSPECIFIED_CONTEXT: 654 case Feature::WEB_PAGE_CONTEXT: { 655 // Web page context; it's too expensive to run the full bindings code. 656 // Hard-code that the app and webstore APIs are available... 657 RegisterBinding("app", context); 658 RegisterBinding("webstore", context); 659 660 // ... and that the runtime API might be available if any extension can 661 // connect to it. 662 bool runtime_is_available = false; 663 for (ExtensionSet::const_iterator it = extensions_.begin(); 664 it != extensions_.end(); ++it) { 665 ExternallyConnectableInfo* info = 666 static_cast<ExternallyConnectableInfo*>((*it)->GetManifestData( 667 extension_manifest_keys::kExternallyConnectable)); 668 if (info && info->matches.MatchesURL(context->GetURL())) { 669 runtime_is_available = true; 670 break; 671 } 672 } 673 if (runtime_is_available) 674 RegisterBinding("runtime", context); 675 break; 676 } 677 678 case Feature::BLESSED_EXTENSION_CONTEXT: 679 case Feature::UNBLESSED_EXTENSION_CONTEXT: 680 case Feature::CONTENT_SCRIPT_CONTEXT: { 681 // Extension context; iterate through all the APIs and bind the available 682 // ones. 683 FeatureProvider* feature_provider = FeatureProvider::GetByName("api"); 684 const std::vector<std::string>& apis = 685 feature_provider->GetAllFeatureNames(); 686 for (std::vector<std::string>::const_iterator it = apis.begin(); 687 it != apis.end(); ++it) { 688 const std::string& api_name = *it; 689 Feature* feature = feature_provider->GetFeature(api_name); 690 DCHECK(feature); 691 if (feature->IsInternal()) 692 continue; 693 694 // If this API name has parent features, then this must be a function or 695 // event, so we should not register. 696 bool parent_feature_available = false; 697 for (Feature* parent = feature_provider->GetParent(feature); 698 parent != NULL; parent = feature_provider->GetParent(parent)) { 699 if (context->IsAnyFeatureAvailableToContext(parent->name())) { 700 parent_feature_available = true; 701 break; 702 } 703 } 704 if (parent_feature_available) 705 continue; 706 707 if (!context->IsAnyFeatureAvailableToContext(api_name)) { 708 DeregisterBinding(api_name, context); 709 continue; 710 } 711 712 RegisterBinding(api_name, context); 713 } 714 break; 715 } 716 } 717 } 718 719 void Dispatcher::DeregisterBinding(const std::string& api_name, 720 ChromeV8Context* context) { 721 std::string bind_name; 722 v8::Handle<v8::Object> bind_object = 723 GetOrCreateBindObjectIfAvailable(api_name, &bind_name, context); 724 v8::Handle<v8::String> v8_bind_name = v8::String::New(bind_name.c_str()); 725 if (!bind_object.IsEmpty() && bind_object->HasRealNamedProperty(v8_bind_name)) 726 bind_object->Delete(v8_bind_name); 727 } 728 729 v8::Handle<v8::Object> Dispatcher::GetOrCreateBindObjectIfAvailable( 730 const std::string& api_name, 731 std::string* bind_name, 732 ChromeV8Context* context) { 733 std::vector<std::string> split; 734 base::SplitString(api_name, '.', &split); 735 736 v8::Handle<v8::Object> bind_object; 737 738 // Check if this API has an ancestor. If the API's ancestor is available and 739 // the API is not available, don't install the bindings for this API. If 740 // the API is available and its ancestor is not, delete the ancestor and 741 // install the bindings for the API. This is to prevent loading the ancestor 742 // API schema if it will not be needed. 743 // 744 // For example: 745 // If app is available and app.window is not, just install app. 746 // If app.window is available and app is not, delete app and install 747 // app.window on a new object so app does not have to be loaded. 748 FeatureProvider* feature_provider = FeatureProvider::GetByName("api"); 749 std::string ancestor_name; 750 bool only_ancestor_available = false; 751 752 for (size_t i = 0; i < split.size() - 1; ++i) { 753 ancestor_name += (i ? ".": "") + split[i]; 754 if (feature_provider->GetFeature(ancestor_name) && 755 context->GetAvailability(ancestor_name).is_available() && 756 !context->GetAvailability(api_name).is_available()) { 757 only_ancestor_available = true; 758 break; 759 } 760 761 if (bind_object.IsEmpty()) { 762 bind_object = AsObjectOrEmpty(GetOrCreateChrome(context)); 763 if (bind_object.IsEmpty()) 764 return v8::Handle<v8::Object>(); 765 } 766 bind_object = GetOrCreateObject(bind_object, split[i]); 767 } 768 769 if (only_ancestor_available) 770 return v8::Handle<v8::Object>(); 771 772 if (bind_name) 773 *bind_name = split.back(); 774 775 return bind_object.IsEmpty() ? 776 AsObjectOrEmpty(GetOrCreateChrome(context)) : bind_object; 777 } 778 779 void Dispatcher::RegisterBinding(const std::string& api_name, 780 ChromeV8Context* context) { 781 std::string bind_name; 782 v8::Handle<v8::Object> bind_object = 783 GetOrCreateBindObjectIfAvailable(api_name, &bind_name, context); 784 785 // Empty if the bind object failed to be created, probably because the 786 // extension overrode chrome with a non-object, e.g. window.chrome = true. 787 if (bind_object.IsEmpty()) 788 return; 789 790 v8::Local<v8::String> v8_api_name = v8::String::New(api_name.c_str()); 791 if (bind_object->HasRealNamedProperty(v8_api_name)) { 792 // The bind object may already have the property if the API has been 793 // registered before (or if the extension has put something there already, 794 // but, whatevs). 795 // 796 // In the former case, we need to re-register the bindings for the APIs 797 // which the extension now has permissions for (if any), but not touch any 798 // others so that we don't destroy state such as event listeners. 799 // 800 // TODO(kalman): Only register available APIs to make this all moot. 801 if (bind_object->HasRealNamedCallbackProperty(v8_api_name)) 802 return; // lazy binding still there, nothing to do 803 if (bind_object->Get(v8_api_name)->IsObject()) 804 return; // binding has already been fully installed 805 } 806 807 ModuleSystem* module_system = context->module_system(); 808 if (lazy_bindings_map_.find(api_name) != lazy_bindings_map_.end()) { 809 InstallBindings(module_system, context->v8_context(), api_name); 810 } else if (!source_map_.Contains(api_name)) { 811 module_system->RegisterNativeHandler( 812 api_name, 813 scoped_ptr<NativeHandler>(new BindingGeneratingNativeHandler( 814 module_system, 815 api_name, 816 "binding"))); 817 module_system->SetNativeLazyField(bind_object, 818 bind_name, 819 api_name, 820 "binding"); 821 } else { 822 module_system->SetLazyField(bind_object, 823 bind_name, 824 api_name, 825 "binding"); 826 } 827 } 828 829 // NOTE: please use the naming convention "foo_natives" for these. 830 void Dispatcher::RegisterNativeHandlers(ModuleSystem* module_system, 831 ChromeV8Context* context) { 832 module_system->RegisterNativeHandler("event_natives", 833 scoped_ptr<NativeHandler>(EventBindings::Create(this, context))); 834 module_system->RegisterNativeHandler("messaging_natives", 835 scoped_ptr<NativeHandler>(MessagingBindings::Get(this, context))); 836 module_system->RegisterNativeHandler("apiDefinitions", 837 scoped_ptr<NativeHandler>(new ApiDefinitionsNatives(this, context))); 838 module_system->RegisterNativeHandler("sendRequest", 839 scoped_ptr<NativeHandler>( 840 new SendRequestNatives(this, request_sender_.get(), context))); 841 module_system->RegisterNativeHandler("setIcon", 842 scoped_ptr<NativeHandler>( 843 new SetIconNatives(this, request_sender_.get(), context))); 844 module_system->RegisterNativeHandler( 845 "contentWatcherNative", 846 content_watcher_->MakeNatives(context)); 847 module_system->RegisterNativeHandler("activityLogger", 848 scoped_ptr<NativeHandler>(new APIActivityLogger(this, context))); 849 module_system->RegisterNativeHandler("renderViewObserverNatives", 850 scoped_ptr<NativeHandler>(new RenderViewObserverNatives(this, context))); 851 852 // Natives used by multiple APIs. 853 module_system->RegisterNativeHandler("file_system_natives", 854 scoped_ptr<NativeHandler>(new FileSystemNatives(context))); 855 856 // Custom bindings. 857 module_system->RegisterNativeHandler("app", 858 scoped_ptr<NativeHandler>(new AppBindings(this, context))); 859 module_system->RegisterNativeHandler("app_runtime", 860 scoped_ptr<NativeHandler>( 861 new AppRuntimeCustomBindings(this, context))); 862 module_system->RegisterNativeHandler("app_window_natives", 863 scoped_ptr<NativeHandler>( 864 new AppWindowCustomBindings(this, context))); 865 module_system->RegisterNativeHandler("context_menus", 866 scoped_ptr<NativeHandler>( 867 new ContextMenusCustomBindings(this, context))); 868 module_system->RegisterNativeHandler("extension", 869 scoped_ptr<NativeHandler>( 870 new ExtensionCustomBindings(this, context))); 871 module_system->RegisterNativeHandler("sync_file_system", 872 scoped_ptr<NativeHandler>( 873 new SyncFileSystemCustomBindings(this, context))); 874 module_system->RegisterNativeHandler("feedback_private", 875 scoped_ptr<NativeHandler>(new FeedbackPrivateCustomBindings( 876 this, context))); 877 module_system->RegisterNativeHandler("file_browser_handler", 878 scoped_ptr<NativeHandler>(new FileBrowserHandlerCustomBindings( 879 this, context))); 880 module_system->RegisterNativeHandler("file_browser_private", 881 scoped_ptr<NativeHandler>(new FileBrowserPrivateCustomBindings( 882 this, context))); 883 module_system->RegisterNativeHandler("i18n", 884 scoped_ptr<NativeHandler>( 885 new I18NCustomBindings(this, context))); 886 module_system->RegisterNativeHandler("mediaGalleries", 887 scoped_ptr<NativeHandler>( 888 new MediaGalleriesCustomBindings(this, context))); 889 module_system->RegisterNativeHandler("page_actions", 890 scoped_ptr<NativeHandler>( 891 new PageActionsCustomBindings(this, context))); 892 module_system->RegisterNativeHandler("page_capture", 893 scoped_ptr<NativeHandler>( 894 new PageCaptureCustomBindings(this, context))); 895 module_system->RegisterNativeHandler("runtime", 896 scoped_ptr<NativeHandler>(new RuntimeCustomBindings(this, context))); 897 module_system->RegisterNativeHandler("tabs", 898 scoped_ptr<NativeHandler>(new TabsCustomBindings(this, context))); 899 module_system->RegisterNativeHandler("tts", 900 scoped_ptr<NativeHandler>(new TTSCustomBindings(this, context))); 901 module_system->RegisterNativeHandler("web_request", 902 scoped_ptr<NativeHandler>( 903 new WebRequestCustomBindings(this, context))); 904 module_system->RegisterNativeHandler("webstore", 905 scoped_ptr<NativeHandler>(new WebstoreBindings(this, context))); 906 } 907 908 void Dispatcher::PopulateSourceMap() { 909 // Libraries. 910 source_map_.RegisterSource("contentWatcher", IDR_CONTENT_WATCHER_JS); 911 source_map_.RegisterSource("entryIdManager", IDR_ENTRY_ID_MANAGER); 912 source_map_.RegisterSource(kEventModule, IDR_EVENT_BINDINGS_JS); 913 source_map_.RegisterSource("imageUtil", IDR_IMAGE_UTIL_JS); 914 source_map_.RegisterSource("json_schema", IDR_JSON_SCHEMA_JS); 915 source_map_.RegisterSource("lastError", IDR_LAST_ERROR_JS); 916 source_map_.RegisterSource("messaging", IDR_MESSAGING_JS); 917 source_map_.RegisterSource("schemaUtils", IDR_SCHEMA_UTILS_JS); 918 source_map_.RegisterSource("sendRequest", IDR_SEND_REQUEST_JS); 919 source_map_.RegisterSource("setIcon", IDR_SET_ICON_JS); 920 source_map_.RegisterSource("test", IDR_TEST_CUSTOM_BINDINGS_JS); 921 source_map_.RegisterSource("unload_event", IDR_UNLOAD_EVENT_JS); 922 source_map_.RegisterSource("utils", IDR_UTILS_JS); 923 924 // Custom bindings. 925 source_map_.RegisterSource("app", IDR_APP_CUSTOM_BINDINGS_JS); 926 source_map_.RegisterSource("app.runtime", IDR_APP_RUNTIME_CUSTOM_BINDINGS_JS); 927 source_map_.RegisterSource("app.window", IDR_APP_WINDOW_CUSTOM_BINDINGS_JS); 928 source_map_.RegisterSource("bluetooth", IDR_BLUETOOTH_CUSTOM_BINDINGS_JS); 929 source_map_.RegisterSource("browserAction", 930 IDR_BROWSER_ACTION_CUSTOM_BINDINGS_JS); 931 source_map_.RegisterSource("contextMenus", 932 IDR_CONTEXT_MENUS_CUSTOM_BINDINGS_JS); 933 source_map_.RegisterSource("declarativeContent", 934 IDR_DECLARATIVE_CONTENT_CUSTOM_BINDINGS_JS); 935 source_map_.RegisterSource("declarativeWebRequest", 936 IDR_DECLARATIVE_WEBREQUEST_CUSTOM_BINDINGS_JS); 937 source_map_.RegisterSource("downloads", 938 IDR_DOWNLOADS_CUSTOM_BINDINGS_JS); 939 source_map_.RegisterSource("experimental.offscreen", 940 IDR_EXPERIMENTAL_OFFSCREENTABS_CUSTOM_BINDINGS_JS); 941 source_map_.RegisterSource("extension", IDR_EXTENSION_CUSTOM_BINDINGS_JS); 942 source_map_.RegisterSource("feedbackPrivate", 943 IDR_FEEDBACK_PRIVATE_CUSTOM_BINDINGS_JS); 944 source_map_.RegisterSource("fileBrowserHandler", 945 IDR_FILE_BROWSER_HANDLER_CUSTOM_BINDINGS_JS); 946 source_map_.RegisterSource("fileBrowserPrivate", 947 IDR_FILE_BROWSER_PRIVATE_CUSTOM_BINDINGS_JS); 948 source_map_.RegisterSource("fileSystem", 949 IDR_FILE_SYSTEM_CUSTOM_BINDINGS_JS); 950 source_map_.RegisterSource("i18n", IDR_I18N_CUSTOM_BINDINGS_JS); 951 source_map_.RegisterSource("input.ime", IDR_INPUT_IME_CUSTOM_BINDINGS_JS); 952 source_map_.RegisterSource("mediaGalleries", 953 IDR_MEDIA_GALLERIES_CUSTOM_BINDINGS_JS); 954 source_map_.RegisterSource("notifications", 955 IDR_NOTIFICATIONS_CUSTOM_BINDINGS_JS); 956 source_map_.RegisterSource("omnibox", IDR_OMNIBOX_CUSTOM_BINDINGS_JS); 957 source_map_.RegisterSource("pageActions", 958 IDR_PAGE_ACTIONS_CUSTOM_BINDINGS_JS); 959 source_map_.RegisterSource("pageAction", IDR_PAGE_ACTION_CUSTOM_BINDINGS_JS); 960 source_map_.RegisterSource("pageCapture", 961 IDR_PAGE_CAPTURE_CUSTOM_BINDINGS_JS); 962 source_map_.RegisterSource("permissions", IDR_PERMISSIONS_CUSTOM_BINDINGS_JS); 963 source_map_.RegisterSource("runtime", IDR_RUNTIME_CUSTOM_BINDINGS_JS); 964 source_map_.RegisterSource("syncFileSystem", 965 IDR_SYNC_FILE_SYSTEM_CUSTOM_BINDINGS_JS); 966 source_map_.RegisterSource("systemIndicator", 967 IDR_SYSTEM_INDICATOR_CUSTOM_BINDINGS_JS); 968 source_map_.RegisterSource("tabCapture", IDR_TAB_CAPTURE_CUSTOM_BINDINGS_JS); 969 source_map_.RegisterSource("tabs", IDR_TABS_CUSTOM_BINDINGS_JS); 970 source_map_.RegisterSource("tts", IDR_TTS_CUSTOM_BINDINGS_JS); 971 source_map_.RegisterSource("ttsEngine", IDR_TTS_ENGINE_CUSTOM_BINDINGS_JS); 972 source_map_.RegisterSource("webRequest", IDR_WEB_REQUEST_CUSTOM_BINDINGS_JS); 973 source_map_.RegisterSource("webRequestInternal", 974 IDR_WEB_REQUEST_INTERNAL_CUSTOM_BINDINGS_JS); 975 source_map_.RegisterSource("webstore", IDR_WEBSTORE_CUSTOM_BINDINGS_JS); 976 source_map_.RegisterSource("windowControls", IDR_WINDOW_CONTROLS_JS); 977 source_map_.RegisterSource("binding", IDR_BINDING_JS); 978 979 // Custom types sources. 980 source_map_.RegisterSource("ChromeSetting", IDR_CHROME_SETTING_JS); 981 source_map_.RegisterSource("StorageArea", IDR_STORAGE_AREA_JS); 982 source_map_.RegisterSource("ContentSetting", IDR_CONTENT_SETTING_JS); 983 source_map_.RegisterSource("ChromeDirectSetting", 984 IDR_CHROME_DIRECT_SETTING_JS); 985 986 // Platform app sources that are not API-specific.. 987 source_map_.RegisterSource("tagWatcher", IDR_TAG_WATCHER_JS); 988 // Note: webView not webview so that this doesn't interfere with the 989 // chrome.webview API bindings. 990 source_map_.RegisterSource("webView", IDR_WEB_VIEW_JS); 991 source_map_.RegisterSource("webViewExperimental", 992 IDR_WEB_VIEW_EXPERIMENTAL_JS); 993 source_map_.RegisterSource("denyWebView", IDR_WEB_VIEW_DENY_JS); 994 source_map_.RegisterSource("adView", IDR_AD_VIEW_JS); 995 source_map_.RegisterSource("denyAdView", IDR_AD_VIEW_DENY_JS); 996 source_map_.RegisterSource("platformApp", IDR_PLATFORM_APP_JS); 997 source_map_.RegisterSource("injectAppTitlebar", IDR_INJECT_APP_TITLEBAR_JS); 998 } 999 1000 void Dispatcher::PopulateLazyBindingsMap() { 1001 lazy_bindings_map_["app"] = InstallAppBindings; 1002 lazy_bindings_map_["webstore"] = InstallWebstoreBindings; 1003 } 1004 1005 void Dispatcher::InstallBindings(ModuleSystem* module_system, 1006 v8::Handle<v8::Context> v8_context, 1007 const std::string& api) { 1008 std::map<std::string, BindingInstaller>::const_iterator lazy_binding = 1009 lazy_bindings_map_.find(api); 1010 if (lazy_binding != lazy_bindings_map_.end()) { 1011 v8::Handle<v8::Object> global(v8_context->Global()); 1012 v8::Handle<v8::Object> chrome = 1013 global->Get(v8::String::New("chrome"))->ToObject(); 1014 (*lazy_binding->second)(module_system, chrome); 1015 } else { 1016 module_system->Require(api); 1017 } 1018 } 1019 1020 void Dispatcher::DidCreateScriptContext( 1021 WebFrame* frame, v8::Handle<v8::Context> v8_context, int extension_group, 1022 int world_id) { 1023 #if !defined(ENABLE_EXTENSIONS) 1024 return; 1025 #endif 1026 1027 std::string extension_id = GetExtensionID(frame, world_id); 1028 1029 const Extension* extension = extensions_.GetByID(extension_id); 1030 if (!extension && !extension_id.empty()) { 1031 // There are conditions where despite a context being associated with an 1032 // extension, no extension actually gets found. Ignore "invalid" because 1033 // CSP blocks extension page loading by switching the extension ID to 1034 // "invalid". This isn't interesting. 1035 if (extension_id != "invalid") { 1036 LOG(ERROR) << "Extension \"" << extension_id << "\" not found"; 1037 RenderThread::Get()->RecordUserMetrics("ExtensionNotFound_ED"); 1038 } 1039 1040 extension_id = ""; 1041 } 1042 1043 Feature::Context context_type = ClassifyJavaScriptContext( 1044 extension_id, extension_group, 1045 UserScriptSlave::GetDataSourceURLForFrame(frame), 1046 frame->document().securityOrigin()); 1047 1048 ChromeV8Context* context = 1049 new ChromeV8Context(v8_context, frame, extension, context_type); 1050 v8_context_set_.Add(context); 1051 1052 { 1053 scoped_ptr<ModuleSystem> module_system(new ModuleSystem(context, 1054 &source_map_)); 1055 context->set_module_system(module_system.Pass()); 1056 } 1057 ModuleSystem* module_system = context->module_system(); 1058 1059 // Enable natives in startup. 1060 ModuleSystem::NativesEnabledScope natives_enabled_scope( 1061 module_system); 1062 1063 RegisterNativeHandlers(module_system, context); 1064 1065 module_system->RegisterNativeHandler("chrome", 1066 scoped_ptr<NativeHandler>(new ChromeNativeHandler(context))); 1067 module_system->RegisterNativeHandler("print", 1068 scoped_ptr<NativeHandler>(new PrintNativeHandler(context))); 1069 module_system->RegisterNativeHandler("lazy_background_page", 1070 scoped_ptr<NativeHandler>( 1071 new LazyBackgroundPageNativeHandler(this, context))); 1072 module_system->RegisterNativeHandler("logging", 1073 scoped_ptr<NativeHandler>(new LoggingNativeHandler(context))); 1074 module_system->RegisterNativeHandler("schema_registry", 1075 v8_schema_registry_->AsNativeHandler()); 1076 module_system->RegisterNativeHandler("v8_context", 1077 scoped_ptr<NativeHandler>(new V8ContextNativeHandler(context, this))); 1078 module_system->RegisterNativeHandler("test_features", 1079 scoped_ptr<NativeHandler>(new TestFeaturesNativeHandler(context))); 1080 1081 int manifest_version = extension ? extension->manifest_version() : 1; 1082 bool send_request_disabled = 1083 (extension && Manifest::IsUnpackedLocation(extension->location()) && 1084 BackgroundInfo::HasLazyBackgroundPage(extension)); 1085 module_system->RegisterNativeHandler("process", 1086 scoped_ptr<NativeHandler>(new ProcessInfoNativeHandler( 1087 this, context, context->GetExtensionID(), 1088 context->GetContextTypeDescription(), 1089 ChromeRenderProcessObserver::is_incognito_process(), 1090 manifest_version, send_request_disabled))); 1091 1092 // chrome.Event is part of the public API (although undocumented). Make it 1093 // lazily evalulate to Event from event_bindings.js. For extensions only 1094 // though, not all webpages! 1095 if (context->extension()) { 1096 v8::Handle<v8::Object> chrome = AsObjectOrEmpty(GetOrCreateChrome(context)); 1097 if (!chrome.IsEmpty()) 1098 module_system->SetLazyField(chrome, "Event", kEventModule, "Event"); 1099 } 1100 1101 AddOrRemoveBindingsForContext(context); 1102 1103 bool is_within_platform_app = IsWithinPlatformApp(); 1104 // Inject custom JS into the platform app context. 1105 if (is_within_platform_app) { 1106 module_system->Require("platformApp"); 1107 } 1108 1109 if (context_type == Feature::BLESSED_EXTENSION_CONTEXT && 1110 is_within_platform_app && 1111 GetCurrentChannel() <= chrome::VersionInfo::CHANNEL_DEV && 1112 CommandLine::ForCurrentProcess()->HasSwitch( 1113 switches::kEnableAppWindowControls)) { 1114 module_system->Require("windowControls"); 1115 } 1116 1117 // Only platform apps support the <webview> tag, because the "webView" and 1118 // "denyWebView" modules will affect the performance of DOM modifications 1119 // (http://crbug.com/196453). 1120 if (context_type == Feature::BLESSED_EXTENSION_CONTEXT && 1121 is_within_platform_app) { 1122 // Note: setting up the WebView class here, not the chrome.webview API. 1123 // The API will be automatically set up when first used. 1124 if (extension->HasAPIPermission(APIPermission::kWebView)) { 1125 module_system->Require("webView"); 1126 // TODO(mtomasz): Remove the Files app from the whitelist in M-31. 1127 // crbug.com/297936 1128 bool includeExperimental = 1129 GetCurrentChannel() <= chrome::VersionInfo::CHANNEL_DEV || 1130 extension->id() == extension_misc::kIdentityApiUiAppId || 1131 extension->id() == "hhaomjibdihmijegdhdafkllkbggdgoj"; // Files App. 1132 if (!includeExperimental) { 1133 // TODO(asargent) We need a whitelist for webview experimental. 1134 // crbug.com/264852 1135 std::string id_hash = base::SHA1HashString(extension->id()); 1136 std::string hexencoded_id_hash = base::HexEncode(id_hash.c_str(), 1137 id_hash.length()); 1138 if (hexencoded_id_hash == "8C3741E3AF0B93B6E8E0DDD499BB0B74839EA578" || 1139 hexencoded_id_hash == "E703483CEF33DEC18B4B6DD84B5C776FB9182BDB") 1140 includeExperimental = true; 1141 } 1142 if (includeExperimental) 1143 module_system->Require("webViewExperimental"); 1144 } else { 1145 module_system->Require("denyWebView"); 1146 } 1147 } 1148 1149 // Same comment as above for <adview> tag. 1150 if (context_type == Feature::BLESSED_EXTENSION_CONTEXT && 1151 is_within_platform_app) { 1152 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableAdview)) { 1153 if (extension->HasAPIPermission(APIPermission::kAdView)) { 1154 module_system->Require("adView"); 1155 } else { 1156 module_system->Require("denyAdView"); 1157 } 1158 } 1159 } 1160 1161 VLOG(1) << "Num tracked contexts: " << v8_context_set_.size(); 1162 } 1163 1164 std::string Dispatcher::GetExtensionID(const WebFrame* frame, int world_id) { 1165 if (world_id != 0) { 1166 // Isolated worlds (content script). 1167 return user_script_slave_->GetExtensionIdForIsolatedWorld(world_id); 1168 } 1169 1170 // TODO(kalman): Delete this check. 1171 if (frame->document().securityOrigin().isUnique()) 1172 return std::string(); 1173 1174 // Extension pages (chrome-extension:// URLs). 1175 GURL frame_url = UserScriptSlave::GetDataSourceURLForFrame(frame); 1176 return extensions_.GetExtensionOrAppIDByURL(frame_url); 1177 } 1178 1179 bool Dispatcher::IsWithinPlatformApp() { 1180 for (std::set<std::string>::iterator iter = active_extension_ids_.begin(); 1181 iter != active_extension_ids_.end(); ++iter) { 1182 const Extension* extension = extensions_.GetByID(*iter); 1183 if (extension && extension->is_platform_app()) 1184 return true; 1185 } 1186 return false; 1187 } 1188 1189 void Dispatcher::WillReleaseScriptContext( 1190 WebFrame* frame, v8::Handle<v8::Context> v8_context, int world_id) { 1191 ChromeV8Context* context = v8_context_set_.GetByV8Context(v8_context); 1192 if (!context) 1193 return; 1194 1195 context->DispatchOnUnloadEvent(); 1196 // TODO(kalman): add an invalidation observer interface to ChromeV8Context. 1197 request_sender_->InvalidateSource(context); 1198 1199 v8_context_set_.Remove(context); 1200 VLOG(1) << "Num tracked contexts: " << v8_context_set_.size(); 1201 } 1202 1203 void Dispatcher::DidCreateDocumentElement(WebKit::WebFrame* frame) { 1204 if (IsWithinPlatformApp()) { 1205 // WebKit doesn't let us define an additional user agent stylesheet, so we 1206 // insert the default platform app stylesheet into all documents that are 1207 // loaded in each app. 1208 std::string stylesheet = 1209 ResourceBundle::GetSharedInstance(). 1210 GetRawDataResource(IDR_PLATFORM_APP_CSS).as_string(); 1211 ReplaceFirstSubstringAfterOffset(&stylesheet, 0, 1212 "$FONTFAMILY", system_font_family_); 1213 ReplaceFirstSubstringAfterOffset(&stylesheet, 0, 1214 "$FONTSIZE", system_font_size_); 1215 frame->document().insertUserStyleSheet( 1216 WebString::fromUTF8(stylesheet), WebDocument::UserStyleUserLevel); 1217 } 1218 1219 content_watcher_->DidCreateDocumentElement(frame); 1220 } 1221 1222 void Dispatcher::OnActivateExtension(const std::string& extension_id) { 1223 const Extension* extension = extensions_.GetByID(extension_id); 1224 if (!extension) { 1225 // Extension was activated but was never loaded. This probably means that 1226 // the renderer failed to load it (or the browser failed to tell us when it 1227 // did). Failures shouldn't happen, but instead of crashing there (which 1228 // executes on all renderers) be conservative and only crash in the renderer 1229 // of the extension which failed to load; this one. 1230 std::string& error = extension_load_errors_[extension_id]; 1231 char minidump[256]; 1232 base::debug::Alias(&minidump); 1233 base::snprintf(minidump, arraysize(minidump), 1234 "e::dispatcher:%s:%s", extension_id.c_str(), error.c_str()); 1235 CHECK(extension) << extension_id << " was never loaded: " << error; 1236 } 1237 1238 active_extension_ids_.insert(extension_id); 1239 1240 // This is called when starting a new extension page, so start the idle 1241 // handler ticking. 1242 RenderThread::Get()->ScheduleIdleHandler(kInitialExtensionIdleHandlerDelayMs); 1243 1244 UpdateActiveExtensions(); 1245 1246 if (is_webkit_initialized_) { 1247 InitOriginPermissions(extension); 1248 // DOMActivity logger for a main world controlled by an extension (as in 1249 // the case of an extension background page, options page, popup etc.) 1250 // gets an empty title. 1251 DOMActivityLogger::AttachToWorld(DOMActivityLogger::kMainWorldId, 1252 extension_id, 1253 extension->url(), 1254 string16()); 1255 1256 if (IsWithinPlatformApp()) 1257 EnableCustomElementWhiteList(); 1258 } 1259 } 1260 1261 void Dispatcher::InitOriginPermissions(const Extension* extension) { 1262 // TODO(jstritar): We should try to remove this special case. Also, these 1263 // whitelist entries need to be updated when the kManagement permission 1264 // changes. 1265 if (extension->HasAPIPermission(APIPermission::kManagement)) { 1266 WebSecurityPolicy::addOriginAccessWhitelistEntry( 1267 extension->url(), 1268 WebString::fromUTF8(chrome::kChromeUIScheme), 1269 WebString::fromUTF8(chrome::kChromeUIExtensionIconHost), 1270 false); 1271 } 1272 1273 AddOrRemoveOriginPermissions( 1274 UpdatedExtensionPermissionsInfo::ADDED, 1275 extension, 1276 extension->GetActivePermissions()->explicit_hosts()); 1277 } 1278 1279 void Dispatcher::AddOrRemoveOriginPermissions( 1280 UpdatedExtensionPermissionsInfo::Reason reason, 1281 const Extension* extension, 1282 const URLPatternSet& origins) { 1283 for (URLPatternSet::const_iterator i = origins.begin(); 1284 i != origins.end(); ++i) { 1285 const char* schemes[] = { 1286 chrome::kHttpScheme, 1287 chrome::kHttpsScheme, 1288 chrome::kFileScheme, 1289 chrome::kChromeUIScheme, 1290 }; 1291 for (size_t j = 0; j < arraysize(schemes); ++j) { 1292 if (i->MatchesScheme(schemes[j])) { 1293 ((reason == UpdatedExtensionPermissionsInfo::REMOVED) ? 1294 WebSecurityPolicy::removeOriginAccessWhitelistEntry : 1295 WebSecurityPolicy::addOriginAccessWhitelistEntry)( 1296 extension->url(), 1297 WebString::fromUTF8(schemes[j]), 1298 WebString::fromUTF8(i->host()), 1299 i->match_subdomains()); 1300 } 1301 } 1302 } 1303 } 1304 1305 void Dispatcher::EnableCustomElementWhiteList() { 1306 WebKit::WebRuntimeFeatures::enableCustomElements(true); 1307 WebKit::WebCustomElement::allowTagName("webview"); 1308 // TODO(fsamuel): Add <adview> to the whitelist once it has been converted 1309 // into a custom element. 1310 WebKit::WebCustomElement::allowTagName("browser-plugin"); 1311 } 1312 1313 void Dispatcher::AddOrRemoveBindings(const std::string& extension_id) { 1314 v8_context_set().ForEach( 1315 extension_id, 1316 NULL, // all render views 1317 base::Bind(&Dispatcher::AddOrRemoveBindingsForContext, 1318 base::Unretained(this))); 1319 } 1320 1321 void Dispatcher::OnUpdatePermissions(int reason_id, 1322 const std::string& extension_id, 1323 const APIPermissionSet& apis, 1324 const URLPatternSet& explicit_hosts, 1325 const URLPatternSet& scriptable_hosts) { 1326 const Extension* extension = extensions_.GetByID(extension_id); 1327 if (!extension) 1328 return; 1329 1330 scoped_refptr<const PermissionSet> delta = 1331 new PermissionSet(apis, explicit_hosts, scriptable_hosts); 1332 scoped_refptr<const PermissionSet> old_active = 1333 extension->GetActivePermissions(); 1334 UpdatedExtensionPermissionsInfo::Reason reason = 1335 static_cast<UpdatedExtensionPermissionsInfo::Reason>(reason_id); 1336 1337 const PermissionSet* new_active = NULL; 1338 switch (reason) { 1339 case UpdatedExtensionPermissionsInfo::ADDED: 1340 new_active = PermissionSet::CreateUnion(old_active.get(), delta.get()); 1341 break; 1342 case UpdatedExtensionPermissionsInfo::REMOVED: 1343 new_active = 1344 PermissionSet::CreateDifference(old_active.get(), delta.get()); 1345 break; 1346 } 1347 1348 PermissionsData::SetActivePermissions(extension, new_active); 1349 AddOrRemoveOriginPermissions(reason, extension, explicit_hosts); 1350 AddOrRemoveBindings(extension->id()); 1351 } 1352 1353 void Dispatcher::OnUpdateTabSpecificPermissions( 1354 int page_id, 1355 int tab_id, 1356 const std::string& extension_id, 1357 const URLPatternSet& origin_set) { 1358 RenderView* view = TabFinder::Find(tab_id); 1359 1360 // For now, the message should only be sent to the render view that contains 1361 // the target tab. This may change. Either way, if this is the target tab it 1362 // gives us the chance to check against the page ID to avoid races. 1363 DCHECK(view); 1364 if (view && view->GetPageId() != page_id) 1365 return; 1366 1367 const Extension* extension = extensions_.GetByID(extension_id); 1368 if (!extension) 1369 return; 1370 1371 PermissionsData::UpdateTabSpecificPermissions( 1372 extension, 1373 tab_id, 1374 new PermissionSet(APIPermissionSet(), origin_set, URLPatternSet())); 1375 } 1376 1377 void Dispatcher::OnClearTabSpecificPermissions( 1378 int tab_id, 1379 const std::vector<std::string>& extension_ids) { 1380 for (std::vector<std::string>::const_iterator it = extension_ids.begin(); 1381 it != extension_ids.end(); ++it) { 1382 const Extension* extension = extensions_.GetByID(*it); 1383 if (extension) 1384 PermissionsData::ClearTabSpecificPermissions(extension, tab_id); 1385 } 1386 } 1387 1388 void Dispatcher::OnUpdateUserScripts( 1389 base::SharedMemoryHandle scripts) { 1390 DCHECK(base::SharedMemory::IsHandleValid(scripts)) << "Bad scripts handle"; 1391 user_script_slave_->UpdateScripts(scripts); 1392 UpdateActiveExtensions(); 1393 } 1394 1395 void Dispatcher::UpdateActiveExtensions() { 1396 // In single-process mode, the browser process reports the active extensions. 1397 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess)) 1398 return; 1399 1400 std::set<std::string> active_extensions = active_extension_ids_; 1401 user_script_slave_->GetActiveExtensions(&active_extensions); 1402 child_process_logging::SetActiveExtensions(active_extensions); 1403 } 1404 1405 void Dispatcher::OnUsingWebRequestAPI( 1406 bool adblock, bool adblock_plus, bool other) { 1407 webrequest_adblock_ = adblock; 1408 webrequest_adblock_plus_ = adblock_plus; 1409 webrequest_other_ = other; 1410 } 1411 1412 void Dispatcher::OnShouldSuspend(const std::string& extension_id, 1413 int sequence_id) { 1414 RenderThread::Get()->Send( 1415 new ExtensionHostMsg_ShouldSuspendAck(extension_id, sequence_id)); 1416 } 1417 1418 void Dispatcher::OnSuspend(const std::string& extension_id) { 1419 // Dispatch the suspend event. This doesn't go through the standard event 1420 // dispatch machinery because it requires special handling. We need to let 1421 // the browser know when we are starting and stopping the event dispatch, so 1422 // that it still considers the extension idle despite any activity the suspend 1423 // event creates. 1424 DispatchEvent(extension_id, kOnSuspendEvent); 1425 RenderThread::Get()->Send(new ExtensionHostMsg_SuspendAck(extension_id)); 1426 } 1427 1428 void Dispatcher::OnCancelSuspend(const std::string& extension_id) { 1429 DispatchEvent(extension_id, kOnSuspendCanceledEvent); 1430 } 1431 1432 // TODO(kalman): This is checking for the wrong thing, it should be checking if 1433 // the frame's security origin is unique. The extension sandbox directive is 1434 // checked for in chrome/common/extensions/csp_handler.cc. 1435 bool Dispatcher::IsSandboxedPage(const GURL& url) const { 1436 if (url.SchemeIs(extensions::kExtensionScheme)) { 1437 const Extension* extension = extensions_.GetByID(url.host()); 1438 if (extension) { 1439 return extensions::SandboxedPageInfo::IsSandboxedPage(extension, 1440 url.path()); 1441 } 1442 } 1443 return false; 1444 } 1445 1446 Feature::Context Dispatcher::ClassifyJavaScriptContext( 1447 const std::string& extension_id, 1448 int extension_group, 1449 const GURL& url, 1450 const WebKit::WebSecurityOrigin& origin) { 1451 DCHECK_GE(extension_group, 0); 1452 if (extension_group == EXTENSION_GROUP_CONTENT_SCRIPTS) { 1453 return extensions_.Contains(extension_id) ? 1454 Feature::CONTENT_SCRIPT_CONTEXT : Feature::UNSPECIFIED_CONTEXT; 1455 } 1456 1457 // We have an explicit check for sandboxed pages before checking whether the 1458 // extension is active in this process because: 1459 // 1. Sandboxed pages run in the same process as regular extension pages, so 1460 // the extension is considered active. 1461 // 2. ScriptContext creation (which triggers bindings injection) happens 1462 // before the SecurityContext is updated with the sandbox flags (after 1463 // reading the CSP header), so the caller can't check if the context's 1464 // security origin is unique yet. 1465 if (IsSandboxedPage(url)) 1466 return Feature::WEB_PAGE_CONTEXT; 1467 1468 if (IsExtensionActive(extension_id)) 1469 return Feature::BLESSED_EXTENSION_CONTEXT; 1470 1471 // TODO(kalman): This isUnique() check is wrong, it should be performed as 1472 // part of IsSandboxedPage(). 1473 if (!origin.isUnique() && extensions_.ExtensionBindingsAllowed(url)) { 1474 return extensions_.Contains(extension_id) ? 1475 Feature::UNBLESSED_EXTENSION_CONTEXT : Feature::UNSPECIFIED_CONTEXT; 1476 } 1477 1478 if (url.is_valid()) 1479 return Feature::WEB_PAGE_CONTEXT; 1480 1481 return Feature::UNSPECIFIED_CONTEXT; 1482 } 1483 1484 void Dispatcher::OnExtensionResponse(int request_id, 1485 bool success, 1486 const base::ListValue& response, 1487 const std::string& error) { 1488 request_sender_->HandleResponse(request_id, success, response, error); 1489 } 1490 1491 bool Dispatcher::CheckContextAccessToExtensionAPI( 1492 const std::string& function_name, ChromeV8Context* context) const { 1493 if (!context) { 1494 DLOG(ERROR) << "Not in a v8::Context"; 1495 return false; 1496 } 1497 1498 if (!context->extension()) { 1499 v8::ThrowException( 1500 v8::Exception::Error(v8::String::New("Not in an extension."))); 1501 return false; 1502 } 1503 1504 // Theoretically we could end up with bindings being injected into sandboxed 1505 // frames, for example content scripts. Don't let them execute API functions. 1506 WebKit::WebFrame* frame = context->web_frame(); 1507 if (IsSandboxedPage(UserScriptSlave::GetDataSourceURLForFrame(frame))) { 1508 static const char kMessage[] = 1509 "%s cannot be used within a sandboxed frame."; 1510 std::string error_msg = base::StringPrintf(kMessage, function_name.c_str()); 1511 v8::ThrowException( 1512 v8::Exception::Error(v8::String::New(error_msg.c_str()))); 1513 return false; 1514 } 1515 1516 Feature::Availability availability = context->GetAvailability(function_name); 1517 if (!availability.is_available()) { 1518 APIActivityLogger::LogBlockedCall(context->extension()->id(), 1519 function_name, 1520 availability.result()); 1521 v8::ThrowException(v8::Exception::Error( 1522 v8::String::New(availability.message().c_str()))); 1523 } 1524 1525 return availability.is_available(); 1526 } 1527 1528 void Dispatcher::DispatchEvent(const std::string& extension_id, 1529 const std::string& event_name) const { 1530 base::ListValue args; 1531 args.Set(0, new base::StringValue(event_name)); 1532 args.Set(1, new base::ListValue()); 1533 v8_context_set_.ForEach( 1534 extension_id, 1535 NULL, // all render views 1536 base::Bind(&CallModuleMethod, 1537 kEventModule, 1538 kEventDispatchFunction, 1539 &args)); 1540 } 1541 1542 void Dispatcher::InvokeModuleSystemMethod( 1543 content::RenderView* render_view, 1544 const std::string& extension_id, 1545 const std::string& module_name, 1546 const std::string& function_name, 1547 const base::ListValue& args, 1548 bool user_gesture) { 1549 scoped_ptr<WebScopedUserGesture> web_user_gesture; 1550 if (user_gesture) 1551 web_user_gesture.reset(new WebScopedUserGesture); 1552 1553 v8_context_set_.ForEach( 1554 extension_id, 1555 render_view, 1556 base::Bind(&CallModuleMethod, module_name, function_name, &args)); 1557 1558 // Reset the idle handler each time there's any activity like event or message 1559 // dispatch, for which Invoke is the chokepoint. 1560 if (is_extension_process_) { 1561 RenderThread::Get()->ScheduleIdleHandler( 1562 kInitialExtensionIdleHandlerDelayMs); 1563 } 1564 1565 // Tell the browser process when an event has been dispatched with a lazy 1566 // background page active. 1567 const Extension* extension = extensions_.GetByID(extension_id); 1568 if (extension && BackgroundInfo::HasLazyBackgroundPage(extension) && 1569 module_name == kEventModule && 1570 function_name == kEventDispatchFunction) { 1571 RenderView* background_view = 1572 ExtensionHelper::GetBackgroundPage(extension_id); 1573 if (background_view) { 1574 background_view->Send(new ExtensionHostMsg_EventAck( 1575 background_view->GetRoutingID())); 1576 } 1577 } 1578 } 1579 1580 } // namespace extensions 1581