use super::*;
const PLATFORM_BASELINE_SCRIPT: &str = concat!(
"(function(){try{",
"var el=document.documentElement;",
"el.classList.add('lx-desktop');",
"el.setAttribute('data-lx-platform','windows');",
"var s=document.createElement('style');",
"s.setAttribute('data-lingxia-base','');",
"s.textContent='",
"html.lx-desktop,html.lx-desktop body{-webkit-user-select:text;user-select:text;}",
"html.lx-desktop .no-select,html.lx-desktop [data-lx-no-select]{-webkit-user-select:none;user-select:none;}",
"';(document.head||el).appendChild(s);",
"}catch(e){}})();"
);
pub(crate) fn install_document_scripts(
webview: &ICoreWebView2,
inject_platform_baseline: bool,
) -> StdResult<()> {
let script = r#"
(function() {
if (window.__LingXiaWindowsInjected) return;
window.__LingXiaWindowsInjected = true;
if (window.chrome && window.chrome.webview && !window.__LingXiaNativeMessageListener) {
window.__LingXiaNativeMessageListener = true;
window.chrome.webview.addEventListener('message', function(event) {
try {
var payload = typeof event.data === 'string' ? event.data : JSON.stringify(event.data);
if (typeof window.__LingXiaRecvMessage === 'function') {
window.__LingXiaRecvMessage(payload);
} else {
console.warn('[LingXia] __LingXiaRecvMessage not available');
}
} catch (e) {}
});
}
// Windows uses WebView2's native web message channel
// (`chrome.webview.postMessage` -> `WebMessageReceived`). WebView2
// does not expose Android-style WebMessagePort transfer to Win32,
// so `supportsMessagePort` intentionally stays false.
window.LingXiaProxy = window.LingXiaProxy || {
supportsMessagePort: function() { return false; },
getPort: function() { return ''; },
postMessage: function(message) {
window.chrome && window.chrome.webview && window.chrome.webview.postMessage(String(message));
}
};
// Embedded native components: the page bridge posts component
// messages (component.mount/update/unmount, ...) through this
// object; they travel in a tagged envelope so the host can route
// them separately from regular bridge traffic. A lightweight
// scroll tracker keeps native overlays aligned with document
// coordinates; it stays dormant until a component message is
// actually sent.
if (!window.NativeComponentBridge) {
var lxNcActive = false;
var lxNcScrollScheduled = false;
var lxNcPost = function(payload) {
try {
window.chrome && window.chrome.webview && window.chrome.webview.postMessage(JSON.stringify({
__lingxia_native_component__: true,
payload: String(payload)
}));
} catch (e) {}
};
var lxNcPostScroll = function() {
lxNcScrollScheduled = false;
lxNcPost(JSON.stringify({
action: 'page.scroll',
x: window.scrollX || 0,
y: window.scrollY || 0
}));
};
var lxNcScheduleScroll = function() {
if (!lxNcActive || lxNcScrollScheduled) return;
lxNcScrollScheduled = true;
window.requestAnimationFrame(lxNcPostScroll);
};
window.addEventListener('scroll', lxNcScheduleScroll, { passive: true });
window.addEventListener('resize', lxNcScheduleScroll);
// Elastic overscroll is a compositor-level transform: it never
// updates scroll positions or element rects, so a native
// overlay cannot follow the bounce and visibly tears away
// from its element. A page that actually hosts native
// components trades the bounce for alignment.
var lxNcDisableOverscroll = function() {
try {
var style = document.createElement('style');
style.textContent = 'html, body { overscroll-behavior: none; }';
(document.head || document.documentElement).appendChild(style);
} catch (e) {}
};
window.NativeComponentBridge = {
postMessage: function(message) {
if (!lxNcActive) {
lxNcActive = true;
lxNcDisableOverscroll();
lxNcPostScroll();
}
lxNcPost(message);
}
};
}
// Page console/error capture is handled natively via the WebView2
// DevTools Protocol (see windows/console.rs) — no console override is
// injected here, so the page's own `console` is left untouched and
// uncaught exceptions + browser diagnostics are captured too.
})();
"#;
let webview = webview.clone();
let script = if inject_platform_baseline {
format!("{PLATFORM_BASELINE_SCRIPT}{script}")
} else {
script.to_string()
};
AddScriptToExecuteOnDocumentCreatedCompletedHandler::wait_for_async_operation(
Box::new(move |handler| unsafe {
let script = CoTaskMemPWSTR::from(script.as_str());
webview
.AddScriptToExecuteOnDocumentCreated(*script.as_ref().as_pcwstr(), &handler)
.map_err(webview2_com::Error::WindowsError)
}),
Box::new(|result, _id| result),
)
.map_err(map_webview2_error)?;
Ok(())
}