Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(382)

Side by Side Diff: extensions/browser/api/runtime/runtime_api.cc

Issue 1970613003: Add a new app API to enable watchdog behavior restarts in kiosk apps (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Nitty nit. Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
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
OLDNEW
« no previous file with comments | « extensions/browser/api/runtime/runtime_api.h ('k') | extensions/browser/extension_function_histogram_value.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698