Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Download hack for Slack #334

Merged
merged 1 commit into from
Feb 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 88 additions & 42 deletions app/services/services/tab-webcontents/api.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BrowserWindow, session, webContents } from 'electron';
import { BrowserWindow, DidCreateWindowDetails, webContents } from 'electron';
import { fromEvent, merge } from 'rxjs';
import { filter, take } from 'rxjs/operators';
import { RPC } from '../../lib/types';
Expand Down Expand Up @@ -121,46 +121,92 @@ export const addOnNotificationCloseObserver = (wc: Electron.WebContents, obs: RP
return () => { };
};

/**
* Gmail has a weird way to open PDF for printing.
* They are opened with a `about:blank` or `about:blank#blocked` url, then code is injected into the new tab/window,
* which is then redirected to a new URL which will trigger the print.
*
* What we are doing to handle this case:
* - Create a new hidden BrowserWindow and attach is to `event.newGuest`
* - Wait for this window to trigger a download
* - If within 2 seconds we received a download, we close the window
* - Else, we show it or dispatch it
*
* NB: When overriding the User Agent to bypass Google blacklist, ALL URLs seems to be handled that way instead of just downloads...
*
* NB: As the print PDF thingy is tied to the PDF viewer which doesn't really work in Electron yet,
* we receive a download event instead of a print event.
*/
export const handleHackGoogleAppsURLs =
async (event: Event, options: Partial<Electron.BrowserWindowConstructorOptions>): Promise<BrowserWindow | undefined> => {
const actualOptions: Electron.BrowserWindowConstructorOptions = {
...options,
width: 400,
height: 400,
show: false,
};
const guest = new BrowserWindow(actualOptions);
(event as any).newGuest = guest;

// Wait 2 seconds to receive a downloadItem
const downloadItem = await Promise.race([
new Promise<boolean>(resolve => {
session.defaultSession!.once('will-download', () => resolve(true));
}),
new Promise<void>(resolve => { setTimeout(resolve, 2000); }),
]);

// If download event received, we close the webcontents
if (downloadItem) {
guest.close();
return;
// /**
// * Gmail has a weird way to open PDF for printing.
// * They are opened with a `about:blank` or `about:blank#blocked` url, then code is injected into the new tab/window,
// * which is then redirected to a new URL which will trigger the print.
// *
// * What we are doing to handle this case:
// * - Create a new hidden BrowserWindow and attach is to `event.newGuest`
// * - Wait for this window to trigger a download
// * - If within 2 seconds we received a download, we close the window
// * - Else, we show it or dispatch it
// *
// * NB: When overriding the User Agent to bypass Google blacklist, ALL URLs seems to be handled that way instead of just downloads...
// *
// * NB: As the print PDF thingy is tied to the PDF viewer which doesn't really work in Electron yet,
// * we receive a download event instead of a print event.
// */
// export const handleHackGoogleAppsURLs =
// async (event: Event, options: Partial<Electron.BrowserWindowConstructorOptions>): Promise<BrowserWindow | undefined> => {
// const actualOptions: Electron.BrowserWindowConstructorOptions = {
// ...options,
// width: 400,
// height: 400,
// show: false,
// };
// const guest = new BrowserWindow(actualOptions);
// (event as any).newGuest = guest;

// // Wait 2 seconds to receive a downloadItem
// const downloadItem = await Promise.race([
// new Promise<boolean>(resolve => {
// session.defaultSession!.once('will-download', () => resolve(true));
// }),
// new Promise<void>(resolve => { setTimeout(resolve, 2000); }),
// ]);

// // If download event received, we close the webcontents
// if (downloadItem) {
// guest.close();
// return;
// }

// return guest;
// };

const downloadHackPerfixes: string[] = [
'https://files.slack.com', // Download file from Slack.com
'about:blank', //
];

const getDownloadHackPrefix = (url : string): (string | null) => {
for (const prefix of downloadHackPerfixes) {
if (url.startsWith(prefix)) {
return prefix;
}
}
return null;
}

return guest;
};
export const handleDownloadHack = (wc: Electron.WebContents, url: string): boolean => {
let result: boolean = false;
const downloadUrlPrefix = getDownloadHackPrefix(url);
if (downloadUrlPrefix !== null) {
result = true;
wc.once('did-create-window', (window: BrowserWindow, details: DidCreateWindowDetails) => {
if (getDownloadHackPrefix(details.url) !== downloadUrlPrefix) {
return;
}
// Wait 2 seconds to receive a downloadItem
Promise.race([
new Promise<boolean>(resolve => {
wc.session.once('will-download', () => resolve(true));
}),
new Promise<boolean>(resolve => {
setTimeout(() => resolve(false), 2000)
}),
])
.then(downloadItem => {
if (downloadItem) {
window.close();
}
else if (window.webContents.getURL().startsWith(downloadUrlPrefix)) {
// if popup is still the same after 2 seconds, we show it to let it finish
window.show();
}
});
});
}
return result;
}
12 changes: 9 additions & 3 deletions app/services/services/tab-webcontents/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
addOnPreventUnload,
awaitDomReady,
getWebContentsFromIdOrThrow,
handleHackGoogleAppsURLs,
handleDownloadHack,
} from './api';
import {
AlertDialogProviderService,
Expand Down Expand Up @@ -266,10 +266,12 @@ export class TabWebContentsServiceImpl extends TabWebContentsService implements

async setUrlDispatcherProvider(provider: RPC.Node<UrlDispatcherProviderService>) {
return new ServiceSubscription(this.onNewWebviews().subscribe(wc => {

wc.setWindowOpenHandler((details: HandlerDetails) => {

log.debug('WindowOpen', details, process.type);
// log.debug('WindowOpen', details, process.type);

let useDownloadHack = false;

if (details.disposition === 'new-window') {
if (this.isNewWindowForUserRequest(details)) {
Expand All @@ -282,6 +284,9 @@ export class TabWebContentsServiceImpl extends TabWebContentsService implements
provider.dispatchUrl(details.url, wc.id, DEFAULT_BROWSER);
return { action: 'deny' };
}
else if (details.disposition === 'foreground-tab') {
useDownloadHack = handleDownloadHack(wc, details.url);
}

//vk: 2023.09.29 don't know how to verify this hack
// will fix it later
Expand Down Expand Up @@ -309,6 +314,7 @@ export class TabWebContentsServiceImpl extends TabWebContentsService implements
action: 'allow',
overrideBrowserWindowOptions: {
fullscreen: false,
show: !useDownloadHack,
}
};
});
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "station-desktop-app",
"private": true,
"productName": "Station",
"version": "2.7.0-b2",
"version": "2.8.0-b1",
"description": "Station",
"homepage": "https://getstation.com",
"author": {
Expand Down