···202202 }
203203});
204204205205-webview.addEventListener('new-window', async (e) => {
206206- DEBUG && console.log('[page] new-window:', e.url);
207207- e.preventDefault();
208208- await api.window.open(e.url, { role: 'child-content', width: 1024, height: 768 });
209209-});
205205+// NOTE: The 'new-window' webview event was removed in Electron 22.
206206+// Popups from webview guests (window.open / target="_blank") are now handled
207207+// by setWindowOpenHandler on the guest webContents, attached via
208208+// did-attach-webview in backend/electron/ipc.ts.
210209211210webview.addEventListener('did-fail-load', (e) => {
212211 if (e.errorCode === -3) return;
+1-1
app/settings/settings.js
···700700 const attrText = document.createElement('p');
701701 attrText.className = 'help-text';
702702 attrText.innerHTML = `
703703- Ad blocking powered by <a href="https://github.com/nickshanks/adblocker" target="_blank" style="color: var(--link-color, #88f);">@cliqz/adblocker</a> (MPL-2.0).<br>
703703+ Ad blocking powered by <a href="https://github.com/ghostery/adblocker" target="_blank" style="color: var(--link-color, #88f);">@ghostery/adblocker</a> (MPL-2.0).<br>
704704 Filter lists: <a href="https://easylist.to/" target="_blank" style="color: var(--link-color, #88f);">EasyList</a> and <a href="https://easylist.to/easylist/easyprivacy.txt" target="_blank" style="color: var(--link-color, #88f);">EasyPrivacy</a>.<br><br>
705705 Cookie consent handling by <a href="https://github.com/cavi-au/Consent-O-Matic" target="_blank" style="color: var(--link-color, #88f);">Consent-O-Matic</a> (MIT).<br>
706706 Developed by Janus Bager Kristensen and Rolf Bagge, <a href="https://cavi.au.dk/" target="_blank" style="color: var(--link-color, #88f);">CAVI</a> - Center for Advanced Visualization and Interaction, Aarhus University.
+2-2
backend/electron/adblocker.ts
···11/**
22 * Ad Blocker Module for Electron
33 *
44- * Provides native ad blocking using @cliqz/adblocker-electron.
44+ * Provides native ad blocking using @ghostery/adblocker-electron.
55 * This approach avoids webRequest API conflicts with browser extensions.
66 *
77 * Features:
···1212 */
13131414import { session, Session } from 'electron';
1515-import { ElectronBlocker, Request } from '@cliqz/adblocker-electron';
1515+import { ElectronBlocker, Request } from '@ghostery/adblocker-electron';
1616import fetch from 'cross-fetch';
17171818import { getProfileSession } from './session-partition.js';
+135-19
backend/electron/ipc.ts
···19981998 }
19991999 });
2000200020012001- // Track JS window.open() from web content windows
20012001+ // Handle window.open() / target="_blank" from <webview> guest webContents.
20022002+ // The host BrowserWindow loads peek://app/page/index.html which contains a <webview>.
20032003+ // Keystrokes and window.open() calls from web content go to the guest's webContents,
20042004+ // NOT the host's. We use did-attach-webview to intercept when the guest attaches
20052005+ // and set up a setWindowOpenHandler that routes popups through Peek's window system
20062006+ // instead of letting Electron create raw default BrowserWindows.
20022007 if (url.startsWith('http://') || url.startsWith('https://')) {
20032003- win.webContents.setWindowOpenHandler((details: Electron.HandlerDetails) => {
20042004- // Track the URL that JS is trying to open
20052005- try {
20062006- const popupTrack = trackWindowLoad(details.url, {
20072007- source: 'window-open',
20082008- sourceId: url,
20092009- windowType: 'main',
20102010- });
20112011- if (popupTrack.created) {
20122012- publish('system', PubSubScopes.GLOBAL, 'item:created', {
20132013- itemId: popupTrack.itemId,
20142014- itemType: 'url',
20152015- content: details.url
20162016- });
20082008+ win.webContents.on('did-attach-webview', (_event, guestWebContents) => {
20092009+ console.log(`[webview-popup] Guest webContents attached to window ${win.id}, adding setWindowOpenHandler`);
20102010+20112011+ guestWebContents.setWindowOpenHandler(({ url: popupUrl }) => {
20122012+ if (popupUrl.startsWith('http://') || popupUrl.startsWith('https://')) {
20132013+ console.log(`[webview-popup] Intercepted popup from window ${win.id}: ${popupUrl}`);
20142014+20152015+ // Open asynchronously — create a proper Peek page container window.
20162016+ // This runs outside the synchronous handler return.
20172017+ (async () => {
20182018+ try {
20192019+ // Track the popup URL in history
20202020+ try {
20212021+ const popupTrack = trackWindowLoad(popupUrl, {
20222022+ source: 'window-open',
20232023+ sourceId: url,
20242024+ windowType: 'main',
20252025+ });
20262026+ if (popupTrack.created) {
20272027+ publish('system', PubSubScopes.GLOBAL, 'item:created', {
20282028+ itemId: popupTrack.itemId,
20292029+ itemType: 'url',
20302030+ content: popupUrl
20312031+ });
20322032+ }
20332033+ } catch (e) {
20342034+ DEBUG && console.log('Failed to track webview popup:', e);
20352035+ }
20362036+20372037+ // Inherit group mode from parent window if applicable
20382038+ let groupMode: { groupId: string; groupName: string; color: string } | undefined = undefined;
20392039+ const parentContext = getContextEntry('mode', win.id);
20402040+ if (parentContext && parentContext.value === 'group' && parentContext.metadata) {
20412041+ groupMode = {
20422042+ groupId: parentContext.metadata.groupId as string,
20432043+ groupName: parentContext.metadata.groupName as string,
20442044+ color: parentContext.metadata.color as string,
20452045+ };
20462046+ }
20472047+20482048+ // Get the source address from the parent window's registration
20492049+ const parentInfo = getWindowInfo(win.id);
20502050+ const source = parentInfo ? parentInfo.source : 'system';
20512051+20522052+ // Build page container URL
20532053+ const parentBounds = win.getBounds();
20542054+ const pageParams = new URLSearchParams({
20552055+ url: popupUrl,
20562056+ x: String(parentBounds.x + 30),
20572057+ y: String(parentBounds.y + 30),
20582058+ width: String(1024),
20592059+ height: String(768),
20602060+ });
20612061+ const loadUrl = `peek://app/page/index.html?${pageParams.toString()}`;
20622062+20632063+ // Determine IZUI session state for transient detection
20642064+ const coordinator = getIzuiCoordinator();
20652065+ const isTransient = coordinator.isTransient();
20662066+ coordinator.evaluateOnShow();
20672067+20682068+ // Use profile-specific session for isolation
20692069+ const profileSession = getProfileSession();
20702070+20712071+ // Create the new BrowserWindow (page container)
20722072+ const popupWin = new BrowserWindow({
20732073+ frame: false,
20742074+ width: 1024,
20752075+ height: 768,
20762076+ x: parentBounds.x + 30,
20772077+ y: parentBounds.y + 30,
20782078+ show: isHeadless() ? false : true,
20792079+ transparent: true,
20802080+ webPreferences: {
20812081+ preload: getPreloadPath(),
20822082+ session: profileSession,
20832083+ webviewTag: true,
20842084+ },
20852085+ });
20862086+20872087+ // Register in window manager with proper IZUI role
20882088+ const popupParams: Record<string, unknown> = {
20892089+ address: popupUrl,
20902090+ transient: isTransient,
20912091+ parentWindowId: win.id,
20922092+ role: 'child-content',
20932093+ };
20942094+ if (groupMode) {
20952095+ popupParams.groupMode = groupMode;
20962096+ }
20972097+ registerWindow(popupWin.id, source, popupParams);
20982098+ coordinator.pushWindow(popupWin.id);
20992099+21002100+ // Set mode context (inherit group mode or detect from URL)
21012101+ if (groupMode) {
21022102+ addContextEntry('mode', 'group', {
21032103+ windowId: popupWin.id,
21042104+ source,
21052105+ metadata: { ...groupMode, url: popupUrl, inheritedFrom: win.id },
21062106+ });
21072107+ } else {
21082108+ const detectedMode = detectModeFromUrl(popupUrl);
21092109+ addContextEntry('mode', detectedMode, {
21102110+ windowId: popupWin.id,
21112111+ source,
21122112+ metadata: { url: popupUrl },
21132113+ });
21142114+ }
21152115+21162116+ // Load the page container URL
21172117+ await popupWin.loadURL(loadUrl);
21182118+21192119+ // Add ESC handler (also sets up did-attach-webview for the new window)
21202120+ addEscHandler(popupWin);
21212121+21222122+ // Update dock visibility
21232123+ updateDockVisibility();
21242124+21252125+ console.log(`[webview-popup] Created Peek window ${popupWin.id} for popup: ${popupUrl}`);
21262126+ } catch (e) {
21272127+ console.error('[webview-popup] Failed to create popup window:', e);
21282128+ }
21292129+ })();
21302130+21312131+ // Deny the default BrowserWindow creation
21322132+ return { action: 'deny' };
20172133 }
20182018- } catch (e) {
20192019- DEBUG && console.log('Failed to track setWindowOpenHandler:', e);
20202020- }
20212021- return { action: 'allow' };
21342134+21352135+ // Allow non-http(s) URLs (e.g. about:blank) to open normally
21362136+ return { action: 'allow' };
21372137+ });
20222138 });
20232139 }
20242140
···839839// ========== Bundled Web Extensions API ==========
840840841841/**
842842- * Adblocker API - Native ad blocking powered by @cliqz/adblocker-electron
842842+ * Adblocker API - Native ad blocking powered by @ghostery/adblocker-electron
843843 */
844844api.adblocker = {
845845 /**