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

Unified 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, 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 side-by-side diff with in-line comments
Download patch
Index: chrome/browser/chromeos/power/extension_event_observer.cc
diff --git a/chrome/browser/chromeos/power/extension_event_observer.cc b/chrome/browser/chromeos/power/extension_event_observer.cc
new file mode 100644
index 0000000000000000000000000000000000000000..2af86ae83edfffe43362d0dcc66821d2db2fbc55
--- /dev/null
+++ b/chrome/browser/chromeos/power/extension_event_observer.cc
@@ -0,0 +1,272 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/power/extension_event_observer.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/extensions/api/gcm.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "content/public/browser/notification_service.h"
+#include "extensions/browser/extension_host.h"
+#include "extensions/browser/process_manager.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/manifest_handlers/background_info.h"
+#include "extensions/common/permissions/api_permission.h"
+#include "extensions/common/permissions/permissions_data.h"
+
+namespace chromeos {
+
+namespace {
+// The number of milliseconds that we should wait after receiving a
+// DarkSuspendImminent signal before attempting to report readiness to suspend.
+const int kDarkSuspendDelayMs = 1000;
+}
+
+ExtensionEventObserver::TestApi::TestApi(
+ base::WeakPtr<ExtensionEventObserver> parent)
+ : parent_(parent) {
+}
+
+ExtensionEventObserver::TestApi::~TestApi() {
+}
+
+bool ExtensionEventObserver::TestApi::MaybeRunSuspendReadinessCallback() {
+ if (!parent_ || parent_->suspend_readiness_callback_.callback().is_null())
+ return false;
+
+ parent_->suspend_readiness_callback_.callback().Run();
+ parent_->suspend_readiness_callback_.Cancel();
+ return true;
+}
+
+bool ExtensionEventObserver::TestApi::WillDelaySuspendForExtensionHost(
+ extensions::ExtensionHost* host) {
+ if (!parent_)
+ return false;
+
+ return parent_->keepalive_sources_.contains(host);
+}
+
+struct ExtensionEventObserver::KeepaliveSources {
+ std::set<int> unacked_push_messages;
+ std::set<uint64> pending_network_requests;
+};
+
+ExtensionEventObserver::ExtensionEventObserver()
+ : should_delay_suspend_(true),
+ suspend_is_pending_(false),
+ suspend_keepalive_count_(0),
+ weak_factory_(this) {
+ registrar_.Add(this, chrome::NOTIFICATION_PROFILE_ADDED,
+ content::NotificationService::AllBrowserContextsAndSources());
+ registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
+ content::NotificationService::AllBrowserContextsAndSources());
+
+ DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver(this);
+}
+
+ExtensionEventObserver::~ExtensionEventObserver() {
+ for (Profile* profile : active_profiles_)
+ extensions::ProcessManager::Get(profile)->RemoveObserver(this);
+
+ for (const auto& pair : keepalive_sources_) {
+ extensions::ExtensionHost* host =
+ const_cast<extensions::ExtensionHost*>(pair.first);
+ host->RemoveObserver(this);
+ }
+
+ DBusThreadManager::Get()->GetPowerManagerClient()->RemoveObserver(this);
+}
+
+scoped_ptr<ExtensionEventObserver::TestApi>
+ExtensionEventObserver::CreateTestApi() {
+ return make_scoped_ptr(
+ new ExtensionEventObserver::TestApi(weak_factory_.GetWeakPtr()));
+}
+
+void ExtensionEventObserver::SetShouldDelaySuspend(bool should_delay) {
+ should_delay_suspend_ = should_delay;
+ if (!should_delay_suspend_ && suspend_is_pending_) {
+ // There is a suspend attempt pending but this class should no longer be
+ // delaying it. Immediately report readiness.
+ suspend_is_pending_ = false;
+ power_manager_callback_.Run();
+ power_manager_callback_.Reset();
+ suspend_readiness_callback_.Cancel();
+ }
+}
+
+void ExtensionEventObserver::Observe(
+ int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ switch (type) {
+ case chrome::NOTIFICATION_PROFILE_ADDED: {
+ OnProfileAdded(content::Source<Profile>(source).ptr());
+ break;
+ }
+ case chrome::NOTIFICATION_PROFILE_DESTROYED: {
+ OnProfileDestroyed(content::Source<Profile>(source).ptr());
+ break;
+ }
+ default:
+ NOTREACHED();
+ }
+}
+
+void ExtensionEventObserver::OnBackgroundHostCreated(
+ extensions::ExtensionHost* host) {
+ // We only care about ExtensionHosts for extensions that use GCM and have a
+ // lazy background page.
+ if (!host->extension()->permissions_data()->HasAPIPermission(
+ extensions::APIPermission::kGcm) ||
+ !extensions::BackgroundInfo::HasLazyBackgroundPage(host->extension()))
+ return;
+
+ auto result =
+ keepalive_sources_.add(host, make_scoped_ptr(new KeepaliveSources()));
+
+ if (result.second)
+ host->AddObserver(this);
+}
+
+void ExtensionEventObserver::OnExtensionHostDestroyed(
+ const extensions::ExtensionHost* host) {
+ DCHECK(keepalive_sources_.contains(host));
+
+ scoped_ptr<KeepaliveSources> sources =
+ keepalive_sources_.take_and_erase(host);
+
+ suspend_keepalive_count_ -= sources->unacked_push_messages.size();
+ suspend_keepalive_count_ -= sources->pending_network_requests.size();
+ MaybeReportSuspendReadiness();
+}
+
+void ExtensionEventObserver::OnExtensionMessageDispatched(
+ const extensions::ExtensionHost* host,
+ const std::string& event_name,
+ int message_id) {
+ DCHECK(keepalive_sources_.contains(host));
+
+ if (event_name != extensions::api::gcm::OnMessage::kEventName)
+ return;
+
+ keepalive_sources_.get(host)->unacked_push_messages.insert(message_id);
+ ++suspend_keepalive_count_;
+}
+
+void ExtensionEventObserver::OnExtensionMessageAcked(
+ const extensions::ExtensionHost* host,
+ int message_id) {
+ DCHECK(keepalive_sources_.contains(host));
+
+ if (keepalive_sources_.get(host)->unacked_push_messages.erase(message_id) >
+ 0) {
+ --suspend_keepalive_count_;
+ MaybeReportSuspendReadiness();
+ }
+}
+
+void ExtensionEventObserver::OnNetworkRequestStarted(
+ const extensions::ExtensionHost* host,
+ uint64 request_id) {
+ DCHECK(keepalive_sources_.contains(host));
+
+ KeepaliveSources* sources = keepalive_sources_.get(host);
+
+ // We only care about network requests that were started while a push message
+ // is pending. This is an indication that the network request is related to
+ // the push message.
+ if (sources->unacked_push_messages.empty())
+ return;
+
+ sources->pending_network_requests.insert(request_id);
+ ++suspend_keepalive_count_;
+}
+
+void ExtensionEventObserver::OnNetworkRequestDone(
+ const extensions::ExtensionHost* host,
+ uint64 request_id) {
+ DCHECK(keepalive_sources_.contains(host));
+
+ if (keepalive_sources_.get(host)->pending_network_requests.erase(request_id) >
+ 0) {
+ --suspend_keepalive_count_;
+ MaybeReportSuspendReadiness();
+ }
+}
+
+void ExtensionEventObserver::SuspendImminent() {
+ if (should_delay_suspend_)
+ OnSuspendImminent(false);
+}
+
+void ExtensionEventObserver::DarkSuspendImminent() {
+ if (should_delay_suspend_)
+ OnSuspendImminent(true);
+}
+
+void ExtensionEventObserver::SuspendDone(const base::TimeDelta& duration) {
+ suspend_is_pending_ = false;
+ power_manager_callback_.Reset();
+ suspend_readiness_callback_.Cancel();
+}
+
+void ExtensionEventObserver::OnProfileAdded(Profile* profile) {
+ auto result = active_profiles_.insert(profile);
+
+ if (result.second)
+ extensions::ProcessManager::Get(profile)->AddObserver(this);
+}
+
+void ExtensionEventObserver::OnProfileDestroyed(Profile* profile) {
+ if (active_profiles_.erase(profile) == 0)
+ return;
+
+ extensions::ProcessManager::Get(profile)->RemoveObserver(this);
+}
+
+void ExtensionEventObserver::OnSuspendImminent(bool dark_suspend) {
+ if (suspend_is_pending_) {
+ LOG(WARNING) << "OnSuspendImminent called while previous suspend attempt "
+ << "is still pending.";
+ }
+
+ suspend_is_pending_ = true;
+ power_manager_callback_ = DBusThreadManager::Get()
+ ->GetPowerManagerClient()
+ ->GetSuspendReadinessCallback();
+
+ suspend_readiness_callback_.Reset(
+ base::Bind(&ExtensionEventObserver::MaybeReportSuspendReadiness,
+ weak_factory_.GetWeakPtr()));
+
+ // Unfortunately, there is a race between the arrival of the
+ // DarkSuspendImminent signal and OnExtensionMessageDispatched. As a result,
+ // there is no way to tell from within this method if a push message is about
+ // to arrive. To try and deal with this, we wait one second before attempting
+ // to report suspend readiness. If there is a push message pending, we should
+ // receive it within that time and increment |suspend_keepalive_count_| to
+ // prevent this callback from reporting ready.
+ base::MessageLoopProxy::current()->PostDelayedTask(
+ FROM_HERE, suspend_readiness_callback_.callback(),
+ dark_suspend ? base::TimeDelta::FromMilliseconds(kDarkSuspendDelayMs)
+ : base::TimeDelta());
+}
+
+void ExtensionEventObserver::MaybeReportSuspendReadiness() {
+ if (!suspend_is_pending_ || suspend_keepalive_count_ > 0 ||
+ power_manager_callback_.is_null())
+ return;
+
+ suspend_is_pending_ = false;
+ power_manager_callback_.Run();
+ power_manager_callback_.Reset();
+}
+
+} // namespace chromeos

Powered by Google App Engine
This is Rietveld 408576698