| 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" |
| 16 #include "base/threading/thread_task_runner_handle.h" | 17 #include "base/threading/thread_task_runner_handle.h" |
| 17 #include "base/values.h" | 18 #include "base/values.h" |
| 18 #include "base/version.h" | 19 #include "base/version.h" |
| 20 #include "components/prefs/pref_registry_simple.h" |
| 21 #include "components/prefs/pref_service.h" |
| 19 #include "content/public/browser/browser_context.h" | 22 #include "content/public/browser/browser_context.h" |
| 20 #include "content/public/browser/child_process_security_policy.h" | 23 #include "content/public/browser/child_process_security_policy.h" |
| 21 #include "content/public/browser/notification_service.h" | 24 #include "content/public/browser/notification_service.h" |
| 22 #include "content/public/browser/render_frame_host.h" | 25 #include "content/public/browser/render_frame_host.h" |
| 23 #include "content/public/browser/render_process_host.h" | 26 #include "content/public/browser/render_process_host.h" |
| 24 #include "extensions/browser/api/runtime/runtime_api_delegate.h" | 27 #include "extensions/browser/api/runtime/runtime_api_delegate.h" |
| 25 #include "extensions/browser/event_router.h" | 28 #include "extensions/browser/event_router.h" |
| 26 #include "extensions/browser/extension_host.h" | 29 #include "extensions/browser/extension_host.h" |
| 27 #include "extensions/browser/extension_prefs.h" | 30 #include "extensions/browser/extension_prefs.h" |
| 28 #include "extensions/browser/extension_registry.h" | 31 #include "extensions/browser/extension_registry.h" |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 73 "pending_on_installed_event_dispatch_info"; | 76 "pending_on_installed_event_dispatch_info"; |
| 74 | 77 |
| 75 // Previously installed version number. | 78 // Previously installed version number. |
| 76 const char kPrefPreviousVersion[] = "previous_version"; | 79 const char kPrefPreviousVersion[] = "previous_version"; |
| 77 | 80 |
| 78 // The name of the directory to be returned by getPackageDirectoryEntry. This | 81 // The name of the directory to be returned by getPackageDirectoryEntry. This |
| 79 // particular value does not matter to user code, but is chosen for consistency | 82 // particular value does not matter to user code, but is chosen for consistency |
| 80 // with the equivalent Pepper API. | 83 // with the equivalent Pepper API. |
| 81 const char kPackageDirectoryPath[] = "crxfs"; | 84 const char kPackageDirectoryPath[] = "crxfs"; |
| 82 | 85 |
| 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 |
| 83 void DispatchOnStartupEventImpl(BrowserContext* browser_context, | 111 void DispatchOnStartupEventImpl(BrowserContext* browser_context, |
| 84 const std::string& extension_id, | 112 const std::string& extension_id, |
| 85 bool first_call, | 113 bool first_call, |
| 86 ExtensionHost* host) { | 114 ExtensionHost* host) { |
| 87 // A NULL host from the LazyBackgroundTaskQueue means the page failed to | 115 // A NULL host from the LazyBackgroundTaskQueue means the page failed to |
| 88 // load. Give up. | 116 // load. Give up. |
| 89 if (!host && !first_call) | 117 if (!host && !first_call) |
| 90 return; | 118 return; |
| 91 | 119 |
| 92 // Don't send onStartup events to incognito browser contexts. | 120 // Don't send onStartup events to incognito browser contexts. |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 144 /////////////////////////////////////////////////////////////////////////////// | 172 /////////////////////////////////////////////////////////////////////////////// |
| 145 | 173 |
| 146 static base::LazyInstance<BrowserContextKeyedAPIFactory<RuntimeAPI> > | 174 static base::LazyInstance<BrowserContextKeyedAPIFactory<RuntimeAPI> > |
| 147 g_factory = LAZY_INSTANCE_INITIALIZER; | 175 g_factory = LAZY_INSTANCE_INITIALIZER; |
| 148 | 176 |
| 149 // static | 177 // static |
| 150 BrowserContextKeyedAPIFactory<RuntimeAPI>* RuntimeAPI::GetFactoryInstance() { | 178 BrowserContextKeyedAPIFactory<RuntimeAPI>* RuntimeAPI::GetFactoryInstance() { |
| 151 return g_factory.Pointer(); | 179 return g_factory.Pointer(); |
| 152 } | 180 } |
| 153 | 181 |
| 182 // static |
| 183 void RuntimeAPI::RegisterPrefs(PrefRegistrySimple* registry) { |
| 184 registry->RegisterBooleanPref(kPrefLastRestartWasDueToDelayedRestartApi, |
| 185 false); |
| 186 registry->RegisterDoublePref(kPrefLastRestartAfterDelayTime, 0.0); |
| 187 } |
| 188 |
| 154 template <> | 189 template <> |
| 155 void BrowserContextKeyedAPIFactory<RuntimeAPI>::DeclareFactoryDependencies() { | 190 void BrowserContextKeyedAPIFactory<RuntimeAPI>::DeclareFactoryDependencies() { |
| 156 DependsOn(ProcessManagerFactory::GetInstance()); | 191 DependsOn(ProcessManagerFactory::GetInstance()); |
| 157 } | 192 } |
| 158 | 193 |
| 159 RuntimeAPI::RuntimeAPI(content::BrowserContext* context) | 194 RuntimeAPI::RuntimeAPI(content::BrowserContext* context) |
| 160 : browser_context_(context), | 195 : browser_context_(context), |
| 196 extension_registry_observer_(this), |
| 197 process_manager_observer_(this), |
| 198 minimum_duration_between_restarts_(base::TimeDelta::FromHours( |
| 199 kMinDurationBetweenSuccessiveRestartsHours)), |
| 161 dispatch_chrome_updated_event_(false), | 200 dispatch_chrome_updated_event_(false), |
| 162 extension_registry_observer_(this), | 201 did_read_delayed_restart_preferences_(false), |
| 163 process_manager_observer_(this) { | 202 was_last_restart_due_to_delayed_restart_api_(false), |
| 203 weak_ptr_factory_(this) { |
| 164 // RuntimeAPI is redirected in incognito, so |browser_context_| is never | 204 // RuntimeAPI is redirected in incognito, so |browser_context_| is never |
| 165 // incognito. | 205 // incognito. |
| 166 DCHECK(!browser_context_->IsOffTheRecord()); | 206 DCHECK(!browser_context_->IsOffTheRecord()); |
| 167 | 207 |
| 168 registrar_.Add(this, | 208 registrar_.Add(this, |
| 169 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED, | 209 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED, |
| 170 content::Source<BrowserContext>(context)); | 210 content::Source<BrowserContext>(context)); |
| 171 extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_)); | 211 extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_)); |
| 172 process_manager_observer_.Add(ProcessManager::Get(browser_context_)); | 212 process_manager_observer_.Add(ProcessManager::Get(browser_context_)); |
| 173 | 213 |
| (...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 312 | 352 |
| 313 void RuntimeAPI::OpenURL(const GURL& update_url) { | 353 void RuntimeAPI::OpenURL(const GURL& update_url) { |
| 314 delegate_->OpenURL(update_url); | 354 delegate_->OpenURL(update_url); |
| 315 } | 355 } |
| 316 | 356 |
| 317 bool RuntimeAPI::GetPlatformInfo(runtime::PlatformInfo* info) { | 357 bool RuntimeAPI::GetPlatformInfo(runtime::PlatformInfo* info) { |
| 318 return delegate_->GetPlatformInfo(info); | 358 return delegate_->GetPlatformInfo(info); |
| 319 } | 359 } |
| 320 | 360 |
| 321 bool RuntimeAPI::RestartDevice(std::string* error_message) { | 361 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 } |
| 322 return delegate_->RestartDevice(error_message); | 376 return delegate_->RestartDevice(error_message); |
| 323 } | 377 } |
| 324 | 378 |
| 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 |
| 325 bool RuntimeAPI::OpenOptionsPage(const Extension* extension) { | 431 bool RuntimeAPI::OpenOptionsPage(const Extension* extension) { |
| 326 return delegate_->OpenOptionsPage(extension); | 432 return delegate_->OpenOptionsPage(extension); |
| 327 } | 433 } |
| 328 | 434 |
| 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 |
| 329 /////////////////////////////////////////////////////////////////////////////// | 499 /////////////////////////////////////////////////////////////////////////////// |
| 330 | 500 |
| 331 // static | 501 // static |
| 332 void RuntimeEventRouter::DispatchOnStartupEvent( | 502 void RuntimeEventRouter::DispatchOnStartupEvent( |
| 333 content::BrowserContext* context, | 503 content::BrowserContext* context, |
| 334 const std::string& extension_id) { | 504 const std::string& extension_id) { |
| 335 DispatchOnStartupEventImpl(context, extension_id, true, NULL); | 505 DispatchOnStartupEventImpl(context, extension_id, true, NULL); |
| 336 } | 506 } |
| 337 | 507 |
| 338 // static | 508 // static |
| (...skipping 207 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 546 std::string message; | 716 std::string message; |
| 547 bool result = | 717 bool result = |
| 548 RuntimeAPI::GetFactoryInstance()->Get(browser_context())->RestartDevice( | 718 RuntimeAPI::GetFactoryInstance()->Get(browser_context())->RestartDevice( |
| 549 &message); | 719 &message); |
| 550 if (!result) { | 720 if (!result) { |
| 551 return RespondNow(Error(message)); | 721 return RespondNow(Error(message)); |
| 552 } | 722 } |
| 553 return RespondNow(NoArguments()); | 723 return RespondNow(NoArguments()); |
| 554 } | 724 } |
| 555 | 725 |
| 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 |
| 556 ExtensionFunction::ResponseAction RuntimeGetPlatformInfoFunction::Run() { | 761 ExtensionFunction::ResponseAction RuntimeGetPlatformInfoFunction::Run() { |
| 557 runtime::PlatformInfo info; | 762 runtime::PlatformInfo info; |
| 558 if (!RuntimeAPI::GetFactoryInstance() | 763 if (!RuntimeAPI::GetFactoryInstance() |
| 559 ->Get(browser_context()) | 764 ->Get(browser_context()) |
| 560 ->GetPlatformInfo(&info)) { | 765 ->GetPlatformInfo(&info)) { |
| 561 return RespondNow(Error(kPlatformInfoUnavailable)); | 766 return RespondNow(Error(kPlatformInfoUnavailable)); |
| 562 } | 767 } |
| 563 return RespondNow( | 768 return RespondNow( |
| 564 ArgumentList(runtime::GetPlatformInfo::Results::Create(info))); | 769 ArgumentList(runtime::GetPlatformInfo::Results::Create(info))); |
| 565 } | 770 } |
| (...skipping 13 matching lines...) Expand all Loading... |
| 579 content::ChildProcessSecurityPolicy* policy = | 784 content::ChildProcessSecurityPolicy* policy = |
| 580 content::ChildProcessSecurityPolicy::GetInstance(); | 785 content::ChildProcessSecurityPolicy::GetInstance(); |
| 581 policy->GrantReadFileSystem(renderer_id, filesystem_id); | 786 policy->GrantReadFileSystem(renderer_id, filesystem_id); |
| 582 std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); | 787 std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); |
| 583 dict->SetString("fileSystemId", filesystem_id); | 788 dict->SetString("fileSystemId", filesystem_id); |
| 584 dict->SetString("baseName", relative_path); | 789 dict->SetString("baseName", relative_path); |
| 585 return RespondNow(OneArgument(std::move(dict))); | 790 return RespondNow(OneArgument(std::move(dict))); |
| 586 } | 791 } |
| 587 | 792 |
| 588 } // namespace extensions | 793 } // namespace extensions |
| OLD | NEW |