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