Chromium Code Reviews| 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 |