From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Cezary Kulakowski <cezary@openfin.co>
Date: Mon, 22 Jul 2024 16:23:13 +0200
Subject: fix: adjust headless mode handling in native widget

We need this method as we create window in headless mode and we
switch it back to normal mode only after inital paint is done in
order to get some events like WebContents.beginFrameSubscription.
If we don't set `is_headless_` to false then some child windows
e.g. autofill popups will be created in headless mode leading to
ui problems (like dissapearing popup during typing in html's
input list).

We also need to ensure that an initial paint is scheduled when
the compositor is unsuspended in headles mode.

Additionally, this patch reverts
https://chromium-review.googlesource.com/c/chromium/src/+/6936895
as we depend on the removed functionality in this patch.

2026-02-06: Partial revert of the following patch to prevent
additional headless changes from breaking macOS window behavior.
https://chromium-review.googlesource.com/c/chromium/src/+/7487666

diff --git a/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm b/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm
index 94bd32ce1ddd3f8b4315cd06be59d7550b591891..ad005e0a19a7763da089fccc659d93c8815dc860 100644
--- a/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm
+++ b/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm
@@ -231,6 +231,7 @@ @implementation NativeWidgetMacNSWindow {
   BOOL _isEnforcingNeverMadeVisible;
   BOOL _activationIndependence;
   BOOL _isTooltip;
+  BOOL _isHeadless;
   BOOL _isShufflingForOrdering;
   BOOL _miniaturizationInProgress;
   std::unique_ptr<NativeWidgetMacNSWindowHeadlessInfo> _headless_info;
@@ -238,6 +239,7 @@ @implementation NativeWidgetMacNSWindow {
 @synthesize bridgedNativeWidgetId = _bridgedNativeWidgetId;
 @synthesize bridge = _bridge;
 @synthesize isTooltip = _isTooltip;
+@synthesize isHeadless = _isHeadless;
 @synthesize isShufflingForOrdering = _isShufflingForOrdering;
 @synthesize preventKeyWindow = _preventKeyWindow;
 @synthesize childWindowAddedHandler = _childWindowAddedHandler;
@@ -259,23 +261,6 @@ - (instancetype)initWithContentRect:(NSRect)contentRect
   return self;
 }
 
-- (BOOL)isHeadless {
-  return _headless_info != nullptr;
-}
-
-- (void)setIsHeadless:(BOOL)isHeadless {
-  // NativeWidgetMacNSWindowHeadlessInfo constructor overrides certain NSWindow
-  // methods in order to implement headless mode behavior. This affects all
-  // NativeWidgetMacNSWindow instances, however, the overrides will fallback to
-  // the original implementations if there is no headless info associated with
-  // the window.
-  if (isHeadless) {
-    _headless_info = std::make_unique<NativeWidgetMacNSWindowHeadlessInfo>();
-  } else {
-    _headless_info.reset();
-  }
-}
-
 - (NativeWidgetMacNSWindowHeadlessInfo*)headlessInfo {
   return _headless_info.get();
 }
diff --git a/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm b/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm
index 4d81f8bce7febc78bb620a46683f9c7dcf459227..672d90ed9bf76ba4c66d83d3f0ccd9970b0bb1b3 100644
--- a/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm
+++ b/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm
@@ -535,7 +535,7 @@ NSUInteger CountBridgedWindows(NSArray* child_windows) {
   is_translucent_window_ = params->is_translucent;
   pending_restoration_data_ = params->state_restoration_data;
 
-  if (display::Screen::Get()->IsHeadless()) {
+  if (params->is_headless_mode_window) {
     [window_ setIsHeadless:YES];
   }
 
diff --git a/components/remote_cocoa/common/native_widget_ns_window.mojom b/components/remote_cocoa/common/native_widget_ns_window.mojom
index 7387ef1852678d48908bd1d7dc0e5d44ca613195..4aa37d1f54758e2acbf9b4e467cc22fc1e092e68 100644
--- a/components/remote_cocoa/common/native_widget_ns_window.mojom
+++ b/components/remote_cocoa/common/native_widget_ns_window.mojom
@@ -83,6 +83,8 @@ struct NativeWidgetNSWindowInitParams {
   // NSWindowCollectionBehaviorParticipatesInCycle (this is not the
   // default for NSWindows with NSWindowStyleMaskBorderless).
   bool force_into_collection_cycle;
+  // If true, the window was created in headless mode.
+  bool is_headless_mode_window;
   // An opaque blob of AppKit data which includes, among other things, a
   // window's workspace and fullscreen state, and can be retrieved from or
   // applied to a window.
diff --git a/ui/views/cocoa/native_widget_mac_ns_window_host.h b/ui/views/cocoa/native_widget_mac_ns_window_host.h
index 2239b085ac7fd87fe06aef1001551f8afe8e21e4..9ead3ab0755fe5c3500893325f0597e07e7241cc 100644
--- a/ui/views/cocoa/native_widget_mac_ns_window_host.h
+++ b/ui/views/cocoa/native_widget_mac_ns_window_host.h
@@ -556,6 +556,7 @@ class VIEWS_EXPORT NativeWidgetMacNSWindowHost
   bool is_miniaturized_ = false;
   bool is_window_key_ = false;
   bool is_mouse_capture_active_ = false;
+  bool is_headless_mode_window_ = false;
   bool is_zoomed_ = false;
   gfx::Rect window_bounds_before_fullscreen_;
 
diff --git a/ui/views/cocoa/native_widget_mac_ns_window_host.mm b/ui/views/cocoa/native_widget_mac_ns_window_host.mm
index a1185e4f63de04c56448257567533764476d6c3c..a79f6a28aae530dab3043fb30f8f0a9778f7230c 100644
--- a/ui/views/cocoa/native_widget_mac_ns_window_host.mm
+++ b/ui/views/cocoa/native_widget_mac_ns_window_host.mm
@@ -466,6 +466,7 @@ void HandleAccelerator(const ui::Accelerator& accelerator,
   if (!is_tooltip) {
     tooltip_manager_ = std::make_unique<TooltipManagerMac>(GetNSWindowMojo());
   }
+  is_headless_mode_window_ = params.ShouldInitAsHeadless();
 
   if (params.workspace.length()) {
     std::string restoration_data;
@@ -483,6 +484,7 @@ void HandleAccelerator(const ui::Accelerator& accelerator,
     window_params->modal_type = widget->widget_delegate()->GetModalType();
     window_params->is_translucent =
         params.opacity == Widget::InitParams::WindowOpacity::kTranslucent;
+    window_params->is_headless_mode_window = is_headless_mode_window_;
     window_params->is_tooltip = is_tooltip;
 
     // macOS likes to put shadows on most things. However, frameless windows
@@ -664,9 +666,10 @@ void HandleAccelerator(const ui::Accelerator& accelerator,
   // case it will never become visible but we want its compositor to produce
   // frames for screenshooting and screencasting.
   UpdateCompositorProperties();
-  layer()->SetVisible(is_visible_);
-  if (is_visible_ || display::Screen::Get()->IsHeadless()) {
+  layer()->SetVisible(is_visible_ || is_headless_mode_window_);
+  if (is_visible_ || is_headless_mode_window_) {
     compositor_->Unsuspend();
+    layer()->SchedulePaint(layer()->bounds());
   }
 
   // Register the CGWindowID (used to identify this window for video capture)
diff --git a/ui/views/widget/widget.cc b/ui/views/widget/widget.cc
index 76579d56af01b14504ce240f09b60bdb7f5e6b6a..5a6ae46a0033c3bdc9cebc86e09c1aa08d989708 100644
--- a/ui/views/widget/widget.cc
+++ b/ui/views/widget/widget.cc
@@ -223,6 +223,18 @@ ui::ZOrderLevel Widget::InitParams::EffectiveZOrderLevel() const {
   }
 }
 
+bool Widget::InitParams::ShouldInitAsHeadless() const {
+  if (headless_mode) {
+    return true;
+  }
+
+  if (Widget* top_level_widget = GetTopLevelWidgetForNativeView(parent)) {
+    return top_level_widget->is_headless();
+  }
+
+  return false;
+}
+
 void Widget::InitParams::SetParent(Widget* parent_widget) {
   SetParent(parent_widget->GetNativeView());
 }
@@ -470,6 +482,7 @@ void Widget::Init(InitParams params) {
 
   params.child |= (params.type == InitParams::TYPE_CONTROL);
   is_top_level_ = !params.child;
+  is_headless_ = params.ShouldInitAsHeadless();
   is_autosized_ = params.autosize;
 
   if (params.opacity == views::Widget::InitParams::WindowOpacity::kInferred &&
diff --git a/ui/views/widget/widget.h b/ui/views/widget/widget.h
index fe512d76f83fa9979f1ba87d55f22fdfb9e73fef..f37aef44731dd69a9822f8dbff2372f17f24a9b1 100644
--- a/ui/views/widget/widget.h
+++ b/ui/views/widget/widget.h
@@ -324,6 +324,11 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate,
     // case where |activatable| is |kDefault|.
     bool CanActivate() const;
 
+    // Returns whether the widget should be initialized as headless by checking
+    // if |headless_mode| or the associated top level widget's |is_headless_|
+    // are set.
+    bool ShouldInitAsHeadless() const;
+
     // Returns the z-order level, based on the overriding |z_order| but also
     // taking into account special levels due to |type|.
     ui::ZOrderLevel EffectiveZOrderLevel() const;
@@ -504,6 +509,9 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate,
     // If true then the widget uses software compositing.
     bool force_software_compositing = false;
 
+    // If set, the widget was created in headless mode.
+    bool headless_mode = false;
+
     // If set, the window size will follow the content preferred size.
     bool autosize = false;
 
@@ -1291,6 +1299,11 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate,
   // with it. TYPE_CONTROL and TYPE_TOOLTIP is not considered top level.
   bool is_top_level() const { return is_top_level_; }
 
+  // True if widget was created in headless mode.
+  bool is_headless() const { return is_headless_; }
+
+  void DisableHeadlessMode() { is_headless_ = false; }
+
   // True if the window size will follow the content preferred size.
   bool is_autosized() const { return is_autosized_; }
 
@@ -1732,6 +1745,9 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate,
   // If true, the mouse is currently down.
   bool is_mouse_button_pressed_ = false;
 
+  // If set, the widget was created in headless mode.
+  bool is_headless_ = false;
+
   // If set, the window size will follow the content preferred size.
   bool is_autosized_ = false;
 
