Chromium Code Reviews| 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 on the watchdog timer | |
| 87 // timing out. | |
| 88 constexpr char kPrefLastRestartAfterDelayTime[] = | |
| 89 "last_restart_on_watchdog_time"; | |
| 90 | |
| 91 // Error and status messages strings for the restartAfterDelay() API. | |
| 92 constexpr char kErrorInvalidArgument[] = "Invalid argument: *."; | |
| 93 constexpr char kErrorOnlyKioskModeAllowed[] = | |
| 94 "API available only for ChromeOS kiosk mode."; | |
| 95 constexpr char kErrorOnlyFirstExtensionAllowed[] = | |
| 96 "Not the first extension to call this API."; | |
| 97 constexpr char kErrorInvalidStatus[] = "Invalid restart request status."; | |
| 98 constexpr char kErrorRequestedTooSoon[] = | |
| 99 "Restart was requested too soon. It was throttled instead."; | |
| 100 | |
| 101 constexpr int kMinDurationBetweenSuccessiveRestartsHours = 3; | |
| 102 | |
| 103 // This is used for browsertests, so that we can test the restartAfterDelay | |
| 104 // API without a kiost app. | |
| 105 bool allow_non_kiost_apps_restart_api_for_test = false; | |
| 106 | |
| 83 void DispatchOnStartupEventImpl(BrowserContext* browser_context, | 107 void DispatchOnStartupEventImpl(BrowserContext* browser_context, |
| 84 const std::string& extension_id, | 108 const std::string& extension_id, |
| 85 bool first_call, | 109 bool first_call, |
| 86 ExtensionHost* host) { | 110 ExtensionHost* host) { |
| 87 // A NULL host from the LazyBackgroundTaskQueue means the page failed to | 111 // A NULL host from the LazyBackgroundTaskQueue means the page failed to |
| 88 // load. Give up. | 112 // load. Give up. |
| 89 if (!host && !first_call) | 113 if (!host && !first_call) |
| 90 return; | 114 return; |
| 91 | 115 |
| 92 // Don't send onStartup events to incognito browser contexts. | 116 // Don't send onStartup events to incognito browser contexts. |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 144 /////////////////////////////////////////////////////////////////////////////// | 168 /////////////////////////////////////////////////////////////////////////////// |
| 145 | 169 |
| 146 static base::LazyInstance<BrowserContextKeyedAPIFactory<RuntimeAPI> > | 170 static base::LazyInstance<BrowserContextKeyedAPIFactory<RuntimeAPI> > |
| 147 g_factory = LAZY_INSTANCE_INITIALIZER; | 171 g_factory = LAZY_INSTANCE_INITIALIZER; |
| 148 | 172 |
| 149 // static | 173 // static |
| 150 BrowserContextKeyedAPIFactory<RuntimeAPI>* RuntimeAPI::GetFactoryInstance() { | 174 BrowserContextKeyedAPIFactory<RuntimeAPI>* RuntimeAPI::GetFactoryInstance() { |
| 151 return g_factory.Pointer(); | 175 return g_factory.Pointer(); |
| 152 } | 176 } |
| 153 | 177 |
| 178 // static | |
| 179 void RuntimeAPI::RegisterPrefs(PrefRegistrySimple* registry) { | |
| 180 registry->RegisterDoublePref(kPrefLastRestartAfterDelayTime, 0.0); | |
| 181 } | |
| 182 | |
| 154 template <> | 183 template <> |
| 155 void BrowserContextKeyedAPIFactory<RuntimeAPI>::DeclareFactoryDependencies() { | 184 void BrowserContextKeyedAPIFactory<RuntimeAPI>::DeclareFactoryDependencies() { |
| 156 DependsOn(ProcessManagerFactory::GetInstance()); | 185 DependsOn(ProcessManagerFactory::GetInstance()); |
| 157 } | 186 } |
| 158 | 187 |
| 159 RuntimeAPI::RuntimeAPI(content::BrowserContext* context) | 188 RuntimeAPI::RuntimeAPI(content::BrowserContext* context) |
| 160 : browser_context_(context), | 189 : browser_context_(context), |
| 161 dispatch_chrome_updated_event_(false), | |
| 162 extension_registry_observer_(this), | 190 extension_registry_observer_(this), |
| 163 process_manager_observer_(this) { | 191 process_manager_observer_(this), |
| 192 minimum_duration_between_restarts_(base::TimeDelta::FromHours( | |
| 193 kMinDurationBetweenSuccessiveRestartsHours)), | |
| 194 weak_ptr_factory_(this) { | |
| 164 // RuntimeAPI is redirected in incognito, so |browser_context_| is never | 195 // RuntimeAPI is redirected in incognito, so |browser_context_| is never |
| 165 // incognito. | 196 // incognito. |
| 166 DCHECK(!browser_context_->IsOffTheRecord()); | 197 DCHECK(!browser_context_->IsOffTheRecord()); |
| 167 | 198 |
| 168 registrar_.Add(this, | 199 registrar_.Add(this, |
| 169 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED, | 200 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED, |
| 170 content::Source<BrowserContext>(context)); | 201 content::Source<BrowserContext>(context)); |
| 171 extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_)); | 202 extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_)); |
| 172 process_manager_observer_.Add(ProcessManager::Get(browser_context_)); | 203 process_manager_observer_.Add(ProcessManager::Get(browser_context_)); |
| 173 | 204 |
| 174 delegate_ = ExtensionsBrowserClient::Get()->CreateRuntimeAPIDelegate( | 205 delegate_ = ExtensionsBrowserClient::Get()->CreateRuntimeAPIDelegate( |
| 175 browser_context_); | 206 browser_context_); |
| 176 | 207 |
| 208 delegate_->SetOnDeviceShutdownCallback(base::Bind( | |
| 209 &RuntimeAPI::OnDeviceShutdown, weak_ptr_factory_.GetWeakPtr())); | |
| 210 | |
| 177 // Check if registered events are up-to-date. We can only do this once | 211 // Check if registered events are up-to-date. We can only do this once |
| 178 // per browser context, since it updates internal state when called. | 212 // per browser context, since it updates internal state when called. |
| 179 dispatch_chrome_updated_event_ = | 213 dispatch_chrome_updated_event_ = |
| 180 ExtensionsBrowserClient::Get()->DidVersionUpdate(browser_context_); | 214 ExtensionsBrowserClient::Get()->DidVersionUpdate(browser_context_); |
| 181 } | 215 } |
| 182 | 216 |
| 183 RuntimeAPI::~RuntimeAPI() { | 217 RuntimeAPI::~RuntimeAPI() { |
| 184 } | 218 } |
| 185 | 219 |
| 186 void RuntimeAPI::Observe(int type, | 220 void RuntimeAPI::Observe(int type, |
| (...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 312 | 346 |
| 313 void RuntimeAPI::OpenURL(const GURL& update_url) { | 347 void RuntimeAPI::OpenURL(const GURL& update_url) { |
| 314 delegate_->OpenURL(update_url); | 348 delegate_->OpenURL(update_url); |
| 315 } | 349 } |
| 316 | 350 |
| 317 bool RuntimeAPI::GetPlatformInfo(runtime::PlatformInfo* info) { | 351 bool RuntimeAPI::GetPlatformInfo(runtime::PlatformInfo* info) { |
| 318 return delegate_->GetPlatformInfo(info); | 352 return delegate_->GetPlatformInfo(info); |
| 319 } | 353 } |
| 320 | 354 |
| 321 bool RuntimeAPI::RestartDevice(std::string* error_message) { | 355 bool RuntimeAPI::RestartDevice(std::string* error_message) { |
| 322 return delegate_->RestartDevice(error_message); | 356 expecting_non_watchdog_restart_ = delegate_->RestartDevice(error_message); |
| 357 return expecting_non_watchdog_restart_; | |
| 358 } | |
| 359 | |
| 360 RuntimeAPI::RestartOnWatchdogStatus RuntimeAPI::RestartDeviceOnWatchdogTimeout( | |
| 361 const std::string& extension_id, | |
| 362 int seconds_from_now) { | |
| 363 // To achieve as much accuracy as possible, record the time of the call as | |
| 364 // |now| here. | |
| 365 const base::Time now = base::Time::NowFromSystemTime(); | |
| 366 | |
| 367 if (schedule_restart_first_extension_id_.empty()) { | |
| 368 schedule_restart_first_extension_id_ = extension_id; | |
| 369 } else if (extension_id != schedule_restart_first_extension_id_) { | |
| 370 // We only allow the first extension to call this API to call it repeatedly. | |
| 371 // Any other extension will fail. | |
| 372 return RestartOnWatchdogStatus::FAILED_NOT_FIRST_EXTENSION; | |
| 373 } | |
| 374 | |
| 375 MaybeCancelRunningWatchdogTimer(); | |
| 376 | |
| 377 if (seconds_from_now == -1) { | |
| 378 // We already stopped the running timer (if any). | |
| 379 return RestartOnWatchdogStatus::SUCCESS_RESTART_CANCELED; | |
| 380 } | |
| 381 | |
| 382 // Try to read any previous successful restart attempt time resulting from | |
| 383 // this API. | |
| 384 PrefService* pref_service = | |
| 385 ExtensionsBrowserClient::Get()->GetPrefServiceForContext( | |
| 386 browser_context_); | |
| 387 DCHECK(pref_service); | |
| 388 double last_restart_time = | |
| 389 pref_service->GetDouble(kPrefLastRestartAfterDelayTime); | |
| 390 | |
| 391 return ScheduleDelayedRestart(now, seconds_from_now, last_restart_time); | |
| 323 } | 392 } |
| 324 | 393 |
| 325 bool RuntimeAPI::OpenOptionsPage(const Extension* extension) { | 394 bool RuntimeAPI::OpenOptionsPage(const Extension* extension) { |
| 326 return delegate_->OpenOptionsPage(extension); | 395 return delegate_->OpenOptionsPage(extension); |
| 327 } | 396 } |
| 328 | 397 |
| 398 void RuntimeAPI::MaybeCancelRunningWatchdogTimer() { | |
| 399 if (watchdog_timer_.IsRunning()) | |
| 400 watchdog_timer_.Stop(); | |
| 401 } | |
| 402 | |
| 403 RuntimeAPI::RestartOnWatchdogStatus RuntimeAPI::ScheduleDelayedRestart( | |
| 404 const base::Time& now, | |
| 405 int seconds_from_now, | |
| 406 double stored_last_restart) { | |
| 407 base::TimeDelta delay_till_restart = | |
| 408 base::TimeDelta::FromSeconds(seconds_from_now); | |
| 409 | |
| 410 // Throttle restart requests that are received too soon successively. | |
| 411 bool was_throttled = false; | |
| 412 base::Time last_restart_time = base::Time::FromDoubleT(stored_last_restart); | |
| 413 base::Time future_restart_time = now + delay_till_restart; | |
| 414 base::TimeDelta future_time_since_last_restart = | |
| 415 future_restart_time > last_restart_time | |
| 416 ? future_restart_time - last_restart_time | |
| 417 : base::TimeDelta(); | |
| 418 if (future_time_since_last_restart < minimum_duration_between_restarts_) { | |
| 419 // Schedule the restart after |minimum_duration_between_restarts_| has | |
| 420 // passed. | |
| 421 delay_till_restart = | |
| 422 minimum_duration_between_restarts_ - (now - last_restart_time); | |
| 423 was_throttled = true; | |
| 424 } | |
| 425 | |
| 426 watchdog_timer_.Start(FROM_HERE, delay_till_restart, | |
| 427 base::Bind(&RuntimeAPI::OnRestartWatchdogTimeout, | |
| 428 weak_ptr_factory_.GetWeakPtr())); | |
| 429 | |
| 430 return was_throttled ? RestartOnWatchdogStatus::FAILED_THROTTLED | |
| 431 : RestartOnWatchdogStatus::SUCCESS_RESTART_SCHEDULED; | |
| 432 } | |
| 433 | |
| 434 void RuntimeAPI::OnRestartWatchdogTimeout() { | |
| 435 // We can persist "now" as the last successful restart time, assuming that the | |
| 436 // following restart request will succeed, since it can only fail if requested | |
| 437 // by non kiosk apps, and we prevent that from the beginning (unless in | |
| 438 // browsertests). | |
| 439 // This assumption is important, since once restart is requested, we might not | |
| 440 // have enough time to persist the data to disk. | |
| 441 expecting_watchdog_restart_ = true; | |
| 442 std::string error_message; | |
| 443 const bool success = delegate_->RestartDevice(&error_message); | |
| 444 | |
| 445 if (!success && !allow_non_kiost_apps_restart_api_for_test) { | |
| 446 // This is breaking our above assumption and should never be reached. | |
| 447 NOTREACHED(); | |
| 448 } | |
| 449 } | |
| 450 | |
| 451 void RuntimeAPI::OnDeviceShutdown() { | |
|
Devlin
2016/06/10 00:34:45
This worries me for a couple of reasons:
- We typi
xiyuan
2016/06/10 16:33:48
This would not work because the restartAfterDelay
Devlin
2016/06/10 18:06:17
Sorry, I was unclear. I meant we should have a me
afakhry
2016/06/13 14:46:00
I didn't get this comment until I read your explan
| |
| 452 if (expecting_non_watchdog_restart_) { | |
| 453 // Non-watchdog restarts should never clear the watchdog throttle. | |
| 454 return; | |
| 455 } | |
| 456 | |
| 457 // Persist "now" as the current last watchdog restart time if we are expecting | |
| 458 // one, clear it otherwise. | |
| 459 double restart_time = 0.0; | |
| 460 if (expecting_watchdog_restart_) | |
| 461 restart_time = base::Time::NowFromSystemTime().ToDoubleT(); | |
| 462 | |
| 463 PrefService* pref_service = | |
| 464 ExtensionsBrowserClient::Get()->GetPrefServiceForContext( | |
| 465 browser_context_); | |
| 466 DCHECK(pref_service); | |
| 467 pref_service->SetDouble(kPrefLastRestartAfterDelayTime, restart_time); | |
| 468 } | |
| 469 | |
| 470 void RuntimeAPI::AllowNonKiostAppsInRestartOnWatchdogForTesting() { | |
| 471 allow_non_kiost_apps_restart_api_for_test = true; | |
| 472 } | |
| 473 | |
| 329 /////////////////////////////////////////////////////////////////////////////// | 474 /////////////////////////////////////////////////////////////////////////////// |
| 330 | 475 |
| 331 // static | 476 // static |
| 332 void RuntimeEventRouter::DispatchOnStartupEvent( | 477 void RuntimeEventRouter::DispatchOnStartupEvent( |
| 333 content::BrowserContext* context, | 478 content::BrowserContext* context, |
| 334 const std::string& extension_id) { | 479 const std::string& extension_id) { |
| 335 DispatchOnStartupEventImpl(context, extension_id, true, NULL); | 480 DispatchOnStartupEventImpl(context, extension_id, true, NULL); |
| 336 } | 481 } |
| 337 | 482 |
| 338 // static | 483 // static |
| (...skipping 207 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 546 std::string message; | 691 std::string message; |
| 547 bool result = | 692 bool result = |
| 548 RuntimeAPI::GetFactoryInstance()->Get(browser_context())->RestartDevice( | 693 RuntimeAPI::GetFactoryInstance()->Get(browser_context())->RestartDevice( |
| 549 &message); | 694 &message); |
| 550 if (!result) { | 695 if (!result) { |
| 551 return RespondNow(Error(message)); | 696 return RespondNow(Error(message)); |
| 552 } | 697 } |
| 553 return RespondNow(NoArguments()); | 698 return RespondNow(NoArguments()); |
| 554 } | 699 } |
| 555 | 700 |
| 701 ExtensionFunction::ResponseAction RuntimeRestartAfterDelayFunction::Run() { | |
| 702 if (!allow_non_kiost_apps_restart_api_for_test && | |
| 703 !ExtensionsBrowserClient::Get()->IsRunningInForcedAppMode()) { | |
| 704 return RespondNow(Error(kErrorOnlyKioskModeAllowed)); | |
| 705 } | |
| 706 | |
| 707 std::unique_ptr<api::runtime::RestartAfterDelay::Params> params( | |
| 708 api::runtime::RestartAfterDelay::Params::Create(*args_)); | |
| 709 EXTENSION_FUNCTION_VALIDATE(params.get()); | |
| 710 int seconds = params->seconds; | |
| 711 | |
| 712 if (seconds < -1) | |
| 713 return RespondNow(Error(kErrorInvalidArgument, base::IntToString(seconds))); | |
| 714 | |
| 715 RuntimeAPI::RestartOnWatchdogStatus request_status = | |
| 716 RuntimeAPI::GetFactoryInstance() | |
| 717 ->Get(browser_context()) | |
| 718 ->RestartDeviceOnWatchdogTimeout(extension()->id(), seconds); | |
| 719 | |
| 720 switch (request_status) { | |
| 721 case RuntimeAPI::RestartOnWatchdogStatus::FAILED_NOT_FIRST_EXTENSION: | |
| 722 return RespondNow(Error(kErrorOnlyFirstExtensionAllowed)); | |
| 723 | |
| 724 case RuntimeAPI::RestartOnWatchdogStatus::FAILED_THROTTLED: | |
| 725 return RespondNow(Error(kErrorRequestedTooSoon)); | |
| 726 | |
| 727 case RuntimeAPI::RestartOnWatchdogStatus::SUCCESS_RESTART_CANCELED: | |
| 728 case RuntimeAPI::RestartOnWatchdogStatus::SUCCESS_RESTART_SCHEDULED: | |
| 729 return RespondNow(NoArguments()); | |
| 730 } | |
| 731 | |
| 732 NOTREACHED(); | |
| 733 return RespondNow(Error(kErrorInvalidStatus)); | |
| 734 } | |
| 735 | |
| 556 ExtensionFunction::ResponseAction RuntimeGetPlatformInfoFunction::Run() { | 736 ExtensionFunction::ResponseAction RuntimeGetPlatformInfoFunction::Run() { |
| 557 runtime::PlatformInfo info; | 737 runtime::PlatformInfo info; |
| 558 if (!RuntimeAPI::GetFactoryInstance() | 738 if (!RuntimeAPI::GetFactoryInstance() |
| 559 ->Get(browser_context()) | 739 ->Get(browser_context()) |
| 560 ->GetPlatformInfo(&info)) { | 740 ->GetPlatformInfo(&info)) { |
| 561 return RespondNow(Error(kPlatformInfoUnavailable)); | 741 return RespondNow(Error(kPlatformInfoUnavailable)); |
| 562 } | 742 } |
| 563 return RespondNow( | 743 return RespondNow( |
| 564 ArgumentList(runtime::GetPlatformInfo::Results::Create(info))); | 744 ArgumentList(runtime::GetPlatformInfo::Results::Create(info))); |
| 565 } | 745 } |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 579 content::ChildProcessSecurityPolicy* policy = | 759 content::ChildProcessSecurityPolicy* policy = |
| 580 content::ChildProcessSecurityPolicy::GetInstance(); | 760 content::ChildProcessSecurityPolicy::GetInstance(); |
| 581 policy->GrantReadFileSystem(renderer_id, filesystem_id); | 761 policy->GrantReadFileSystem(renderer_id, filesystem_id); |
| 582 std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); | 762 std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); |
| 583 dict->SetString("fileSystemId", filesystem_id); | 763 dict->SetString("fileSystemId", filesystem_id); |
| 584 dict->SetString("baseName", relative_path); | 764 dict->SetString("baseName", relative_path); |
| 585 return RespondNow(OneArgument(std::move(dict))); | 765 return RespondNow(OneArgument(std::move(dict))); |
| 586 } | 766 } |
| 587 | 767 |
| 588 } // namespace extensions | 768 } // namespace extensions |
| OLD | NEW |