experiments in a post-browser web
10
fork

Configure Feed

Select the types of activity you want to include in your feed.

refactor(page): migrate canvas page host to tile-preload + strict (Phase 3.11b-page)

Canvas page windows (created when opening web URLs) previously loaded with
preload.js because getTileWebPreferencesForUrl returns null for https:// URLs
and the loadUrl rewrite to peek://app/page/index.html happens after preload
assignment.

Fix: mint a trustedBuiltin page-host token before BrowserWindow creation for
every canvas window (useCanvas=true) and inject tile-preload.cjs + the token
into webPreferences.additionalArguments, bypassing the tileWebPrefs lookup
entirely. Register each canvas window with registerTrustedBuiltinWindow so
the pubsub extensionBroadcaster forwards messages to page.js. Apply the same
treatment to the openPopupInPageHost helper (webview target=_blank popups).

Also port the one remaining api.invoke('get-app-prefs') call in page.js to
api.app.getPrefs() which routes through the tile:app:get-prefs strict handler
added in Phase 3.11b-shims.

Files changed:
app/page/page.js — replace api.invoke with api.app.getPrefs()
backend/electron/ipc.ts — canvas window preload routing + token mint
+ registerTrustedBuiltinWindow for pubsub

Validation:
grep "api.invoke" app/page/page.js → 0 matches
TypeScript shape follows identical pattern used in page-glue.ts / cmd-glue.ts

+54 -5
+1 -1
app/page/page.js
··· 844 844 // Read drag hold delay from prefs 845 845 (async () => { 846 846 try { 847 - const prefs = await api.invoke('get-app-prefs'); 847 + const prefs = await api.app.getPrefs(); 848 848 if (prefs && typeof prefs.dragHoldDelay === 'number') { 849 849 DRAG_HOLD_THRESHOLD = Math.max(0, prefs.dragHoldDelay * 1000); 850 850 }
+53 -4
backend/electron/ipc.ts
··· 85 85 DEBUG, 86 86 } from './config.js'; 87 87 88 - import { getTileWebPreferencesForUrl } from './tile-launcher.js'; 88 + import { getTileWebPreferencesForUrl, registerTrustedBuiltinWindow } from './tile-launcher.js'; 89 89 import { getLazyTileManifest } from './tile-lazy.js'; 90 90 import { createTrustedBuiltinGrant } from './tile-manifest.js'; 91 91 import { generateToken } from './tile-tokens.js'; ··· 1078 1078 }; 1079 1079 DEBUG && console.log('[window-open] settings window: assigned trustedBuiltin tile-preload token'); 1080 1080 } 1081 + 1082 + // Canvas page host windows use tile-preload with a trustedBuiltin grant so 1083 + // page.js can use api.window.*, api.datastore.*, api.context.*, api.profiles.*, 1084 + // api.chromeExtensions.*, api.app.*, api.subscribe/publish, etc. The token is 1085 + // minted here (before BrowserWindow creation) so it can be injected into 1086 + // additionalArguments. A unique entry-id prevents Map collisions when multiple 1087 + // canvas windows are open simultaneously. 1088 + const PAGE_HOST_TILE_ID = 'page-host'; 1089 + let pageHostToken: string | null = null; 1090 + let pageHostEntryId: string | null = null; 1091 + if (useCanvas) { 1092 + pageHostEntryId = `canvas-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`; 1093 + const pageHostGrant = createTrustedBuiltinGrant(PAGE_HOST_TILE_ID); 1094 + pageHostToken = generateToken(PAGE_HOST_TILE_ID, pageHostEntryId, pageHostGrant); 1095 + } 1096 + 1081 1097 // Prepare browser window options 1082 1098 const winOptions: Electron.BrowserWindowConstructorOptions = { 1083 1099 frame: isWebPage ? false : frameDefault, // Web pages are always frameless ··· 1091 1107 ...(useCanvas ? { hasShadow: false, resizable: false } : {}), 1092 1108 webPreferences: { 1093 1109 ...options.webPreferences, 1094 - preload: tileWebPrefs ? tileWebPrefs.preload : getPreloadPath(), 1095 - additionalArguments: tileWebPrefs ? tileWebPrefs.additionalArguments : options.webPreferences?.additionalArguments, 1110 + // Canvas page host windows get tile-preload with a trustedBuiltin grant; 1111 + // other v2 tile URLs get their manifest-derived preload; everything else 1112 + // falls back to the legacy preload.js. 1113 + preload: useCanvas 1114 + ? getTilePreloadPath() 1115 + : tileWebPrefs ? tileWebPrefs.preload : getPreloadPath(), 1116 + additionalArguments: useCanvas 1117 + ? [ 1118 + `--tile-id=${PAGE_HOST_TILE_ID}`, 1119 + `--tile-entry=${pageHostEntryId!}`, 1120 + `--tile-token=${pageHostToken!}`, 1121 + ] 1122 + : tileWebPrefs ? tileWebPrefs.additionalArguments : options.webPreferences?.additionalArguments, 1096 1123 session: profileSession, 1097 1124 webviewTag: useCanvas ? true : (options.webPreferences?.webviewTag || false), 1098 1125 } ··· 1232 1259 1233 1260 if (options.visibleOnAllWorkspaces) { 1234 1261 win.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true }); 1262 + } 1263 + 1264 + // Register canvas page host windows with the tile broadcaster so pubsub 1265 + // messages (tag:item-added, sync:pull-completed, etc.) are forwarded to 1266 + // page.js via the same extensionBroadcaster path used by feature tiles. 1267 + // Token cleanup (revokeToken) happens automatically on 'closed'. 1268 + if (useCanvas && pageHostToken && pageHostEntryId) { 1269 + registerTrustedBuiltinWindow(PAGE_HOST_TILE_ID, pageHostEntryId, win, pageHostToken); 1270 + DEBUG && console.log(`[page-host] Registered canvas window ${win.id} as tile ${PAGE_HOST_TILE_ID}:${pageHostEntryId}`); 1235 1271 } 1236 1272 1237 1273 // Track window position for display-watcher home display restoration ··· 1483 1519 // Use profile-specific session for isolation 1484 1520 const profileSession = getProfileSession(); 1485 1521 1522 + // Mint a trustedBuiltin token for this popup canvas page host window. 1523 + const popupEntryId = `canvas-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`; 1524 + const popupGrant = createTrustedBuiltinGrant(PAGE_HOST_TILE_ID); 1525 + const popupToken = generateToken(PAGE_HOST_TILE_ID, popupEntryId, popupGrant); 1526 + 1486 1527 // Create the new BrowserWindow (page container) with fullscreen canvas 1487 1528 const popupWin = new BrowserWindow({ 1488 1529 frame: false, ··· 1493 1534 show: isHeadless() ? false : true, 1494 1535 transparent: true, 1495 1536 webPreferences: { 1496 - preload: getPreloadPath(), 1537 + preload: getTilePreloadPath(), 1538 + additionalArguments: [ 1539 + `--tile-id=${PAGE_HOST_TILE_ID}`, 1540 + `--tile-entry=${popupEntryId}`, 1541 + `--tile-token=${popupToken}`, 1542 + ], 1497 1543 session: profileSession, 1498 1544 webviewTag: true, 1499 1545 }, 1500 1546 }); 1547 + 1548 + // Register popup with the tile broadcaster for pubsub forwarding. 1549 + registerTrustedBuiltinWindow(PAGE_HOST_TILE_ID, popupEntryId, popupWin, popupToken); 1501 1550 1502 1551 // Set up fullscreen transparent canvas for the popup 1503 1552 if (!isHeadless()) {