namespace Avalonia_PC.Views { public partial class MainWindow { private const string BridgeScript = """ if (!window.__appBridgeInstalled) { window.__appBridgeInstalled = true; window.isWebView2 = true; const pending = new Map(); const tryOpenDevTools = () => { window.invokeCSharpAction(JSON.stringify({ kind: 'app-open-devtools' })); }; window.__dispatchAppResponse = function(jsonStr) { const payload = JSON.parse(jsonStr); const responseId = payload.id ?? payload.Id; const entry = pending.get(responseId); if (!entry) return; pending.delete(responseId); entry.resolve(new Response(payload.body ?? payload.Body ?? '', { status: payload.statusCode ?? payload.StatusCode ?? 200, statusText: payload.statusMessage ?? payload.StatusMessage ?? 'OK', headers: payload.headers ?? payload.Headers ?? { 'Content-Type': 'application/json' } })); }; const nativeFetch = window.fetch ? window.fetch.bind(window) : null; const NativeXMLHttpRequest = window.XMLHttpRequest; const sendAppBridgeRequest = ({ requestUrl, method, headers, body, timeoutMs = 30000 }) => { const id = globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random()}`; const responsePromise = new Promise((resolve, reject) => { const timeoutId = setTimeout(() => { pending.delete(id); reject(new Error(`Timed out waiting for ${requestUrl}`)); }, timeoutMs); pending.set(id, { resolve: response => { clearTimeout(timeoutId); resolve(response); }, reject: error => { clearTimeout(timeoutId); reject(error); } }); }); window.invokeCSharpAction(JSON.stringify({ kind: 'app-request', id, url: requestUrl, method, headers, body })); return responsePromise; }; document.addEventListener('keydown', event => { if (event.key === 'F12' || (event.ctrlKey && event.shiftKey && (event.key === 'I' || event.key === 'i'))) { event.preventDefault(); tryOpenDevTools(); } }, true); document.addEventListener('contextmenu', event => { if (event.shiftKey) { event.preventDefault(); tryOpenDevTools(); } }, true); window.fetch = async (input, init) => { const request = input instanceof Request ? input : null; const requestUrl = typeof input === 'string' || input instanceof URL ? input.toString() : request?.url; if (!requestUrl || !requestUrl.startsWith('app://')) { if (!nativeFetch) throw new Error('window.fetch is not available.'); return nativeFetch(input, init); } const combinedHeaders = new Headers(request?.headers); if (init?.headers) { new Headers(init.headers).forEach((value, key) => combinedHeaders.set(key, value)); } const headers = {}; combinedHeaders.forEach((value, key) => headers[key] = value); let body = init?.body; if (body === undefined && request) { body = await request.clone().text(); } if (body && typeof body !== 'string') { body = await new Response(body).text(); } return sendAppBridgeRequest({ requestUrl, method: init?.method ?? request?.method ?? 'GET', headers, body: body ?? null, timeoutMs: 30000 }); }; class BridgeXMLHttpRequest { constructor() { this._native = new NativeXMLHttpRequest(); this._isAppRequest = false; this._requestUrl = ''; this._method = 'GET'; this._headers = {}; this._responseHeaders = {}; this._responseHeadersRaw = ''; this._aborted = false; this.readyState = 0; this.status = 0; this.statusText = ''; this.response = null; this.responseText = ''; this.responseType = ''; this.responseURL = ''; this.timeout = 0; this.withCredentials = false; this.onreadystatechange = null; this.onload = null; this.onerror = null; this.ontimeout = null; this.onabort = null; this.onloadend = null; this.upload = { addEventListener: () => {}, removeEventListener: () => {} }; this._native.onreadystatechange = () => { if (this._isAppRequest) { return; } this.readyState = this._native.readyState; this.status = this._native.status; this.statusText = this._native.statusText; this.responseURL = this._native.responseURL ?? ''; this.response = this._native.response; this.responseText = this._native.responseText ?? ''; this._raiseReadyStateChange(); }; this._native.onload = event => { if (!this._isAppRequest && typeof this.onload === 'function') { this.onload(event); } }; this._native.onerror = event => { if (!this._isAppRequest && typeof this.onerror === 'function') { this.onerror(event); } }; this._native.ontimeout = event => { if (!this._isAppRequest && typeof this.ontimeout === 'function') { this.ontimeout(event); } }; this._native.onabort = event => { if (!this._isAppRequest && typeof this.onabort === 'function') { this.onabort(event); } }; this._native.onloadend = event => { if (!this._isAppRequest && typeof this.onloadend === 'function') { this.onloadend(event); } }; } open(method, url, async = true, user, password) { const requestUrl = typeof url === 'string' || url instanceof URL ? url.toString() : `${url ?? ''}`; this._requestUrl = requestUrl; this._method = method ?? 'GET'; this._isAppRequest = requestUrl.startsWith('app://'); this._headers = {}; this._responseHeaders = {}; this._responseHeadersRaw = ''; this._aborted = false; if (!this._isAppRequest) { this._native.open(method, url, async, user, password); return; } this.readyState = 1; this._raiseReadyStateChange(); } setRequestHeader(name, value) { if (!this._isAppRequest) { this._native.setRequestHeader(name, value); return; } this._headers[name] = value; } getAllResponseHeaders() { if (!this._isAppRequest) { return this._native.getAllResponseHeaders(); } return this._responseHeadersRaw; } getResponseHeader(name) { if (!this._isAppRequest) { return this._native.getResponseHeader(name); } return this._responseHeaders[name.toLowerCase()] ?? null; } overrideMimeType(mimeType) { if (!this._isAppRequest && typeof this._native.overrideMimeType === 'function') { this._native.overrideMimeType(mimeType); } } abort() { if (!this._isAppRequest) { this._native.abort(); return; } this._aborted = true; if (typeof this.onabort === 'function') { this.onabort(); } if (typeof this.onloadend === 'function') { this.onloadend(); } } async send(body = null) { if (!this._isAppRequest) { this._native.send(body); return; } let requestBody = body; if (requestBody && typeof requestBody !== 'string') { requestBody = await new Response(requestBody).text(); } try { const response = await sendAppBridgeRequest({ requestUrl: this._requestUrl, method: this._method, headers: this._headers, body: requestBody ?? null, timeoutMs: this.timeout > 0 ? this.timeout : 30000 }); if (this._aborted) { return; } this.status = response.status; this.statusText = response.statusText; this.responseURL = this._requestUrl; this._responseHeaders = {}; this._responseHeadersRaw = ''; response.headers.forEach((value, key) => { this._responseHeaders[key.toLowerCase()] = value; this._responseHeadersRaw += `${key}: ${value}\r\n`; }); const text = await response.text(); this.responseText = text; this.response = this.responseType === 'json' ? (text ? JSON.parse(text) : null) : text; this.readyState = 4; this._raiseReadyStateChange(); if (typeof this.onload === 'function') { this.onload(); } if (typeof this.onloadend === 'function') { this.onloadend(); } } catch (error) { if (this._aborted) { return; } this.status = 0; this.statusText = ''; this.readyState = 4; this._raiseReadyStateChange(); const errorMessage = error?.message ?? ''; if (errorMessage.includes('Timed out waiting') && typeof this.ontimeout === 'function') { this.ontimeout(error); } else if (typeof this.onerror === 'function') { this.onerror(error); } if (typeof this.onloadend === 'function') { this.onloadend(); } } } _raiseReadyStateChange() { if (typeof this.onreadystatechange === 'function') { this.onreadystatechange(); } } } window.XMLHttpRequest = BridgeXMLHttpRequest; } """; } }