1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
content / browser / gpu / browser_child_process_backgrounded_bridge.mm [blame]
// 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