|
|
|
@@ -7,7 +7,7 @@ const { ElectronBlocker, adsAndTrackingLists } = require('@cliqz/adblocker-elect
|
|
|
|
const { autoUpdater } = require('electron-updater');
|
|
|
|
const { autoUpdater } = require('electron-updater');
|
|
|
|
|
|
|
|
|
|
|
|
const CONFIG_PATH = path.join(os.homedir(), '.ESH-Media.json');
|
|
|
|
const CONFIG_PATH = path.join(os.homedir(), '.ESH-Media.json');
|
|
|
|
const BLOCKER_CACHE_PATH = path.join(os.homedir(), '.ESH-Media-adblock-v2.bin');
|
|
|
|
const BLOCKER_CACHE_PATH = path.join(os.homedir(), '.ESH-Media-adblock-v3.bin');
|
|
|
|
const DEFAULT_TRUSTED_DOMAINS = [
|
|
|
|
const DEFAULT_TRUSTED_DOMAINS = [
|
|
|
|
// Google ecosystem (OAuth)
|
|
|
|
// Google ecosystem (OAuth)
|
|
|
|
'google.com', 'accounts.google.com', 'googleapis.com', 'googleusercontent.com',
|
|
|
|
'google.com', 'accounts.google.com', 'googleapis.com', 'googleusercontent.com',
|
|
|
|
@@ -72,8 +72,26 @@ function getBlocker() {
|
|
|
|
'https://easylist-downloads.adblockplus.org/ruadlist+easylist.txt', // RuAdList
|
|
|
|
'https://easylist-downloads.adblockplus.org/ruadlist+easylist.txt', // RuAdList
|
|
|
|
];
|
|
|
|
];
|
|
|
|
const b = await ElectronBlocker.fromLists(fetchFn, [...adsAndTrackingLists, ...russianLists]);
|
|
|
|
const b = await ElectronBlocker.fromLists(fetchFn, [...adsAndTrackingLists, ...russianLists]);
|
|
|
|
// Whitelist TMDB so the movie search API is not blocked
|
|
|
|
// Whitelist domains that need ALL requests passed through unfiltered.
|
|
|
|
b.addFilters(['@@||api.themoviedb.org^', '@@||image.tmdb.org^', '@@||themoviedb.org^']);
|
|
|
|
// Tracking-list false positives on these break critical functionality:
|
|
|
|
|
|
|
|
// • Google: OAuth/login integrity checks fail without gstatic + analytics endpoints
|
|
|
|
|
|
|
|
// → "Возможно, этот браузер или приложение небезопасны" error
|
|
|
|
|
|
|
|
// • Yandex/Mail/Microsoft/Apple: same OAuth-style integrity flows
|
|
|
|
|
|
|
|
// • TMDB: movie search API and poster CDN
|
|
|
|
|
|
|
|
const whitelist = [
|
|
|
|
|
|
|
|
'@@||api.themoviedb.org^', '@@||image.tmdb.org^', '@@||themoviedb.org^',
|
|
|
|
|
|
|
|
'@@||google.com^', '@@||googleapis.com^', '@@||googleusercontent.com^',
|
|
|
|
|
|
|
|
'@@||gstatic.com^', '@@||youtube.com^', '@@||ytimg.com^', '@@||googlevideo.com^',
|
|
|
|
|
|
|
|
'@@||google-analytics.com^', '@@||googletagmanager.com^',
|
|
|
|
|
|
|
|
'@@||yandex.ru^', '@@||yandex.com^', '@@||yastatic.net^', '@@||mc.yandex.ru^',
|
|
|
|
|
|
|
|
'@@||github.com^', '@@||githubassets.com^', '@@||githubusercontent.com^',
|
|
|
|
|
|
|
|
'@@||vk.com^', '@@||vk.ru^', '@@||vkuser.net^',
|
|
|
|
|
|
|
|
'@@||mail.ru^', '@@||my.mail.ru^', '@@||imgsmail.ru^',
|
|
|
|
|
|
|
|
'@@||microsoft.com^', '@@||microsoftonline.com^', '@@||live.com^', '@@||office.com^',
|
|
|
|
|
|
|
|
'@@||apple.com^', '@@||icloud.com^',
|
|
|
|
|
|
|
|
'@@||facebook.com^', '@@||fbcdn.net^',
|
|
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
b.updateFromDiff({ added: whitelist });
|
|
|
|
fs.writeFileSync(BLOCKER_CACHE_PATH, Buffer.from(b.serialize()));
|
|
|
|
fs.writeFileSync(BLOCKER_CACHE_PATH, Buffer.from(b.serialize()));
|
|
|
|
console.log('[adblock] filter lists downloaded and cached');
|
|
|
|
console.log('[adblock] filter lists downloaded and cached');
|
|
|
|
return b;
|
|
|
|
return b;
|
|
|
|
@@ -316,6 +334,20 @@ ipcMain.handle('check-update-now', async () => {
|
|
|
|
|
|
|
|
|
|
|
|
// --- Window ---
|
|
|
|
// --- Window ---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function attachDevToolsShortcut(webContents) {
|
|
|
|
|
|
|
|
// Ctrl+Shift+I / F12 open DevTools on this webContents.
|
|
|
|
|
|
|
|
// Always available so a kiosk machine can be debugged without un-kiosking.
|
|
|
|
|
|
|
|
webContents.on('before-input-event', (_e, input) => {
|
|
|
|
|
|
|
|
if (input.type !== 'keyDown') return;
|
|
|
|
|
|
|
|
const isDevToolsCombo =
|
|
|
|
|
|
|
|
(input.control && input.shift && (input.key === 'I' || input.key === 'i')) ||
|
|
|
|
|
|
|
|
input.key === 'F12';
|
|
|
|
|
|
|
|
if (isDevToolsCombo) {
|
|
|
|
|
|
|
|
try { webContents.openDevTools({ mode: 'detach' }); } catch (_) {}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function createWindow() {
|
|
|
|
async function createWindow() {
|
|
|
|
mainWindow = new BrowserWindow({
|
|
|
|
mainWindow = new BrowserWindow({
|
|
|
|
width: 1280,
|
|
|
|
width: 1280,
|
|
|
|
@@ -329,6 +361,8 @@ async function createWindow() {
|
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
attachDevToolsShortcut(mainWindow.webContents);
|
|
|
|
|
|
|
|
|
|
|
|
if (isDev) {
|
|
|
|
if (isDev) {
|
|
|
|
mainWindow.loadURL(RENDERER_URL);
|
|
|
|
mainWindow.loadURL(RENDERER_URL);
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
@@ -488,19 +522,22 @@ async function restoreSession() {
|
|
|
|
const sess = cfg.openedSession;
|
|
|
|
const sess = cfg.openedSession;
|
|
|
|
if (!sess || !Array.isArray(sess.tabs) || !sess.tabs.length) return;
|
|
|
|
if (!sess || !Array.isArray(sess.tabs) || !sess.tabs.length) return;
|
|
|
|
console.log(`[session] restoring ${sess.tabs.length} tab(s), active=${sess.activeName}`);
|
|
|
|
console.log(`[session] restoring ${sess.tabs.length} tab(s), active=${sess.activeName}`);
|
|
|
|
// Spawn each saved tab by replaying create-view. ipcMain.emit triggers the handler
|
|
|
|
// Spawn each saved tab by replaying create-view, sequentially with a small delay.
|
|
|
|
// synchronously; the view's loadURL is fire-and-forget. We chain via setTimeout to
|
|
|
|
// Concurrent create-view calls in v1.0.3 caused races: multiple setLoader/addChild
|
|
|
|
// avoid stacking N loaders simultaneously.
|
|
|
|
// interleaved → some views ended up unmounted (white screen). Spacing them out
|
|
|
|
|
|
|
|
// gives each view time to mount before the next steals currentView.
|
|
|
|
|
|
|
|
const fakeEvent = { sender: mainWindow.webContents };
|
|
|
|
for (const tab of sess.tabs) {
|
|
|
|
for (const tab of sess.tabs) {
|
|
|
|
if (!tab?.name || !tab?.url) continue;
|
|
|
|
if (!tab?.name || !tab?.url) continue;
|
|
|
|
ipcMain.emit('create-view', { sender: mainWindow.webContents }, tab.name, tab.url, tab.imageUrl || '', 1.0, !!tab.useProxy);
|
|
|
|
ipcMain.emit('create-view', fakeEvent, tab.name, tab.url, tab.imageUrl || '', 1.0, !!tab.useProxy);
|
|
|
|
|
|
|
|
await new Promise(r => setTimeout(r, 150));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// After all spawned, the last one is `currentView`. Switch to the saved active if different.
|
|
|
|
// Switch to saved active if it isn't already the last-spawned (currentView).
|
|
|
|
if (sess.activeName === 'home') {
|
|
|
|
if (sess.activeName === 'home') {
|
|
|
|
ipcMain.emit('hide-view', { sender: mainWindow.webContents });
|
|
|
|
ipcMain.emit('hide-view', fakeEvent);
|
|
|
|
sendOpenedApps('home');
|
|
|
|
sendOpenedApps('home');
|
|
|
|
} else if (sess.activeName && sess.activeName !== currentView?.name) {
|
|
|
|
} else if (sess.activeName && sess.activeName !== currentView?.name) {
|
|
|
|
ipcMain.emit('show-view', { sender: mainWindow.webContents }, sess.activeName);
|
|
|
|
ipcMain.emit('show-view', fakeEvent, sess.activeName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
} catch (e) {
|
|
|
|
console.warn('[session] restore failed:', e.message);
|
|
|
|
console.warn('[session] restore failed:', e.message);
|
|
|
|
@@ -539,6 +576,7 @@ ipcMain.on('create-view', async (_event, name, url, imageUrl, _zoom, useProxy) =
|
|
|
|
openedApps.push(appEntry);
|
|
|
|
openedApps.push(appEntry);
|
|
|
|
currentView = appEntry;
|
|
|
|
currentView = appEntry;
|
|
|
|
view.setBounds(getViewBounds());
|
|
|
|
view.setBounds(getViewBounds());
|
|
|
|
|
|
|
|
attachDevToolsShortcut(view.webContents);
|
|
|
|
|
|
|
|
|
|
|
|
view.webContents.on('did-finish-load', () => {
|
|
|
|
view.webContents.on('did-finish-load', () => {
|
|
|
|
removeLoader();
|
|
|
|
removeLoader();
|
|
|
|
@@ -1172,7 +1210,10 @@ app.whenReady().then(async () => {
|
|
|
|
app.userAgentFallback = cleanUserAgent;
|
|
|
|
app.userAgentFallback = cleanUserAgent;
|
|
|
|
session.defaultSession.setUserAgent(cleanUserAgent);
|
|
|
|
session.defaultSession.setUserAgent(cleanUserAgent);
|
|
|
|
|
|
|
|
|
|
|
|
// Add Referer to image requests so hotlink protection doesn't block them
|
|
|
|
// Add Referer to image requests so hotlink protection doesn't block them.
|
|
|
|
|
|
|
|
// (Sec-CH-UA spoofing was tried in 1.0.4 and caused white pages — reverted.
|
|
|
|
|
|
|
|
// Google embedded-browser detection is now mitigated only via adblock whitelist
|
|
|
|
|
|
|
|
// of gstatic/google-analytics/etc., which previously was being eaten silently.)
|
|
|
|
session.defaultSession.webRequest.onBeforeSendHeaders(
|
|
|
|
session.defaultSession.webRequest.onBeforeSendHeaders(
|
|
|
|
{ urls: ['https://*/*', 'http://*/*'] },
|
|
|
|
{ urls: ['https://*/*', 'http://*/*'] },
|
|
|
|
(details, callback) => {
|
|
|
|
(details, callback) => {
|
|
|
|
|