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

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: Kill renderer if it sends a bad message id Created 5 years, 10 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 namespace {
25 // The number of milliseconds that we should wait after receiving a
26 // DarkSuspendImminent signal before attempting to report readiness to suspend.
27 const int kDarkSuspendDelayMs = 1000;
28 }
29
30 ExtensionEventObserver::TestApi::TestApi(
31 base::WeakPtr<ExtensionEventObserver> parent)
32 : parent_(parent) {
33 }
34
35 ExtensionEventObserver::TestApi::~TestApi() {
36 }
37
38 bool ExtensionEventObserver::TestApi::MaybeRunSuspendReadinessCallback() {
39 if (!parent_ || parent_->suspend_readiness_callback_.callback().is_null())
40 return false;
41
42 parent_->suspend_readiness_callback_.callback().Run();
43 parent_->suspend_readiness_callback_.Cancel();
44 return true;
45 }
46
47 bool ExtensionEventObserver::TestApi::WillDelaySuspendForExtensionHost(
48 extensions::ExtensionHost* host) {
49 if (!parent_)
50 return false;
51
52 return parent_->keepalive_sources_.contains(host);
53 }
54
55 struct ExtensionEventObserver::KeepaliveSources {
56 std::set<int> unacked_push_messages;
57 std::set<uint64> pending_network_requests;
58 };
59
60 ExtensionEventObserver::ExtensionEventObserver()
61 : should_delay_suspend_(true),
62 suspend_is_pending_(false),
63 suspend_keepalive_count_(0),
64 weak_factory_(this) {
65 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_ADDED,
66 content::NotificationService::AllBrowserContextsAndSources());
67 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
68 content::NotificationService::AllBrowserContextsAndSources());
69
70 DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver(this);
71 }
72
73 ExtensionEventObserver::~ExtensionEventObserver() {
74 for (Profile* profile : active_profiles_)
75 extensions::ProcessManager::Get(profile)->RemoveObserver(this);
76
77 for (const auto& pair : keepalive_sources_) {
78 extensions::ExtensionHost* host =
79 const_cast<extensions::ExtensionHost*>(pair.first);
80 host->RemoveObserver(this);
81 }
82
83 DBusThreadManager::Get()->GetPowerManagerClient()->RemoveObserver(this);
84 }
85
86 scoped_ptr<ExtensionEventObserver::TestApi>
87 ExtensionEventObserver::CreateTestApi() {
88 return make_scoped_ptr(
89 new ExtensionEventObserver::TestApi(weak_factory_.GetWeakPtr()));
90 }
91
92 void ExtensionEventObserver::SetShouldDelaySuspend(bool should_delay) {
93 should_delay_suspend_ = should_delay;
94 if (!should_delay_suspend_ && suspend_is_pending_) {
95 // There is a suspend attempt pending but this class should no longer be
96 // delaying it. Immediately report readiness.
97 suspend_is_pending_ = false;
98 power_manager_callback_.Run();
99 power_manager_callback_.Reset();
100 suspend_readiness_callback_.Cancel();
101 }
102 }
103
104 void ExtensionEventObserver::Observe(
105 int type,
106 const content::NotificationSource& source,
107 const content::NotificationDetails& details) {
108 switch (type) {
109 case chrome::NOTIFICATION_PROFILE_ADDED: {
110 OnProfileAdded(content::Source<Profile>(source).ptr());
111 break;
112 }
113 case chrome::NOTIFICATION_PROFILE_DESTROYED: {
114 OnProfileDestroyed(content::Source<Profile>(source).ptr());
115 break;
116 }
117 default:
118 NOTREACHED();
119 }
120 }
121
122 void ExtensionEventObserver::OnBackgroundHostCreated(
123 extensions::ExtensionHost* host) {
124 // We only care about ExtensionHosts for extensions that use GCM and have a
125 // lazy background page.
126 if (!host->extension()->permissions_data()->HasAPIPermission(
127 extensions::APIPermission::kGcm) ||
128 !extensions::BackgroundInfo::HasLazyBackgroundPage(host->extension()))
129 return;
130
131 auto result =
132 keepalive_sources_.add(host, make_scoped_ptr(new KeepaliveSources()));
133
134 if (result.second)
135 host->AddObserver(this);
136 }
137
138 void ExtensionEventObserver::OnExtensionHostDestroyed(
139 const extensions::ExtensionHost* host) {
140 DCHECK(keepalive_sources_.contains(host));
141
142 scoped_ptr<KeepaliveSources> sources =
143 keepalive_sources_.take_and_erase(host);
144
145 suspend_keepalive_count_ -= sources->unacked_push_messages.size();
146 suspend_keepalive_count_ -= sources->pending_network_requests.size();
147 MaybeReportSuspendReadiness();
148 }
149
150 void ExtensionEventObserver::OnExtensionMessageDispatched(
151 const extensions::ExtensionHost* host,
152 const std::string& event_name,
153 int message_id) {
154 DCHECK(keepalive_sources_.contains(host));
155
156 if (event_name != extensions::api::gcm::OnMessage::kEventName)
157 return;
158
159 keepalive_sources_.get(host)->unacked_push_messages.insert(message_id);
160 ++suspend_keepalive_count_;
161 }
162
163 void ExtensionEventObserver::OnExtensionMessageAcked(
164 const extensions::ExtensionHost* host,
165 int message_id) {
166 DCHECK(keepalive_sources_.contains(host));
167
168 if (keepalive_sources_.get(host)->unacked_push_messages.erase(message_id) >
169 0) {
170 --suspend_keepalive_count_;
171 MaybeReportSuspendReadiness();
172 }
173 }
174
175 void ExtensionEventObserver::OnNetworkRequestStarted(
176 const extensions::ExtensionHost* host,
177 uint64 request_id) {
178 DCHECK(keepalive_sources_.contains(host));
179
180 KeepaliveSources* sources = keepalive_sources_.get(host);
181
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;
187
188 sources->pending_network_requests.insert(request_id);
189 ++suspend_keepalive_count_;
190 }
191
192 void ExtensionEventObserver::OnNetworkRequestDone(
193 const extensions::ExtensionHost* host,
194 uint64 request_id) {
195 DCHECK(keepalive_sources_.contains(host));
196
197 if (keepalive_sources_.get(host)->pending_network_requests.erase(request_id) >
198 0) {
199 --suspend_keepalive_count_;
200 MaybeReportSuspendReadiness();
201 }
202 }
203
204 void ExtensionEventObserver::SuspendImminent() {
205 if (should_delay_suspend_)
206 OnSuspendImminent(false);
207 }
208
209 void ExtensionEventObserver::DarkSuspendImminent() {
210 if (should_delay_suspend_)
211 OnSuspendImminent(true);
212 }
213
214 void ExtensionEventObserver::SuspendDone(const base::TimeDelta& duration) {
215 suspend_is_pending_ = false;
216 power_manager_callback_.Reset();
217 suspend_readiness_callback_.Cancel();
218 }
219
220 void ExtensionEventObserver::OnProfileAdded(Profile* profile) {
221 auto result = active_profiles_.insert(profile);
222
223 if (result.second)
224 extensions::ProcessManager::Get(profile)->AddObserver(this);
225 }
226
227 void ExtensionEventObserver::OnProfileDestroyed(Profile* profile) {
228 if (active_profiles_.erase(profile) == 0)
229 return;
230
231 extensions::ProcessManager::Get(profile)->RemoveObserver(this);
232 }
233
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.";
238 }
239
240 suspend_is_pending_ = true;
241 power_manager_callback_ = DBusThreadManager::Get()
242 ->GetPowerManagerClient()
243 ->GetSuspendReadinessCallback();
244
245 suspend_readiness_callback_.Reset(
246 base::Bind(&ExtensionEventObserver::MaybeReportSuspendReadiness,
247 weak_factory_.GetWeakPtr()));
248
249 // Unfortunately, there is a race between the arrival of the
250 // DarkSuspendImminent signal and OnExtensionMessageDispatched. 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::MessageLoopProxy::current()->PostDelayedTask(
257 FROM_HERE, suspend_readiness_callback_.callback(),
258 dark_suspend ? base::TimeDelta::FromMilliseconds(kDarkSuspendDelayMs)
259 : base::TimeDelta());
260 }
261
262 void ExtensionEventObserver::MaybeReportSuspendReadiness() {
263 if (!suspend_is_pending_ || suspend_keepalive_count_ > 0 ||
264 power_manager_callback_.is_null())
265 return;
266
267 suspend_is_pending_ = false;
268 power_manager_callback_.Run();
269 power_manager_callback_.Reset();
270 }
271
272 } // namespace chromeos
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698