Skip to content

Commit a7ba6d3

Browse files
Remove IPC listener on timeout Fixes #161 (#162)
* Remove IPC listener on timeout. And goodbye jasmime, electron-ava to the rescue. Fixes #161 * better naming and documentation #161
1 parent a7ae6f7 commit a7ba6d3

File tree

9 files changed

+60
-61
lines changed

9 files changed

+60
-61
lines changed

lib/exportJob.js

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const HTML_DPI = 96
2323
const MICRONS_INCH_RATIO = 25400
2424
const MAX_EVENT_WAIT = 10000
2525
const IPC_MAIN_CHANNEL_RENDER = 'READY_TO_RENDER'
26+
const eventPrefix = 'job.render.'
2627

2728
const DEFAULT_OPTIONS = {
2829
closeWindow: true,
@@ -57,7 +58,12 @@ class ExportJob extends EventEmitter {
5758
* the filesystem
5859
*/
5960
constructor (input, output, args, options) {
60-
super({ wildcard: true })
61+
super({
62+
// Allow listeners to provide wildcards
63+
wildcard: true,
64+
// displays the event name if maxListeners is reached for an event
65+
verboseMemoryLeak: true
66+
})
6167
this.jobId = uuid()
6268
this.input = _.isArray(input) ? input : [input]
6369
this.output = output
@@ -79,7 +85,6 @@ class ExportJob extends EventEmitter {
7985
* Render markdown or html to pdf
8086
*/
8187
render (window) {
82-
const eventPrefix = 'job.render.'
8388
this.emit(`${eventPrefix}start`)
8489

8590
const win = this._launchBrowserWindow()
@@ -396,6 +401,7 @@ class ExportJob extends EventEmitter {
396401
if (this.args.disableCache) {
397402
loadOpts.extraHeaders += 'pragma: no-cache\n'
398403
}
404+
this.emit(`${eventPrefix}loadurl`, { url: url })
399405
window.loadURL(wargs.urlWithArgs(url, {}), loadOpts)
400406
}
401407

@@ -422,37 +428,55 @@ class ExportJob extends EventEmitter {
422428

423429
_waitForBrowserEvent (waitForJSEvent, window, generateFunction) {
424430
const eventName = _.size(waitForJSEvent) > 0 ? waitForJSEvent : 'view-ready'
425-
this._attachIPCListener(eventName, generateFunction)
426-
this._executeJSListener(eventName, generateFunction, window)
431+
const ipcListener = this._attachIPCListener(eventName, generateFunction)
432+
this._executeJSListener(eventName, ipcListener, generateFunction, window)
427433
}
428434

429435
/**
430436
* responsible for executing JS in the browser that will wait for the page
431437
* to emit an event before capturing the page.
432438
*
433439
* @param eventName
440+
* @param ipcListener The listener for the ready event. This needs cancelled
441+
* if there is a timeout before it the event is received
434442
* @param generateFunction
435443
* @param window
436444
* @private
437445
*/
438-
_executeJSListener (eventName, generateFunction, window) {
446+
_executeJSListener (eventName, ipcListener, generateFunction, window) {
439447
// event.detail will only exist if a CustomEvent was emitted
440448
const cmd = `var ipcRenderer = require('electron').ipcRenderer
441449
document.body.addEventListener('${eventName}',
442450
function(event) {
443451
ipcRenderer.send('${IPC_MAIN_CHANNEL_RENDER}', '${this.jobId}', event.detail)
444452
}
445453
)`
446-
// Don't let things hang forever
447-
const timeout = setTimeout(() => {
448-
this.emit('window.event.wait.timeout', {eventName: eventName})
449-
generateFunction()
450-
}, this.args.outputWait > 0 ? this.args.outputWait : MAX_EVENT_WAIT)
451454

455+
// Don't let a ready event hang, set a max timeout interval
456+
const f = this._cancelReadyEvent.bind(this, eventName, ipcListener, generateFunction)
457+
const maxWait = this.args.outputWait > 0 ? this.args.outputWait : MAX_EVENT_WAIT
458+
const timeout = setTimeout(f, maxWait)
459+
460+
// clear the timeout as soon as we get the ready event from the browser
452461
this.once('window.event.wait.end', () => clearTimeout(timeout))
462+
453463
window.webContents.executeJavaScript(cmd)
454464
}
455465

466+
/**
467+
* Invoked when a ready event has not been received before the max timeout is reached
468+
* @param eventName The eventName provided by the client
469+
* @param ipcListener The ipcMain listener waiting for the IPC_MAIN_CHANNEL_RENDER
470+
* event from the renderer process
471+
* @param generateFunction A callback function to invoke to capture the window
472+
* @private
473+
*/
474+
_cancelReadyEvent (eventName, ipcListener, generateFunction) {
475+
this.emit('window.event.wait.timeout', {eventName: eventName})
476+
electron.ipcMain.removeListener(IPC_MAIN_CHANNEL_RENDER, ipcListener)
477+
generateFunction()
478+
}
479+
456480
/**
457481
* Listen for the browser to emit the READY_TO_RENDER event and when it does
458482
* emit our own event so the max load timer is removed.
@@ -478,6 +502,7 @@ class ExportJob extends EventEmitter {
478502
}
479503
}
480504
electron.ipcMain.on(IPC_MAIN_CHANNEL_RENDER, listener)
505+
return listener
481506
}
482507

483508
/**

package.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@
77
"fix": "standard --fix",
88
"lint": "standard",
99
"unit-test": "ava | tap-diff",
10-
"test": "npm run lint && ava | tap-diff",
11-
"\\": "This effectively runs jasmine tests as an electron application (but it doesn't work for Ava)",
12-
"integration-test": "electron node_modules/.bin/jasmine"
10+
"test": "npm run fix && ava **/*-test.js | tap-diff && electron-ava --tap **/*-test-it.js | tap-diff"
1311
},
1412
"bin": {
1513
"electron-pdf": "cli.js"
@@ -36,6 +34,7 @@
3634
"homepage": "https://github.com/fraserxu/electron-pdf",
3735
"devDependencies": {
3836
"ava": "^0.18.0",
37+
"electron-ava": "^0.3.0",
3938
"jasmine": "^2.5.2",
4039
"standard": "^8.4.0",
4140
"tap-diff": "^0.1.1",

spec/index-test.js

Lines changed: 0 additions & 11 deletions
This file was deleted.

spec/source-test.js

Lines changed: 0 additions & 16 deletions
This file was deleted.

spec/support/jasmine.json

Lines changed: 0 additions & 11 deletions
This file was deleted.

spec/test-sample.html

Lines changed: 0 additions & 10 deletions
This file was deleted.

test/exportJob-test-it.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { test } from 'ava'
2+
3+
import electron from 'electron'
4+
5+
import ExportJob from '../lib/exportJob'
6+
7+
const args = {}
8+
const options = {
9+
pageSize: 'Letter'
10+
}
11+
let job = new ExportJob(['input'], 'output.pdf', args, options)
12+
13+
// Ready Event, wait times
14+
test('_readyEventTimeout removes ipc listener', t => {
15+
const generateFunction = () => {}
16+
const ipcListener = job._attachIPCListener('myEvent', generateFunction)
17+
t.is(electron.ipcMain.listenerCount('READY_TO_RENDER'), 1)
18+
job._cancelReadyEvent('myEvent', ipcListener, generateFunction)
19+
t.is(electron.ipcMain.listenerCount('READY_TO_RENDER'), 0)
20+
})
21+

test/exportJob-test.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import {test} from 'ava'
2+
23
import _ from 'lodash'
4+
35
import validator from 'validator'
46

57
import ExportJob from '../lib/exportJob'

0 commit comments

Comments
 (0)