desktop: the notification plugin's refused is_permission_granted probe at page load (macOS, Linux; the webview has no plugin API) is no longer logged as a start error in webview.log

This commit is contained in:
CRTkafa 2026-10-13 02:03:51 +08:00
parent 7901beee1e
commit 2e66aa42b6
3 changed files with 23 additions and 1 deletions

View file

@ -118,7 +118,11 @@
if (t && t !== window && (t.src || t.href)) { record('load', 'could not load ' + (t.src || t.href)); return; }
record('window.onerror', ev.error || ev.message, ev.error && ev.error.stack);
}, true);
// tauri-plugin-notification's init script asks for the notification permission at load (macOS, Linux); the webview
// has no plugin API by design (capabilities/main.json), so that call is refused: expected, not an error
function pluginProbe(e) { return /plugin:notification\|is_permission_granted not allowed/.test(text(e)); }
window.addEventListener('unhandledrejection', function (ev) {
if (pluginProbe(ev.reason)) { ev.preventDefault(); return; }
if (!booted()) record('unhandledrejection', ev.reason, ev.reason && ev.reason.stack);
});
// React's root reports an error no boundary caught (the tree is gone) here: src/main.tsx onUncaughtError

View file

@ -52,12 +52,20 @@ export function report(where: string, e: unknown, componentStack?: string | null
return r;
}
/**
* tauri-plugin-notification's init script asks for the notification permission when the page loads (macOS, Linux).
* The webview has no plugin API by design (capabilities/main.json), so the call is refused: expected, never reported.
*/
export function ignoredRejection(e: unknown): boolean {
return /plugin:notification\|is_permission_granted not allowed/.test(errorMessage(e));
}
/** window.onerror and unhandled promise rejections, after React took over from public/boot.js. */
export function installGlobalHandlers(): void {
// public/boot.js stops logging on its own from here
(window as { __storeboundCrashHandlers?: boolean }).__storeboundCrashHandlers = true;
window.addEventListener('error', ev => { report('window.onerror', ev.error ?? ev.message); });
window.addEventListener('unhandledrejection', ev => { report('unhandledrejection', ev.reason, null, 'warn'); });
window.addEventListener('unhandledrejection', ev => { if (!ignoredRejection(ev.reason)) report('unhandledrejection', ev.reason, null, 'warn'); });
}
/** Clipboard first, then the old selection way (the webview may refuse clipboard access). True when copied. */

View file

@ -8,6 +8,7 @@ import { runInNewContext } from 'node:vm';
import { parseAst } from 'vite';
import { rewrite } from '../build/freeze-safe';
import { DEFAULT_SETTINGS } from '../src/api';
import { ignoredRejection } from '../src/lib/crash';
import { NO_OVERRIDES } from '../src/lib/identity';
import { normalizeAccounts, normalizeProjects, normalizeSettings, normalizeWorkspace } from '../src/lib/normalize';
@ -85,6 +86,15 @@ describe('error screens are wired in', () => {
expect(src('src/main.tsx')).toContain("dataset.booted = '1'");
expect(src('src/main.tsx')).toContain('<ErrorBoundary where="app" full>');
});
test("the notification plugin's refused permission probe is not reported (boot.js and crash.ts)", () => {
// seen on Linux in CI (Xvfb): the plugin's init script calls is_permission_granted, which the capability refuses
const probe = 'Command plugin:notification|is_permission_granted not allowed by ACL';
expect(ignoredRejection(probe)).toBe(true);
expect(ignoredRejection(new Error(probe))).toBe(true);
expect(ignoredRejection('Command plugin:notification|notify not allowed by ACL')).toBe(false);
expect(ignoredRejection(new Error('sidecar did not start'))).toBe(false);
expect(src('public/boot.js')).toContain('/plugin:notification\\|is_permission_granted not allowed/.test(text(e))');
});
test('every screen and app tab has its own boundary', () => {
expect(src('src/App.tsx')).toMatch(/<ErrorBoundary key=\{route\.screen === 'app'/);
expect(src('src/screens/AppView.tsx')).toMatch(/<ErrorBoundary key=\{tab\}/);