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/browser/extensions/api/runtime/runtime_api.h" 6 7 #include <utility> 8 9 #include "base/logging.h" 10 #include "base/memory/scoped_ptr.h" 11 #include "base/values.h" 12 #include "chrome/browser/browser_process.h" 13 #include "chrome/browser/chrome_notification_types.h" 14 #include "chrome/browser/extensions/extension_host.h" 15 #include "chrome/browser/extensions/extension_service.h" 16 #include "chrome/browser/extensions/extension_system.h" 17 #include "chrome/browser/extensions/updater/extension_updater.h" 18 #include "chrome/browser/profiles/profile.h" 19 #include "chrome/browser/profiles/profile_manager.h" 20 #include "chrome/browser/ui/browser_finder.h" 21 #include "chrome/browser/ui/browser_navigator.h" 22 #include "chrome/browser/ui/browser_window.h" 23 #include "chrome/common/extensions/api/runtime.h" 24 #include "chrome/common/omaha_query_params/omaha_query_params.h" 25 #include "content/public/browser/child_process_security_policy.h" 26 #include "content/public/browser/notification_service.h" 27 #include "content/public/browser/render_process_host.h" 28 #include "content/public/browser/render_view_host.h" 29 #include "extensions/browser/event_router.h" 30 #include "extensions/browser/extensions_browser_client.h" 31 #include "extensions/browser/lazy_background_task_queue.h" 32 #include "extensions/browser/process_manager.h" 33 #include "extensions/common/error_utils.h" 34 #include "extensions/common/extension.h" 35 #include "extensions/common/manifest_handlers/background_info.h" 36 #include "url/gurl.h" 37 #include "webkit/browser/fileapi/isolated_context.h" 38 39 #if defined(OS_CHROMEOS) 40 #include "chrome/browser/chromeos/login/user_manager.h" 41 #include "chromeos/dbus/dbus_thread_manager.h" 42 #include "chromeos/dbus/power_manager_client.h" 43 #endif 44 45 using content::BrowserContext; 46 47 namespace GetPlatformInfo = extensions::api::runtime::GetPlatformInfo; 48 49 namespace extensions { 50 51 namespace runtime = api::runtime; 52 53 namespace { 54 55 const char kNoBackgroundPageError[] = "You do not have a background page."; 56 const char kPageLoadError[] = "Background page failed to load."; 57 const char kInstallReason[] = "reason"; 58 const char kInstallReasonChromeUpdate[] = "chrome_update"; 59 const char kInstallReasonUpdate[] = "update"; 60 const char kInstallReasonInstall[] = "install"; 61 const char kInstallPreviousVersion[] = "previousVersion"; 62 const char kInvalidUrlError[] = "Invalid URL."; 63 const char kUpdatesDisabledError[] = "Autoupdate is not enabled."; 64 const char kUpdateFound[] = "update_available"; 65 const char kUpdateNotFound[] = "no_update"; 66 const char kUpdateThrottled[] = "throttled"; 67 68 // A preference key storing the url loaded when an extension is uninstalled. 69 const char kUninstallUrl[] = "uninstall_url"; 70 71 // The name of the directory to be returned by getPackageDirectoryEntry. This 72 // particular value does not matter to user code, but is chosen for consistency 73 // with the equivalent Pepper API. 74 const char kPackageDirectoryPath[] = "crxfs"; 75 76 static void DispatchOnStartupEventImpl( 77 Profile* profile, 78 const std::string& extension_id, 79 bool first_call, 80 ExtensionHost* host) { 81 // A NULL host from the LazyBackgroundTaskQueue means the page failed to 82 // load. Give up. 83 if (!host && !first_call) 84 return; 85 86 // Don't send onStartup events to incognito profiles. 87 if (profile->IsOffTheRecord()) 88 return; 89 90 if (g_browser_process->IsShuttingDown() || 91 !g_browser_process->profile_manager()->IsValidProfile(profile)) 92 return; 93 ExtensionSystem* system = ExtensionSystem::Get(profile); 94 if (!system) 95 return; 96 97 // If this is a persistent background page, we want to wait for it to load 98 // (it might not be ready, since this is startup). But only enqueue once. 99 // If it fails to load the first time, don't bother trying again. 100 const Extension* extension = 101 system->extension_service()->extensions()->GetByID(extension_id); 102 if (extension && BackgroundInfo::HasPersistentBackgroundPage(extension) && 103 first_call && 104 system->lazy_background_task_queue()-> 105 ShouldEnqueueTask(profile, extension)) { 106 system->lazy_background_task_queue()->AddPendingTask( 107 profile, extension_id, 108 base::Bind(&DispatchOnStartupEventImpl, 109 profile, extension_id, false)); 110 return; 111 } 112 113 scoped_ptr<base::ListValue> event_args(new base::ListValue()); 114 scoped_ptr<Event> event(new Event(runtime::OnStartup::kEventName, 115 event_args.Pass())); 116 system->event_router()->DispatchEventToExtension(extension_id, event.Pass()); 117 } 118 119 void SetUninstallUrl(ExtensionPrefs* prefs, 120 const std::string& extension_id, 121 const std::string& url_string) { 122 prefs->UpdateExtensionPref(extension_id, 123 kUninstallUrl, 124 new base::StringValue(url_string)); 125 } 126 127 #if defined(ENABLE_EXTENSIONS) 128 std::string GetUninstallUrl(ExtensionPrefs* prefs, 129 const std::string& extension_id) { 130 std::string url_string; 131 prefs->ReadPrefAsString(extension_id, kUninstallUrl, &url_string); 132 return url_string; 133 } 134 #endif // defined(ENABLE_EXTENSIONS) 135 136 } // namespace 137 138 /////////////////////////////////////////////////////////////////////////////// 139 140 RuntimeAPI::RuntimeAPI(content::BrowserContext* context) 141 : browser_context_(context), 142 dispatch_chrome_updated_event_(false), 143 registered_for_updates_(false) { 144 registrar_.Add(this, chrome::NOTIFICATION_EXTENSIONS_READY, 145 content::Source<BrowserContext>(context)); 146 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED, 147 content::Source<BrowserContext>(context)); 148 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALLED, 149 content::Source<BrowserContext>(context)); 150 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNINSTALLED, 151 content::Source<BrowserContext>(context)); 152 153 // Check if registered events are up-to-date. We can only do this once 154 // per browser context, since it updates internal state when called. 155 dispatch_chrome_updated_event_ = 156 ExtensionsBrowserClient::Get()->DidVersionUpdate(browser_context_); 157 } 158 159 RuntimeAPI::~RuntimeAPI() { 160 if (registered_for_updates_) { 161 ExtensionSystem::GetForBrowserContext(browser_context_)-> 162 extension_service()->RemoveUpdateObserver(this); 163 } 164 } 165 166 void RuntimeAPI::Observe(int type, 167 const content::NotificationSource& source, 168 const content::NotificationDetails& details) { 169 switch (type) { 170 case chrome::NOTIFICATION_EXTENSIONS_READY: { 171 OnExtensionsReady(); 172 break; 173 } 174 case chrome::NOTIFICATION_EXTENSION_LOADED: { 175 const Extension* extension = 176 content::Details<const Extension>(details).ptr(); 177 OnExtensionLoaded(extension); 178 break; 179 } 180 case chrome::NOTIFICATION_EXTENSION_INSTALLED: { 181 const Extension* extension = 182 content::Details<const InstalledExtensionInfo>(details)->extension; 183 OnExtensionInstalled(extension); 184 break; 185 } 186 case chrome::NOTIFICATION_EXTENSION_UNINSTALLED: { 187 const Extension* extension = 188 content::Details<const Extension>(details).ptr(); 189 OnExtensionUninstalled(extension); 190 break; 191 } 192 default: 193 NOTREACHED(); 194 break; 195 } 196 } 197 198 void RuntimeAPI::OnExtensionsReady() { 199 // We're done restarting Chrome after an update. 200 dispatch_chrome_updated_event_ = false; 201 202 registered_for_updates_ = true; 203 204 ExtensionSystem::GetForBrowserContext(browser_context_)->extension_service()-> 205 AddUpdateObserver(this); 206 } 207 208 void RuntimeAPI::OnExtensionLoaded(const Extension* extension) { 209 if (!dispatch_chrome_updated_event_) 210 return; 211 212 // Dispatch the onInstalled event with reason "chrome_update". 213 base::MessageLoop::current()->PostTask( 214 FROM_HERE, 215 base::Bind(&RuntimeEventRouter::DispatchOnInstalledEvent, 216 browser_context_, 217 extension->id(), 218 Version(), 219 true)); 220 } 221 222 void RuntimeAPI::OnExtensionInstalled(const Extension* extension) { 223 // Get the previous version to check if this is an upgrade. 224 ExtensionService* service = ExtensionSystem::GetForBrowserContext( 225 browser_context_)->extension_service(); 226 const Extension* old = service->GetExtensionById(extension->id(), true); 227 Version old_version; 228 if (old) 229 old_version = *old->version(); 230 231 // Dispatch the onInstalled event. 232 base::MessageLoop::current()->PostTask( 233 FROM_HERE, 234 base::Bind(&RuntimeEventRouter::DispatchOnInstalledEvent, 235 browser_context_, 236 extension->id(), 237 old_version, 238 false)); 239 240 } 241 242 void RuntimeAPI::OnExtensionUninstalled(const Extension* extension) { 243 Profile* profile = Profile::FromBrowserContext(browser_context_); 244 RuntimeEventRouter::OnExtensionUninstalled(profile, extension->id()); 245 } 246 247 void RuntimeAPI::OnAppUpdateAvailable(const Extension* extension) { 248 Profile* profile = Profile::FromBrowserContext(browser_context_); 249 RuntimeEventRouter::DispatchOnUpdateAvailableEvent( 250 profile, extension->id(), extension->manifest()->value()); 251 } 252 253 void RuntimeAPI::OnChromeUpdateAvailable() { 254 Profile* profile = Profile::FromBrowserContext(browser_context_); 255 RuntimeEventRouter::DispatchOnBrowserUpdateAvailableEvent(profile); 256 } 257 258 /////////////////////////////////////////////////////////////////////////////// 259 260 // static 261 void RuntimeEventRouter::DispatchOnStartupEvent( 262 content::BrowserContext* context, const std::string& extension_id) { 263 // TODO(jamescook): Convert to BrowserContext all the way down. 264 Profile* profile = static_cast<Profile*>(context); 265 DispatchOnStartupEventImpl(profile, extension_id, true, NULL); 266 } 267 268 // static 269 void RuntimeEventRouter::DispatchOnInstalledEvent( 270 content::BrowserContext* context, 271 const std::string& extension_id, 272 const Version& old_version, 273 bool chrome_updated) { 274 if (!ExtensionsBrowserClient::Get()->IsValidContext(context)) 275 return; 276 ExtensionSystem* system = ExtensionSystem::GetForBrowserContext(context); 277 if (!system) 278 return; 279 280 scoped_ptr<base::ListValue> event_args(new base::ListValue()); 281 base::DictionaryValue* info = new base::DictionaryValue(); 282 event_args->Append(info); 283 if (old_version.IsValid()) { 284 info->SetString(kInstallReason, kInstallReasonUpdate); 285 info->SetString(kInstallPreviousVersion, old_version.GetString()); 286 } else if (chrome_updated) { 287 info->SetString(kInstallReason, kInstallReasonChromeUpdate); 288 } else { 289 info->SetString(kInstallReason, kInstallReasonInstall); 290 } 291 DCHECK(system->event_router()); 292 scoped_ptr<Event> event(new Event(runtime::OnInstalled::kEventName, 293 event_args.Pass())); 294 system->event_router()->DispatchEventWithLazyListener(extension_id, 295 event.Pass()); 296 } 297 298 // static 299 void RuntimeEventRouter::DispatchOnUpdateAvailableEvent( 300 Profile* profile, 301 const std::string& extension_id, 302 const base::DictionaryValue* manifest) { 303 ExtensionSystem* system = ExtensionSystem::Get(profile); 304 if (!system) 305 return; 306 307 scoped_ptr<base::ListValue> args(new base::ListValue); 308 args->Append(manifest->DeepCopy()); 309 DCHECK(system->event_router()); 310 scoped_ptr<Event> event(new Event(runtime::OnUpdateAvailable::kEventName, 311 args.Pass())); 312 system->event_router()->DispatchEventToExtension(extension_id, event.Pass()); 313 } 314 315 // static 316 void RuntimeEventRouter::DispatchOnBrowserUpdateAvailableEvent( 317 Profile* profile) { 318 ExtensionSystem* system = ExtensionSystem::Get(profile); 319 if (!system) 320 return; 321 322 scoped_ptr<base::ListValue> args(new base::ListValue); 323 DCHECK(system->event_router()); 324 scoped_ptr<Event> event(new Event( 325 runtime::OnBrowserUpdateAvailable::kEventName, args.Pass())); 326 system->event_router()->BroadcastEvent(event.Pass()); 327 } 328 329 // static 330 void RuntimeEventRouter::DispatchOnRestartRequiredEvent( 331 Profile* profile, 332 const std::string& app_id, 333 api::runtime::OnRestartRequired::Reason reason) { 334 ExtensionSystem* system = ExtensionSystem::Get(profile); 335 if (!system) 336 return; 337 338 scoped_ptr<Event> event( 339 new Event(runtime::OnRestartRequired::kEventName, 340 api::runtime::OnRestartRequired::Create(reason))); 341 342 DCHECK(system->event_router()); 343 system->event_router()->DispatchEventToExtension(app_id, event.Pass()); 344 } 345 346 // static 347 void RuntimeEventRouter::OnExtensionUninstalled( 348 Profile* profile, 349 const std::string& extension_id) { 350 #if defined(ENABLE_EXTENSIONS) 351 GURL uninstall_url(GetUninstallUrl(ExtensionPrefs::Get(profile), 352 extension_id)); 353 354 if (uninstall_url.is_empty()) 355 return; 356 357 Browser* browser = chrome::FindLastActiveWithProfile(profile, 358 chrome::GetActiveDesktop()); 359 if (!browser) 360 browser = new Browser(Browser::CreateParams(profile, 361 chrome::GetActiveDesktop())); 362 363 chrome::NavigateParams params(browser, uninstall_url, 364 content::PAGE_TRANSITION_CLIENT_REDIRECT); 365 params.disposition = NEW_FOREGROUND_TAB; 366 params.user_gesture = false; 367 chrome::Navigate(¶ms); 368 #endif // defined(ENABLE_EXTENSIONS) 369 } 370 371 bool RuntimeGetBackgroundPageFunction::RunImpl() { 372 ExtensionSystem* system = ExtensionSystem::Get(GetProfile()); 373 ExtensionHost* host = system->process_manager()-> 374 GetBackgroundHostForExtension(extension_id()); 375 if (system->lazy_background_task_queue()->ShouldEnqueueTask(GetProfile(), 376 GetExtension())) { 377 system->lazy_background_task_queue()->AddPendingTask( 378 GetProfile(), 379 extension_id(), 380 base::Bind(&RuntimeGetBackgroundPageFunction::OnPageLoaded, this)); 381 } else if (host) { 382 OnPageLoaded(host); 383 } else { 384 error_ = kNoBackgroundPageError; 385 return false; 386 } 387 388 return true; 389 } 390 391 void RuntimeGetBackgroundPageFunction::OnPageLoaded(ExtensionHost* host) { 392 if (host) { 393 SendResponse(true); 394 } else { 395 error_ = kPageLoadError; 396 SendResponse(false); 397 } 398 } 399 400 bool RuntimeSetUninstallUrlFunction::RunImpl() { 401 std::string url_string; 402 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &url_string)); 403 404 GURL url(url_string); 405 if (!url.is_valid()) { 406 error_ = ErrorUtils::FormatErrorMessage(kInvalidUrlError, url_string); 407 return false; 408 } 409 410 SetUninstallUrl( 411 ExtensionPrefs::Get(GetProfile()), extension_id(), url_string); 412 return true; 413 } 414 415 bool RuntimeReloadFunction::RunImpl() { 416 // We can't call ReloadExtension directly, since when this method finishes 417 // it tries to decrease the reference count for the extension, which fails 418 // if the extension has already been reloaded; so instead we post a task. 419 base::MessageLoop::current()->PostTask( 420 FROM_HERE, 421 base::Bind(&ExtensionService::ReloadExtension, 422 GetProfile()->GetExtensionService()->AsWeakPtr(), 423 extension_id())); 424 return true; 425 } 426 427 RuntimeRequestUpdateCheckFunction::RuntimeRequestUpdateCheckFunction() { 428 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UPDATE_FOUND, 429 content::NotificationService::AllSources()); 430 } 431 432 bool RuntimeRequestUpdateCheckFunction::RunImpl() { 433 ExtensionSystem* system = ExtensionSystem::Get(GetProfile()); 434 ExtensionService* service = system->extension_service(); 435 ExtensionUpdater* updater = service->updater(); 436 if (!updater) { 437 error_ = kUpdatesDisabledError; 438 return false; 439 } 440 441 did_reply_ = false; 442 if (!updater->CheckExtensionSoon(extension_id(), base::Bind( 443 &RuntimeRequestUpdateCheckFunction::CheckComplete, this))) { 444 did_reply_ = true; 445 SetResult(new base::StringValue(kUpdateThrottled)); 446 SendResponse(true); 447 } 448 return true; 449 } 450 451 void RuntimeRequestUpdateCheckFunction::CheckComplete() { 452 if (did_reply_) 453 return; 454 455 did_reply_ = true; 456 457 // Since no UPDATE_FOUND notification was seen, this generally would mean 458 // that no update is found, but a previous update check might have already 459 // queued up an update, so check for that here to make sure we return the 460 // right value. 461 ExtensionSystem* system = ExtensionSystem::Get(GetProfile()); 462 ExtensionService* service = system->extension_service(); 463 const Extension* update = service->GetPendingExtensionUpdate(extension_id()); 464 if (update) { 465 ReplyUpdateFound(update->VersionString()); 466 } else { 467 SetResult(new base::StringValue(kUpdateNotFound)); 468 } 469 SendResponse(true); 470 } 471 472 void RuntimeRequestUpdateCheckFunction::Observe( 473 int type, 474 const content::NotificationSource& source, 475 const content::NotificationDetails& details) { 476 if (did_reply_) 477 return; 478 479 DCHECK(type == chrome::NOTIFICATION_EXTENSION_UPDATE_FOUND); 480 typedef const std::pair<std::string, Version> UpdateDetails; 481 const std::string& id = content::Details<UpdateDetails>(details)->first; 482 const Version& version = content::Details<UpdateDetails>(details)->second; 483 if (id == extension_id()) { 484 ReplyUpdateFound(version.GetString()); 485 } 486 } 487 488 void RuntimeRequestUpdateCheckFunction::ReplyUpdateFound( 489 const std::string& version) { 490 did_reply_ = true; 491 results_.reset(new base::ListValue); 492 results_->AppendString(kUpdateFound); 493 base::DictionaryValue* details = new base::DictionaryValue; 494 results_->Append(details); 495 details->SetString("version", version); 496 SendResponse(true); 497 } 498 499 bool RuntimeRestartFunction::RunImpl() { 500 #if defined(OS_CHROMEOS) 501 if (chromeos::UserManager::Get()->IsLoggedInAsKioskApp()) { 502 chromeos::DBusThreadManager::Get() 503 ->GetPowerManagerClient() 504 ->RequestRestart(); 505 return true; 506 } 507 #endif 508 SetError("Function available only for ChromeOS kiosk mode."); 509 return false; 510 } 511 512 bool RuntimeGetPlatformInfoFunction::RunImpl() { 513 GetPlatformInfo::Results::PlatformInfo info; 514 515 const char* os = chrome::OmahaQueryParams::getOS(); 516 if (strcmp(os, "mac") == 0) { 517 info.os = GetPlatformInfo::Results::PlatformInfo::OS_MAC_; 518 } else if (strcmp(os, "win") == 0) { 519 info.os = GetPlatformInfo::Results::PlatformInfo::OS_WIN_; 520 } else if (strcmp(os, "android") == 0) { 521 info.os = GetPlatformInfo::Results::PlatformInfo::OS_ANDROID_; 522 } else if (strcmp(os, "cros") == 0) { 523 info.os = GetPlatformInfo::Results::PlatformInfo::OS_CROS_; 524 } else if (strcmp(os, "linux") == 0) { 525 info.os = GetPlatformInfo::Results::PlatformInfo::OS_LINUX_; 526 } else if (strcmp(os, "openbsd") == 0) { 527 info.os = GetPlatformInfo::Results::PlatformInfo::OS_OPENBSD_; 528 } else { 529 NOTREACHED(); 530 return false; 531 } 532 533 const char* arch = chrome::OmahaQueryParams::getArch(); 534 if (strcmp(arch, "arm") == 0) { 535 info.arch = GetPlatformInfo::Results::PlatformInfo::ARCH_ARM; 536 } else if (strcmp(arch, "x86") == 0) { 537 info.arch = GetPlatformInfo::Results::PlatformInfo::ARCH_X86_32; 538 } else if (strcmp(arch, "x64") == 0) { 539 info.arch = GetPlatformInfo::Results::PlatformInfo::ARCH_X86_64; 540 } else { 541 NOTREACHED(); 542 return false; 543 } 544 545 const char* nacl_arch = chrome::OmahaQueryParams::getNaclArch(); 546 if (strcmp(nacl_arch, "arm") == 0) { 547 info.nacl_arch = GetPlatformInfo::Results::PlatformInfo::NACL_ARCH_ARM; 548 } else if (strcmp(nacl_arch, "x86-32") == 0) { 549 info.nacl_arch = GetPlatformInfo::Results::PlatformInfo::NACL_ARCH_X86_32; 550 } else if (strcmp(nacl_arch, "x86-64") == 0) { 551 info.nacl_arch = GetPlatformInfo::Results::PlatformInfo::NACL_ARCH_X86_64; 552 } else { 553 NOTREACHED(); 554 return false; 555 } 556 557 results_ = GetPlatformInfo::Results::Create(info); 558 return true; 559 } 560 561 bool RuntimeGetPackageDirectoryEntryFunction::RunImpl() { 562 fileapi::IsolatedContext* isolated_context = 563 fileapi::IsolatedContext::GetInstance(); 564 DCHECK(isolated_context); 565 566 std::string relative_path = kPackageDirectoryPath; 567 base::FilePath path = extension_->path(); 568 std::string filesystem_id = isolated_context->RegisterFileSystemForPath( 569 fileapi::kFileSystemTypeNativeLocal, path, &relative_path); 570 571 int renderer_id = render_view_host_->GetProcess()->GetID(); 572 content::ChildProcessSecurityPolicy* policy = 573 content::ChildProcessSecurityPolicy::GetInstance(); 574 policy->GrantReadFileSystem(renderer_id, filesystem_id); 575 base::DictionaryValue* dict = new base::DictionaryValue(); 576 SetResult(dict); 577 dict->SetString("fileSystemId", filesystem_id); 578 dict->SetString("baseName", relative_path); 579 return true; 580 } 581 582 } // namespace extensions 583