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 39using content::BrowserContext; 40 41namespace extensions { 42 43namespace runtime = core_api::runtime; 44 45namespace { 46 47const char kNoBackgroundPageError[] = "You do not have a background page."; 48const char kPageLoadError[] = "Background page failed to load."; 49const char kInstallId[] = "id"; 50const char kInstallReason[] = "reason"; 51const char kInstallReasonChromeUpdate[] = "chrome_update"; 52const char kInstallReasonUpdate[] = "update"; 53const char kInstallReasonInstall[] = "install"; 54const char kInstallReasonSharedModuleUpdate[] = "shared_module_update"; 55const char kInstallPreviousVersion[] = "previousVersion"; 56const char kInvalidUrlError[] = "Invalid URL."; 57const char kPlatformInfoUnavailable[] = "Platform information unavailable."; 58 59const char kUpdatesDisabledError[] = "Autoupdate is not enabled."; 60 61// A preference key storing the url loaded when an extension is uninstalled. 62const 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. 67const char kPackageDirectoryPath[] = "crxfs"; 68 69void 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 113void 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) 121std::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 133static base::LazyInstance<BrowserContextKeyedAPIFactory<RuntimeAPI> > 134 g_factory = LAZY_INSTANCE_INITIALIZER; 135 136// static 137BrowserContextKeyedAPIFactory<RuntimeAPI>* RuntimeAPI::GetFactoryInstance() { 138 return g_factory.Pointer(); 139} 140 141RuntimeAPI::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 165RuntimeAPI::~RuntimeAPI() { 166 delegate_->RemoveUpdateObserver(this); 167} 168 169void 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 201void 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 217void 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 231void 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 249void 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 258void 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 268void RuntimeAPI::OnAppUpdateAvailable(const Extension* extension) { 269 RuntimeEventRouter::DispatchOnUpdateAvailableEvent( 270 browser_context_, extension->id(), extension->manifest()->value()); 271} 272 273void RuntimeAPI::OnChromeUpdateAvailable() { 274 RuntimeEventRouter::DispatchOnBrowserUpdateAvailableEvent(browser_context_); 275} 276 277void RuntimeAPI::OnBackgroundHostStartup(const Extension* extension) { 278 RuntimeEventRouter::DispatchOnStartupEvent(browser_context_, extension->id()); 279} 280 281void RuntimeAPI::ReloadExtension(const std::string& extension_id) { 282 delegate_->ReloadExtension(extension_id); 283} 284 285bool RuntimeAPI::CheckForUpdates( 286 const std::string& extension_id, 287 const RuntimeAPIDelegate::UpdateCheckCallback& callback) { 288 return delegate_->CheckForUpdates(extension_id, callback); 289} 290 291void RuntimeAPI::OpenURL(const GURL& update_url) { 292 delegate_->OpenURL(update_url); 293} 294 295bool RuntimeAPI::GetPlatformInfo(runtime::PlatformInfo* info) { 296 return delegate_->GetPlatformInfo(info); 297} 298 299bool RuntimeAPI::RestartDevice(std::string* error_message) { 300 return delegate_->RestartDevice(error_message); 301} 302 303/////////////////////////////////////////////////////////////////////////////// 304 305// static 306void RuntimeEventRouter::DispatchOnStartupEvent( 307 content::BrowserContext* context, 308 const std::string& extension_id) { 309 DispatchOnStartupEventImpl(context, extension_id, true, NULL); 310} 311 312// static 313void 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 367void 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 384void 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 398void 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 415void 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 429ExtensionFunction::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 448void RuntimeGetBackgroundPageFunction::OnPageLoaded(ExtensionHost* host) { 449 if (host) { 450 Respond(NoArguments()); 451 } else { 452 Respond(Error(kPageLoadError)); 453 } 454} 455 456ExtensionFunction::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 470ExtensionFunction::ResponseAction RuntimeReloadFunction::Run() { 471 RuntimeAPI::GetFactoryInstance()->Get(browser_context())->ReloadExtension( 472 extension_id()); 473 return RespondNow(NoArguments()); 474} 475 476ExtensionFunction::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 488void 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 500ExtensionFunction::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 511ExtensionFunction::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 522ExtensionFunction::ResponseAction 523RuntimeGetPackageDirectoryEntryFunction::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