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

* fix: abnormal behavior of windows background material

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>

Co-authored-by: zoy <zoy-l@outlook.com>

* chore: update patches

Co-authored-by: patchup[bot] <73610968+patchup[bot]@users.noreply.github.com>

* fix: setting background material after init

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: zoy <zoy-l@outlook.com>
Co-authored-by: patchup[bot] <73610968+patchup[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
This commit is contained in:
trop[bot] 2025-07-18 10:30:37 +02:00 committed by GitHub
parent 658d52ecf1
commit b20e91d86f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 195 additions and 107 deletions

View file

@ -102,7 +102,6 @@ fix_use_delegated_generic_capturer_when_available.patch
expose_webblob_path_to_allow_embedders_to_get_file_paths.patch
fix_move_autopipsettingshelper_behind_branding_buildflag.patch
revert_remove_the_allowaggressivethrottlingwithwebsocket_feature.patch
fix_activate_background_material_on_windows.patch
feat_allow_passing_of_objecttemplate_to_objecttemplatebuilder.patch
chore_remove_check_is_test_on_script_injection_tracker.patch
fix_restore_original_resize_performance_on_macos.patch
@ -138,3 +137,4 @@ revert_update_siso-chromium_image.patch
build_set_mac_sdk_minimum_to_10.patch
partitionalloc_use_fewer_vmas_by_default_on_linux_systems.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 ad5044fbb4b8611a740da34f3f0bfe6dbb416f38..3762c5297e9009399df4ea3b45bf77326cd0f270 100644
--- a/ui/views/win/hwnd_message_handler.cc
+++ b/ui/views/win/hwnd_message_handler.cc
@@ -939,13 +939,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,
@@ -1734,7 +1734,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);
}
}
@@ -2342,17 +2342,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 ca11e7008d3c528c643b760d0a7e980065f90629..9039ae483467c46f35370cd486e56537daaa9340 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
@@ -179,6 +179,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 21049abeffd7b4bf314aec9fb5a63d5f773e53f5..04fcffd6bcfb7cbd18aa890e3cda259e842e23fa 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
@@ -93,6 +93,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 ad5044fbb4b8611a740da34f3f0bfe6dbb416f38..fc549cdff52393e8e7fb34f9c80be44adf6e171b 100644
--- a/ui/views/win/hwnd_message_handler.cc
+++ b/ui/views/win/hwnd_message_handler.cc
@@ -939,13 +939,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,
@@ -1029,7 +1029,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) {
@@ -1624,11 +1631,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;
}
@@ -2342,17 +2354,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);
}
void BaseWindow::SetBackgroundMaterial(const std::string& material_type) {
window_->SetBackgroundMaterial(material_type);
void BaseWindow::SetBackgroundMaterial(const std::string& material) {
window_->SetBackgroundMaterial(material);
}
#if BUILDFLAG(IS_MAC)

View file

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

View file

@ -4,6 +4,7 @@
#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/web_contents/web_contents_impl.h" // nogncheck
#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() {
web_contents()->GetRenderViewHost()->GetWidget()->Focus();
}

View file

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

View file

@ -233,6 +233,10 @@ class NativeWindow : public base::SupportsUserData,
// Vibrancy API
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);
// Traffic Light API

View file

@ -80,6 +80,7 @@
#include "base/win/windows_version.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_window_tree_host_win.h"
#include "shell/common/color_util.h"
#include "skia/ext/skia_utils_win.h"
#include "ui/display/win/screen_win.h"
@ -352,6 +353,7 @@ NativeWindowViews::NativeWindowViews(const gin_helper::Dictionary& options,
if (!has_frame()) {
// Set Window style so that we get a minimize and maximize animation when
// frameless.
DWORD frame_style = WS_CAPTION | WS_OVERLAPPED;
if (resizable_)
frame_style |= WS_THICKFRAME;
@ -685,13 +687,7 @@ void NativeWindowViews::SetEnabledInternal(bool enable) {
void NativeWindowViews::Maximize() {
#if BUILDFLAG(IS_WIN)
if (IsTranslucent()) {
// 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);
if (transparent()) {
restore_bounds_ = GetBounds();
auto display = display::Screen::GetScreen()->GetDisplayNearestWindow(
GetNativeWindow());
@ -715,15 +711,10 @@ void NativeWindowViews::Unmaximize() {
return;
#if BUILDFLAG(IS_WIN)
if (IsTranslucent()) {
if (transparent()) {
SetBounds(restore_bounds_, false);
NotifyWindowUnmaximize();
if (transparent()) {
UpdateThickFrame();
}
if (rounded_corner_) {
SetRoundedCorners(true);
}
UpdateThickFrame();
return;
}
#endif
@ -740,7 +731,7 @@ bool NativeWindowViews::IsMaximized() const {
return true;
#if BUILDFLAG(IS_WIN)
if (IsTranslucent() && !IsMinimized()) {
if (transparent() && !IsMinimized()) {
// If the window is the same dimensions and placement as the
// display, we consider it maximized.
auto display = display::Screen::GetScreen()->GetDisplayNearestWindow(
@ -762,15 +753,10 @@ void NativeWindowViews::Minimize() {
void NativeWindowViews::Restore() {
#if BUILDFLAG(IS_WIN)
if (IsMaximized() && IsTranslucent()) {
if (IsMaximized() && transparent()) {
SetBounds(restore_bounds_, false);
NotifyWindowRestore();
if (transparent()) {
UpdateThickFrame();
}
if (rounded_corner_) {
SetRoundedCorners(true);
}
UpdateThickFrame();
return;
}
#endif
@ -916,7 +902,7 @@ gfx::Size NativeWindowViews::GetContentSize() const {
gfx::Rect NativeWindowViews::GetNormalBounds() const {
#if BUILDFLAG(IS_WIN)
if (IsMaximized() && IsTranslucent())
if (IsMaximized() && transparent())
return restore_bounds_;
#endif
return widget()->GetRestoredBounds();
@ -1557,25 +1543,53 @@ void NativeWindowViews::SetBackgroundMaterial(const std::string& material) {
return;
DWM_SYSTEMBACKDROP_TYPE backdrop_type = GetBackdropFromString(material);
HRESULT result =
DwmSetWindowAttribute(GetAcceleratedWidget(), DWMWA_SYSTEMBACKDROP_TYPE,
&backdrop_type, sizeof(backdrop_type));
const bool is_translucent = backdrop_type != DWMSBT_NONE &&
backdrop_type != DWMSBT_AUTO && !has_frame();
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))
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
// remove the caption color so it doesn't render a caption bar (since the
// window is frameless)
COLORREF caption_color = DWMWA_COLOR_DEFAULT;
if (backdrop_type != DWMSBT_NONE && backdrop_type != DWMSBT_AUTO &&
!has_frame()) {
caption_color = DWMWA_COLOR_NONE;
}
result = DwmSetWindowAttribute(GetAcceleratedWidget(), DWMWA_CAPTION_COLOR,
&caption_color, sizeof(caption_color));
COLORREF caption_color =
is_translucent ? DWMWA_COLOR_NONE : DWMWA_COLOR_DEFAULT;
result = DwmSetWindowAttribute(hwnd, DWMWA_CAPTION_COLOR, &caption_color,
sizeof(caption_color));
if (FAILED(result))
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
}
@ -1897,7 +1911,7 @@ ui::mojom::WindowShowState NativeWindowViews::GetRestoredState() {
if (IsMaximized()) {
#if BUILDFLAG(IS_WIN)
// Restore maximized state for windows that are not translucent.
if (!IsTranslucent()) {
if (!transparent()) {
return ui::mojom::WindowShowState::kMaximized;
}
#else

View file

@ -64,7 +64,16 @@ bool ElectronDesktopWindowTreeHostWin::GetDwmFrameInsetsInPixels(
gfx::Insets* insets) const {
// Set DWMFrameInsets to prevent maximized frameless window from bleeding
// into other monitors.
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:
// DwmExtendFrameIntoClientArea({0, 0, 0, 0});
//