OLD | NEW |
---|---|
(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 | |
OLD | NEW |