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

This commit is contained in:
Shelley Vohr 2025-08-05 16:34:17 +02:00 committed by GitHub
commit e6ac03d0ee
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 195 additions and 107 deletions

View file

@ -110,7 +110,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
@ -149,3 +148,4 @@ fix_osr_stutter_fix_backport_for_electron.patch
do_not_check_the_order_of_display_id_order_on_windows.patch
make_focus_methods_in_webcontentsviewchildframe_notimplemented.patch
cherry-pick-f1e6422a355c.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 ecab7d7f95998a055f01d7b5180a8fb80eddb32c..5c74a092920cdb3b7e98d218c75874cac2d0542f 100644
--- a/ui/views/win/hwnd_message_handler.cc
+++ b/ui/views/win/hwnd_message_handler.cc
@@ -954,13 +954,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,
@@ -1757,7 +1757,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);
}
}
@@ -2365,17 +2365,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 4780229a4453ad5ca2c89b786459f64c58bf2272..9074b7883f2fcf1341683c68ab7a398acefb0559 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
@@ -169,6 +169,10 @@ void DesktopWindowTreeHostWin::FinishTouchDrag(gfx::Point screen_point) {
}
}
+void DesktopWindowTreeHostWin::SetIsTranslucent(bool is_translucent) {
+ message_handler_->set_is_translucent(is_translucent);
+}
+
////////////////////////////////////////////////////////////////////////////////
// DesktopWindowTreeHostWin, WidgetObserver implementation:
void DesktopWindowTreeHostWin::OnWidgetThemeChanged(Widget* widget) {
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 37109b8d3d439073b5c9e2ea3597c36f32de5704..888fa89d3641ea08a413c0351262cdc967ec12f8 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 : public DesktopWindowTreeHost,
// 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 ecab7d7f95998a055f01d7b5180a8fb80eddb32c..303c186b0a7fbf6b9f7a40e00fc708fecbf68e8d 100644
--- a/ui/views/win/hwnd_message_handler.cc
+++ b/ui/views/win/hwnd_message_handler.cc
@@ -954,13 +954,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,
@@ -1055,7 +1055,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) {
@@ -1647,11 +1654,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;
}
@@ -2365,17 +2377,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

@ -231,6 +231,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

@ -79,6 +79,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"
@ -353,6 +354,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;
@ -648,13 +650,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());
@ -678,15 +674,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
@ -703,7 +694,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(
@ -725,15 +716,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
@ -879,7 +865,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();
@ -1519,25 +1505,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
}
@ -1870,7 +1884,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});
//