1// Copyright 2017 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "headless/lib/browser/headless_browser_impl.h"
6
7#import "base/mac/scoped_objc_class_swizzler.h"
8#include "base/no_destructor.h"
9#include "content/public/browser/render_widget_host_view.h"
10#include "content/public/browser/web_contents.h"
11#include "headless/lib/browser/headless_web_contents_impl.h"
12#import "ui/base/cocoa/base_view.h"
13#import "ui/gfx/mac/coordinate_conversion.h"
14
15// Overrides events and actions for NSPopUpButtonCell.
16@interface FakeNSPopUpButtonCell : NSObject
17@end
18
19@implementation FakeNSPopUpButtonCell
20
21- (void)performClickWithFrame:(NSRect)frame inView:(NSView*)view {
22}
23
24- (void)attachPopUpWithFrame:(NSRect)frame inView:(NSView*)view {
25}
26
27@end
28
29namespace headless {
30
31namespace {
32
33// Swizzles all event and acctions for NSPopUpButtonCell to avoid showing in
34// headless mode.
35class HeadlessPopUpMethods {
36 public:
37  static void Init() {
38    static base::NoDestructor<HeadlessPopUpMethods> swizzler;
39    ALLOW_UNUSED_LOCAL(swizzler);
40  }
41
42 private:
43  friend class base::NoDestructor<HeadlessPopUpMethods>;
44  HeadlessPopUpMethods()
45      : popup_perform_click_swizzler_([NSPopUpButtonCell class],
46                                      [FakeNSPopUpButtonCell class],
47                                      @selector(performClickWithFrame:inView:)),
48        popup_attach_swizzler_([NSPopUpButtonCell class],
49                               [FakeNSPopUpButtonCell class],
50                               @selector(attachPopUpWithFrame:inView:)) {}
51
52  base::mac::ScopedObjCClassSwizzler popup_perform_click_swizzler_;
53  base::mac::ScopedObjCClassSwizzler popup_attach_swizzler_;
54
55  DISALLOW_COPY_AND_ASSIGN(HeadlessPopUpMethods);
56};
57
58NSString* const kActivityReason = @"Batch headless process";
59const NSActivityOptions kActivityOptions =
60    (NSActivityUserInitiatedAllowingIdleSystemSleep |
61     NSActivityLatencyCritical) &
62    ~(NSActivitySuddenTerminationDisabled |
63      NSActivityAutomaticTerminationDisabled);
64
65}  // namespace
66
67void HeadlessBrowserImpl::PlatformInitialize() {
68  HeadlessPopUpMethods::Init();
69}
70
71void HeadlessBrowserImpl::PlatformStart() {
72  // Disallow headless to be throttled as a background process.
73  [[NSProcessInfo processInfo] beginActivityWithOptions:kActivityOptions
74                                                 reason:kActivityReason];
75}
76
77void HeadlessBrowserImpl::PlatformInitializeWebContents(
78    HeadlessWebContentsImpl* web_contents) {
79  NSView* web_view =
80      web_contents->web_contents()->GetNativeView().GetNativeNSView();
81  [web_view setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
82  // TODO(eseckler): Support enabling BeginFrameControl on Mac. This is tricky
83  // because it's a ui::Compositor startup setting and ui::Compositors are
84  // recycled on Mac, see browser_compositor_view_mac.mm.
85}
86
87void HeadlessBrowserImpl::PlatformSetWebContentsBounds(
88    HeadlessWebContentsImpl* web_contents,
89    const gfx::Rect& bounds) {
90  NSView* web_view =
91      web_contents->web_contents()->GetNativeView().GetNativeNSView();
92  NSRect frame = gfx::ScreenRectToNSRect(bounds);
93  [web_view setFrame:frame];
94
95  content::RenderWidgetHostView* host_view =
96      web_contents->web_contents()->GetRenderWidgetHostView();
97  if (host_view)
98    host_view->SetWindowFrameInScreen(bounds);
99}
100
101ui::Compositor* HeadlessBrowserImpl::PlatformGetCompositor(
102    HeadlessWebContentsImpl* web_contents) {
103  // TODO(eseckler): Support BeginFrameControl on Mac.
104  return nullptr;
105}
106
107}  // namespace headless
108