From b2e695c2e2e74c8c7d73531410fe09ad81352ed0 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 15 Apr 2025 20:33:15 +0200 Subject: [PATCH] fix: paint and flash issues on macOS (#46628) * fix: paint and flash issues on macOS Co-authored-by: Shelley Vohr * Adhere to paintWhenInitiallyHidden Co-authored-by: Shelley Vohr * fix: patch indices --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- patches/chromium/.patches | 2 +- ...less_mode_handling_in_native_widget.patch} | 23 +++++++++++++++-- .../api/electron_api_browser_window.cc | 2 +- shell/browser/native_window_mac.mm | 7 ++++-- shell/common/options_switches.h | 4 +++ spec/api-browser-window-spec.ts | 25 +++++++++++++++++++ 6 files changed, 57 insertions(+), 6 deletions(-) rename patches/chromium/{fix_add_method_which_disables_headless_mode_on_native_widget.patch => fix_adjust_headless_mode_handling_in_native_widget.patch} (51%) diff --git a/patches/chromium/.patches b/patches/chromium/.patches index 9e9016d5d83d..7e801c05a49d 100644 --- a/patches/chromium/.patches +++ b/patches/chromium/.patches @@ -131,7 +131,7 @@ osr_shared_texture_remove_keyed_mutex_on_win_dxgi.patch feat_allow_usage_of_sccontentsharingpicker_on_supported_platforms.patch chore_partial_revert_of.patch fix_software_compositing_infinite_loop.patch -fix_add_method_which_disables_headless_mode_on_native_widget.patch +fix_adjust_headless_mode_handling_in_native_widget.patch refactor_unfilter_unresponsive_events.patch build_disable_thin_lto_mac.patch build_add_public_config_simdutf_config.patch diff --git a/patches/chromium/fix_add_method_which_disables_headless_mode_on_native_widget.patch b/patches/chromium/fix_adjust_headless_mode_handling_in_native_widget.patch similarity index 51% rename from patches/chromium/fix_add_method_which_disables_headless_mode_on_native_widget.patch rename to patches/chromium/fix_adjust_headless_mode_handling_in_native_widget.patch index 5679169ed250..da3d6e2d8022 100644 --- a/patches/chromium/fix_add_method_which_disables_headless_mode_on_native_widget.patch +++ b/patches/chromium/fix_adjust_headless_mode_handling_in_native_widget.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Cezary Kulakowski Date: Mon, 22 Jul 2024 16:23:13 +0200 -Subject: fix: add method which disables headless mode on native widget +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 @@ -9,8 +9,27 @@ 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. +input list). +We also need to ensure that an initial paint is scheduled when +the compositor is unsuspended in headles mode. + +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 fed3d6a70139443d76ce6181df69bb490c46a081..06783f617ec63702a95d460fc6f14dd94f95917e 100644 +--- a/ui/views/cocoa/native_widget_mac_ns_window_host.mm ++++ b/ui/views/cocoa/native_widget_mac_ns_window_host.mm +@@ -654,9 +654,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_); ++ 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.h b/ui/views/widget/widget.h index 6dae25a61bfaf26c59f044628629771c36be73f3..2e76f18cf48303462c7489a01d002a3531695b83 100644 --- a/ui/views/widget/widget.h diff --git a/shell/browser/api/electron_api_browser_window.cc b/shell/browser/api/electron_api_browser_window.cc index 8d92652b033e..742ca07976da 100644 --- a/shell/browser/api/electron_api_browser_window.cc +++ b/shell/browser/api/electron_api_browser_window.cc @@ -47,7 +47,7 @@ BrowserWindow::BrowserWindow(gin::Arguments* args, // Copy the show setting to webContents, but only if we don't want to paint // when initially hidden bool paint_when_initially_hidden = true; - options.Get("paintWhenInitiallyHidden", &paint_when_initially_hidden); + options.Get(options::kPaintWhenInitiallyHidden, &paint_when_initially_hidden); if (!paint_when_initially_hidden) { bool show = true; options.Get(options::kShow, &show); diff --git a/shell/browser/native_window_mac.mm b/shell/browser/native_window_mac.mm index dbeceb836ea7..16a33ebafc23 100644 --- a/shell/browser/native_window_mac.mm +++ b/shell/browser/native_window_mac.mm @@ -154,6 +154,9 @@ NativeWindowMac::NativeWindowMac(const gin_helper::Dictionary& options, bool hiddenInMissionControl = false; options.Get(options::kHiddenInMissionControl, &hiddenInMissionControl); + bool paint_when_initially_hidden = true; + options.Get(options::kPaintWhenInitiallyHidden, &paint_when_initially_hidden); + // The window without titlebar is treated the same with frameless window. if (title_bar_style_ != TitleBarStyle::kNormal) set_has_frame(false); @@ -194,8 +197,8 @@ NativeWindowMac::NativeWindowMac(const gin_helper::Dictionary& options, params.bounds = bounds; params.delegate = this; params.type = views::Widget::InitParams::TYPE_WINDOW; - // Allow painting before shown, to be later disabled in ElectronNSWindow. - params.headless_mode = true; + // Possibly allow painting before shown - later disabled in ElectronNSWindow. + params.headless_mode = paint_when_initially_hidden; if (IsTranslucent()) { params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent; } diff --git a/shell/common/options_switches.h b/shell/common/options_switches.h index 7f2608410c4a..f8c920fdd7df 100644 --- a/shell/common/options_switches.h +++ b/shell/common/options_switches.h @@ -33,6 +33,10 @@ inline constexpr std::string_view kMaximizable = "maximizable"; inline constexpr std::string_view kFullScreenable = "fullscreenable"; inline constexpr std::string_view kClosable = "closable"; +// Whether to paint when the window is initially hidden. +inline constexpr std::string_view kPaintWhenInitiallyHidden = + "paintWhenInitiallyHidden"; + // whether to keep the window out of mission control inline constexpr std::string_view kHiddenInMissionControl = "hiddenInMissionControl"; diff --git a/spec/api-browser-window-spec.ts b/spec/api-browser-window-spec.ts index 2a6f58fb945d..0a37bd50b8e9 100755 --- a/spec/api-browser-window-spec.ts +++ b/spec/api-browser-window-spec.ts @@ -6474,6 +6474,31 @@ describe('BrowserWindow module', () => { w.loadFile(path.join(fixtures, 'pages', 'send-after-node.html')); }); + // TODO(codebytere): fix on Windows and Linux too + ifdescribe(process.platform === 'darwin')('window.webContents initial paint', () => { + afterEach(closeAllWindows); + it('paints when a window is initially hidden', async () => { + const w = new BrowserWindow({ show: false }); + await w.loadFile(path.join(fixtures, 'pages', 'a.html')); + + const entries = await w.webContents.executeJavaScript(` + new Promise((resolve) => { + const observer = new PerformanceObserver((performance) => { + observer.disconnect(); + resolve(performance.getEntries()); + }); + observer.observe({ entryTypes: ['paint'] }); + }); + + const header = document.createElement('h1'); + header.innerText = 'Paint me!!'; + document.getElementById('div').appendChild(header); + `); + + expect(JSON.stringify(entries)).to.eq('{}'); + }); + }); + describe('window.webContents.focus()', () => { afterEach(closeAllWindows); it('focuses window', async () => {