Skip to content

Commit

Permalink
Refactor Captcha Window Manager (#355)
Browse files Browse the repository at this point in the history
* Start Refactoring CaptchaWindowManager

This commit starts the refactor for the new CaptchaWindowManager. This manager will be in charge of all captcha windows instead of having a manager instance per window.

At this time, the old captcha window manager is still in use. Once the new manage is written. the regular WindowManager will have to be adjusted to use the new CaptchaWindowManager

* Add Injectable Options for Youtube and Captcha Windows

This commit updates the create window functions for Youtube and Captcha to allow custom options and webPreferences to be injected in. This allows further customizations when creating these windows.

* More Implementation Progress

This commit adds more progress towards implementing a new CaptchaWindowManager.
- Specific functions to spawn youtube and captcha windows are added
- The end session handler is implemented.
- Adjustments to the Youtube Launch handler

* Transition to Use New Captcha Window Manager

This commit replaces the old implementation of WindowManager to use the new CaptchaWindowManager for captcha/youtube functions.

The old captcha window manager implementation is removed. There is still some code in the WindowManager that needs to be moved over to the CaptchaWindowManager. For now, these calls are just proxied so they work.

* Cleanup WindowManager

This commit adds some cleanup for the window manager.

The change theme handler has been commented out until #350 is addressed.

* Use Harvest State when Spawning New Captcha Window

This commit updates the Captcha Window Manager to automatically start harvesting on a new window if the rest of the windows are in the harvesting state. This allows you to open/close all captcha windows, but have new window harvest if a captcha is still required.

Issue: #106

* Increase Speed of Captcha Stopping

Instead of reloading the page, the captcha window now only loads the captcha methods once and resets when it stops harvesting. The captcha form is hidden to display the idle animation again. This prevents a reload cycle from happening everytime the captcha finishes harvesting

Issue: #272

* Remove Proxy on captcha window load

This commit removes the captcha server proxy when the window loads. This allows the youtube window experience to function properly.

* Fix Captcha Windows not closing on deactivate

This commit adds a call to always close the captcha windows when the main window closes. This should include both when closing and deactivating.

* Fix Captcha Window Auto Spawn

This commit fixes a bug where a captcha window would not be spawned if it was needed.

fixes #97
fixes #106
fixes #272
fixes #317
  • Loading branch information
pr1sm authored Feb 23, 2019
1 parent ebdb4fc commit 6b429d4
Show file tree
Hide file tree
Showing 9 changed files with 392 additions and 307 deletions.
403 changes: 276 additions & 127 deletions packages/frontend/lib/_electron/captchaWindowManager.js

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions packages/frontend/lib/_electron/dialogManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class DialogManager {
* @param {Object} options Options of a dialog.
*/
_onRequestShowMessage(ev, options) {
if (!(options)) {
if (!options) {
ev.sender.send(IPCKeys.FinishShowMessage, new Error('Invalid arguments.'), null);
return;
}
Expand All @@ -71,7 +71,7 @@ class DialogManager {
* @param {Object} options Options of a dialog.
*/
_onRequestShowOpenDialog(ev, options) {
if (!(options)) {
if (!options) {
ev.sender.send(IPCKeys.FinishShowOpenDialog, new Error('Invalid arguments.'), null);
return;
}
Expand Down
4 changes: 1 addition & 3 deletions packages/frontend/lib/_electron/env.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ let _isDevelopment = process.env.NODE_ENV === 'development';
* @param {String} envFname name of environment file to open
*/
function _setUpEnvironment(envFname) {
const envConfig = dotenv.parse(
fs.readFileSync(path.join(__dirname, `../../${envFname}`)),
);
const envConfig = dotenv.parse(fs.readFileSync(path.join(__dirname, `../../${envFname}`)));
if (envConfig) {
Object.keys(envConfig).forEach(k => {
if (k.startsWith('NEBULA_')) {
Expand Down
4 changes: 1 addition & 3 deletions packages/frontend/lib/_electron/mainMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,7 @@ class MainMenu {
{
label: 'Toggle Developer Tools',
accelerator: (() =>
process.platform === 'darwin'
? 'Alt+Command+I'
: 'Ctrl+Shift+I')(),
process.platform === 'darwin' ? 'Alt+Command+I' : 'Ctrl+Shift+I')(),
click: (item, focusedWindow) => {
if (focusedWindow) {
focusedWindow.toggleDevTools();
Expand Down
215 changes: 63 additions & 152 deletions packages/frontend/lib/_electron/windowManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,7 @@ const log = require('electron-log');

const IPCKeys = require('../common/constants');
const nebulaEnv = require('./env');
const {
createAboutWindow,
createAuthWindow,
createCaptchaWindow,
createMainWindow,
createYouTubeWindow,
urls,
} = require('./windows');
const { createAboutWindow, createAuthWindow, createMainWindow, urls } = require('./windows');

const CaptchaWindowManager = require('./captchaWindowManager');

Expand Down Expand Up @@ -100,9 +93,9 @@ class WindowManager {
this._auth = null;

/**
* Captcha Windows
* Separate manager to handle captcha windows
*/
this._captchas = new Map();
this._captchaWindowManager = new CaptchaWindowManager(context);

/**
* IPC Function Definitions
Expand All @@ -111,11 +104,8 @@ class WindowManager {
context.ipc.on(IPCKeys.RequestSendMessage, this._onRequestSendMessage.bind(this));
context.ipc.on(IPCKeys.RequestGetWindowIDs, this._onRequestGetWindowIDs.bind(this));
context.ipc.on(IPCKeys.RequestCloseWindow, this._onRequestWindowClose.bind(this));
context.ipc.on(
IPCKeys.RequestCloseAllCaptchaWindows,
this._onRequestCloseAllCaptchaWindows.bind(this),
);
context.ipc.on(IPCKeys.ChangeTheme, this.onRequestChangeTheme.bind(this));
// TODO: Add this back in #350 (https://github.com/walmat/nebula/issues/350)
// context.ipc.on(IPCKeys.ChangeTheme, this.onRequestChangeTheme.bind(this));
}

/**
Expand Down Expand Up @@ -145,7 +135,7 @@ class WindowManager {
* @param {String} tag String matching window to be created
* @return {BrowserWindow} Created window
*/
async createNewWindow(tag, opts) {
async createNewWindow(tag) {
let w; // window reference
const session = await this._context._authManager.getSession();
if (session || ['auth', 'about'].includes(tag)) {
Expand All @@ -154,64 +144,35 @@ class WindowManager {
if (this._aboutDialog) {
return this._aboutDialog;
}
w = await createAboutWindow();
w = createAboutWindow();
this._aboutDialog = w;
break;
}
case 'auth': {
if (this._auth) {
return this._auth;
}
w = await createAuthWindow();
w = createAuthWindow();
this._auth = w;
break;
}
case 'main': {
if (this._main) {
return this._main;
}
w = await createMainWindow();
w = createMainWindow();
this._main = w;
this._context.taskLauncher.start();
break;
}
case 'captcha': {
if (this._captchas.size < 5) {
if (!this._context.captchaServerManager.isRunning) {
console.log('[DEBUG]: Starting captcha server...');
this._context.captchaServerManager.start();
}
w = await createCaptchaWindow(opts);
this._captchas.set(w.id, new CaptchaWindowManager(this._context, w));
w.webContents.session.setProxy(
{
proxyRules: `http://127.0.0.1:${this._context.captchaServerManager.port}`,
proxyBypassRules: '.google.com,.gstatic.com',
},
() => {
w.loadURL('http://checkout.shopify.com');
},
);
}
break;
default: {
// throw error if unsupported tag was passed
throw new Error('Window Tag is Unsupported!');
}
case 'youtube': {
w = await createYouTubeWindow();
break;
}
default:
break;
}

if (tag !== 'captcha') {
w.loadURL(urls.get(tag));
}

// Make sure window was created before adding event listeners
if (w) {
this.addWindowEventListeners(w);
}

w.loadURL(urls.get(tag));
this.addWindowEventListeners(w);
return w;
}
return this.transitionToDeauthedState();
Expand Down Expand Up @@ -253,47 +214,28 @@ class WindowManager {
* @param {BrowserWindow} win reference to the window being closed
*/
handleClose(win) {
// Store the winId in the upper scope so we don't throw an exception when
// Trying to access win (which could already be destroyed)
const winId = win.id;
return () => {
if (nebulaEnv.isDevelopment()) {
console.log(`Window was closed, id = ${win.id}`);
console.log(`Window was closed, id = ${winId}`);
}
this._windows.delete(win.id);
this._notifyUpdateWindowIDs(win.id);
this._windows.delete(winId);
this._notifyUpdateWindowIDs(winId);

if (this._aboutDialog && win.id === this._aboutDialog.id) {
if (this._aboutDialog && winId === this._aboutDialog.id) {
this._aboutDialog = null;
} else if (this._main && win.id === this._main.id) {
} else if (this._main && winId === this._main.id) {
this._main = null;
} else if (this._auth && win.id === this._auth.id) {
// Always close captcha windows when the main window closes
this._captchaWindowManager.closeAllCaptchaWindows();
} else if (this._auth && winId === this._auth.id) {
this._auth = null;
} else if (this._captchas.size > 0) {
this._captchas.forEach(captchaWindowManager => {
if (win.id === captchaWindowManager._captchaWindow.id) {
// deregister the interval from the captcha window
WindowManager.handleCloseCaptcha(this._captchas.get(win.id));
this._captchas.delete(win.id);
} else if (
captchaWindowManager._youtubeWindow &&
win.id === captchaWindowManager._youtubeWindow.id
) {
captchaWindowManager._youtubeWindow = null;
}
});
if (this._captchas.size === 0) {
// Close the server
console.log('[DEBUG]: Stopping captcha server...');
this._context.captchaServerManager.stop();
}
}
};
}

static handleCloseCaptcha(win) {
win._tokens = [];
clearInterval(win._checkTokens);
win._checkTokens = null;
}

/**
* Function to handle the transition between main -> auth window
*/
Expand Down Expand Up @@ -361,10 +303,20 @@ class WindowManager {
* @param {IPCEvent} ev Event data.
*/
_onRequestCreateNewWindow(ev, tag, opts) {
const createdWindow = this.createNewWindow(tag, opts);
ev.sender.send(IPCKeys.FinishCreateNewWindow);

this._notifyUpdateWindowIDs(createdWindow.id);
if (tag === 'captcha') {
// Use captcha window manager to spawn captcha window
this._captchaWindowManager.spawnCaptchaWindow(opts);
ev.sender.send(IPCKeys.FinishCreateNewWindow);
} else {
try {
const createdWindow = this.createNewWindow(tag);
ev.sender.send(IPCKeys.FinishCreateNewWindow);
this._notifyUpdateWindowIDs(createdWindow.id);
} catch (err) {
console.log('[ERROR]: %s', err.message);
ev.sender.send(IPCKeys.FinishCreateNewWindow);
}
}
}

/**
Expand Down Expand Up @@ -398,94 +350,53 @@ class WindowManager {
* @param {IPCEvent} ev Event data.
* @param {Number} id corresponding window id
*/
_onRequestWindowClose(ev, id) {
_onRequestWindowClose(_, id) {
if (this._main && this._main.id === id) {
// close all windows
this._captchaWindowManager.closeAllCaptchaWindows();
this._windows.forEach(w => {
w.close();
});
this._context.taskLauncher.stop();
} else if (this._auth && this._auth.id === id) {
this._captchaWindowManager.closeAllCaptchaWindows();
this._windows.forEach(w => {
w.close();
});
} else if (this._captchas.size > 0) {
this._captchas.forEach(w => {
if (id === w._captchaWindow.id) {
if (w._youtubeWindow) {
w._youtubeWindow.close();
}
w._captchaWindow.close();
}
});
} else if (this._aboutDialog && this._aboutDialog.id === id) {
this._windows.forEach(w => {
if (w.id === id) {
w.close();
}
});
}
}

/**
* Request to close all open captcha windows
* @param {EventEmitter} ev - close event
* BUG: closes one at a time..
*/
_onRequestCloseAllCaptchaWindows() {
if (this._captchas.size > 0) {
this._captchas.forEach(captchaWindowManager => {
captchaWindowManager._captchaWindow.close();
if (captchaWindowManager._youtubeWindow) {
captchaWindowManager._youtubeWindow.close();
}
});
}
}

/**
* Start Harvesting Captchas for a specific task
* // TODO This should be moved to CaptchaWindowManager when issue #97 gets tackled
* // https://github.com/walmat/nebula/issues/97
*
* Forward call to Captcha Window Manager
*/
async onRequestStartHarvestingCaptcha(runnerId, siteKey) {
let open = false;
if (this._captchas.size === 0) {
open = true;
// TODO: This should encorporate the themes in #350 (https://github.com/walmat/nebula/issues/350)
await this.createNewWindow('captcha');
}
this._captchas.forEach(captchaWindowManager => {
captchaWindowManager.startHarvestingCaptcha(runnerId, siteKey, open);
});
startHarvestingCaptcha(runnerId, siteKey) {
this._captchaWindowManager.startHarvesting(runnerId, siteKey);
}

/**
* Stop Harvesting Captchas for a specific task
* // TODO This should be moved to CaptchaWindowManager when issue #97 gets tackled
* // https://github.com/walmat/nebula/issues/97
*
* Forward call to Captcha Window Manager
*/
onRequestStopHarvestingCaptcha(runnerId, siteKey) {
if (this._captchas.size > 0) {
this._captchas.forEach(captchaWindowManager => {
captchaWindowManager.stopHarvestingCaptcha(runnerId, siteKey);
});
}
stopHarvestingCaptcha(runnerId, siteKey) {
this._captchaWindowManager.stopHarvesting(runnerId, siteKey);
}

onRequestChangeTheme(_, opts) {
const { backgroundColor } = opts;

this._captchas.forEach((__, windowId) => {
const win = this._windows.get(windowId);
// #350 (https://github.com/walmat/nebula/issues/350)
/**
* I've tried:
* 1. win.setBackgroundColor(backgroundColor);
* 2. win.webContents.browserWindowOptions.backgroundColor = backgroundColor;
*/
});
}
// // TODO: Add this back in #350 (https://github.com/walmat/nebula/issues/350)
// onRequestChangeTheme(_, opts) {
// const { backgroundColor } = opts;
// // TODO: Use captcha window manager in this case...
// this._captchas.forEach((__, windowId) => {
// const win = this._windows.get(windowId);
// /**
// * I've tried:
// * 1. win.setBackgroundColor(backgroundColor);
// * 2. win.webContents.browserWindowOptions.backgroundColor = backgroundColor;
// */
// });
// }
}

module.exports = WindowManager;
Loading

0 comments on commit 6b429d4

Please sign in to comment.