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

Side by Side Diff: chrome/browser/chromeos/power/extension_event_observer.cc

Issue 823703004: Tracking push events for lucid sleep (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Observers are better. Now with tests! Created 5 years, 11 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
(Empty)
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.
4
5 #include "chrome/browser/chromeos/power/extension_event_observer.h"
6
7 #include "base/bind.h"
8 #include "base/logging.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "chrome/browser/chrome_notification_types.h"
11 #include "chrome/browser/profiles/profile.h"
12 #include "chrome/common/extensions/api/gcm.h"
13 #include "chromeos/dbus/dbus_thread_manager.h"
14 #include "content/public/browser/notification_service.h"
15 #include "extensions/browser/extension_host.h"
16 #include "extensions/browser/process_manager.h"
17 #include "extensions/common/extension.h"
18 #include "extensions/common/manifest_handlers/background_info.h"
19 #include "extensions/common/permissions/api_permission.h"
20 #include "extensions/common/permissions/permissions_data.h"
21
22 namespace chromeos {
23
24 ExtensionEventObserver::TestApi::TestApi(
25 base::WeakPtr<ExtensionEventObserver> parent)
26 : parent_(parent) {
27 }
28
29 ExtensionEventObserver::TestApi::~TestApi() {
30 }
31
32 bool ExtensionEventObserver::TestApi::MaybeRunSuspendReadinessCallback() {
33 if (!parent_ || parent_->suspend_readiness_callback_.callback().is_null())
34 return false;
35
36 parent_->suspend_readiness_callback_.callback().Run();
37 parent_->suspend_readiness_callback_.Cancel();
38 return true;
39 }
40
41 bool ExtensionEventObserver::TestApi::WillDelaySuspendForExtensionHost(
42 extensions::ExtensionHost* host) {
43 if (!parent_)
44 return false;
45
46 return parent_->keepalive_sources_.contains(host);
47 }
48
49 struct ExtensionEventObserver::KeepaliveSources {
50 std::set<int> unacked_push_messages;
51 std::set<uint64> pending_network_requests;
52 };
53
54 ExtensionEventObserver::ExtensionEventObserver()
55 : should_delay_suspend_(true),
56 suspend_is_pending_(false),
57 suspend_keepalive_count_(0),
58 weak_factory_(this) {
59 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_ADDED,
60 content::NotificationService::AllBrowserContextsAndSources());
61 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
62 content::NotificationService::AllBrowserContextsAndSources());
63
64 DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver(this);
65 }
66
67 ExtensionEventObserver::~ExtensionEventObserver() {
68 for (Profile* profile : active_profiles_) {
Daniel Erat 2015/01/13 23:49:09 nit: omit curly brackets
Chirantan Ekbote 2015/01/15 05:06:35 Done.
69 extensions::ProcessManager::Get(profile)->RemoveObserver(this);
70 }
71
72 for (const auto& pair : keepalive_sources_) {
73 extensions::ExtensionHost* host =
74 const_cast<extensions::ExtensionHost*>(pair.first);
75 host->RemoveObserver(this);
76 }
77
78 DBusThreadManager::Get()->GetPowerManagerClient()->RemoveObserver(this);
79 }
80
81 scoped_ptr<ExtensionEventObserver::TestApi>
82 ExtensionEventObserver::GetApiForTesting() {
83 return make_scoped_ptr(
84 new ExtensionEventObserver::TestApi(weak_factory_.GetWeakPtr()));
85 }
86
87 void ExtensionEventObserver::ShouldDelaySuspend(bool should_delay) {
88 should_delay_suspend_ = should_delay;
89 if (!should_delay_suspend_ && suspend_is_pending_) {
90 // There is a suspend attempt pending but this class should no longer be
91 // delaying it. Immediately report readiness.
92 suspend_is_pending_ = false;
93 power_manager_callback_.Run();
94 power_manager_callback_.Reset();
95 suspend_readiness_callback_.Cancel();
96 }
97 }
98
99 void ExtensionEventObserver::Observe(
100 int type,
101 const content::NotificationSource& source,
102 const content::NotificationDetails& details) {
103 switch (type) {
104 case chrome::NOTIFICATION_PROFILE_ADDED: {
105 OnProfileAdded(content::Source<Profile>(source).ptr());
106 break;
107 }
108 case chrome::NOTIFICATION_PROFILE_DESTROYED: {
109 OnProfileDestroyed(content::Source<Profile>(source).ptr());
110 break;
111 }
112 default:
113 NOTREACHED();
114 }
115 }
116
117 void ExtensionEventObserver::OnBackgroundHostCreated(
118 extensions::ExtensionHost* host) {
119 // We only care about ExtensionHosts for extensions that use GCM and have a
120 // lazy background page.
121 if (!host->extension()->permissions_data()->HasAPIPermission(
122 extensions::APIPermission::kGcm) ||
123 !extensions::BackgroundInfo::HasLazyBackgroundPage(host->extension()))
124 return;
125
126 auto result =
127 keepalive_sources_.add(host, make_scoped_ptr(new KeepaliveSources()));
128
129 if (result.second)
130 host->AddObserver(this);
131 }
132
133 void ExtensionEventObserver::OnExtensionHostDestroyed(
134 const extensions::ExtensionHost* host) {
135 DCHECK(keepalive_sources_.contains(host));
136
137 scoped_ptr<KeepaliveSources> sources =
138 keepalive_sources_.take_and_erase(host);
139
140 suspend_keepalive_count_ -= sources->unacked_push_messages.size();
141 suspend_keepalive_count_ -= sources->pending_network_requests.size();
142 MaybeReportSuspendReadiness();
143 }
144
145 void ExtensionEventObserver::OnExtensionMessageDispatched(
146 const extensions::ExtensionHost* host,
147 const std::string& event_name,
148 int message_id) {
149 DCHECK(keepalive_sources_.contains(host));
150
151 if (event_name != extensions::api::gcm::OnMessage::kEventName)
152 return;
153
154 keepalive_sources_.get(host)->unacked_push_messages.insert(message_id);
155 ++suspend_keepalive_count_;
156 }
157
158 void ExtensionEventObserver::OnExtensionMessageAcked(
159 const extensions::ExtensionHost* host,
160 int message_id) {
161 DCHECK(keepalive_sources_.contains(host));
162
163 if (keepalive_sources_.get(host)->unacked_push_messages.erase(message_id) >
164 0) {
165 --suspend_keepalive_count_;
166 MaybeReportSuspendReadiness();
167 }
168 }
169
170 void ExtensionEventObserver::OnNetworkRequestStarted(
171 const extensions::ExtensionHost* host,
172 uint64 request_id) {
173 DCHECK(keepalive_sources_.contains(host));
174
175 KeepaliveSources* sources = keepalive_sources_.get(host);
176
177 // We only care about network requests that were started while a push message
178 // is pending. This is an indication that the network request is related to
179 // the push message.
180 if (sources->unacked_push_messages.empty())
181 return;
182
183 sources->pending_network_requests.insert(request_id);
184 ++suspend_keepalive_count_;
185 }
186
187 void ExtensionEventObserver::OnNetworkRequestDone(
188 const extensions::ExtensionHost* host,
189 uint64 request_id) {
190 DCHECK(keepalive_sources_.contains(host));
191
192 if (keepalive_sources_.get(host)->pending_network_requests.erase(request_id) >
193 0) {
194 --suspend_keepalive_count_;
195 MaybeReportSuspendReadiness();
196 }
197 }
198
199 void ExtensionEventObserver::SuspendImminent() {
200 if (should_delay_suspend_)
201 OnSuspendImminent(false);
202 }
203
204 void ExtensionEventObserver::DarkSuspendImminent() {
205 if (should_delay_suspend_)
206 OnSuspendImminent(true);
207 }
208
209 void ExtensionEventObserver::SuspendDone(const base::TimeDelta& duration) {
210 suspend_is_pending_ = false;
211 power_manager_callback_.Reset();
212 suspend_readiness_callback_.Cancel();
213 }
214
215 void ExtensionEventObserver::OnProfileAdded(Profile* profile) {
216 auto result = active_profiles_.insert(profile);
217
218 if (result.second)
219 extensions::ProcessManager::Get(profile)->AddObserver(this);
220 }
221
222 void ExtensionEventObserver::OnProfileDestroyed(Profile* profile) {
223 if (active_profiles_.erase(profile) == 0)
224 return;
225
226 extensions::ProcessManager::Get(profile)->RemoveObserver(this);
227 }
228
229 void ExtensionEventObserver::OnSuspendImminent(bool dark_suspend) {
230 if (suspend_is_pending_) {
231 LOG(WARNING) << "OnSuspendImminent called while previous suspend attempt "
232 << "is still pending.";
233 }
234
235 suspend_is_pending_ = true;
236 power_manager_callback_ = DBusThreadManager::Get()
237 ->GetPowerManagerClient()
238 ->GetSuspendReadinessCallback();
239
240 suspend_readiness_callback_.Reset(
241 base::Bind(&ExtensionEventObserver::MaybeReportSuspendReadiness,
242 weak_factory_.GetWeakPtr()));
243
244 // Unfortunately, there is a race between the arrival of the
245 // DarkSuspendImminent signal and OnExtensionMessageDispatched. As a result,
246 // there is no way to tell from within this method if a push message is about
247 // to arrive. To try and deal with this, we wait one second before attempting
248 // to report suspend readiness. If there is a push message pending, we should
249 // receive it within that time and increment |suspend_keepalive_count_| to
250 // prevent this callback from reporting ready.
251 base::MessageLoopProxy::current()->PostDelayedTask(
252 FROM_HERE, suspend_readiness_callback_.callback(),
253 dark_suspend ? base::TimeDelta::FromSeconds(1) : base::TimeDelta());
Daniel Erat 2015/01/13 23:49:10 nit: move this to a millisecond constant at the to
Chirantan Ekbote 2015/01/15 05:06:35 Done.
254 }
255
256 void ExtensionEventObserver::MaybeReportSuspendReadiness() {
257 if (!suspend_is_pending_ || suspend_keepalive_count_ > 0 ||
258 power_manager_callback_.is_null())
259 return;
260
261 suspend_is_pending_ = false;
262 power_manager_callback_.Run();
263 power_manager_callback_.Reset();
264 }
265
266 } // namespace chromeos
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698