fix: proper transparency on macOS and Windows (#15105)

This commit is contained in:
Amr Bashir
2026-03-14 02:17:53 +02:00
committed by GitHub
parent b2e6c2a802
commit 646fef61f2
14 changed files with 167 additions and 105 deletions

8
Cargo.lock generated
View File

@@ -1173,9 +1173,9 @@ dependencies = [
[[package]]
name = "cef"
version = "144.1.0+144.0.7"
version = "145.6.1+145.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd219b8919445d9a848015e0c3d50fd6b89f5521e711e956aa13f7f6f52992eb"
checksum = "549537e80f5655f48678e6496282cdea6f2b34b66d0e729d3517d7c7ec78954b"
dependencies = [
"cef-dll-sys",
"libloading 0.9.0",
@@ -1185,9 +1185,9 @@ dependencies = [
[[package]]
name = "cef-dll-sys"
version = "144.1.0+144.0.7"
version = "145.6.1+145.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abb6189602922e778b99ffc8a8635e696b1417457be3270fd53831eb30bb4843"
checksum = "687f31c0937d9914d3e5887b94c6194bd8e3e533026cef2cc3ae7b4fe5245b6d"
dependencies = [
"anyhow",
"cmake",

View File

@@ -6,9 +6,9 @@ license = "Apache-2.0 OR MIT"
publish = false
[dependencies]
cef = { version = "=144.1.0+144.0.7", default-features = false }
cef = { version = "=145.6.1+145.0.28", default-features = false }
# Not actually used directly, just locking it.
cef-dll-sys = { version = "=144.1.0+144.0.7", default-features = false }
cef-dll-sys = { version = "=145.6.1+145.0.28", default-features = false }
[features]
default = ["sandbox"]

View File

@@ -210,6 +210,17 @@ fn cfg_alias(alias: &str, has_feature: bool) {
}
}
fn default_windows_app_manifest() -> &'static str {
let runtime = env::var("DEP_TAURI_RUNTIME")
.expect("missing `cargo:runtime` instruction, please update tauri to latest");
if runtime == "cef" {
include_str!("windows-cef-app-manifest.xml")
} else {
include_str!("windows-app-manifest.xml")
}
}
/// Attributes used on Windows.
#[allow(dead_code)]
#[derive(Debug)]
@@ -255,7 +266,7 @@ impl WindowsAttributes {
pub fn new() -> Self {
Self {
window_icon_path: Default::default(),
app_manifest: Some(include_str!("windows-app-manifest.xml").into()),
app_manifest: Some(default_windows_app_manifest().into()),
}
}

View File

@@ -1,3 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<dependency>
<dependentAssembly>

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<!-- This compatibility section is from the standar pre - built binary package, specifically from %CEF_ROOT%/tests/cefsimple/win/compatibility.manifest -->
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!--The ID below indicates application support for Windows Vista -->
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
<!--The ID below indicates application support for Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
<!--The ID below indicates application support for Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<!--The ID below indicates application support for Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<!--The ID below indicates application support for Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<!-- This tag is required for XAML islands usage in the process for media scenarios. -->
<!-- This version corresponds to the Windows 10 May 2019 Update. -->
<maxversiontested Id="10.0.18362.0"/>
</application>
</compatibility>
<dependency>
<dependentAssembly>
<assemblyIdentity type="Win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"></assemblyIdentity>
</dependentAssembly>
</dependency>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="asInvoker" />
</requestedPrivileges>
</security>
</trustInfo>
</assembly>

View File

@@ -6,5 +6,5 @@
"tauri": "2.10.3",
"tauri-build": "2.5.6",
"tauri-plugin": "2.5.4",
"cef": "144.1.0+144.0.7"
"cef": "145.6.1+145.0.28"
}

View File

@@ -19,9 +19,9 @@ html5ever = "0.29"
raw-window-handle = "0.6"
url = "2"
http = "1"
cef = { version = "=144.1.0", default-features = false }
cef = { version = "=145.6.1", default-features = false }
# Not actually used directly, just locking it.
cef-dll-sys = "=144.1.0"
cef-dll-sys = "=145.6.1"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
kuchiki = { package = "kuchikiki", version = "0.8.8-speedreader" }

View File

@@ -4,6 +4,7 @@
use base64::Engine;
use cef::{rc::*, *};
use cef_dll_sys::cef_runtime_style_t;
use dioxus_debug_cell::RefCell;
use sha2::{Digest, Sha256};
use std::{
@@ -46,6 +47,9 @@ type CefOsEvent<'a> = *mut u8;
type CefOsEvent<'a> = Option<&'a mut sys::MSG>;
type AddressChangedHandler = dyn Fn(&url::Url) + Send + Sync;
/// CEF transparent color value (ARGB)
const TRANSPARENT: u32 = 0x00000000;
#[inline]
fn color_to_cef_argb(color: tauri_utils::config::Color) -> u32 {
let (r, g, b, a) = color.into();
@@ -1119,9 +1123,24 @@ wrap_browser_view_delegate! {
initialization_scripts: Arc<Vec<CefInitScript>>,
devtools_protocol_handlers: Arc<Mutex<Vec<Arc<DevToolsProtocolHandler>>>>,
devtools_observer_registration: Arc<Mutex<Option<cef::Registration>>>,
webview_attributes: Arc<RefCell<WebviewAttributes>>,
}
impl ViewDelegate {}
impl ViewDelegate {
fn on_theme_changed(&self, view: Option<&mut View>) {
let Some(view) = view else { return; };
let webview_attributes = self.webview_attributes.borrow();
#[cfg(any(not(target_os = "macos"), feature = "macos-private-api"))]
if webview_attributes.transparent {
view.set_background_color(TRANSPARENT);
} else if let Some(color) = webview_attributes.background_color {
let color = color_to_cef_argb(color);
view.set_background_color(color);
}
}
}
impl BrowserViewDelegate {
fn on_browser_created(&self, _browser_view: Option<&mut BrowserView>, browser: Option<&mut Browser>) {
@@ -1247,6 +1266,18 @@ wrap_window_delegate! {
}
fn on_theme_changed(&self, view: Option<&mut View>) {
let Some(view) = view else { return; };
let attrs = self.attributes.borrow();
#[cfg(any(not(target_os = "macos"), feature = "macos-private-api"))]
if attrs.transparent.unwrap_or_default() {
view.set_background_color(TRANSPARENT);
} else if let Some(color) = attrs.background_color {
let color = color_to_cef_argb(color);
view.set_background_color(color);
}
if std::mem::take(&mut *self.suppress_next_theme_changed.borrow_mut()) {
return;
}
@@ -1308,11 +1339,22 @@ wrap_window_delegate! {
}
#[cfg(target_os = "macos")]
apply_titlebar_style(
window,
a.title_bar_style.unwrap_or(TitleBarStyle::Visible),
a.hidden_title.unwrap_or(false)
);
{
let decorations = a.decorations.unwrap_or(true);
// default to transparent title bar if decorations are disabled, otherwise use visible title bar
let default_style = if decorations {
TitleBarStyle::Visible
} else {
TitleBarStyle::Transparent
};
let style = a.title_bar_style.unwrap_or(default_style);
// default to hidden title if decorations are disabled, otherwise show title
let hidden_title = a.hidden_title.unwrap_or(!decorations);
apply_titlebar_style(window, style, hidden_title);
}
if let Some(title) = &a.title {
window.set_title(Some(&CefString::from(title.as_str())));
@@ -1397,15 +1439,6 @@ wrap_window_delegate! {
// TODO: Implement shadow control for CEF
}
#[cfg(any(not(target_os = "macos"), feature = "macos-private-api"))]
if a.transparent.unwrap_or_default() {
window.set_background_color(0x00000000);
}
if let Some(color) = a.background_color {
window.set_background_color(color_to_cef_argb(color));
}
if let Some(focusable) = a.focusable {
window.set_focusable(if focusable { 1 } else { 0 });
}
@@ -2049,7 +2082,6 @@ fn handle_webview_message<T: UserEvent>(
}
}
WebviewMessage::SetBackgroundColor(color) => {
let color_value = color_opt_to_cef_argb(color);
if let Some(bv) = context
.windows
.borrow()
@@ -2061,7 +2093,9 @@ fn handle_webview_message<T: UserEvent>(
.find(|w| w.webview_id == webview_id)
})
{
bv.inner.set_background_color(color_value)
bv.webview_attributes.borrow_mut().background_color = color;
bv.inner.set_background_color(color.map(color_to_cef_argb));
}
}
WebviewMessage::ClearAllBrowsingData => {
@@ -3078,10 +3112,13 @@ fn handle_window_message<T: UserEvent>(
WindowMessage::SetBackgroundColor(color) => {
if let Some(app_window) = context.windows.borrow().get(&window_id) {
app_window.attributes.borrow_mut().background_color = color;
if let Some(window) = app_window.window() {
let color_value = color_opt_to_cef_argb(color);
window.set_background_color(color_value);
}
let Some(window) = app_window.window() else {
return;
};
let color = color.map(color_to_cef_argb).unwrap_or_else(|| {
window.theme_color(ColorId::CEF_ColorPrimaryBackground.get_raw() as _)
});
window.set_background_color(color);
}
}
WindowMessage::StartDragging => {
@@ -3338,6 +3375,7 @@ fn create_browser_window<T: UserEvent>(
initialization_scripts,
devtools_protocol_handlers,
devtools_observer_registration,
webview_attributes: Arc::new(RefCell::new(webview_attributes)),
}],
window_event_listeners: Arc::new(Mutex::new(HashMap::new())),
webview_event_listeners: Arc::new(Mutex::new(HashMap::new())),
@@ -3714,12 +3752,29 @@ pub(crate) fn create_webview<T: UserEvent>(
let window_handle = window.window_handle();
let runtime_style = platform_specific_attributes
.iter()
.find_map(|attr| match attr {
WebviewAtribute::RuntimeStyle { style } => Some(*style),
})
.unwrap_or(if matches!(kind, WebviewKind::WindowChild) {
CefRuntimeStyle::Alloy
} else {
CefRuntimeStyle::Chrome
});
let cef_runtime_style: RuntimeStyle = match runtime_style {
CefRuntimeStyle::Alloy => cef_runtime_style_t::CEF_RUNTIME_STYLE_ALLOY.into(),
CefRuntimeStyle::Chrome => cef_runtime_style_t::CEF_RUNTIME_STYLE_CHROME.into(),
};
if kind == WebviewKind::WindowChild {
#[cfg(target_os = "macos")]
let window_handle = ensure_valid_content_view(window_handle);
let window_info = cef::WindowInfo::default()
let mut window_info = cef::WindowInfo::default()
.set_as_child(window_handle, bounds.as_ref().unwrap_or(&Rect::default()));
window_info.runtime_style = cef_runtime_style;
let Some(browser_host) = browser_host_create_browser_sync(
Some(&window_info),
@@ -3754,15 +3809,6 @@ pub(crate) fn create_webview<T: UserEvent>(
browser.set_bounds(bounds.as_ref());
}
#[cfg(any(not(target_os = "macos"), feature = "macos-private-api"))]
if webview_attributes.transparent {
browser.set_background_color(0x00000000);
}
if let Some(background_color) = webview_attributes.background_color {
browser.set_background_color(color_to_cef_argb(background_color));
}
let initial_bounds_ratio = if webview_attributes.auto_resize {
Some(webview_bounds_ratio(&window, bounds.clone(), &browser))
} else {
@@ -3801,6 +3847,7 @@ pub(crate) fn create_webview<T: UserEvent>(
initialization_scripts,
devtools_protocol_handlers,
devtools_observer_registration,
webview_attributes: Arc::new(RefCell::new(webview_attributes)),
});
} else {
let browser_id = Arc::new(RefCell::new(0));
@@ -3809,26 +3856,19 @@ pub(crate) fn create_webview<T: UserEvent>(
Arc<dyn Fn(crate::DevToolsProtocol) + Send + Sync>,
>::new()));
let devtools_observer_registration = Arc::new(Mutex::new(None));
let webview_attributes = Arc::new(RefCell::new(webview_attributes));
#[allow(clippy::unnecessary_find_map)]
let mut browser_view_delegate = BrowserViewDelegateImpl::new(
browser_id.clone(),
platform_specific_attributes
.iter()
.find_map(|attr| match attr {
WebviewAtribute::RuntimeStyle { style } => Some(*style),
})
.unwrap_or(if matches!(kind, WebviewKind::WindowChild) {
CefRuntimeStyle::Alloy
} else {
CefRuntimeStyle::Chrome
}),
runtime_style,
context.scheme_handler_registry.clone(),
label.clone(),
uri_scheme_protocols.clone(),
initialization_scripts.clone(),
devtools_protocol_handlers.clone(),
devtools_observer_registration.clone(),
webview_attributes.clone(),
);
let browser_view = browser_view_create(
@@ -3843,15 +3883,6 @@ pub(crate) fn create_webview<T: UserEvent>(
let browser_webview = CefWebview::BrowserView(browser_view.clone());
#[cfg(any(not(target_os = "macos"), feature = "macos-private-api"))]
if webview_attributes.transparent {
browser_view.set_background_color(0x00000000);
}
if let Some(background_color) = webview_attributes.background_color {
browser_view.set_background_color(color_to_cef_argb(background_color));
}
window.add_child_view(Some(&mut View::from(&browser_view)));
context
@@ -3871,6 +3902,7 @@ pub(crate) fn create_webview<T: UserEvent>(
initialization_scripts,
devtools_protocol_handlers,
devtools_observer_registration,
webview_attributes,
});
}
}
@@ -3947,13 +3979,10 @@ fn browser_settings_from_webview_attributes(
} else {
sys::cef_state_t::STATE_DISABLED
}),
background_color: if let Some(color) = webview_attributes.background_color {
color_to_cef_argb(color)
} else if webview_attributes.transparent {
0x00000000
} else {
0xFFFFFFFF
},
background_color: webview_attributes
.background_color
.map(color_to_cef_argb)
.unwrap_or(0),
..Default::default()
}
}

View File

@@ -34,6 +34,22 @@ impl CefWebview {
}
}
pub fn set_background_color(&self, color: Option<u32>) {
match self {
CefWebview::BrowserView(view) => {
let window = view.window();
let color = color.or_else(|| {
window.map(|w| w.theme_color(ColorId::CEF_ColorPrimaryBackground.get_raw() as _))
});
if let Some(color) = color {
view.set_background_color(color);
}
}
_ => {}
}
}
pub fn bounds(&self) -> cef::Rect {
match self {
CefWebview::BrowserView(view) => view.bounds(),
@@ -58,13 +74,6 @@ impl CefWebview {
}
}
pub fn set_background_color(&self, color: cef::Color) {
match self {
CefWebview::BrowserView(view) => view.set_background_color(color),
CefWebview::Browser(browser) => browser.set_background_color(color),
}
}
pub fn set_visible(&self, visible: i32) {
match self {
CefWebview::BrowserView(view) => view.set_visible(visible),
@@ -91,7 +100,6 @@ trait CefBrowserExt {
fn bounds(&self) -> cef::Rect;
fn set_bounds(&self, rect: Option<&cef::Rect>);
fn scale_factor(&self) -> f64;
fn set_background_color(&self, color: cef::Color);
fn set_visible(&self, visible: i32);
fn close(&self);
fn set_parent(&self, parent: &cef::Window);

View File

@@ -107,10 +107,6 @@ impl CefBrowserExt for cef::Browser {
.unwrap_or(1.0)
}
fn set_background_color(&self, _color: cef::Color) {
// TODO: Implement background color setting for Linux/X11
}
fn set_visible(&self, visible: i32) {
let Some(xid) = self.xid() else {
return;

View File

@@ -62,27 +62,6 @@ impl CefBrowserExt for cef::Browser {
screen.map(|s| s.backingScaleFactor()).unwrap_or(1.0)
}
fn set_background_color(&self, color: cef::Color) {
let Some(nsview) = self.nsview() else {
return;
};
let red = ((color >> 16) & 0xFF) as f64 / 255.0;
let green = ((color >> 8) & 0xFF) as f64 / 255.0;
let blue = (color & 0xFF) as f64 / 255.0;
let alpha = ((color >> 24) & 0xFF) as f64 / 255.0;
let color = unsafe { NSColor::colorWithRed_green_blue_alpha(red, green, blue, alpha) };
let color = unsafe { color.CGColor() };
nsview.setWantsLayer(true);
let Some(layer) = (unsafe { nsview.layer() }) else {
return;
};
let _: () = unsafe { msg_send![&layer, setBackgroundColor: &*color] };
}
fn set_visible(&self, visible: i32) {
let Some(nsview) = self.nsview() else {
return;

View File

@@ -72,10 +72,6 @@ impl CefBrowserExt for cef::Browser {
dpi_to_scale_factor(dpi)
}
fn set_background_color(&self, color: cef::Color) {
// TODO:
}
fn set_visible(&self, visible: i32) {
let Some(hwnd) = self.hwnd() else {
return;

View File

@@ -12,7 +12,7 @@ use tauri_runtime::{
WebviewEventId, WindowDispatch, WindowEventId,
dpi::{PhysicalPosition, PhysicalSize, Position, Rect, Size},
monitor::Monitor,
webview::{DetachedWebview, PendingWebview},
webview::{DetachedWebview, PendingWebview, WebviewAttributes},
window::{
CursorIcon, DetachedWindow, DetachedWindowWebview, PendingWindow, RawWindow, WebviewEvent,
WindowBuilder, WindowBuilderBase, WindowEvent, WindowId,
@@ -301,6 +301,7 @@ pub(crate) struct AppWebview {
/// Keeps the DevTools message observer registered. Dropping this unregisters the observer.
#[allow(dead_code)]
pub devtools_observer_registration: Arc<Mutex<Option<cef::Registration>>>,
pub webview_attributes: Arc<RefCell<WebviewAttributes>>,
}
#[derive(Debug, Clone)]

View File

@@ -255,6 +255,10 @@ fn main() {
alias("dev", dev);
println!("cargo:dev={dev}");
println!(
"cargo:runtime={}",
if has_feature("cef") { "cef" } else { "wry" }
);
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
let mobile = target_os == "ios" || target_os == "android";