fix: abnormal behavior of windows background material (#47956)

* fix: abnormal behavior of windows background material

* chore: update patches

---------

Co-authored-by: patchup[bot] <73610968+patchup[bot]@users.noreply.github.com>
This commit is contained in:
Shelley Vohr 2025-08-04 17:17:00 +02:00 committed by GitHub
commit c7ae20c4f2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 195 additions and 107 deletions

View file

@ -109,7 +109,6 @@ fix_use_delegated_generic_capturer_when_available.patch
expose_webblob_path_to_allow_embedders_to_get_file_paths.patch expose_webblob_path_to_allow_embedders_to_get_file_paths.patch
fix_move_autopipsettingshelper_behind_branding_buildflag.patch fix_move_autopipsettingshelper_behind_branding_buildflag.patch
revert_remove_the_allowaggressivethrottlingwithwebsocket_feature.patch revert_remove_the_allowaggressivethrottlingwithwebsocket_feature.patch
fix_activate_background_material_on_windows.patch
feat_allow_passing_of_objecttemplate_to_objecttemplatebuilder.patch feat_allow_passing_of_objecttemplate_to_objecttemplatebuilder.patch
chore_remove_check_is_test_on_script_injection_tracker.patch chore_remove_check_is_test_on_script_injection_tracker.patch
fix_restore_original_resize_performance_on_macos.patch fix_restore_original_resize_performance_on_macos.patch
@ -142,3 +141,4 @@ fix_linter_error.patch
chore_grandfather_in_electron_views_and_delegates.patch chore_grandfather_in_electron_views_and_delegates.patch
refactor_patch_electron_permissiontypes_into_blink.patch refactor_patch_electron_permissiontypes_into_blink.patch
fix_add_macos_memory_query_fallback_to_avoid_crash.patch fix_add_macos_memory_query_fallback_to_avoid_crash.patch
fix_resolve_dynamic_background_material_update_issue_on_windows_11.patch

View file

@ -1,68 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: clavin <clavin@electronjs.org>
Date: Mon, 11 Dec 2023 20:43:34 -0300
Subject: fix: activate background material on windows
This patch adds a condition to the HWND message handler to allow windows
with translucent background materials to become activated.
It also ensures the lParam of WM_NCACTIVATE is set to -1 so as to not repaint
the client area, which can lead to a title bar incorrectly being displayed in
frameless windows.
This patch likely can't be upstreamed as-is, as Chromium doesn't have
this use case in mind currently.
diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc
index 1a10bd1a6c527633f97d6eee6525b1e45a3fcd3d..2b983ae3ec3e1d17951add818c2610582d586377 100644
--- a/ui/views/win/hwnd_message_handler.cc
+++ b/ui/views/win/hwnd_message_handler.cc
@@ -937,13 +937,13 @@ void HWNDMessageHandler::FrameTypeChanged() {
void HWNDMessageHandler::PaintAsActiveChanged() {
if (!delegate_->HasNonClientView() || !delegate_->CanActivate() ||
- !delegate_->HasFrame() ||
+ (!delegate_->HasFrame() && !is_translucent_) ||
(delegate_->GetFrameMode() == FrameMode::CUSTOM_DRAWN)) {
return;
}
DefWindowProcWithRedrawLock(WM_NCACTIVATE, delegate_->ShouldPaintAsActive(),
- 0);
+ delegate_->HasFrame() ? 0 : -1);
}
void HWNDMessageHandler::SetWindowIcons(const gfx::ImageSkia& window_icon,
@@ -1732,7 +1732,7 @@ void HWNDMessageHandler::OnActivateApp(BOOL active, DWORD thread_id) {
if (delegate_->HasNonClientView() && !active &&
thread_id != GetCurrentThreadId()) {
// Update the native frame if it is rendering the non-client area.
- if (HasSystemFrame()) {
+ if (is_translucent_ || HasSystemFrame()) {
DefWindowProcWithRedrawLock(WM_NCACTIVATE, FALSE, 0);
}
}
@@ -2340,17 +2340,18 @@ LRESULT HWNDMessageHandler::OnNCActivate(UINT message,
delegate_->SchedulePaint();
}
- // Calling DefWindowProc is only necessary if there's a system frame being
- // drawn. Otherwise it can draw an incorrect title bar and cause visual
- // corruption.
- if (!delegate_->HasFrame() ||
+ // If the window is translucent, it may have the Mica background.
+ // In that case, it's necessary to call |DefWindowProc|, but we can
+ // pass -1 in the lParam to prevent any non-client area elements from
+ // being displayed.
+ if ((!delegate_->HasFrame() && !is_translucent_) ||
delegate_->GetFrameMode() == FrameMode::CUSTOM_DRAWN) {
SetMsgHandled(TRUE);
return TRUE;
}
return DefWindowProcWithRedrawLock(WM_NCACTIVATE, paint_as_active || active,
- 0);
+ delegate_->HasFrame() ? 0 : -1);
}
LRESULT HWNDMessageHandler::OnNCCalcSize(BOOL mode, LPARAM l_param) {

View file

@ -0,0 +1,115 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: zoy <zoy-l@outlook.com>
Date: Mon, 5 May 2025 23:28:53 +0800
Subject: fix: resolve dynamic background material update issue on Windows 11
This patch addresses issues with background materials on Windows 11,
such as the background turning black when maximizing the window and
dynamic background material settings not taking effect.
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
index a415140b94e467adfbc3dbbaa026e897a0f66c06..41470fd55bf2053eb6d523bda3b544f448bfb094 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
@@ -176,6 +176,10 @@ void DesktopWindowTreeHostWin::FinishTouchDrag(gfx::Point screen_point) {
}
}
+void DesktopWindowTreeHostWin::SetIsTranslucent(bool is_translucent) {
+ message_handler_->set_is_translucent(is_translucent);
+}
+
// DesktopWindowTreeHostWin, DesktopWindowTreeHost implementation:
void DesktopWindowTreeHostWin::Init(const Widget::InitParams& params) {
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h
index b85d1cdec49b10628d2f3d3d2e07513beb830456..232b5121fc2b138c91559d740c5178f0562df66b 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h
@@ -94,6 +94,8 @@ class VIEWS_EXPORT DesktopWindowTreeHostWin
// false.
void FinishTouchDrag(gfx::Point screen_point);
+ void SetIsTranslucent(bool is_translucent);
+
protected:
// Overridden from DesktopWindowTreeHost:
void Init(const Widget::InitParams& params) override;
diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc
index 1a10bd1a6c527633f97d6eee6525b1e45a3fcd3d..7de4e205fede08d6e0d38ec4aa72c4fa47ac6d9e 100644
--- a/ui/views/win/hwnd_message_handler.cc
+++ b/ui/views/win/hwnd_message_handler.cc
@@ -937,13 +937,13 @@ void HWNDMessageHandler::FrameTypeChanged() {
void HWNDMessageHandler::PaintAsActiveChanged() {
if (!delegate_->HasNonClientView() || !delegate_->CanActivate() ||
- !delegate_->HasFrame() ||
+ (!delegate_->HasFrame() && !is_translucent_) ||
(delegate_->GetFrameMode() == FrameMode::CUSTOM_DRAWN)) {
return;
}
DefWindowProcWithRedrawLock(WM_NCACTIVATE, delegate_->ShouldPaintAsActive(),
- 0);
+ delegate_->HasFrame() ? 0 : -1);
}
void HWNDMessageHandler::SetWindowIcons(const gfx::ImageSkia& window_icon,
@@ -1027,7 +1027,14 @@ void HWNDMessageHandler::SizeConstraintsChanged() {
// allowing ui::GetResizableFrameThickness() to be used consistently when
// removing the visible system frame.
const bool had_caption_on_init = window_style() & WS_CAPTION;
- const bool can_resize = !is_translucent_ && delegate_->CanResize();
+
+ // In Chromium, the !is_translucent_ check was introduced for Glic-specific
+ // behavior. Since Electron does not use Glic, this restriction can be safely
+ // removed. Keeping the is_translucent_ check disables maximization for
+ // translucent framed windows. Original code: !is_translucent_ &&
+ // delegate_->CanResize() See:
+ // https://chromium-review.googlesource.com/c/chromium/src/+/6372329
+ const bool can_resize = delegate_->CanResize();
const bool can_maximize = can_resize && delegate_->CanMaximize();
auto set_style_func = [&style](LONG bit, bool should_set) {
@@ -1622,11 +1629,16 @@ void HWNDMessageHandler::ResetWindowRegion(bool force, bool redraw) {
// through, but that isn't the case when using Direct3D to draw transparent
// windows. So we route translucent windows throught to the delegate to
// allow for a custom hit mask.
- if (!is_translucent_ && !custom_window_region_.is_valid() &&
+ // patch: fix_resolve_dynamic_background_material_update_issue_on_windows_11
+ // Our translucent windows use the native frame by default, and we should not
+ // set a custom region when the window is maximized; otherwise, it will cause
+ // a white title bar to appear under Windows 11.
+ if (!custom_window_region_.is_valid() &&
(IsFrameSystemDrawn() || !delegate_->HasNonClientView())) {
if (force) {
SetWindowRgn(hwnd(), nullptr, redraw);
}
+
return;
}
@@ -2340,17 +2352,18 @@ LRESULT HWNDMessageHandler::OnNCActivate(UINT message,
delegate_->SchedulePaint();
}
- // Calling DefWindowProc is only necessary if there's a system frame being
- // drawn. Otherwise it can draw an incorrect title bar and cause visual
- // corruption.
- if (!delegate_->HasFrame() ||
+ // If the window is translucent, it may have the Mica background.
+ // In that case, it's necessary to call |DefWindowProc|, but we can
+ // pass -1 in the lParam to prevent any non-client area elements from
+ // being displayed.
+ if ((!delegate_->HasFrame() && !is_translucent_) ||
delegate_->GetFrameMode() == FrameMode::CUSTOM_DRAWN) {
SetMsgHandled(TRUE);
return TRUE;
}
return DefWindowProcWithRedrawLock(WM_NCACTIVATE, paint_as_active || active,
- 0);
+ delegate_->HasFrame() ? 0 : -1);
}
LRESULT HWNDMessageHandler::OnNCCalcSize(BOOL mode, LPARAM l_param) {

View file

@ -850,8 +850,8 @@ void BaseWindow::SetVibrancy(v8::Isolate* isolate,
window_->SetVibrancy(type, animation_duration_ms); window_->SetVibrancy(type, animation_duration_ms);
} }
void BaseWindow::SetBackgroundMaterial(const std::string& material_type) { void BaseWindow::SetBackgroundMaterial(const std::string& material) {
window_->SetBackgroundMaterial(material_type); window_->SetBackgroundMaterial(material);
} }
#if BUILDFLAG(IS_MAC) #if BUILDFLAG(IS_MAC)

View file

@ -196,7 +196,7 @@ class BaseWindow : public gin_helper::TrackableObject<BaseWindow>,
virtual void SetVibrancy(v8::Isolate* isolate, virtual void SetVibrancy(v8::Isolate* isolate,
v8::Local<v8::Value> value, v8::Local<v8::Value> value,
gin_helper::Arguments* args); gin_helper::Arguments* args);
void SetBackgroundMaterial(const std::string& vibrancy); virtual void SetBackgroundMaterial(const std::string& material);
#if BUILDFLAG(IS_MAC) #if BUILDFLAG(IS_MAC)
std::string GetAlwaysOnTopLevel() const; std::string GetAlwaysOnTopLevel() const;

View file

@ -4,6 +4,7 @@
#include "shell/browser/api/electron_api_browser_window.h" #include "shell/browser/api/electron_api_browser_window.h"
#include "base/containers/fixed_flat_set.h"
#include "content/browser/renderer_host/render_widget_host_owner_delegate.h" // nogncheck #include "content/browser/renderer_host/render_widget_host_owner_delegate.h" // nogncheck
#include "content/browser/web_contents/web_contents_impl.h" // nogncheck #include "content/browser/web_contents/web_contents_impl.h" // nogncheck
#include "content/public/browser/render_process_host.h" #include "content/public/browser/render_process_host.h"
@ -247,6 +248,18 @@ void BrowserWindow::SetBackgroundColor(const std::string& color_name) {
} }
} }
void BrowserWindow::SetBackgroundMaterial(const std::string& material) {
BaseWindow::SetBackgroundMaterial(material);
static constexpr auto materialTypes =
base::MakeFixedFlatSet<std::string_view>({"tabbed", "mica", "acrylic"});
if (materialTypes.contains(material)) {
SetBackgroundColor(ToRGBAHex(SK_ColorTRANSPARENT));
} else if (material == "none") {
SetBackgroundColor(ToRGBAHex(SK_ColorWHITE));
}
}
void BrowserWindow::FocusOnWebView() { void BrowserWindow::FocusOnWebView() {
web_contents()->GetRenderViewHost()->GetWidget()->Focus(); web_contents()->GetRenderViewHost()->GetWidget()->Focus();
} }

View file

@ -64,6 +64,7 @@ class BrowserWindow : public BaseWindow,
void Focus() override; void Focus() override;
void Blur() override; void Blur() override;
void SetBackgroundColor(const std::string& color_name) override; void SetBackgroundColor(const std::string& color_name) override;
void SetBackgroundMaterial(const std::string& material) override;
void OnWindowShow() override; void OnWindowShow() override;
void OnWindowHide() override; void OnWindowHide() override;

View file

@ -231,6 +231,10 @@ class NativeWindow : public base::SupportsUserData,
// Vibrancy API // Vibrancy API
virtual void SetVibrancy(const std::string& type, int duration); virtual void SetVibrancy(const std::string& type, int duration);
const std::string& background_material() const {
return background_material_;
}
virtual void SetBackgroundMaterial(const std::string& type); virtual void SetBackgroundMaterial(const std::string& type);
// Traffic Light API // Traffic Light API

View file

@ -79,6 +79,7 @@
#include "base/win/windows_version.h" #include "base/win/windows_version.h"
#include "shell/browser/ui/views/win_frame_view.h" #include "shell/browser/ui/views/win_frame_view.h"
#include "shell/browser/ui/win/electron_desktop_native_widget_aura.h" #include "shell/browser/ui/win/electron_desktop_native_widget_aura.h"
#include "shell/browser/ui/win/electron_desktop_window_tree_host_win.h"
#include "shell/common/color_util.h" #include "shell/common/color_util.h"
#include "skia/ext/skia_utils_win.h" #include "skia/ext/skia_utils_win.h"
#include "ui/display/win/screen_win.h" #include "ui/display/win/screen_win.h"
@ -354,6 +355,7 @@ NativeWindowViews::NativeWindowViews(const gin_helper::Dictionary& options,
if (!has_frame()) { if (!has_frame()) {
// Set Window style so that we get a minimize and maximize animation when // Set Window style so that we get a minimize and maximize animation when
// frameless. // frameless.
DWORD frame_style = WS_CAPTION | WS_OVERLAPPED; DWORD frame_style = WS_CAPTION | WS_OVERLAPPED;
if (resizable_) if (resizable_)
frame_style |= WS_THICKFRAME; frame_style |= WS_THICKFRAME;
@ -634,13 +636,7 @@ void NativeWindowViews::SetEnabledInternal(bool enable) {
void NativeWindowViews::Maximize() { void NativeWindowViews::Maximize() {
#if BUILDFLAG(IS_WIN) #if BUILDFLAG(IS_WIN)
if (IsTranslucent()) { if (transparent()) {
// Semi-transparent windows with backgroundMaterial not set to 'none', and
// not fully transparent, require manual handling of rounded corners when
// maximized.
if (rounded_corner_)
SetRoundedCorners(false);
restore_bounds_ = GetBounds(); restore_bounds_ = GetBounds();
auto display = display::Screen::GetScreen()->GetDisplayNearestWindow( auto display = display::Screen::GetScreen()->GetDisplayNearestWindow(
GetNativeWindow()); GetNativeWindow());
@ -664,15 +660,10 @@ void NativeWindowViews::Unmaximize() {
return; return;
#if BUILDFLAG(IS_WIN) #if BUILDFLAG(IS_WIN)
if (IsTranslucent()) { if (transparent()) {
SetBounds(restore_bounds_, false); SetBounds(restore_bounds_, false);
NotifyWindowUnmaximize(); NotifyWindowUnmaximize();
if (transparent()) { UpdateThickFrame();
UpdateThickFrame();
}
if (rounded_corner_) {
SetRoundedCorners(true);
}
return; return;
} }
#endif #endif
@ -689,7 +680,7 @@ bool NativeWindowViews::IsMaximized() const {
return true; return true;
#if BUILDFLAG(IS_WIN) #if BUILDFLAG(IS_WIN)
if (IsTranslucent() && !IsMinimized()) { if (transparent() && !IsMinimized()) {
// If the window is the same dimensions and placement as the // If the window is the same dimensions and placement as the
// display, we consider it maximized. // display, we consider it maximized.
auto display = display::Screen::GetScreen()->GetDisplayNearestWindow( auto display = display::Screen::GetScreen()->GetDisplayNearestWindow(
@ -711,15 +702,10 @@ void NativeWindowViews::Minimize() {
void NativeWindowViews::Restore() { void NativeWindowViews::Restore() {
#if BUILDFLAG(IS_WIN) #if BUILDFLAG(IS_WIN)
if (IsMaximized() && IsTranslucent()) { if (IsMaximized() && transparent()) {
SetBounds(restore_bounds_, false); SetBounds(restore_bounds_, false);
NotifyWindowRestore(); NotifyWindowRestore();
if (transparent()) { UpdateThickFrame();
UpdateThickFrame();
}
if (rounded_corner_) {
SetRoundedCorners(true);
}
return; return;
} }
#endif #endif
@ -865,7 +851,7 @@ gfx::Size NativeWindowViews::GetContentSize() const {
gfx::Rect NativeWindowViews::GetNormalBounds() const { gfx::Rect NativeWindowViews::GetNormalBounds() const {
#if BUILDFLAG(IS_WIN) #if BUILDFLAG(IS_WIN)
if (IsMaximized() && IsTranslucent()) if (IsMaximized() && transparent())
return restore_bounds_; return restore_bounds_;
#endif #endif
return widget()->GetRestoredBounds(); return widget()->GetRestoredBounds();
@ -1505,25 +1491,53 @@ void NativeWindowViews::SetBackgroundMaterial(const std::string& material) {
return; return;
DWM_SYSTEMBACKDROP_TYPE backdrop_type = GetBackdropFromString(material); DWM_SYSTEMBACKDROP_TYPE backdrop_type = GetBackdropFromString(material);
HRESULT result = const bool is_translucent = backdrop_type != DWMSBT_NONE &&
DwmSetWindowAttribute(GetAcceleratedWidget(), DWMWA_SYSTEMBACKDROP_TYPE, backdrop_type != DWMSBT_AUTO && !has_frame();
&backdrop_type, sizeof(backdrop_type));
HWND hwnd = GetAcceleratedWidget();
// We need to update margins ourselves since Chromium won't.
// See: ui/views/widget/widget_hwnd_utils.cc#157
// See: src/ui/views/win/hwnd_message_handler.cc#1793
MARGINS m = {0, 0, 0, 0};
if (is_translucent)
m = {-1, -1, -1, -1};
HRESULT result = DwmExtendFrameIntoClientArea(hwnd, &m);
if (FAILED(result))
LOG(WARNING) << "Failed to extend frame into client area";
result = DwmSetWindowAttribute(hwnd, DWMWA_SYSTEMBACKDROP_TYPE,
&backdrop_type, sizeof(backdrop_type));
if (FAILED(result)) if (FAILED(result))
LOG(WARNING) << "Failed to set background material to " << material; LOG(WARNING) << "Failed to set background material to " << material;
auto* desktop_window_tree_host =
static_cast<ElectronDesktopWindowTreeHostWin*>(
GetNativeWindow()->GetHost());
// Synchronize the internal state; otherwise, the background material may not
// work properly.
if (desktop_window_tree_host) {
desktop_window_tree_host->SetIsTranslucent(is_translucent);
}
auto* desktop_native_widget_aura =
static_cast<ElectronDesktopNativeWidgetAura*>(widget()->native_widget());
desktop_native_widget_aura->UpdateWindowTransparency();
// For frameless windows with a background material set, we also need to // For frameless windows with a background material set, we also need to
// remove the caption color so it doesn't render a caption bar (since the // remove the caption color so it doesn't render a caption bar (since the
// window is frameless) // window is frameless)
COLORREF caption_color = DWMWA_COLOR_DEFAULT; COLORREF caption_color =
if (backdrop_type != DWMSBT_NONE && backdrop_type != DWMSBT_AUTO && is_translucent ? DWMWA_COLOR_NONE : DWMWA_COLOR_DEFAULT;
!has_frame()) { result = DwmSetWindowAttribute(hwnd, DWMWA_CAPTION_COLOR, &caption_color,
caption_color = DWMWA_COLOR_NONE; sizeof(caption_color));
}
result = DwmSetWindowAttribute(GetAcceleratedWidget(), DWMWA_CAPTION_COLOR,
&caption_color, sizeof(caption_color));
if (FAILED(result)) if (FAILED(result))
LOG(WARNING) << "Failed to set caption color to transparent"; LOG(WARNING) << "Failed to set caption color to transparent";
// Activate the non-client area of the window
DefWindowProc(hwnd, WM_NCACTIVATE, TRUE, has_frame() ? 0 : -1);
#endif #endif
} }
@ -1846,7 +1860,7 @@ ui::mojom::WindowShowState NativeWindowViews::GetRestoredState() {
if (IsMaximized()) { if (IsMaximized()) {
#if BUILDFLAG(IS_WIN) #if BUILDFLAG(IS_WIN)
// Restore maximized state for windows that are not translucent. // Restore maximized state for windows that are not translucent.
if (!IsTranslucent()) { if (!transparent()) {
return ui::mojom::WindowShowState::kMaximized; return ui::mojom::WindowShowState::kMaximized;
} }
#else #else

View file

@ -64,7 +64,16 @@ bool ElectronDesktopWindowTreeHostWin::GetDwmFrameInsetsInPixels(
gfx::Insets* insets) const { gfx::Insets* insets) const {
// Set DWMFrameInsets to prevent maximized frameless window from bleeding // Set DWMFrameInsets to prevent maximized frameless window from bleeding
// into other monitors. // into other monitors.
if (IsMaximized() && !native_window_view_->has_frame()) { if (IsMaximized() && !native_window_view_->has_frame()) {
// We avoid doing this when the window is translucent (e.g. using
// backgroundMaterial effects), because setting zero insets can interfere
// with DWM rendering of blur or acrylic, potentially causing visual
// glitches.
const std::string& bg_material = native_window_view_->background_material();
if (!bg_material.empty() && bg_material != "none") {
return false;
}
// This would be equivalent to calling: // This would be equivalent to calling:
// DwmExtendFrameIntoClientArea({0, 0, 0, 0}); // DwmExtendFrameIntoClientArea({0, 0, 0, 0});
// //