From 417ef0003245df49ab4e6e837309a6cd4c41277d Mon Sep 17 00:00:00 2001 From: SWangHash <88996709+SWangHash@users.noreply.github.com> Date: Wed, 17 Jun 2026 15:03:05 +0800 Subject: [PATCH 1/2] fix(browser): fallback to iframe when webview API unavailable on OHOS OHOS platform does not support embedded webview creation via Tauri API. Added automatic fallback to iframe mode when webview API is detected as unavailable, allowing browser scene and panel to work on OHOS. --- .../src/app/scenes/browser/BrowserPanel.tsx | 27 +++++++++++++++---- .../src/app/scenes/browser/BrowserScene.tsx | 27 +++++++++++++++---- 2 files changed, 44 insertions(+), 10 deletions(-) diff --git a/src/web-ui/src/app/scenes/browser/BrowserPanel.tsx b/src/web-ui/src/app/scenes/browser/BrowserPanel.tsx index 30b920a719..d9bb56cade 100644 --- a/src/web-ui/src/app/scenes/browser/BrowserPanel.tsx +++ b/src/web-ui/src/app/scenes/browser/BrowserPanel.tsx @@ -70,6 +70,17 @@ function isWebviewNotFoundError(error: unknown): boolean { return formatUnknownError(error).toLowerCase().includes('webview not found'); } +function isWebviewApiUnavailable(error: unknown): boolean { + const message = formatUnknownError(error); + const lowered = message.toLowerCase(); + return ( + lowered.includes('webview api not available') || + lowered.includes('webview is not supported') || + lowered.includes('webview unavailable') || + lowered.includes('no webview') + ); +} + async function waitForWebviewCreated(handle: BrowserWebviewHandle): Promise { await new Promise((resolve, reject) => { let settled = false; @@ -135,6 +146,7 @@ const BrowserPanel: React.FC = ({ isActive, initialUrl }) => const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); const [isInspectorActive, setIsInspectorActive] = useState(false); + const [webviewUnavailable, setWebviewUnavailable] = useState(false); const addContext = useContextStore((s) => s.addContext); @@ -278,9 +290,14 @@ const BrowserPanel: React.FC = ({ isActive, initialUrl }) => .catch(() => {}); }, 500); } catch (loadError) { - const message = formatUnknownError(loadError); - log.error('Load browser panel url failed', loadError); - setError(message); + if (isWebviewApiUnavailable(loadError)) { + log.warn('Webview API not available, falling back to iframe'); + setWebviewUnavailable(true); + } else { + const message = formatUnknownError(loadError); + log.error('Load browser panel url failed', loadError); + setError(message); + } } finally { setIsLoading(false); } @@ -296,7 +313,7 @@ const BrowserPanel: React.FC = ({ isActive, initialUrl }) => // Activate / deactivate webview based on shouldShowWebview useEffect(() => { - if (!isTauri) return; + if (!isTauri || webviewUnavailable) return; if (shouldShowWebview) { if (!webviewRef.current) { @@ -565,7 +582,7 @@ const BrowserPanel: React.FC = ({ isActive, initialUrl }) => ) : null}
- {!isTauri ? ( + {!isTauri || webviewUnavailable ? (