nightmare
Version:
A high-level browser automation library.
86 lines (74 loc) • 2.51 kB
JavaScript
const parent = require('./ipc')(process);
const EventEmitter = require('events');
const util = require('util');
const RENDER_ELEMENT_ID = '__NIGHTMARE_RENDER__';
module.exports = FrameManager;
/**
* FrameManager is an event emitter that produces a 'data' event each time the
* browser window draws to the screen.
* The primary use for this is to ensure that calling `capturePage()` on a
* window will produce an image that is up-to-date with the state of the page.
*/
function FrameManager(window) {
if (!(this instanceof FrameManager)) return new FrameManager(window);
EventEmitter.call(this);
var subscribed = false;
var requestedFrame = false;
var self = this;
this.on('newListener', subscribe);
this.on('removeListener', unsubscribe);
function subscribe() {
if (!subscribed) {
parent.emit('log', 'subscribing to browser window frames');
window.webContents.beginFrameSubscription(receiveFrame);
}
}
function unsubscribe() {
if (!self.listenerCount('data')) {
parent.emit('log', 'unsubscribing from browser window frames')
window.webContents.endFrameSubscription();
subscribed = false;
}
}
function receiveFrame(buffer) {
requestedFrame = false;
self.emit('data', buffer);
}
/**
* In addition to listening for events, calling `requestFrame` will ensure
* that a frame is queued up to render (instead of just waiting for the next
* time the browser chooses to draw a frame).
* @param {Function} [callback] Called when the frame is rendered.
*/
this.requestFrame = function(callback) {
if (callback) {
this.once('data', callback);
}
if (!requestedFrame) {
parent.emit('log', 'altering page to force rendering');
requestedFrame = true;
window.webContents.executeJavaScript(
'(' + triggerRender + ')("' + RENDER_ELEMENT_ID + '")');
}
};
};
util.inherits(FrameManager, EventEmitter);
// this runs in the render process and alters the render tree, forcing Chromium
// to draw a new frame.
var triggerRender = (function (id) {
var renderElement = document.getElementById(id);
if (renderElement) {
renderElement.remove();
}
else {
renderElement = document.createElement('div');
renderElement.id = id;
renderElement.setAttribute('style',
'position: absolute;' +
'left: 0;' +
'top: 0;' +
'width: 1px;' +
'height: 1px;');
document.documentElement.appendChild(renderElement);
}
}).toString();