WebView window event handling fixes (#6038)
* WebView fixes * UA override logic * fix * debug logs * alter all webviews * cookies stuff * remove debug stuff
This commit is contained in:
@@ -1,4 +1,6 @@
|
|||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
use tauri::plugin::TauriPlugin;
|
use tauri::plugin::TauriPlugin;
|
||||||
use tauri::{Manager, PhysicalPosition, PhysicalSize, Runtime};
|
use tauri::{Manager, PhysicalPosition, PhysicalSize, Runtime};
|
||||||
@@ -14,6 +16,148 @@ pub struct AdsState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const AD_LINK: &str = "https://modrinth.com/wrapper/app-ads-cookie";
|
const AD_LINK: &str = "https://modrinth.com/wrapper/app-ads-cookie";
|
||||||
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
const ADS_USER_AGENT: &str = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36";
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn ads_user_agent_override_params() -> String {
|
||||||
|
serde_json::json!({
|
||||||
|
"userAgent": ADS_USER_AGENT,
|
||||||
|
"platform": "Win32",
|
||||||
|
"userAgentMetadata": {
|
||||||
|
"brands": [
|
||||||
|
{ "brand": "Chromium", "version": "128" },
|
||||||
|
{ "brand": "Google Chrome", "version": "128" },
|
||||||
|
{ "brand": "Not=A?Brand", "version": "99" },
|
||||||
|
],
|
||||||
|
"fullVersion": "128.0.0.0",
|
||||||
|
"fullVersionList": [
|
||||||
|
{ "brand": "Chromium", "version": "128.0.0.0" },
|
||||||
|
{ "brand": "Google Chrome", "version": "128.0.0.0" },
|
||||||
|
{ "brand": "Not=A?Brand", "version": "99.0.0.0" },
|
||||||
|
],
|
||||||
|
"platform": "Windows",
|
||||||
|
"platformVersion": "10.0.0",
|
||||||
|
"architecture": "x86",
|
||||||
|
"bitness": "64",
|
||||||
|
"model": "",
|
||||||
|
"mobile": false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn configure_ads_cookie_settings(
|
||||||
|
core_webview2: &webview2_com::Microsoft::Web::WebView2::Win32::ICoreWebView2,
|
||||||
|
) {
|
||||||
|
use webview2_com::Microsoft::Web::WebView2::Win32::{
|
||||||
|
COREWEBVIEW2_TRACKING_PREVENTION_LEVEL_NONE, ICoreWebView2,
|
||||||
|
ICoreWebView2_13, ICoreWebView2Profile3,
|
||||||
|
};
|
||||||
|
use windows_core::Interface;
|
||||||
|
|
||||||
|
match core_webview2
|
||||||
|
.cast::<ICoreWebView2_13>()
|
||||||
|
.and_then(|core_webview2| unsafe { core_webview2.Profile() })
|
||||||
|
.and_then(|profile| profile.cast::<ICoreWebView2Profile3>())
|
||||||
|
{
|
||||||
|
Ok(profile) => {
|
||||||
|
if let Err(error) = unsafe {
|
||||||
|
profile.SetPreferredTrackingPreventionLevel(
|
||||||
|
COREWEBVIEW2_TRACKING_PREVENTION_LEVEL_NONE,
|
||||||
|
)
|
||||||
|
} {
|
||||||
|
tracing::warn!(
|
||||||
|
?error,
|
||||||
|
"Failed to disable ads WebView2 tracking prevention"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(error) => {
|
||||||
|
tracing::warn!(
|
||||||
|
?error,
|
||||||
|
"Failed to access ads WebView2 profile tracking prevention settings"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_webview_visible<R: Runtime>(
|
||||||
|
webview: &tauri::Webview<R>,
|
||||||
|
_visible: bool,
|
||||||
|
) {
|
||||||
|
webview
|
||||||
|
.with_webview(
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
move |wv| {
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
let controller = wv.controller();
|
||||||
|
unsafe { controller.SetIsVisible(_visible) }.ok();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_webview_visible_for_window<R: Runtime>(
|
||||||
|
app: &tauri::AppHandle<R>,
|
||||||
|
webview: &tauri::Webview<R>,
|
||||||
|
visible: bool,
|
||||||
|
) {
|
||||||
|
let is_minimized = app
|
||||||
|
.get_window("main")
|
||||||
|
.and_then(|window| window.is_minimized().ok())
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
set_webview_visible(webview, visible && !is_minimized);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sync_webview_visibility_for_main_window<R: Runtime>(
|
||||||
|
app: &tauri::AppHandle<R>,
|
||||||
|
main_window: &tauri::Window<R>,
|
||||||
|
was_minimized: &AtomicBool,
|
||||||
|
) {
|
||||||
|
let is_minimized = main_window.is_minimized().unwrap_or(false);
|
||||||
|
let was = was_minimized.load(Ordering::SeqCst);
|
||||||
|
|
||||||
|
if is_minimized == was {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
was_minimized.store(is_minimized, Ordering::SeqCst);
|
||||||
|
|
||||||
|
let ads_visible = if is_minimized {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
match app.state::<RwLock<AdsState>>().try_read() {
|
||||||
|
Ok(state) => state.shown && !state.modal_shown,
|
||||||
|
Err(_) => false,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut webviews = Vec::new();
|
||||||
|
let mut seen_webviews = HashSet::new();
|
||||||
|
|
||||||
|
for webview in main_window.webviews() {
|
||||||
|
seen_webviews.insert(webview.label().to_string());
|
||||||
|
webviews.push(webview);
|
||||||
|
}
|
||||||
|
|
||||||
|
for webview in app.webviews().into_values() {
|
||||||
|
if seen_webviews.insert(webview.label().to_string()) {
|
||||||
|
webviews.push(webview);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for webview in webviews {
|
||||||
|
let visible =
|
||||||
|
!is_minimized && (webview.label() != "ads-window" || ads_visible);
|
||||||
|
|
||||||
|
set_webview_visible(&webview, visible);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
||||||
tauri::plugin::Builder::<R>::new("ads")
|
tauri::plugin::Builder::<R>::new("ads")
|
||||||
@@ -30,10 +174,11 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
|||||||
// visible when we refresh, the Aditude wrapper will not make any ad requests
|
// visible when we refresh, the Aditude wrapper will not make any ad requests
|
||||||
// unless Chromium reports the page as visible. The refresh does not reset the
|
// unless Chromium reports the page as visible. The refresh does not reset the
|
||||||
// visibility state.
|
// visibility state.
|
||||||
let app = app.clone();
|
let refresh_app = app.clone();
|
||||||
tauri::async_runtime::spawn(async move {
|
tauri::async_runtime::spawn(async move {
|
||||||
loop {
|
loop {
|
||||||
if let Some(webview) = app.webviews().get_mut("ads-window")
|
if let Some(webview) =
|
||||||
|
refresh_app.webviews().get_mut("ads-window")
|
||||||
{
|
{
|
||||||
let _ = webview.navigate(AD_LINK.parse().unwrap());
|
let _ = webview.navigate(AD_LINK.parse().unwrap());
|
||||||
}
|
}
|
||||||
@@ -43,6 +188,34 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if let Some(main_window) = app.get_window("main") {
|
||||||
|
let app_handle = app.clone();
|
||||||
|
let event_window = main_window.clone();
|
||||||
|
let was_minimized = Arc::new(AtomicBool::new(false));
|
||||||
|
|
||||||
|
main_window.on_window_event(move |_| {
|
||||||
|
sync_webview_visibility_for_main_window(
|
||||||
|
&app_handle,
|
||||||
|
&event_window,
|
||||||
|
&was_minimized,
|
||||||
|
);
|
||||||
|
|
||||||
|
let delayed_app_handle = app_handle.clone();
|
||||||
|
let delayed_event_window = event_window.clone();
|
||||||
|
let delayed_was_minimized = was_minimized.clone();
|
||||||
|
|
||||||
|
tauri::async_runtime::spawn(async move {
|
||||||
|
tokio::time::sleep(Duration::from_millis(100)).await;
|
||||||
|
|
||||||
|
sync_webview_visibility_for_main_window(
|
||||||
|
&delayed_app_handle,
|
||||||
|
&delayed_event_window,
|
||||||
|
&delayed_was_minimized,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
.invoke_handler(tauri::generate_handler![
|
.invoke_handler(tauri::generate_handler![
|
||||||
@@ -103,31 +276,38 @@ pub async fn init_ads_window<R: Runtime>(
|
|||||||
webview.show().ok();
|
webview.show().ok();
|
||||||
webview.set_position(position).ok();
|
webview.set_position(position).ok();
|
||||||
webview.set_size(size).ok();
|
webview.set_size(size).ok();
|
||||||
|
set_webview_visible_for_window(&app, webview, true);
|
||||||
} else {
|
} else {
|
||||||
webview.hide().ok();
|
webview.hide().ok();
|
||||||
webview
|
webview
|
||||||
.set_position(PhysicalPosition::new(-1000, -1000))
|
.set_position(PhysicalPosition::new(-1000, -1000))
|
||||||
.ok();
|
.ok();
|
||||||
|
set_webview_visible(webview, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(webview.clone())
|
Some(webview.clone())
|
||||||
} else if let Some(window) = app.get_window("main") {
|
} else if let Some(window) = app.get_window("main") {
|
||||||
|
#[cfg(windows)]
|
||||||
|
let webview_url =
|
||||||
|
WebviewUrl::External("about:blank".parse().unwrap());
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
let webview_url = WebviewUrl::External(AD_LINK.parse().unwrap());
|
||||||
|
|
||||||
let webview = window.add_child(
|
let webview = window.add_child(
|
||||||
tauri::webview::WebviewBuilder::new(
|
tauri::webview::WebviewBuilder::new("ads-window", webview_url)
|
||||||
"ads-window",
|
.initialization_script_for_all_frames(include_str!(
|
||||||
WebviewUrl::External(
|
"ads-init.js"
|
||||||
AD_LINK.parse().unwrap(),
|
))
|
||||||
),
|
// We use a standard Chrome user agent for compatibility with our ad provider,
|
||||||
)
|
// since Tauri is not recognized by ad providers by default.
|
||||||
.initialization_script_for_all_frames(include_str!("ads-init.js"))
|
// Aditude has separately informed SSPs and IVT vendors that this traffic
|
||||||
// We use a standard Chrome user agent for compatibility with our ad provider,
|
// originates from a desktop app.
|
||||||
// since Tauri is not recognized by ad providers by default.
|
.user_agent(ADS_USER_AGENT)
|
||||||
// Aditude has separately informed SSPs and IVT vendors that this traffic
|
.zoom_hotkeys_enabled(false)
|
||||||
// originates from a desktop app.
|
.transparent(true)
|
||||||
.user_agent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36")
|
.on_new_window(|_, _| {
|
||||||
.zoom_hotkeys_enabled(false)
|
tauri::webview::NewWindowResponse::Deny
|
||||||
.transparent(true)
|
}),
|
||||||
.on_new_window(|_, _| tauri::webview::NewWindowResponse::Deny),
|
|
||||||
// set both the `hide`/`show` state and `position`,
|
// set both the `hide`/`show` state and `position`,
|
||||||
// to ensure that the webview is actually shown/hidden
|
// to ensure that the webview is actually shown/hidden
|
||||||
if state.shown {
|
if state.shown {
|
||||||
@@ -140,15 +320,68 @@ pub async fn init_ads_window<R: Runtime>(
|
|||||||
|
|
||||||
if state.shown {
|
if state.shown {
|
||||||
webview.show().ok();
|
webview.show().ok();
|
||||||
|
set_webview_visible_for_window(&app, &webview, true);
|
||||||
} else {
|
} else {
|
||||||
webview.hide().ok();
|
webview.hide().ok();
|
||||||
|
set_webview_visible(&webview, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
webview.with_webview(#[allow(unused_variables)] |webview2| {
|
webview.with_webview(#[allow(unused_variables)] |webview2| {
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
{
|
{
|
||||||
|
use webview2_com::CallDevToolsProtocolMethodCompletedHandler;
|
||||||
use webview2_com::Microsoft::Web::WebView2::Win32::ICoreWebView2_8;
|
use webview2_com::Microsoft::Web::WebView2::Win32::ICoreWebView2_8;
|
||||||
use windows_core::Interface;
|
use windows_core::Interface;
|
||||||
|
use windows_core::HSTRING;
|
||||||
|
|
||||||
|
let core_webview2 =
|
||||||
|
unsafe { webview2.controller().CoreWebView2() };
|
||||||
|
|
||||||
|
if let Ok(core_webview2) = core_webview2 {
|
||||||
|
configure_ads_cookie_settings(&core_webview2);
|
||||||
|
|
||||||
|
let navigate_webview = core_webview2.clone();
|
||||||
|
let handler =
|
||||||
|
CallDevToolsProtocolMethodCompletedHandler::create(
|
||||||
|
Box::new(move |result: windows_core::Result<()>, _| {
|
||||||
|
if let Err(error) = result {
|
||||||
|
tracing::error!(
|
||||||
|
?error,
|
||||||
|
"Failed to override ads user-agent client hints"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
navigate_webview
|
||||||
|
.Navigate(&HSTRING::from(AD_LINK))
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}) as Box<_>,
|
||||||
|
);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
if let Err(error) = core_webview2
|
||||||
|
.CallDevToolsProtocolMethod(
|
||||||
|
&HSTRING::from(
|
||||||
|
"Emulation.setUserAgentOverride",
|
||||||
|
),
|
||||||
|
&HSTRING::from(
|
||||||
|
ads_user_agent_override_params(),
|
||||||
|
),
|
||||||
|
&handler,
|
||||||
|
)
|
||||||
|
{
|
||||||
|
tracing::error!(
|
||||||
|
?error,
|
||||||
|
"Failed to install ads user-agent client hints override"
|
||||||
|
);
|
||||||
|
|
||||||
|
core_webview2.Navigate(&HSTRING::from(AD_LINK)).ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let webview2_controller = webview2.controller();
|
let webview2_controller = webview2.controller();
|
||||||
let Ok(webview2_8) = unsafe { webview2_controller.CoreWebView2() }
|
let Ok(webview2_8) = unsafe { webview2_controller.CoreWebView2() }
|
||||||
@@ -166,9 +399,9 @@ pub async fn init_ads_window<R: Runtime>(
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(webview) = webview.clone() else {
|
if webview.is_none() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
}
|
||||||
|
|
||||||
// tauri::async_runtime::spawn(async move {
|
// tauri::async_runtime::spawn(async move {
|
||||||
// loop {
|
// loop {
|
||||||
@@ -249,6 +482,7 @@ pub async fn show_ads_window<R: Runtime>(
|
|||||||
webview.set_size(size).ok();
|
webview.set_size(size).ok();
|
||||||
webview.set_position(position).ok();
|
webview.set_position(position).ok();
|
||||||
webview.show().ok();
|
webview.show().ok();
|
||||||
|
set_webview_visible_for_window(&app, webview, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user