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 "content/public/browser/browser_context.h" 16 #include "content/public/browser/child_process_security_policy.h" 17 #include "content/public/browser/notification_service.h" 18 #include "content/public/browser/render_process_host.h" 19 #include "content/public/browser/render_view_host.h" 20 #include "extensions/browser/api/runtime/runtime_api_delegate.h" 21 #include "extensions/browser/event_router.h" 22 #include "extensions/browser/extension_host.h" 23 #include "extensions/browser/extension_prefs.h" 24 #include "extensions/browser/extension_registry.h" 25 #include "extensions/browser/extension_system.h" 26 #include "extensions/browser/extension_util.h" 27 #include "extensions/browser/extensions_browser_client.h" 28 #include "extensions/browser/lazy_background_task_queue.h" 29 #include "extensions/browser/notification_types.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 "storage/browser/fileapi/isolated_context.h" 37 #include "url/gurl.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 std::string GetUninstallURL(ExtensionPrefs* prefs, 121 const std::string& extension_id) { 122 std::string url_string; 123 prefs->ReadPrefAsString(extension_id, kUninstallUrl, &url_string); 124 return url_string; 125 } 126 127 } // namespace 128 129 /////////////////////////////////////////////////////////////////////////////// 130 131 static base::LazyInstance<BrowserContextKeyedAPIFactory<RuntimeAPI> > 132 g_factory = LAZY_INSTANCE_INITIALIZER; 133 134 // static 135 BrowserContextKeyedAPIFactory<RuntimeAPI>* RuntimeAPI::GetFactoryInstance() { 136 return g_factory.Pointer(); 137 } 138 139 RuntimeAPI::RuntimeAPI(content::BrowserContext* context) 140 : browser_context_(context), 141 dispatch_chrome_updated_event_(false), 142 extension_registry_observer_(this) { 143 registrar_.Add(this, 144 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED, 145 content::Source<BrowserContext>(context)); 146 extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_)); 147 148 delegate_ = ExtensionsBrowserClient::Get()->CreateRuntimeAPIDelegate( 149 browser_context_); 150 151 // Check if registered events are up-to-date. We can only do this once 152 // per browser context, since it updates internal state when called. 153 dispatch_chrome_updated_event_ = 154 ExtensionsBrowserClient::Get()->DidVersionUpdate(browser_context_); 155 } 156 157 RuntimeAPI::~RuntimeAPI() { 158 delegate_->RemoveUpdateObserver(this); 159 } 160 161 void RuntimeAPI::Observe(int type, 162 const content::NotificationSource& source, 163 const content::NotificationDetails& details) { 164 DCHECK_EQ(extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED, type); 165 // We're done restarting Chrome after an update. 166 dispatch_chrome_updated_event_ = false; 167 168 delegate_->AddUpdateObserver(this); 169 170 // RuntimeAPI is redirected in incognito, so |browser_context_| is never 171 // incognito. We don't observe incognito ProcessManagers but that is OK 172 // because we don't send onStartup events to incognito browser contexts. 173 DCHECK(!browser_context_->IsOffTheRecord()); 174 // Some tests use partially constructed Profiles without a process manager. 175 ExtensionSystem* extension_system = ExtensionSystem::Get(browser_context_); 176 if (extension_system->process_manager()) 177 extension_system->process_manager()->AddObserver(this); 178 } 179 180 void RuntimeAPI::OnExtensionLoaded(content::BrowserContext* browser_context, 181 const Extension* extension) { 182 if (!dispatch_chrome_updated_event_) 183 return; 184 185 // Dispatch the onInstalled event with reason "chrome_update". 186 base::MessageLoop::current()->PostTask( 187 FROM_HERE, 188 base::Bind(&RuntimeEventRouter::DispatchOnInstalledEvent, 189 browser_context_, 190 extension->id(), 191 Version(), 192 true)); 193 } 194 195 void RuntimeAPI::OnExtensionWillBeInstalled( 196 content::BrowserContext* browser_context, 197 const Extension* extension, 198 bool is_update, 199 bool from_ephemeral, 200 const std::string& old_name) { 201 // Ephemeral apps are not considered to be installed and do not receive 202 // the onInstalled() event. 203 if (util::IsEphemeralApp(extension->id(), browser_context_)) 204 return; 205 206 Version old_version = delegate_->GetPreviousExtensionVersion(extension); 207 208 // Dispatch the onInstalled event. 209 base::MessageLoop::current()->PostTask( 210 FROM_HERE, 211 base::Bind(&RuntimeEventRouter::DispatchOnInstalledEvent, 212 browser_context_, 213 extension->id(), 214 old_version, 215 false)); 216 } 217 218 void RuntimeAPI::OnExtensionUninstalled( 219 content::BrowserContext* browser_context, 220 const Extension* extension, 221 UninstallReason reason) { 222 // Ephemeral apps are not considered to be installed, so the uninstall URL 223 // is not invoked when they are removed. 224 if (util::IsEphemeralApp(extension->id(), browser_context_)) 225 return; 226 227 RuntimeEventRouter::OnExtensionUninstalled( 228 browser_context_, extension->id(), reason); 229 } 230 231 void RuntimeAPI::Shutdown() { 232 // ExtensionSystem deletes its ProcessManager during the Shutdown() phase, so 233 // the observer must be removed here and not in the RuntimeAPI destructor. 234 ProcessManager* process_manager = 235 ExtensionSystem::Get(browser_context_)->process_manager(); 236 // Some tests use partially constructed Profiles without a process manager. 237 if (process_manager) 238 process_manager->RemoveObserver(this); 239 } 240 241 void RuntimeAPI::OnAppUpdateAvailable(const Extension* extension) { 242 RuntimeEventRouter::DispatchOnUpdateAvailableEvent( 243 browser_context_, extension->id(), extension->manifest()->value()); 244 } 245 246 void RuntimeAPI::OnChromeUpdateAvailable() { 247 RuntimeEventRouter::DispatchOnBrowserUpdateAvailableEvent(browser_context_); 248 } 249 250 void RuntimeAPI::OnBackgroundHostStartup(const Extension* extension) { 251 RuntimeEventRouter::DispatchOnStartupEvent(browser_context_, extension->id()); 252 } 253 254 void RuntimeAPI::ReloadExtension(const std::string& extension_id) { 255 delegate_->ReloadExtension(extension_id); 256 } 257 258 bool RuntimeAPI::CheckForUpdates( 259 const std::string& extension_id, 260 const RuntimeAPIDelegate::UpdateCheckCallback& callback) { 261 return delegate_->CheckForUpdates(extension_id, callback); 262 } 263 264 void RuntimeAPI::OpenURL(const GURL& update_url) { 265 delegate_->OpenURL(update_url); 266 } 267 268 bool RuntimeAPI::GetPlatformInfo(runtime::PlatformInfo* info) { 269 return delegate_->GetPlatformInfo(info); 270 } 271 272 bool RuntimeAPI::RestartDevice(std::string* error_message) { 273 return delegate_->RestartDevice(error_message); 274 } 275 276 /////////////////////////////////////////////////////////////////////////////// 277 278 // static 279 void RuntimeEventRouter::DispatchOnStartupEvent( 280 content::BrowserContext* context, 281 const std::string& extension_id) { 282 DispatchOnStartupEventImpl(context, extension_id, true, NULL); 283 } 284 285 // static 286 void RuntimeEventRouter::DispatchOnInstalledEvent( 287 content::BrowserContext* context, 288 const std::string& extension_id, 289 const Version& old_version, 290 bool chrome_updated) { 291 if (!ExtensionsBrowserClient::Get()->IsValidContext(context)) 292 return; 293 ExtensionSystem* system = ExtensionSystem::Get(context); 294 if (!system) 295 return; 296 297 scoped_ptr<base::ListValue> event_args(new base::ListValue()); 298 base::DictionaryValue* info = new base::DictionaryValue(); 299 event_args->Append(info); 300 if (old_version.IsValid()) { 301 info->SetString(kInstallReason, kInstallReasonUpdate); 302 info->SetString(kInstallPreviousVersion, old_version.GetString()); 303 } else if (chrome_updated) { 304 info->SetString(kInstallReason, kInstallReasonChromeUpdate); 305 } else { 306 info->SetString(kInstallReason, kInstallReasonInstall); 307 } 308 DCHECK(system->event_router()); 309 scoped_ptr<Event> event( 310 new Event(runtime::OnInstalled::kEventName, event_args.Pass())); 311 system->event_router()->DispatchEventWithLazyListener(extension_id, 312 event.Pass()); 313 314 if (old_version.IsValid()) { 315 const Extension* extension = 316 ExtensionRegistry::Get(context)->enabled_extensions().GetByID( 317 extension_id); 318 if (extension && SharedModuleInfo::IsSharedModule(extension)) { 319 scoped_ptr<ExtensionSet> dependents = 320 system->GetDependentExtensions(extension); 321 for (ExtensionSet::const_iterator i = dependents->begin(); 322 i != dependents->end(); 323 i++) { 324 scoped_ptr<base::ListValue> sm_event_args(new base::ListValue()); 325 base::DictionaryValue* sm_info = new base::DictionaryValue(); 326 sm_event_args->Append(sm_info); 327 sm_info->SetString(kInstallReason, kInstallReasonSharedModuleUpdate); 328 sm_info->SetString(kInstallPreviousVersion, old_version.GetString()); 329 sm_info->SetString(kInstallId, extension_id); 330 scoped_ptr<Event> sm_event( 331 new Event(runtime::OnInstalled::kEventName, sm_event_args.Pass())); 332 system->event_router()->DispatchEventWithLazyListener((*i)->id(), 333 sm_event.Pass()); 334 } 335 } 336 } 337 } 338 339 // static 340 void RuntimeEventRouter::DispatchOnUpdateAvailableEvent( 341 content::BrowserContext* context, 342 const std::string& extension_id, 343 const base::DictionaryValue* manifest) { 344 ExtensionSystem* system = ExtensionSystem::Get(context); 345 if (!system) 346 return; 347 348 scoped_ptr<base::ListValue> args(new base::ListValue); 349 args->Append(manifest->DeepCopy()); 350 DCHECK(system->event_router()); 351 scoped_ptr<Event> event( 352 new Event(runtime::OnUpdateAvailable::kEventName, args.Pass())); 353 system->event_router()->DispatchEventToExtension(extension_id, event.Pass()); 354 } 355 356 // static 357 void RuntimeEventRouter::DispatchOnBrowserUpdateAvailableEvent( 358 content::BrowserContext* context) { 359 ExtensionSystem* system = ExtensionSystem::Get(context); 360 if (!system) 361 return; 362 363 scoped_ptr<base::ListValue> args(new base::ListValue); 364 DCHECK(system->event_router()); 365 scoped_ptr<Event> event( 366 new Event(runtime::OnBrowserUpdateAvailable::kEventName, args.Pass())); 367 system->event_router()->BroadcastEvent(event.Pass()); 368 } 369 370 // static 371 void RuntimeEventRouter::DispatchOnRestartRequiredEvent( 372 content::BrowserContext* context, 373 const std::string& app_id, 374 core_api::runtime::OnRestartRequired::Reason reason) { 375 ExtensionSystem* system = ExtensionSystem::Get(context); 376 if (!system) 377 return; 378 379 scoped_ptr<Event> event( 380 new Event(runtime::OnRestartRequired::kEventName, 381 core_api::runtime::OnRestartRequired::Create(reason))); 382 383 DCHECK(system->event_router()); 384 system->event_router()->DispatchEventToExtension(app_id, event.Pass()); 385 } 386 387 // static 388 void RuntimeEventRouter::OnExtensionUninstalled( 389 content::BrowserContext* context, 390 const std::string& extension_id, 391 UninstallReason reason) { 392 if (!(reason == UNINSTALL_REASON_USER_INITIATED || 393 reason == UNINSTALL_REASON_MANAGEMENT_API)) { 394 return; 395 } 396 397 GURL uninstall_url( 398 GetUninstallURL(ExtensionPrefs::Get(context), extension_id)); 399 400 if (uninstall_url.is_empty()) 401 return; 402 403 RuntimeAPI::GetFactoryInstance()->Get(context)->OpenURL(uninstall_url); 404 } 405 406 ExtensionFunction::ResponseAction RuntimeGetBackgroundPageFunction::Run() { 407 ExtensionSystem* system = ExtensionSystem::Get(browser_context()); 408 ExtensionHost* host = 409 system->process_manager()->GetBackgroundHostForExtension(extension_id()); 410 if (system->lazy_background_task_queue()->ShouldEnqueueTask(browser_context(), 411 extension())) { 412 system->lazy_background_task_queue()->AddPendingTask( 413 browser_context(), 414 extension_id(), 415 base::Bind(&RuntimeGetBackgroundPageFunction::OnPageLoaded, this)); 416 } else if (host) { 417 OnPageLoaded(host); 418 } else { 419 return RespondNow(Error(kNoBackgroundPageError)); 420 } 421 422 return RespondLater(); 423 } 424 425 void RuntimeGetBackgroundPageFunction::OnPageLoaded(ExtensionHost* host) { 426 if (host) { 427 Respond(NoArguments()); 428 } else { 429 Respond(Error(kPageLoadError)); 430 } 431 } 432 433 ExtensionFunction::ResponseAction RuntimeSetUninstallURLFunction::Run() { 434 std::string url_string; 435 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &url_string)); 436 437 GURL url(url_string); 438 if (!url.is_valid()) { 439 return RespondNow( 440 Error(ErrorUtils::FormatErrorMessage(kInvalidUrlError, url_string))); 441 } 442 SetUninstallURL( 443 ExtensionPrefs::Get(browser_context()), extension_id(), url_string); 444 return RespondNow(NoArguments()); 445 } 446 447 ExtensionFunction::ResponseAction RuntimeReloadFunction::Run() { 448 RuntimeAPI::GetFactoryInstance()->Get(browser_context())->ReloadExtension( 449 extension_id()); 450 return RespondNow(NoArguments()); 451 } 452 453 ExtensionFunction::ResponseAction RuntimeRequestUpdateCheckFunction::Run() { 454 if (!RuntimeAPI::GetFactoryInstance() 455 ->Get(browser_context()) 456 ->CheckForUpdates( 457 extension_id(), 458 base::Bind(&RuntimeRequestUpdateCheckFunction::CheckComplete, 459 this))) { 460 return RespondNow(Error(kUpdatesDisabledError)); 461 } 462 return RespondLater(); 463 } 464 465 void RuntimeRequestUpdateCheckFunction::CheckComplete( 466 const RuntimeAPIDelegate::UpdateCheckResult& result) { 467 if (result.success) { 468 base::DictionaryValue* details = new base::DictionaryValue; 469 details->SetString("version", result.version); 470 Respond(TwoArguments(new base::StringValue(result.response), details)); 471 } else { 472 // HMM(kalman): Why does !success not imply Error()? 473 Respond(OneArgument(new base::StringValue(result.response))); 474 } 475 } 476 477 ExtensionFunction::ResponseAction RuntimeRestartFunction::Run() { 478 std::string message; 479 bool result = 480 RuntimeAPI::GetFactoryInstance()->Get(browser_context())->RestartDevice( 481 &message); 482 if (!result) { 483 return RespondNow(Error(message)); 484 } 485 return RespondNow(NoArguments()); 486 } 487 488 ExtensionFunction::ResponseAction RuntimeGetPlatformInfoFunction::Run() { 489 runtime::PlatformInfo info; 490 if (!RuntimeAPI::GetFactoryInstance() 491 ->Get(browser_context()) 492 ->GetPlatformInfo(&info)) { 493 return RespondNow(Error(kPlatformInfoUnavailable)); 494 } 495 return RespondNow( 496 ArgumentList(runtime::GetPlatformInfo::Results::Create(info))); 497 } 498 499 ExtensionFunction::ResponseAction 500 RuntimeGetPackageDirectoryEntryFunction::Run() { 501 storage::IsolatedContext* isolated_context = 502 storage::IsolatedContext::GetInstance(); 503 DCHECK(isolated_context); 504 505 std::string relative_path = kPackageDirectoryPath; 506 base::FilePath path = extension_->path(); 507 std::string filesystem_id = isolated_context->RegisterFileSystemForPath( 508 storage::kFileSystemTypeNativeLocal, std::string(), path, &relative_path); 509 510 int renderer_id = render_view_host_->GetProcess()->GetID(); 511 content::ChildProcessSecurityPolicy* policy = 512 content::ChildProcessSecurityPolicy::GetInstance(); 513 policy->GrantReadFileSystem(renderer_id, filesystem_id); 514 base::DictionaryValue* dict = new base::DictionaryValue(); 515 dict->SetString("fileSystemId", filesystem_id); 516 dict->SetString("baseName", relative_path); 517 return RespondNow(OneArgument(dict)); 518 } 519 520 } // namespace extensions 521