1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "extensions/browser/api/runtime/runtime_api.h" 6 7 #include <utility> 8 9 #include "base/lazy_instance.h" 10 #include "base/logging.h" 11 #include "base/memory/scoped_ptr.h" 12 #include "base/metrics/histogram.h" 13 #include "base/values.h" 14 #include "base/version.h" 15 #include "chrome/browser/chrome_notification_types.h" 16 #include "content/public/browser/browser_context.h" 17 #include "content/public/browser/child_process_security_policy.h" 18 #include "content/public/browser/notification_service.h" 19 #include "content/public/browser/render_process_host.h" 20 #include "content/public/browser/render_view_host.h" 21 #include "extensions/browser/api/runtime/runtime_api_delegate.h" 22 #include "extensions/browser/event_router.h" 23 #include "extensions/browser/extension_host.h" 24 #include "extensions/browser/extension_prefs.h" 25 #include "extensions/browser/extension_registry.h" 26 #include "extensions/browser/extension_system.h" 27 #include "extensions/browser/extension_util.h" 28 #include "extensions/browser/extensions_browser_client.h" 29 #include "extensions/browser/lazy_background_task_queue.h" 30 #include "extensions/browser/process_manager.h" 31 #include "extensions/common/api/runtime.h" 32 #include "extensions/common/error_utils.h" 33 #include "extensions/common/extension.h" 34 #include "extensions/common/manifest_handlers/background_info.h" 35 #include "extensions/common/manifest_handlers/shared_module_info.h" 36 #include "url/gurl.h" 37 #include "webkit/browser/fileapi/isolated_context.h" 38 39 using content::BrowserContext; 40 41 namespace extensions { 42 43 namespace runtime = core_api::runtime; 44 45 namespace { 46 47 const char kNoBackgroundPageError[] = "You do not have a background page."; 48 const char kPageLoadError[] = "Background page failed to load."; 49 const char kInstallId[] = "id"; 50 const char kInstallReason[] = "reason"; 51 const char kInstallReasonChromeUpdate[] = "chrome_update"; 52 const char kInstallReasonUpdate[] = "update"; 53 const char kInstallReasonInstall[] = "install"; 54 const char kInstallReasonSharedModuleUpdate[] = "shared_module_update"; 55 const char kInstallPreviousVersion[] = "previousVersion"; 56 const char kInvalidUrlError[] = "Invalid URL."; 57 const char kPlatformInfoUnavailable[] = "Platform information unavailable."; 58 59 const char kUpdatesDisabledError[] = "Autoupdate is not enabled."; 60 61 // A preference key storing the url loaded when an extension is uninstalled. 62 const char kUninstallUrl[] = "uninstall_url"; 63 64 // The name of the directory to be returned by getPackageDirectoryEntry. This 65 // particular value does not matter to user code, but is chosen for consistency 66 // with the equivalent Pepper API. 67 const char kPackageDirectoryPath[] = "crxfs"; 68 69 void DispatchOnStartupEventImpl(BrowserContext* browser_context, 70 const std::string& extension_id, 71 bool first_call, 72 ExtensionHost* host) { 73 // A NULL host from the LazyBackgroundTaskQueue means the page failed to 74 // load. Give up. 75 if (!host && !first_call) 76 return; 77 78 // Don't send onStartup events to incognito browser contexts. 79 if (browser_context->IsOffTheRecord()) 80 return; 81 82 if (ExtensionsBrowserClient::Get()->IsShuttingDown() || 83 !ExtensionsBrowserClient::Get()->IsValidContext(browser_context)) 84 return; 85 ExtensionSystem* system = ExtensionSystem::Get(browser_context); 86 if (!system) 87 return; 88 89 // If this is a persistent background page, we want to wait for it to load 90 // (it might not be ready, since this is startup). But only enqueue once. 91 // If it fails to load the first time, don't bother trying again. 92 const Extension* extension = 93 ExtensionRegistry::Get(browser_context)->enabled_extensions().GetByID( 94 extension_id); 95 if (extension && BackgroundInfo::HasPersistentBackgroundPage(extension) && 96 first_call && 97 system->lazy_background_task_queue()->ShouldEnqueueTask(browser_context, 98 extension)) { 99 system->lazy_background_task_queue()->AddPendingTask( 100 browser_context, 101 extension_id, 102 base::Bind( 103 &DispatchOnStartupEventImpl, browser_context, extension_id, false)); 104 return; 105 } 106 107 scoped_ptr<base::ListValue> event_args(new base::ListValue()); 108 scoped_ptr<Event> event( 109 new Event(runtime::OnStartup::kEventName, event_args.Pass())); 110 system->event_router()->DispatchEventToExtension(extension_id, event.Pass()); 111 } 112 113 void SetUninstallURL(ExtensionPrefs* prefs, 114 const std::string& extension_id, 115 const std::string& url_string) { 116 prefs->UpdateExtensionPref( 117 extension_id, kUninstallUrl, new base::StringValue(url_string)); 118 } 119 120 #if defined(ENABLE_EXTENSIONS) 121 std::string GetUninstallURL(ExtensionPrefs* prefs, 122 const std::string& extension_id) { 123 std::string url_string; 124 prefs->ReadPrefAsString(extension_id, kUninstallUrl, &url_string); 125 return url_string; 126 } 127 #endif // defined(ENABLE_EXTENSIONS) 128 129 } // namespace 130 131 /////////////////////////////////////////////////////////////////////////////// 132 133 static base::LazyInstance<BrowserContextKeyedAPIFactory<RuntimeAPI> > 134 g_factory = LAZY_INSTANCE_INITIALIZER; 135 136 // static 137 BrowserContextKeyedAPIFactory<RuntimeAPI>* RuntimeAPI::GetFactoryInstance() { 138 return g_factory.Pointer(); 139 } 140 141 RuntimeAPI::RuntimeAPI(content::BrowserContext* context) 142 : browser_context_(context), dispatch_chrome_updated_event_(false) { 143 registrar_.Add(this, 144 chrome::NOTIFICATION_EXTENSIONS_READY, 145 content::Source<BrowserContext>(context)); 146 registrar_.Add(this, 147 chrome::NOTIFICATION_EXTENSION_LOADED_DEPRECATED, 148 content::Source<BrowserContext>(context)); 149 registrar_.Add(this, 150 chrome::NOTIFICATION_EXTENSION_INSTALLED_DEPRECATED, 151 content::Source<BrowserContext>(context)); 152 registrar_.Add(this, 153 chrome::NOTIFICATION_EXTENSION_UNINSTALLED_DEPRECATED, 154 content::Source<BrowserContext>(context)); 155 156 delegate_ = ExtensionsBrowserClient::Get()->CreateRuntimeAPIDelegate( 157 browser_context_); 158 159 // Check if registered events are up-to-date. We can only do this once 160 // per browser context, since it updates internal state when called. 161 dispatch_chrome_updated_event_ = 162 ExtensionsBrowserClient::Get()->DidVersionUpdate(browser_context_); 163 } 164 165 RuntimeAPI::~RuntimeAPI() { 166 delegate_->RemoveUpdateObserver(this); 167 } 168 169 void RuntimeAPI::Observe(int type, 170 const content::NotificationSource& source, 171 const content::NotificationDetails& details) { 172 switch (type) { 173 case chrome::NOTIFICATION_EXTENSIONS_READY: { 174 OnExtensionsReady(); 175 break; 176 } 177 case chrome::NOTIFICATION_EXTENSION_LOADED_DEPRECATED: { 178 const Extension* extension = 179 content::Details<const Extension>(details).ptr(); 180 OnExtensionLoaded(extension); 181 break; 182 } 183 case chrome::NOTIFICATION_EXTENSION_INSTALLED_DEPRECATED: { 184 const Extension* extension = 185 content::Details<const InstalledExtensionInfo>(details)->extension; 186 OnExtensionInstalled(extension); 187 break; 188 } 189 case chrome::NOTIFICATION_EXTENSION_UNINSTALLED_DEPRECATED: { 190 const Extension* extension = 191 content::Details<const Extension>(details).ptr(); 192 OnExtensionUninstalled(extension); 193 break; 194 } 195 default: 196 NOTREACHED(); 197 break; 198 } 199 } 200 201 void RuntimeAPI::OnExtensionsReady() { 202 // We're done restarting Chrome after an update. 203 dispatch_chrome_updated_event_ = false; 204 205 delegate_->AddUpdateObserver(this); 206 207 // RuntimeAPI is redirected in incognito, so |browser_context_| is never 208 // incognito. We don't observe incognito ProcessManagers but that is OK 209 // because we don't send onStartup events to incognito browser contexts. 210 DCHECK(!browser_context_->IsOffTheRecord()); 211 // Some tests use partially constructed Profiles without a process manager. 212 ExtensionSystem* extension_system = ExtensionSystem::Get(browser_context_); 213 if (extension_system->process_manager()) 214 extension_system->process_manager()->AddObserver(this); 215 } 216 217 void RuntimeAPI::OnExtensionLoaded(const Extension* extension) { 218 if (!dispatch_chrome_updated_event_) 219 return; 220 221 // Dispatch the onInstalled event with reason "chrome_update". 222 base::MessageLoop::current()->PostTask( 223 FROM_HERE, 224 base::Bind(&RuntimeEventRouter::DispatchOnInstalledEvent, 225 browser_context_, 226 extension->id(), 227 Version(), 228 true)); 229 } 230 231 void RuntimeAPI::OnExtensionInstalled(const Extension* extension) { 232 // Ephemeral apps are not considered to be installed and do not receive 233 // the onInstalled() event. 234 if (util::IsEphemeralApp(extension->id(), browser_context_)) 235 return; 236 237 Version old_version = delegate_->GetPreviousExtensionVersion(extension); 238 239 // Dispatch the onInstalled event. 240 base::MessageLoop::current()->PostTask( 241 FROM_HERE, 242 base::Bind(&RuntimeEventRouter::DispatchOnInstalledEvent, 243 browser_context_, 244 extension->id(), 245 old_version, 246 false)); 247 } 248 249 void RuntimeAPI::OnExtensionUninstalled(const Extension* extension) { 250 // Ephemeral apps are not considered to be installed, so the uninstall URL 251 // is not invoked when they are removed. 252 if (util::IsEphemeralApp(extension->id(), browser_context_)) 253 return; 254 255 RuntimeEventRouter::OnExtensionUninstalled(browser_context_, extension->id()); 256 } 257 258 void RuntimeAPI::Shutdown() { 259 // ExtensionSystem deletes its ProcessManager during the Shutdown() phase, so 260 // the observer must be removed here and not in the RuntimeAPI destructor. 261 ProcessManager* process_manager = 262 ExtensionSystem::Get(browser_context_)->process_manager(); 263 // Some tests use partially constructed Profiles without a process manager. 264 if (process_manager) 265 process_manager->RemoveObserver(this); 266 } 267 268 void RuntimeAPI::OnAppUpdateAvailable(const Extension* extension) { 269 RuntimeEventRouter::DispatchOnUpdateAvailableEvent( 270 browser_context_, extension->id(), extension->manifest()->value()); 271 } 272 273 void RuntimeAPI::OnChromeUpdateAvailable() { 274 RuntimeEventRouter::DispatchOnBrowserUpdateAvailableEvent(browser_context_); 275 } 276 277 void RuntimeAPI::OnBackgroundHostStartup(const Extension* extension) { 278 RuntimeEventRouter::DispatchOnStartupEvent(browser_context_, extension->id()); 279 } 280 281 void RuntimeAPI::ReloadExtension(const std::string& extension_id) { 282 delegate_->ReloadExtension(extension_id); 283 } 284 285 bool RuntimeAPI::CheckForUpdates( 286 const std::string& extension_id, 287 const RuntimeAPIDelegate::UpdateCheckCallback& callback) { 288 return delegate_->CheckForUpdates(extension_id, callback); 289 } 290 291 void RuntimeAPI::OpenURL(const GURL& update_url) { 292 delegate_->OpenURL(update_url); 293 } 294 295 bool RuntimeAPI::GetPlatformInfo(runtime::PlatformInfo* info) { 296 return delegate_->GetPlatformInfo(info); 297 } 298 299 bool RuntimeAPI::RestartDevice(std::string* error_message) { 300 return delegate_->RestartDevice(error_message); 301 } 302 303 /////////////////////////////////////////////////////////////////////////////// 304 305 // static 306 void RuntimeEventRouter::DispatchOnStartupEvent( 307 content::BrowserContext* context, 308 const std::string& extension_id) { 309 DispatchOnStartupEventImpl(context, extension_id, true, NULL); 310 } 311 312 // static 313 void RuntimeEventRouter::DispatchOnInstalledEvent( 314 content::BrowserContext* context, 315 const std::string& extension_id, 316 const Version& old_version, 317 bool chrome_updated) { 318 if (!ExtensionsBrowserClient::Get()->IsValidContext(context)) 319 return; 320 ExtensionSystem* system = ExtensionSystem::Get(context); 321 if (!system) 322 return; 323 324 scoped_ptr<base::ListValue> event_args(new base::ListValue()); 325 base::DictionaryValue* info = new base::DictionaryValue(); 326 event_args->Append(info); 327 if (old_version.IsValid()) { 328 info->SetString(kInstallReason, kInstallReasonUpdate); 329 info->SetString(kInstallPreviousVersion, old_version.GetString()); 330 } else if (chrome_updated) { 331 info->SetString(kInstallReason, kInstallReasonChromeUpdate); 332 } else { 333 info->SetString(kInstallReason, kInstallReasonInstall); 334 } 335 DCHECK(system->event_router()); 336 scoped_ptr<Event> event( 337 new Event(runtime::OnInstalled::kEventName, event_args.Pass())); 338 system->event_router()->DispatchEventWithLazyListener(extension_id, 339 event.Pass()); 340 341 if (old_version.IsValid()) { 342 const Extension* extension = 343 ExtensionRegistry::Get(context)->enabled_extensions().GetByID( 344 extension_id); 345 if (extension && SharedModuleInfo::IsSharedModule(extension)) { 346 scoped_ptr<ExtensionSet> dependents = 347 system->GetDependentExtensions(extension); 348 for (ExtensionSet::const_iterator i = dependents->begin(); 349 i != dependents->end(); 350 i++) { 351 scoped_ptr<base::ListValue> sm_event_args(new base::ListValue()); 352 base::DictionaryValue* sm_info = new base::DictionaryValue(); 353 sm_event_args->Append(sm_info); 354 sm_info->SetString(kInstallReason, kInstallReasonSharedModuleUpdate); 355 sm_info->SetString(kInstallPreviousVersion, old_version.GetString()); 356 sm_info->SetString(kInstallId, extension_id); 357 scoped_ptr<Event> sm_event( 358 new Event(runtime::OnInstalled::kEventName, sm_event_args.Pass())); 359 system->event_router()->DispatchEventWithLazyListener((*i)->id(), 360 sm_event.Pass()); 361 } 362 } 363 } 364 } 365 366 // static 367 void RuntimeEventRouter::DispatchOnUpdateAvailableEvent( 368 content::BrowserContext* context, 369 const std::string& extension_id, 370 const base::DictionaryValue* manifest) { 371 ExtensionSystem* system = ExtensionSystem::Get(context); 372 if (!system) 373 return; 374 375 scoped_ptr<base::ListValue> args(new base::ListValue); 376 args->Append(manifest->DeepCopy()); 377 DCHECK(system->event_router()); 378 scoped_ptr<Event> event( 379 new Event(runtime::OnUpdateAvailable::kEventName, args.Pass())); 380 system->event_router()->DispatchEventToExtension(extension_id, event.Pass()); 381 } 382 383 // static 384 void RuntimeEventRouter::DispatchOnBrowserUpdateAvailableEvent( 385 content::BrowserContext* context) { 386 ExtensionSystem* system = ExtensionSystem::Get(context); 387 if (!system) 388 return; 389 390 scoped_ptr<base::ListValue> args(new base::ListValue); 391 DCHECK(system->event_router()); 392 scoped_ptr<Event> event( 393 new Event(runtime::OnBrowserUpdateAvailable::kEventName, args.Pass())); 394 system->event_router()->BroadcastEvent(event.Pass()); 395 } 396 397 // static 398 void RuntimeEventRouter::DispatchOnRestartRequiredEvent( 399 content::BrowserContext* context, 400 const std::string& app_id, 401 core_api::runtime::OnRestartRequired::Reason reason) { 402 ExtensionSystem* system = ExtensionSystem::Get(context); 403 if (!system) 404 return; 405 406 scoped_ptr<Event> event( 407 new Event(runtime::OnRestartRequired::kEventName, 408 core_api::runtime::OnRestartRequired::Create(reason))); 409 410 DCHECK(system->event_router()); 411 system->event_router()->DispatchEventToExtension(app_id, event.Pass()); 412 } 413 414 // static 415 void RuntimeEventRouter::OnExtensionUninstalled( 416 content::BrowserContext* context, 417 const std::string& extension_id) { 418 #if defined(ENABLE_EXTENSIONS) 419 GURL uninstall_url( 420 GetUninstallURL(ExtensionPrefs::Get(context), extension_id)); 421 422 if (uninstall_url.is_empty()) 423 return; 424 425 RuntimeAPI::GetFactoryInstance()->Get(context)->OpenURL(uninstall_url); 426 #endif // defined(ENABLE_EXTENSIONS) 427 } 428 429 ExtensionFunction::ResponseAction RuntimeGetBackgroundPageFunction::Run() { 430 ExtensionSystem* system = ExtensionSystem::Get(browser_context()); 431 ExtensionHost* host = 432 system->process_manager()->GetBackgroundHostForExtension(extension_id()); 433 if (system->lazy_background_task_queue()->ShouldEnqueueTask(browser_context(), 434 GetExtension())) { 435 system->lazy_background_task_queue()->AddPendingTask( 436 browser_context(), 437 extension_id(), 438 base::Bind(&RuntimeGetBackgroundPageFunction::OnPageLoaded, this)); 439 } else if (host) { 440 OnPageLoaded(host); 441 } else { 442 return RespondNow(Error(kNoBackgroundPageError)); 443 } 444 445 return RespondLater(); 446 } 447 448 void RuntimeGetBackgroundPageFunction::OnPageLoaded(ExtensionHost* host) { 449 if (host) { 450 Respond(NoArguments()); 451 } else { 452 Respond(Error(kPageLoadError)); 453 } 454 } 455 456 ExtensionFunction::ResponseAction RuntimeSetUninstallURLFunction::Run() { 457 std::string url_string; 458 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &url_string)); 459 460 GURL url(url_string); 461 if (!url.is_valid()) { 462 return RespondNow( 463 Error(ErrorUtils::FormatErrorMessage(kInvalidUrlError, url_string))); 464 } 465 SetUninstallURL( 466 ExtensionPrefs::Get(browser_context()), extension_id(), url_string); 467 return RespondNow(NoArguments()); 468 } 469 470 ExtensionFunction::ResponseAction RuntimeReloadFunction::Run() { 471 RuntimeAPI::GetFactoryInstance()->Get(browser_context())->ReloadExtension( 472 extension_id()); 473 return RespondNow(NoArguments()); 474 } 475 476 ExtensionFunction::ResponseAction RuntimeRequestUpdateCheckFunction::Run() { 477 if (!RuntimeAPI::GetFactoryInstance() 478 ->Get(browser_context()) 479 ->CheckForUpdates( 480 extension_id(), 481 base::Bind(&RuntimeRequestUpdateCheckFunction::CheckComplete, 482 this))) { 483 return RespondNow(Error(kUpdatesDisabledError)); 484 } 485 return RespondLater(); 486 } 487 488 void RuntimeRequestUpdateCheckFunction::CheckComplete( 489 const RuntimeAPIDelegate::UpdateCheckResult& result) { 490 if (result.success) { 491 base::DictionaryValue* details = new base::DictionaryValue; 492 details->SetString("version", result.version); 493 Respond(TwoArguments(new base::StringValue(result.response), details)); 494 } else { 495 // HMM(kalman): Why does !success not imply Error()? 496 Respond(OneArgument(new base::StringValue(result.response))); 497 } 498 } 499 500 ExtensionFunction::ResponseAction RuntimeRestartFunction::Run() { 501 std::string message; 502 bool result = 503 RuntimeAPI::GetFactoryInstance()->Get(browser_context())->RestartDevice( 504 &message); 505 if (!result) { 506 return RespondNow(Error(message)); 507 } 508 return RespondNow(NoArguments()); 509 } 510 511 ExtensionFunction::ResponseAction RuntimeGetPlatformInfoFunction::Run() { 512 runtime::PlatformInfo info; 513 if (!RuntimeAPI::GetFactoryInstance() 514 ->Get(browser_context()) 515 ->GetPlatformInfo(&info)) { 516 return RespondNow(Error(kPlatformInfoUnavailable)); 517 } 518 return RespondNow( 519 ArgumentList(runtime::GetPlatformInfo::Results::Create(info))); 520 } 521 522 ExtensionFunction::ResponseAction 523 RuntimeGetPackageDirectoryEntryFunction::Run() { 524 fileapi::IsolatedContext* isolated_context = 525 fileapi::IsolatedContext::GetInstance(); 526 DCHECK(isolated_context); 527 528 std::string relative_path = kPackageDirectoryPath; 529 base::FilePath path = extension_->path(); 530 std::string filesystem_id = isolated_context->RegisterFileSystemForPath( 531 fileapi::kFileSystemTypeNativeLocal, std::string(), path, &relative_path); 532 533 int renderer_id = render_view_host_->GetProcess()->GetID(); 534 content::ChildProcessSecurityPolicy* policy = 535 content::ChildProcessSecurityPolicy::GetInstance(); 536 policy->GrantReadFileSystem(renderer_id, filesystem_id); 537 base::DictionaryValue* dict = new base::DictionaryValue(); 538 dict->SetString("fileSystemId", filesystem_id); 539 dict->SetString("baseName", relative_path); 540 return RespondNow(OneArgument(dict)); 541 } 542 543 } // namespace extensions 544