Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / chromeos / power / extension_event_observer.cc
blobc986ef8ef26698a85e395d8a55da220571e13a94
1 // Copyright 2015 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.
5 #include "chrome/browser/chromeos/power/extension_event_observer.h"
7 #include "base/bind.h"
8 #include "base/logging.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/thread_task_runner_handle.h"
11 #include "chrome/browser/chrome_notification_types.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/common/extensions/api/gcm.h"
14 #include "chromeos/dbus/dbus_thread_manager.h"
15 #include "content/public/browser/notification_service.h"
16 #include "extensions/browser/extension_host.h"
17 #include "extensions/browser/process_manager.h"
18 #include "extensions/common/extension.h"
19 #include "extensions/common/manifest_handlers/background_info.h"
20 #include "extensions/common/permissions/api_permission.h"
21 #include "extensions/common/permissions/permissions_data.h"
23 namespace chromeos {
25 namespace {
26 // The number of milliseconds that we should wait after receiving a
27 // DarkSuspendImminent signal before attempting to report readiness to suspend.
28 const int kDarkSuspendDelayMs = 1000;
31 ExtensionEventObserver::TestApi::TestApi(
32 base::WeakPtr<ExtensionEventObserver> parent)
33 : parent_(parent) {
36 ExtensionEventObserver::TestApi::~TestApi() {
39 bool ExtensionEventObserver::TestApi::MaybeRunSuspendReadinessCallback() {
40 if (!parent_ || parent_->suspend_readiness_callback_.callback().is_null())
41 return false;
43 parent_->suspend_readiness_callback_.callback().Run();
44 parent_->suspend_readiness_callback_.Cancel();
45 return true;
48 bool ExtensionEventObserver::TestApi::WillDelaySuspendForExtensionHost(
49 extensions::ExtensionHost* host) {
50 if (!parent_)
51 return false;
53 return parent_->keepalive_sources_.contains(host);
56 struct ExtensionEventObserver::KeepaliveSources {
57 std::set<int> unacked_push_messages;
58 std::set<uint64> pending_network_requests;
61 ExtensionEventObserver::ExtensionEventObserver()
62 : should_delay_suspend_(true),
63 suspend_is_pending_(false),
64 suspend_keepalive_count_(0),
65 weak_factory_(this) {
66 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_ADDED,
67 content::NotificationService::AllBrowserContextsAndSources());
68 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
69 content::NotificationService::AllBrowserContextsAndSources());
71 DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver(this);
74 ExtensionEventObserver::~ExtensionEventObserver() {
75 for (Profile* profile : active_profiles_)
76 extensions::ProcessManager::Get(profile)->RemoveObserver(this);
78 for (const auto& pair : keepalive_sources_) {
79 extensions::ExtensionHost* host =
80 const_cast<extensions::ExtensionHost*>(pair.first);
81 host->RemoveObserver(this);
84 DBusThreadManager::Get()->GetPowerManagerClient()->RemoveObserver(this);
87 scoped_ptr<ExtensionEventObserver::TestApi>
88 ExtensionEventObserver::CreateTestApi() {
89 return make_scoped_ptr(
90 new ExtensionEventObserver::TestApi(weak_factory_.GetWeakPtr()));
93 void ExtensionEventObserver::SetShouldDelaySuspend(bool should_delay) {
94 should_delay_suspend_ = should_delay;
95 if (!should_delay_suspend_ && suspend_is_pending_) {
96 // There is a suspend attempt pending but this class should no longer be
97 // delaying it. Immediately report readiness.
98 suspend_is_pending_ = false;
99 power_manager_callback_.Run();
100 power_manager_callback_.Reset();
101 suspend_readiness_callback_.Cancel();
105 void ExtensionEventObserver::Observe(
106 int type,
107 const content::NotificationSource& source,
108 const content::NotificationDetails& details) {
109 switch (type) {
110 case chrome::NOTIFICATION_PROFILE_ADDED: {
111 OnProfileAdded(content::Source<Profile>(source).ptr());
112 break;
114 case chrome::NOTIFICATION_PROFILE_DESTROYED: {
115 OnProfileDestroyed(content::Source<Profile>(source).ptr());
116 break;
118 default:
119 NOTREACHED();
123 void ExtensionEventObserver::OnBackgroundHostCreated(
124 extensions::ExtensionHost* host) {
125 // We only care about ExtensionHosts for extensions that use GCM and have a
126 // lazy background page.
127 if (!host->extension()->permissions_data()->HasAPIPermission(
128 extensions::APIPermission::kGcm) ||
129 !extensions::BackgroundInfo::HasLazyBackgroundPage(host->extension()))
130 return;
132 auto result =
133 keepalive_sources_.add(host, make_scoped_ptr(new KeepaliveSources()));
135 if (result.second)
136 host->AddObserver(this);
139 void ExtensionEventObserver::OnExtensionHostDestroyed(
140 const extensions::ExtensionHost* host) {
141 DCHECK(keepalive_sources_.contains(host));
143 scoped_ptr<KeepaliveSources> sources =
144 keepalive_sources_.take_and_erase(host);
146 suspend_keepalive_count_ -= sources->unacked_push_messages.size();
147 suspend_keepalive_count_ -= sources->pending_network_requests.size();
148 MaybeReportSuspendReadiness();
151 void ExtensionEventObserver::OnBackgroundEventDispatched(
152 const extensions::ExtensionHost* host,
153 const std::string& event_name,
154 int event_id) {
155 DCHECK(keepalive_sources_.contains(host));
157 if (event_name != extensions::api::gcm::OnMessage::kEventName)
158 return;
160 keepalive_sources_.get(host)->unacked_push_messages.insert(event_id);
161 ++suspend_keepalive_count_;
164 void ExtensionEventObserver::OnBackgroundEventAcked(
165 const extensions::ExtensionHost* host,
166 int event_id) {
167 DCHECK(keepalive_sources_.contains(host));
169 if (keepalive_sources_.get(host)->unacked_push_messages.erase(event_id) > 0) {
170 --suspend_keepalive_count_;
171 MaybeReportSuspendReadiness();
175 void ExtensionEventObserver::OnNetworkRequestStarted(
176 const extensions::ExtensionHost* host,
177 uint64 request_id) {
178 DCHECK(keepalive_sources_.contains(host));
180 KeepaliveSources* sources = keepalive_sources_.get(host);
182 // We only care about network requests that were started while a push message
183 // is pending. This is an indication that the network request is related to
184 // the push message.
185 if (sources->unacked_push_messages.empty())
186 return;
188 sources->pending_network_requests.insert(request_id);
189 ++suspend_keepalive_count_;
192 void ExtensionEventObserver::OnNetworkRequestDone(
193 const extensions::ExtensionHost* host,
194 uint64 request_id) {
195 DCHECK(keepalive_sources_.contains(host));
197 if (keepalive_sources_.get(host)->pending_network_requests.erase(request_id) >
198 0) {
199 --suspend_keepalive_count_;
200 MaybeReportSuspendReadiness();
204 void ExtensionEventObserver::SuspendImminent() {
205 if (should_delay_suspend_)
206 OnSuspendImminent(false);
209 void ExtensionEventObserver::DarkSuspendImminent() {
210 if (should_delay_suspend_)
211 OnSuspendImminent(true);
214 void ExtensionEventObserver::SuspendDone(const base::TimeDelta& duration) {
215 suspend_is_pending_ = false;
216 power_manager_callback_.Reset();
217 suspend_readiness_callback_.Cancel();
220 void ExtensionEventObserver::OnProfileAdded(Profile* profile) {
221 auto result = active_profiles_.insert(profile);
223 if (result.second)
224 extensions::ProcessManager::Get(profile)->AddObserver(this);
227 void ExtensionEventObserver::OnProfileDestroyed(Profile* profile) {
228 if (active_profiles_.erase(profile) == 0)
229 return;
231 extensions::ProcessManager::Get(profile)->RemoveObserver(this);
234 void ExtensionEventObserver::OnSuspendImminent(bool dark_suspend) {
235 if (suspend_is_pending_) {
236 LOG(WARNING) << "OnSuspendImminent called while previous suspend attempt "
237 << "is still pending.";
240 suspend_is_pending_ = true;
241 power_manager_callback_ = DBusThreadManager::Get()
242 ->GetPowerManagerClient()
243 ->GetSuspendReadinessCallback();
245 suspend_readiness_callback_.Reset(
246 base::Bind(&ExtensionEventObserver::MaybeReportSuspendReadiness,
247 weak_factory_.GetWeakPtr()));
249 // Unfortunately, there is a race between the arrival of the
250 // DarkSuspendImminent signal and OnBackgroundEventDispatched. As a result,
251 // there is no way to tell from within this method if a push message is about
252 // to arrive. To try and deal with this, we wait one second before attempting
253 // to report suspend readiness. If there is a push message pending, we should
254 // receive it within that time and increment |suspend_keepalive_count_| to
255 // prevent this callback from reporting ready.
256 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
257 FROM_HERE, suspend_readiness_callback_.callback(),
258 dark_suspend ? base::TimeDelta::FromMilliseconds(kDarkSuspendDelayMs)
259 : base::TimeDelta());
262 void ExtensionEventObserver::MaybeReportSuspendReadiness() {
263 if (!suspend_is_pending_ || suspend_keepalive_count_ > 0 ||
264 power_manager_callback_.is_null())
265 return;
267 suspend_is_pending_ = false;
268 power_manager_callback_.Run();
269 power_manager_callback_.Reset();
272 } // namespace chromeos