// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/gpu/browser_child_process_backgrounded_bridge.h"
#import <AppKit/AppKit.h>
#import <Foundation/Foundation.h>
#include <memory>
#include "base/process/process.h"
#include "content/browser/browser_child_process_host_impl.h"
#include "content/public/browser/child_process_data.h"
namespace content {
namespace {
bool g_notifications_enabled = true;
} // namespace
struct BrowserChildProcessBackgroundedBridge::ObjCStorage {
// Registration IDs for NSApplicationDidBecomeActiveNotification and
// NSApplicationDidResignActiveNotification.
id __strong did_become_active_observer = nil;
id __strong did_resign_active_observer = nil;
};
BrowserChildProcessBackgroundedBridge::BrowserChildProcessBackgroundedBridge(
BrowserChildProcessHostImpl* process)
: process_(process), objc_storage_(std::make_unique<ObjCStorage>()) {
base::PortProvider* port_provider =
BrowserChildProcessHost::GetPortProvider();
if (port_provider->TaskForHandle(process_->GetData().GetProcess().Handle()) !=
MACH_PORT_NULL) {
Initialize();
} else {
// The process has launched but the task port is not available yet. Defer
// initialization until it is.
scoped_port_provider_observer_.Observe(port_provider);
}
}
BrowserChildProcessBackgroundedBridge::
~BrowserChildProcessBackgroundedBridge() {
if (objc_storage_->did_become_active_observer) {
[NSNotificationCenter.defaultCenter
removeObserver:objc_storage_->did_become_active_observer];
}
if (objc_storage_->did_resign_active_observer) {
[NSNotificationCenter.defaultCenter
removeObserver:objc_storage_->did_resign_active_observer];
}
}
void BrowserChildProcessBackgroundedBridge::
SimulateBrowserProcessForegroundedForTesting() {
OnBrowserProcessForegrounded();
}
void BrowserChildProcessBackgroundedBridge::
SimulateBrowserProcessBackgroundedForTesting() {
OnBrowserProcessBackgrounded();
}
// static
void BrowserChildProcessBackgroundedBridge::SetOSNotificationsEnabledForTesting(
bool enabled) {
g_notifications_enabled = enabled;
}
void BrowserChildProcessBackgroundedBridge::Initialize() {
// Do the initial adjustment based on the initial value of the
// TASK_CATEGORY_POLICY role of the browser process.
base::SelfPortProvider self_port_provider;
const base::Process::Priority browser_process_priority =
base::Process::Current().GetPriority(&self_port_provider);
process_->SetProcessPriority(browser_process_priority);
if (!g_notifications_enabled) {
return;
}
// Now subscribe to both NSApplicationDidBecomeActiveNotification and
// NSApplicationDidResignActiveNotification, which are sent when the browser
// process becomes foreground and background, respectively. The blocks
// implicitly captures `this`. It is safe to do so since the subscriptions are
// removed in the destructor.
objc_storage_->did_become_active_observer =
[NSNotificationCenter.defaultCenter
addObserverForName:NSApplicationDidBecomeActiveNotification
object:nil
queue:nil
usingBlock:^(NSNotification* notification) {
OnBrowserProcessForegrounded();
}];
objc_storage_->did_resign_active_observer =
[NSNotificationCenter.defaultCenter
addObserverForName:NSApplicationDidResignActiveNotification
object:nil
queue:nil
usingBlock:^(NSNotification* notification) {
OnBrowserProcessBackgrounded();
}];
}
void BrowserChildProcessBackgroundedBridge::OnReceivedTaskPort(
base::ProcessHandle process_handle) {
if (process_->GetData().GetProcess().Handle() != process_handle) {
return;
}
// Just received the task port for the target process. It is now possible to
// change its TASK_CATEGORY_POLICY.
scoped_port_provider_observer_.Reset();
Initialize();
}
void BrowserChildProcessBackgroundedBridge::OnBrowserProcessForegrounded() {
process_->SetProcessPriority(base::Process::Priority::kUserBlocking);
}
void BrowserChildProcessBackgroundedBridge::OnBrowserProcessBackgrounded() {
process_->SetProcessPriority(base::Process::Priority::kUserVisible);
}
} // namespace content