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

Unified Diff: chrome/browser/chromeos/power/renderer_freezer.cc

Issue 738993002: Re-land chromeos: Add non-extension renderers to the freezer cgroup (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Address comments Created 6 years, 1 month 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/renderer_freezer.cc
diff --git a/chrome/browser/chromeos/power/renderer_freezer.cc b/chrome/browser/chromeos/power/renderer_freezer.cc
index 1aaa79a75293dc5590e924c1c0aa189f970a0c29..6548c9d5c7416fe7e448b3866708ea06c1b9fb43 100644
--- a/chrome/browser/chromeos/power/renderer_freezer.cc
+++ b/chrome/browser/chromeos/power/renderer_freezer.cc
@@ -4,10 +4,24 @@
#include "chrome/browser/chromeos/power/renderer_freezer.h"
+#include <string>
+
#include "base/bind.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
+#include "base/process/process_handle.h"
#include "chromeos/dbus/dbus_thread_manager.h"
+#include "content/public/browser/notification_details.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_source.h"
+#include "content/public/browser/notification_types.h"
+#include "content/public/browser/render_process_host.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/browser/notification_types.h"
+#include "extensions/browser/process_map.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/permissions/api_permission.h"
+#include "extensions/common/permissions/permissions_data.h"
namespace chromeos {
@@ -15,36 +29,33 @@ RendererFreezer::RendererFreezer(scoped_ptr<RendererFreezer::Delegate> delegate)
: frozen_(false),
delegate_(delegate.Pass()),
weak_factory_(this) {
- if (delegate_->CanFreezeRenderers())
- DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver(this);
+ if (delegate_->CanFreezeRenderers()) {
+ DBusThreadManager::Get()
+ ->GetPowerManagerClient()
+ ->SetRenderProcessManagerDelegate(weak_factory_.GetWeakPtr());
+
+ registrar_.Add(
+ this,
+ content::NOTIFICATION_RENDERER_PROCESS_CREATED,
+ content::NotificationService::AllBrowserContextsAndSources());
+ }
}
RendererFreezer::~RendererFreezer() {
- if (delegate_->CanFreezeRenderers())
- DBusThreadManager::Get()->GetPowerManagerClient()->RemoveObserver(this);
+ for (int rph_id : gcm_extension_processes_) {
+ content::RenderProcessHost* host =
+ content::RenderProcessHost::FromID(rph_id);
+ if (host)
+ host->RemoveObserver(this);
+ }
}
void RendererFreezer::SuspendImminent() {
- // If there was already a callback pending, this will cancel it and create a
- // new one.
- suspend_readiness_callback_.Reset(
- base::Bind(&RendererFreezer::OnReadyToSuspend,
- weak_factory_.GetWeakPtr(),
- DBusThreadManager::Get()
- ->GetPowerManagerClient()
- ->GetSuspendReadinessCallback()));
-
- base::MessageLoop::current()->PostTask(
- FROM_HERE, suspend_readiness_callback_.callback());
+ if (delegate_->FreezeRenderers())
+ frozen_ = true;
}
-void RendererFreezer::SuspendDone(const base::TimeDelta& sleep_duration) {
- // If we get a SuspendDone before we've had a chance to run OnReadyForSuspend,
- // we should cancel it because we no longer want to freeze the renderers. If
- // we've already run it then cancelling the callback shouldn't really make a
- // difference.
- suspend_readiness_callback_.Cancel();
-
+void RendererFreezer::SuspendDone() {
if (!frozen_)
return;
@@ -58,13 +69,98 @@ void RendererFreezer::SuspendDone(const base::TimeDelta& sleep_duration) {
frozen_ = false;
}
-void RendererFreezer::OnReadyToSuspend(
- const base::Closure& power_manager_callback) {
- if (delegate_->FreezeRenderers())
- frozen_ = true;
+void RendererFreezer::Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ switch (type) {
+ case content::NOTIFICATION_RENDERER_PROCESS_CREATED: {
+ content::RenderProcessHost* process =
+ content::Source<content::RenderProcessHost>(source).ptr();
+ OnRenderProcessCreated(process);
+ break;
+ }
+ default: {
+ NOTREACHED();
+ break;
+ }
+ }
+}
+
+void RendererFreezer::RenderProcessExited(content::RenderProcessHost* host,
+ base::TerminationStatus status,
+ int exit_code) {
+ auto it = gcm_extension_processes_.find(host->GetID());
+ if (it == gcm_extension_processes_.end()) {
+ LOG(ERROR) << "Received unrequested RenderProcessExited message";
+ return;
+ }
+ gcm_extension_processes_.erase(it);
+
+ // When this function is called, the renderer process has died but the
+ // RenderProcessHost will not be destroyed. If a new renderer process is
+ // created for this RPH, registering as an observer again will trigger a
+ // warning about duplicate observers. To prevent this we just stop observing
+ // this RPH until another renderer process is created for it.
+ host->RemoveObserver(this);
+}
+
+void RendererFreezer::RenderProcessHostDestroyed(
+ content::RenderProcessHost* host) {
+ auto it = gcm_extension_processes_.find(host->GetID());
+ if (it == gcm_extension_processes_.end()) {
+ LOG(ERROR) << "Received unrequested RenderProcessHostDestroyed message";
+ return;
+ }
+
+ gcm_extension_processes_.erase(it);
+}
+
+void RendererFreezer::OnRenderProcessCreated(content::RenderProcessHost* rph) {
+ const int rph_id = rph->GetID();
+
+ if (gcm_extension_processes_.find(rph_id) != gcm_extension_processes_.end()) {
+ LOG(ERROR) << "Received duplicate notifications about the creation of a "
+ << "RenderProcessHost with id " << rph_id;
+ return;
+ }
+
+ // According to extensions::ProcessMap, extensions and renderers have a
+ // many-to-many relationship. Specifically, a hosted app can appear in many
+ // renderers while any other kind of extension can be running in "split mode"
+ // if there is an incognito window open and so could appear in two renderers.
+ //
+ // We don't care about hosted apps because they cannot use GCM so we only need
+ // to worry about extensions in "split mode". Luckily for us this function is
+ // called any time a new renderer process is created so we don't really need
+ // to care whether we are currently in an incognito context. We just need to
+ // iterate over all the extensions in the newly created process and take the
+ // appropriate action based on whether we find an extension using GCM.
+ content::BrowserContext* context = rph->GetBrowserContext();
+ extensions::ExtensionRegistry* registry =
+ extensions::ExtensionRegistry::Get(context);
+ for (const std::string& extension_id :
+ extensions::ProcessMap::Get(context)->GetExtensionsInProcess(rph_id)) {
+ if (!registry->GetExtensionById(extension_id,
+ extensions::ExtensionRegistry::ENABLED)
+ ->permissions_data()
+ ->HasAPIPermission(extensions::APIPermission::kGcm)) {
+ continue;
+ }
+
+ // This renderer has an extension that is using GCM. Make sure it is not
+ // frozen during suspend.
+ delegate_->SetShouldFreezeRenderer(rph->GetHandle(), false);
+ gcm_extension_processes_.insert(rph_id);
+
+ // Watch to see if the renderer process or the RenderProcessHost is
+ // destroyed.
+ rph->AddObserver(this);
+ return;
+ }
- DCHECK(!power_manager_callback.is_null());
- power_manager_callback.Run();
+ // We didn't find an extension in this RenderProcessHost that is using GCM so
+ // we can go ahead and freeze it on suspend.
+ delegate_->SetShouldFreezeRenderer(rph->GetHandle(), true);
}
} // namespace chromeos
« no previous file with comments | « chrome/browser/chromeos/power/renderer_freezer.h ('k') | chrome/browser/chromeos/power/renderer_freezer_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698