| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "extensions/browser/api/runtime/runtime_api.h" | 5 #include "extensions/browser/api/runtime/runtime_api.h" |
| 6 | 6 |
| 7 #include <memory> | 7 #include <memory> |
| 8 #include <utility> | 8 #include <utility> |
| 9 | 9 |
| 10 #include "base/lazy_instance.h" | 10 #include "base/lazy_instance.h" |
| 11 #include "base/location.h" | 11 #include "base/location.h" |
| 12 #include "base/logging.h" | 12 #include "base/logging.h" |
| 13 #include "base/memory/ptr_util.h" | 13 #include "base/memory/ptr_util.h" |
| 14 #include "base/metrics/histogram.h" | 14 #include "base/metrics/histogram.h" |
| 15 #include "base/single_thread_task_runner.h" | 15 #include "base/single_thread_task_runner.h" |
| 16 #include "base/strings/string_number_conversions.h" | |
| 17 #include "base/threading/thread_task_runner_handle.h" | 16 #include "base/threading/thread_task_runner_handle.h" |
| 18 #include "base/values.h" | 17 #include "base/values.h" |
| 19 #include "base/version.h" | 18 #include "base/version.h" |
| 20 #include "components/prefs/pref_registry_simple.h" | |
| 21 #include "components/prefs/pref_service.h" | |
| 22 #include "content/public/browser/browser_context.h" | 19 #include "content/public/browser/browser_context.h" |
| 23 #include "content/public/browser/child_process_security_policy.h" | 20 #include "content/public/browser/child_process_security_policy.h" |
| 24 #include "content/public/browser/notification_service.h" | 21 #include "content/public/browser/notification_service.h" |
| 25 #include "content/public/browser/render_frame_host.h" | 22 #include "content/public/browser/render_frame_host.h" |
| 26 #include "content/public/browser/render_process_host.h" | 23 #include "content/public/browser/render_process_host.h" |
| 27 #include "extensions/browser/api/runtime/runtime_api_delegate.h" | 24 #include "extensions/browser/api/runtime/runtime_api_delegate.h" |
| 28 #include "extensions/browser/event_router.h" | 25 #include "extensions/browser/event_router.h" |
| 29 #include "extensions/browser/extension_host.h" | 26 #include "extensions/browser/extension_host.h" |
| 30 #include "extensions/browser/extension_prefs.h" | 27 #include "extensions/browser/extension_prefs.h" |
| 31 #include "extensions/browser/extension_registry.h" | 28 #include "extensions/browser/extension_registry.h" |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 76 "pending_on_installed_event_dispatch_info"; | 73 "pending_on_installed_event_dispatch_info"; |
| 77 | 74 |
| 78 // Previously installed version number. | 75 // Previously installed version number. |
| 79 const char kPrefPreviousVersion[] = "previous_version"; | 76 const char kPrefPreviousVersion[] = "previous_version"; |
| 80 | 77 |
| 81 // The name of the directory to be returned by getPackageDirectoryEntry. This | 78 // The name of the directory to be returned by getPackageDirectoryEntry. This |
| 82 // particular value does not matter to user code, but is chosen for consistency | 79 // particular value does not matter to user code, but is chosen for consistency |
| 83 // with the equivalent Pepper API. | 80 // with the equivalent Pepper API. |
| 84 const char kPackageDirectoryPath[] = "crxfs"; | 81 const char kPackageDirectoryPath[] = "crxfs"; |
| 85 | 82 |
| 86 // Preference key for storing the last successful restart due to a call to | |
| 87 // chrome.runtime.restartAfterDelay(). | |
| 88 constexpr char kPrefLastRestartAfterDelayTime[] = | |
| 89 "last_restart_after_delay_time"; | |
| 90 // Preference key for storing whether the most recent restart was due to a | |
| 91 // successful call to chrome.runtime.restartAfterDelay(). | |
| 92 constexpr char kPrefLastRestartWasDueToDelayedRestartApi[] = | |
| 93 "last_restart_was_due_to_delayed_restart_api"; | |
| 94 | |
| 95 // Error and status messages strings for the restartAfterDelay() API. | |
| 96 constexpr char kErrorInvalidArgument[] = "Invalid argument: *."; | |
| 97 constexpr char kErrorOnlyKioskModeAllowed[] = | |
| 98 "API available only for ChromeOS kiosk mode."; | |
| 99 constexpr char kErrorOnlyFirstExtensionAllowed[] = | |
| 100 "Not the first extension to call this API."; | |
| 101 constexpr char kErrorInvalidStatus[] = "Invalid restart request status."; | |
| 102 constexpr char kErrorRequestedTooSoon[] = | |
| 103 "Restart was requested too soon. It was throttled instead."; | |
| 104 | |
| 105 constexpr int kMinDurationBetweenSuccessiveRestartsHours = 3; | |
| 106 | |
| 107 // This is used for unit tests, so that we can test the restartAfterDelay | |
| 108 // API without a kiosk app. | |
| 109 bool allow_non_kiosk_apps_restart_api_for_test = false; | |
| 110 | |
| 111 void DispatchOnStartupEventImpl(BrowserContext* browser_context, | 83 void DispatchOnStartupEventImpl(BrowserContext* browser_context, |
| 112 const std::string& extension_id, | 84 const std::string& extension_id, |
| 113 bool first_call, | 85 bool first_call, |
| 114 ExtensionHost* host) { | 86 ExtensionHost* host) { |
| 115 // A NULL host from the LazyBackgroundTaskQueue means the page failed to | 87 // A NULL host from the LazyBackgroundTaskQueue means the page failed to |
| 116 // load. Give up. | 88 // load. Give up. |
| 117 if (!host && !first_call) | 89 if (!host && !first_call) |
| 118 return; | 90 return; |
| 119 | 91 |
| 120 // Don't send onStartup events to incognito browser contexts. | 92 // Don't send onStartup events to incognito browser contexts. |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 172 /////////////////////////////////////////////////////////////////////////////// | 144 /////////////////////////////////////////////////////////////////////////////// |
| 173 | 145 |
| 174 static base::LazyInstance<BrowserContextKeyedAPIFactory<RuntimeAPI> > | 146 static base::LazyInstance<BrowserContextKeyedAPIFactory<RuntimeAPI> > |
| 175 g_factory = LAZY_INSTANCE_INITIALIZER; | 147 g_factory = LAZY_INSTANCE_INITIALIZER; |
| 176 | 148 |
| 177 // static | 149 // static |
| 178 BrowserContextKeyedAPIFactory<RuntimeAPI>* RuntimeAPI::GetFactoryInstance() { | 150 BrowserContextKeyedAPIFactory<RuntimeAPI>* RuntimeAPI::GetFactoryInstance() { |
| 179 return g_factory.Pointer(); | 151 return g_factory.Pointer(); |
| 180 } | 152 } |
| 181 | 153 |
| 182 // static | |
| 183 void RuntimeAPI::RegisterPrefs(PrefRegistrySimple* registry) { | |
| 184 registry->RegisterBooleanPref(kPrefLastRestartWasDueToDelayedRestartApi, | |
| 185 false); | |
| 186 registry->RegisterDoublePref(kPrefLastRestartAfterDelayTime, 0.0); | |
| 187 } | |
| 188 | |
| 189 template <> | 154 template <> |
| 190 void BrowserContextKeyedAPIFactory<RuntimeAPI>::DeclareFactoryDependencies() { | 155 void BrowserContextKeyedAPIFactory<RuntimeAPI>::DeclareFactoryDependencies() { |
| 191 DependsOn(ProcessManagerFactory::GetInstance()); | 156 DependsOn(ProcessManagerFactory::GetInstance()); |
| 192 } | 157 } |
| 193 | 158 |
| 194 RuntimeAPI::RuntimeAPI(content::BrowserContext* context) | 159 RuntimeAPI::RuntimeAPI(content::BrowserContext* context) |
| 195 : browser_context_(context), | 160 : browser_context_(context), |
| 161 dispatch_chrome_updated_event_(false), |
| 196 extension_registry_observer_(this), | 162 extension_registry_observer_(this), |
| 197 process_manager_observer_(this), | 163 process_manager_observer_(this) { |
| 198 minimum_duration_between_restarts_(base::TimeDelta::FromHours( | |
| 199 kMinDurationBetweenSuccessiveRestartsHours)), | |
| 200 dispatch_chrome_updated_event_(false), | |
| 201 did_read_delayed_restart_preferences_(false), | |
| 202 was_last_restart_due_to_delayed_restart_api_(false), | |
| 203 weak_ptr_factory_(this) { | |
| 204 // RuntimeAPI is redirected in incognito, so |browser_context_| is never | 164 // RuntimeAPI is redirected in incognito, so |browser_context_| is never |
| 205 // incognito. | 165 // incognito. |
| 206 DCHECK(!browser_context_->IsOffTheRecord()); | 166 DCHECK(!browser_context_->IsOffTheRecord()); |
| 207 | 167 |
| 208 registrar_.Add(this, | 168 registrar_.Add(this, |
| 209 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED, | 169 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED, |
| 210 content::Source<BrowserContext>(context)); | 170 content::Source<BrowserContext>(context)); |
| 211 extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_)); | 171 extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_)); |
| 212 process_manager_observer_.Add(ProcessManager::Get(browser_context_)); | 172 process_manager_observer_.Add(ProcessManager::Get(browser_context_)); |
| 213 | 173 |
| (...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 352 | 312 |
| 353 void RuntimeAPI::OpenURL(const GURL& update_url) { | 313 void RuntimeAPI::OpenURL(const GURL& update_url) { |
| 354 delegate_->OpenURL(update_url); | 314 delegate_->OpenURL(update_url); |
| 355 } | 315 } |
| 356 | 316 |
| 357 bool RuntimeAPI::GetPlatformInfo(runtime::PlatformInfo* info) { | 317 bool RuntimeAPI::GetPlatformInfo(runtime::PlatformInfo* info) { |
| 358 return delegate_->GetPlatformInfo(info); | 318 return delegate_->GetPlatformInfo(info); |
| 359 } | 319 } |
| 360 | 320 |
| 361 bool RuntimeAPI::RestartDevice(std::string* error_message) { | 321 bool RuntimeAPI::RestartDevice(std::string* error_message) { |
| 362 if (was_last_restart_due_to_delayed_restart_api_ && | |
| 363 (ExtensionsBrowserClient::Get()->IsRunningInForcedAppMode() || | |
| 364 allow_non_kiosk_apps_restart_api_for_test)) { | |
| 365 // We don't allow an app by calling chrome.runtime.restart() to clear the | |
| 366 // throttle enforced on it when calling chrome.runtime.restartAfterDelay(), | |
| 367 // i.e. the app can't unthrottle itself. | |
| 368 // When running in forced kiosk app mode, we assume the following restart | |
| 369 // request will succeed. | |
| 370 PrefService* pref_service = | |
| 371 ExtensionsBrowserClient::Get()->GetPrefServiceForContext( | |
| 372 browser_context_); | |
| 373 DCHECK(pref_service); | |
| 374 pref_service->SetBoolean(kPrefLastRestartWasDueToDelayedRestartApi, true); | |
| 375 } | |
| 376 return delegate_->RestartDevice(error_message); | 322 return delegate_->RestartDevice(error_message); |
| 377 } | 323 } |
| 378 | 324 |
| 379 RuntimeAPI::RestartAfterDelayStatus RuntimeAPI::RestartDeviceAfterDelay( | |
| 380 const std::string& extension_id, | |
| 381 int seconds_from_now) { | |
| 382 // To achieve as much accuracy as possible, record the time of the call as | |
| 383 // |now| here. | |
| 384 const base::Time now = base::Time::NowFromSystemTime(); | |
| 385 | |
| 386 if (schedule_restart_first_extension_id_.empty()) { | |
| 387 schedule_restart_first_extension_id_ = extension_id; | |
| 388 } else if (extension_id != schedule_restart_first_extension_id_) { | |
| 389 // We only allow the first extension to call this API to call it repeatedly. | |
| 390 // Any other extension will fail. | |
| 391 return RestartAfterDelayStatus::FAILED_NOT_FIRST_EXTENSION; | |
| 392 } | |
| 393 | |
| 394 MaybeCancelRunningDelayedRestartTimer(); | |
| 395 | |
| 396 if (seconds_from_now == -1) { | |
| 397 // We already stopped the running timer (if any). | |
| 398 return RestartAfterDelayStatus::SUCCESS_RESTART_CANCELED; | |
| 399 } | |
| 400 | |
| 401 if (!did_read_delayed_restart_preferences_) { | |
| 402 // Try to read any previous successful restart attempt time resulting from | |
| 403 // this API. | |
| 404 PrefService* pref_service = | |
| 405 ExtensionsBrowserClient::Get()->GetPrefServiceForContext( | |
| 406 browser_context_); | |
| 407 DCHECK(pref_service); | |
| 408 | |
| 409 was_last_restart_due_to_delayed_restart_api_ = | |
| 410 pref_service->GetBoolean(kPrefLastRestartWasDueToDelayedRestartApi); | |
| 411 if (was_last_restart_due_to_delayed_restart_api_) { | |
| 412 // We clear this bit if the previous restart was due to this API, so that | |
| 413 // we don't throttle restart requests coming after other restarts or | |
| 414 // shutdowns not caused by the runtime API. | |
| 415 pref_service->SetBoolean(kPrefLastRestartWasDueToDelayedRestartApi, | |
| 416 false); | |
| 417 } | |
| 418 | |
| 419 last_delayed_restart_time_ = base::Time::FromDoubleT( | |
| 420 pref_service->GetDouble(kPrefLastRestartAfterDelayTime)); | |
| 421 | |
| 422 if (!allow_non_kiosk_apps_restart_api_for_test) { | |
| 423 // Don't read every time unless in tests. | |
| 424 did_read_delayed_restart_preferences_ = true; | |
| 425 } | |
| 426 } | |
| 427 | |
| 428 return ScheduleDelayedRestart(now, seconds_from_now); | |
| 429 } | |
| 430 | |
| 431 bool RuntimeAPI::OpenOptionsPage(const Extension* extension) { | 325 bool RuntimeAPI::OpenOptionsPage(const Extension* extension) { |
| 432 return delegate_->OpenOptionsPage(extension); | 326 return delegate_->OpenOptionsPage(extension); |
| 433 } | 327 } |
| 434 | 328 |
| 435 void RuntimeAPI::MaybeCancelRunningDelayedRestartTimer() { | |
| 436 if (restart_after_delay_timer_.IsRunning()) | |
| 437 restart_after_delay_timer_.Stop(); | |
| 438 } | |
| 439 | |
| 440 RuntimeAPI::RestartAfterDelayStatus RuntimeAPI::ScheduleDelayedRestart( | |
| 441 const base::Time& now, | |
| 442 int seconds_from_now) { | |
| 443 base::TimeDelta delay_till_restart = | |
| 444 base::TimeDelta::FromSeconds(seconds_from_now); | |
| 445 | |
| 446 // Throttle restart requests that are received too soon successively, only if | |
| 447 // the previous restart was due to this API. | |
| 448 bool was_throttled = false; | |
| 449 if (was_last_restart_due_to_delayed_restart_api_) { | |
| 450 base::Time future_restart_time = now + delay_till_restart; | |
| 451 base::TimeDelta delta_since_last_restart = | |
| 452 future_restart_time > last_delayed_restart_time_ | |
| 453 ? future_restart_time - last_delayed_restart_time_ | |
| 454 : base::TimeDelta::Max(); | |
| 455 if (delta_since_last_restart < minimum_duration_between_restarts_) { | |
| 456 // Schedule the restart after |minimum_duration_between_restarts_| has | |
| 457 // passed. | |
| 458 delay_till_restart = minimum_duration_between_restarts_ - | |
| 459 (now - last_delayed_restart_time_); | |
| 460 was_throttled = true; | |
| 461 } | |
| 462 } | |
| 463 | |
| 464 restart_after_delay_timer_.Start( | |
| 465 FROM_HERE, delay_till_restart, | |
| 466 base::Bind(&RuntimeAPI::OnDelayedRestartTimerTimeout, | |
| 467 weak_ptr_factory_.GetWeakPtr())); | |
| 468 | |
| 469 return was_throttled ? RestartAfterDelayStatus::FAILED_THROTTLED | |
| 470 : RestartAfterDelayStatus::SUCCESS_RESTART_SCHEDULED; | |
| 471 } | |
| 472 | |
| 473 void RuntimeAPI::OnDelayedRestartTimerTimeout() { | |
| 474 // We can persist "now" as the last successful restart time, assuming that the | |
| 475 // following restart request will succeed, since it can only fail if requested | |
| 476 // by non kiosk apps, and we prevent that from the beginning (unless in | |
| 477 // unit tests). | |
| 478 // This assumption is important, since once restart is requested, we might not | |
| 479 // have enough time to persist the data to disk. | |
| 480 double now = base::Time::NowFromSystemTime().ToDoubleT(); | |
| 481 PrefService* pref_service = | |
| 482 ExtensionsBrowserClient::Get()->GetPrefServiceForContext( | |
| 483 browser_context_); | |
| 484 DCHECK(pref_service); | |
| 485 pref_service->SetDouble(kPrefLastRestartAfterDelayTime, now); | |
| 486 pref_service->SetBoolean(kPrefLastRestartWasDueToDelayedRestartApi, true); | |
| 487 | |
| 488 std::string error_message; | |
| 489 const bool success = delegate_->RestartDevice(&error_message); | |
| 490 | |
| 491 // Make sure our above assumption is maintained. | |
| 492 DCHECK(success || allow_non_kiosk_apps_restart_api_for_test); | |
| 493 } | |
| 494 | |
| 495 void RuntimeAPI::AllowNonKioskAppsInRestartAfterDelayForTesting() { | |
| 496 allow_non_kiosk_apps_restart_api_for_test = true; | |
| 497 } | |
| 498 | |
| 499 /////////////////////////////////////////////////////////////////////////////// | 329 /////////////////////////////////////////////////////////////////////////////// |
| 500 | 330 |
| 501 // static | 331 // static |
| 502 void RuntimeEventRouter::DispatchOnStartupEvent( | 332 void RuntimeEventRouter::DispatchOnStartupEvent( |
| 503 content::BrowserContext* context, | 333 content::BrowserContext* context, |
| 504 const std::string& extension_id) { | 334 const std::string& extension_id) { |
| 505 DispatchOnStartupEventImpl(context, extension_id, true, NULL); | 335 DispatchOnStartupEventImpl(context, extension_id, true, NULL); |
| 506 } | 336 } |
| 507 | 337 |
| 508 // static | 338 // static |
| (...skipping 207 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 716 std::string message; | 546 std::string message; |
| 717 bool result = | 547 bool result = |
| 718 RuntimeAPI::GetFactoryInstance()->Get(browser_context())->RestartDevice( | 548 RuntimeAPI::GetFactoryInstance()->Get(browser_context())->RestartDevice( |
| 719 &message); | 549 &message); |
| 720 if (!result) { | 550 if (!result) { |
| 721 return RespondNow(Error(message)); | 551 return RespondNow(Error(message)); |
| 722 } | 552 } |
| 723 return RespondNow(NoArguments()); | 553 return RespondNow(NoArguments()); |
| 724 } | 554 } |
| 725 | 555 |
| 726 ExtensionFunction::ResponseAction RuntimeRestartAfterDelayFunction::Run() { | |
| 727 if (!allow_non_kiosk_apps_restart_api_for_test && | |
| 728 !ExtensionsBrowserClient::Get()->IsRunningInForcedAppMode()) { | |
| 729 return RespondNow(Error(kErrorOnlyKioskModeAllowed)); | |
| 730 } | |
| 731 | |
| 732 std::unique_ptr<api::runtime::RestartAfterDelay::Params> params( | |
| 733 api::runtime::RestartAfterDelay::Params::Create(*args_)); | |
| 734 EXTENSION_FUNCTION_VALIDATE(params.get()); | |
| 735 int seconds = params->seconds; | |
| 736 | |
| 737 if (seconds <= 0 && seconds != -1) | |
| 738 return RespondNow(Error(kErrorInvalidArgument, base::IntToString(seconds))); | |
| 739 | |
| 740 RuntimeAPI::RestartAfterDelayStatus request_status = | |
| 741 RuntimeAPI::GetFactoryInstance() | |
| 742 ->Get(browser_context()) | |
| 743 ->RestartDeviceAfterDelay(extension()->id(), seconds); | |
| 744 | |
| 745 switch (request_status) { | |
| 746 case RuntimeAPI::RestartAfterDelayStatus::FAILED_NOT_FIRST_EXTENSION: | |
| 747 return RespondNow(Error(kErrorOnlyFirstExtensionAllowed)); | |
| 748 | |
| 749 case RuntimeAPI::RestartAfterDelayStatus::FAILED_THROTTLED: | |
| 750 return RespondNow(Error(kErrorRequestedTooSoon)); | |
| 751 | |
| 752 case RuntimeAPI::RestartAfterDelayStatus::SUCCESS_RESTART_CANCELED: | |
| 753 case RuntimeAPI::RestartAfterDelayStatus::SUCCESS_RESTART_SCHEDULED: | |
| 754 return RespondNow(NoArguments()); | |
| 755 } | |
| 756 | |
| 757 NOTREACHED(); | |
| 758 return RespondNow(Error(kErrorInvalidStatus)); | |
| 759 } | |
| 760 | |
| 761 ExtensionFunction::ResponseAction RuntimeGetPlatformInfoFunction::Run() { | 556 ExtensionFunction::ResponseAction RuntimeGetPlatformInfoFunction::Run() { |
| 762 runtime::PlatformInfo info; | 557 runtime::PlatformInfo info; |
| 763 if (!RuntimeAPI::GetFactoryInstance() | 558 if (!RuntimeAPI::GetFactoryInstance() |
| 764 ->Get(browser_context()) | 559 ->Get(browser_context()) |
| 765 ->GetPlatformInfo(&info)) { | 560 ->GetPlatformInfo(&info)) { |
| 766 return RespondNow(Error(kPlatformInfoUnavailable)); | 561 return RespondNow(Error(kPlatformInfoUnavailable)); |
| 767 } | 562 } |
| 768 return RespondNow( | 563 return RespondNow( |
| 769 ArgumentList(runtime::GetPlatformInfo::Results::Create(info))); | 564 ArgumentList(runtime::GetPlatformInfo::Results::Create(info))); |
| 770 } | 565 } |
| (...skipping 13 matching lines...) Expand all Loading... |
| 784 content::ChildProcessSecurityPolicy* policy = | 579 content::ChildProcessSecurityPolicy* policy = |
| 785 content::ChildProcessSecurityPolicy::GetInstance(); | 580 content::ChildProcessSecurityPolicy::GetInstance(); |
| 786 policy->GrantReadFileSystem(renderer_id, filesystem_id); | 581 policy->GrantReadFileSystem(renderer_id, filesystem_id); |
| 787 std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); | 582 std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); |
| 788 dict->SetString("fileSystemId", filesystem_id); | 583 dict->SetString("fileSystemId", filesystem_id); |
| 789 dict->SetString("baseName", relative_path); | 584 dict->SetString("baseName", relative_path); |
| 790 return RespondNow(OneArgument(std::move(dict))); | 585 return RespondNow(OneArgument(std::move(dict))); |
| 791 } | 586 } |
| 792 | 587 |
| 793 } // namespace extensions | 588 } // namespace extensions |
| OLD | NEW |