Commit 6394c7de authored by 刘松's avatar 刘松

update

parents
'use strict';
var sliced = require('sliced');
var jsesc = require('jsesc');
var fs = require('fs');
var once = require('once');
var createDebug = require('debug');
var debug = createDebug('avatar:action');
var waitms = function waitms(ms, done) {
setTimeout(done, ms);
};
var waitfn = function waitfn() {
var softTimeout = this.timeout || null;
var executionTimer;
var softTimeoutTimer;
var runnerTimeoutTimer;
var self = arguments[0];
var args = sliced(arguments);
var done = args[args.length - 1];
var timeoutTimer = setTimeout(function () {
clearTimeout(executionTimer);
clearTimeout(softTimeoutTimer);
clearTimeout(runnerTimeoutTimer);
done(new Error('.wait() timed out after ' + self.options.waitTimeout + 'msec'));
}, self.options.waitTimeout);
return tick.apply(this, arguments);
function tick(self, fn /** , arg1, arg2..., done**/) {
if (softTimeout) {
softTimeoutTimer = setTimeout(function () {
clearTimeout(runnerTimeoutTimer);
clearTimeout(executionTimer);
clearTimeout(timeoutTimer);
done();
}, softTimeout);
}
var waitDone = function waitDone(err, result) {
if (result) {
clearTimeout(timeoutTimer);
clearTimeout(softTimeoutTimer);
clearTimeout(runnerTimeoutTimer);
return done();
} else if (err) {
clearTimeout(timeoutTimer);
clearTimeout(softTimeoutTimer);
clearTimeout(runnerTimeoutTimer);
return done(err);
} else {
executionTimer = setTimeout(function () {
clearTimeout(runnerTimeoutTimer);
tick.apply(self, args);
}, self.options.pollInterval);
}
};
var newArgs = [fn, waitDone].concat(args.slice(2, -1));
runnerTimeoutTimer = setTimeout(function () {
clearTimeout(executionTimer);
tick.apply(self, args);
}, self.options.pollInterval * 3);
try {
self.evaluateNow.apply(self, newArgs);
} catch (err) {
waitDone(err);
}
}
};
var waitelem = function waitelem(self, selector, done) {
var elementPresent;
eval('elementPresent = function() {' + // eslint-disable-line
" var element = document && document.body && document.querySelector('" + jsesc(selector) + "');" + ' return (element ? true : false);' + '};');
waitfn.apply(this, [self, elementPresent, done]);
};
var focusSelector = function focusSelector(done, selector) {
return this.evaluateNow(function (selector) {
document.querySelector(selector).focus();
}, done.bind(this), selector);
};
var blurSelector = function blurSelector(done, selector) {
return this.evaluateNow(function (selector) {
// it is possible the element has been removed from the DOM
// between the action and the call to blur the element
var element = document.querySelector(selector);
if (element) {
element.blur();
}
}, done.bind(this), selector);
};
exports.enterIframe = function (selector, done) {
debug('.enterIframe() ' + selector);
this.evaluateNow(function (selector) {
var element = document.querySelector(selector);
__avatar__.currentDocument = element.contentDocument; // eslint-disable-line
__avatar__.currentWindow = element.contentWindow; // eslint-disable-line
}, done, selector);
};
exports.exitIframe = function (done) {
debug('.exitIframe()');
this.evaluateNow(function () {
__avatar__.currentDocument = __avatar__.currentWindow.parent.document; // eslint-disable-line
__avatar__.currentWindow = __avatar__.currentWindow.parent; // eslint-disable-line
}, done);
};
exports.waitIframe = function (selector, done) {
debug('.waitIframe() ' + selector);
this.evaluateNow(function (selector, pollInterval, done) {
var element = document.querySelector(selector);
if (!element) {
throw new Error('Unable to find element by selector: ' + selector);
}
function check() {
if (element.contentDocument && element.contentDocument.readyState === 'complete') {
return done();
} else {
setTimeout(function () {
return check();
}, pollInterval);
}
}
check();
}, done, selector, this.options.pollInterval);
};
exports.or = function (options, action, done) {
debug('.or()');
if (arguments.length === 2) {
done = action;
action = null;
}
done = once(done);
Object.keys(options).forEach(function (key) {
return options[key]().then(function () {
return done(null, key);
});
});
action && action();
};
exports.redirect = function (done) {
debug('.redirect()');
this.once('did-get-redirect-request', function (event) {
if (event.isMainFrame) {
done();
}
});
};
exports.waitUrl = function (url, done) {
var webview = this.webview;
function cleanup(error, data) {
webview.removeEventListener('dom-ready', didDomReady);
setImmediate(function () {
return done(error, data);
});
}
function didDomReady() {
var currentUrl = webview.getURL();
if (typeof url === 'string' ? currentUrl === url : url(currentUrl)) {
cleanup();
}
}
webview.addEventListener('dom-ready', didDomReady);
};
exports.goto = function (url, headers, done) {
debug('.goto() url');
if (arguments.length === 2) {
done = headers;
headers = {};
}
headers = Object.assign({}, this._headers, headers);
var webview = this.webview;
var timeout = this.options.gotoTimeout;
if (!url || typeof url !== 'string') {
return done('goto: `url` must be a non-empty string');
}
var httpReferrer = '';
var extraHeaders = '';
for (var key in headers) {
if (key.toLowerCase() === 'referer') {
httpReferrer = headers[key];
continue;
}
extraHeaders += key + ': ' + headers[key] + '\n';
}
var loadUrlOptions = { extraHeaders: extraHeaders };
httpReferrer && (loadUrlOptions.httpReferrer = httpReferrer);
if (webview.getURL() === url) {
return done();
}
var timer = setTimeout(function () {
cleanup(new Error('Navigation timed out after ' + timeout + ' ms'));
}, timeout);
startLoading();
function didDomReady() {
cleanup();
}
function cleanup(error, data) {
clearTimeout(timer);
webview.removeEventListener('dom-ready', didDomReady);
setImmediate(function () {
return done(error, data);
});
}
function startLoading() {
if (webview.isLoading()) {
var didStopLoading = function didStopLoading() {
webview.removeEventListener('did-stop-loading', didStopLoading);
startLoading();
};
webview.addEventListener('did-stop-loading', didStopLoading);
return webview.stop();
}
webview.addEventListener('dom-ready', didDomReady);
webview.loadURL(url, loadUrlOptions);
}
};
exports.appendTo = function (container, done) {
debug('.appendTo()');
var webview = this.webview;
if (webview.parentElement === container) {
return done();
}
var _ready = new Promise(function (resolve) {
var didDomReady = function didDomReady() {
webview.removeEventListener('dom-ready', didDomReady);
resolve();
};
webview.addEventListener('dom-ready', didDomReady);
container.appendChild(webview);
});
_ready.then(function () {
return done();
});
this._ready = this._ready.then(function () {
return _ready;
});
};
exports.debugger = function (done) {
debug('.debugger()');
var webview = this.webview,
_debugger = this._debugger;
if (_debugger) return done(null, _debugger);
var webContents = webview.getWebContents();
if (!webContents.debugger.isAttached()) {
try {
webContents.debugger.attach('1.1');
} catch (error) {
return done(error);
}
} else {
debug('debugger is attached');
}
this._debugger = webContents.debugger;
done(null, this._debugger);
};
exports.reject = function (filter, done) {
var webview = this.webview;
var webContents = webview.getWebContents();
webContents.session.webRequest.onBeforeSendHeaders(filter, function (details, callback) {
callback({ cancel: true });
});
done();
};
exports.response = function (url, optFn, done) {
var _this = this;
debug('.response()');
if (arguments.length === 2) {
done = optFn;
optFn = null;
}
this.debugger().then(function (_debugger) {
var timeout = _this.options.waitTimeout;
var timer = setTimeout(function () {
cleanup(new Error('Response timed out after ' + timeout + ' ms'));
}, timeout);
function onMessage(event, method, _ref) {
var response = _ref.response,
requestId = _ref.requestId,
type = _ref.type;
debug('network message: ' + event + ' ' + method + ' ' + type + ' ' + (response && response.url));
if (method === 'Network.responseReceived' && type === 'XHR') {
if (typeof url === 'string' ? response.url !== url : !url(response.url, response)) return;
_debugger.sendCommand('Network.getResponseBody', { requestId: requestId }, function (err, res) {
if (err && Object.keys(err).length) return cleanup(err);
if (optFn) {
optFn(res) && cleanup(null, res);
} else {
cleanup(null, res);
}
});
}
}
function cleanup(err, result) {
clearTimeout(timer);
_debugger.sendCommand('Network.disable');
_debugger.removeListener('message', onMessage);
if (err) return done(err);
done(null, result);
}
_debugger.on('message', onMessage);
_debugger.sendCommand('Network.enable');
});
};
exports.json = function (url, done) {
debug('.json() ' + url);
this.evaluateNow(function (url) {
return fetch(url, { credentials: 'include' } // eslint-disable-line
).then(function (res) {
return res.json();
});
}, done, url);
};
exports.dev = function (done) {
debug('.dev()');
this.webview.openDevTools();
done();
};
exports.title = function (done) {
debug('.title()');
this.evaluateNow(function () {
return document.title;
}, done);
};
exports.url = function (done) {
debug('.url()');
this.evaluateNow(function () {
return document.location.href;
}, done);
};
exports.path = function (done) {
debug('.path()');
this.evaluateNow(function () {
return document.location.pathname;
}, done);
};
exports.href = function (selector, done) {
debug('.href()');
this.evaluateNow(function (selector) {
var elem = document.querySelector(selector);
if (!elem) {
throw new Error('Unable to find element by selector: ' + selector);
}
return elem.href;
}, done, selector);
};
exports.text = function (selector, done) {
debug('.text() ' + selector);
this.evaluateNow(function (selector) {
var elem = document.querySelector(selector);
if (elem && elem.innerText) {
return elem.innerText.trim();
}
return '';
}, done, selector);
};
exports.visible = function (selector, done) {
debug('.visible() ' + selector);
this.evaluateNow(function (selector) {
var elem = document.querySelector(selector);
if (elem) return elem.offsetWidth > 0 && elem.offsetHeight > 0;else return false;
}, done, selector);
};
exports.exists = function (selector, done) {
debug('.exists() ' + selector);
this.evaluateNow(function (selector) {
return document.querySelector(selector) !== null;
}, done, selector);
};
exports.click = function (selector, done) {
debug('.click() ' + selector);
this.evaluateNow(function (selector) {
document.activeElement.blur();
var element = document.querySelector(selector);
if (!element) {
throw new Error('Unable to find element by selector: ' + selector);
}
var event = document.createEvent('MouseEvent');
event.initEvent('click', true, true);
element.dispatchEvent(event);
}, done, selector);
};
exports.focus = function (selector, done) {
focusSelector.bind(this)(done, selector);
};
exports.userAgent = function (ua, done) {
this.webview.getWebContents().setUserAgent(ua);
done();
};
exports.mousedown = function (selector, done) {
debug('.mousedown() ' + selector);
this.evaluateNow(function (selector) {
var element = document.querySelector(selector);
if (!element) {
throw new Error('Unable to find element by selector: ' + selector);
}
var event = document.createEvent('MouseEvent');
event.initEvent('mousedown', true, true);
element.dispatchEvent(event);
}, done, selector);
};
exports.mouseup = function (selector, done) {
debug('.mouseup() ' + selector);
this.evaluateNow(function (selector) {
var element = document.querySelector(selector);
if (!element) {
throw new Error('Unable to find element by selector: ' + selector);
}
var event = document.createEvent('MouseEvent');
event.initEvent('mouseup', true, true);
element.dispatchEvent(event);
}, done, selector);
};
exports.mouseover = function (selector, done) {
debug('.mouseover() ' + selector);
this.evaluateNow(function (selector) {
var element = document.querySelector(selector);
if (!element) {
throw new Error('Unable to find element by selector: ' + selector);
}
var event = document.createEvent('MouseEvent');
event.initMouseEvent('mouseover', true, true);
element.dispatchEvent(event);
}, done, selector);
};
exports.type = function (selector) {
var text = void 0;
var done = void 0;
if (arguments.length === 2) {
done = arguments[1];
} else {
text = arguments[1];
done = arguments[2];
}
debug('.type() ' + selector + ' ' + text);
var self = this;
var webview = this.webview;
var chars = String(text).split('');
function type() {
var ch = chars.shift();
if (ch === undefined) {
return done();
}
webview.sendInputEvent({
type: 'keyDown',
keyCode: ch
});
webview.sendInputEvent({
type: 'char',
keyCode: ch
});
webview.sendInputEvent({
type: 'keyUp',
keyCode: ch
});
setTimeout(type, self.options.typeInterval);
}
focusSelector.bind(this)(function (err) {
if (err) {
return done(err);
}
var blurDone = blurSelector.bind(this, done, selector);
if ((text || '') === '') {
this.evaluateNow(function (selector) {
document.querySelector(selector).value = '';
}, blurDone, selector);
} else {
type();
}
}, selector);
};
exports.insert = function (selector, text, done) {
if (arguments.length === 2) {
done = text;
text = null;
}
debug('.insert() ' + selector + ' ' + text);
var webview = this.webview;
focusSelector.bind(this)(function (err) {
if (err) {
return done(err);
}
var blurDone = blurSelector.bind(this, done, selector);
if ((text || '') === '') {
this.evaluateNow(function (selector) {
document.querySelector(selector).value = '';
}, blurDone, selector);
} else {
webview.insertText(String(text));
done();
}
}, selector);
};
exports.check = function (selector, done) {
debug('.check() ' + selector);
this.evaluateNow(function (selector) {
var element = document.querySelector(selector);
var event = document.createEvent('HTMLEvents');
element.checked = true;
event.initEvent('change', true, true);
element.dispatchEvent(event);
}, done, selector);
};
exports.uncheck = function (selector, done) {
debug('.uncheck() ' + selector);
this.evaluateNow(function (selector) {
var element = document.querySelector(selector);
var event = document.createEvent('HTMLEvents');
element.checked = null;
event.initEvent('change', true, true);
element.dispatchEvent(event);
}, done, selector);
};
exports.select = function (selector, option, done) {
debug('.select() ' + selector);
this.evaluateNow(function (selector, option) {
var element = document.querySelector(selector);
var event = document.createEvent('HTMLEvents');
element.value = option;
event.initEvent('change', true, true);
element.dispatchEvent(event);
}, done, selector, option);
};
exports.back = function (done) {
debug('.back()');
this.evaluateNow(function () {
window.history.back();
}, done);
};
exports.forward = function (done) {
debug('.forward()');
this.evaluateNow(function () {
window.history.forward();
}, done);
};
exports.refresh = function (done) {
debug('.refresh()');
this.evaluateNow(function () {
window.location.reload();
}, done);
};
exports.wait = function () {
debug('.wait()');
var args = sliced(arguments);
var done = args[args.length - 1];
if (args.length < 2) {
return done();
}
var arg = args[0];
if (typeof arg === 'number') {
if (arg < this.options.waitTimeout) {
waitms(arg, done);
} else {
waitms(this.options.waitTimeout, function () {
done(new Error('.wait() timed out after ' + this.options.waitTimeout + 'msec'));
}.bind(this));
}
} else if (typeof arg === 'string') {
var timeout = null;
if (typeof args[1] === 'number') {
timeout = args[1];
}
waitelem.apply({ timeout: timeout }, [this, arg, done]);
} else if (typeof arg === 'function') {
args.unshift(this);
waitfn.apply(this, args);
} else {
done();
}
};
exports.evaluate = function (fn /** , arg1, arg2... **/) {
debug('.evaluate()');
var args = sliced(arguments);
var done = args[args.length - 1];
var self = this;
var newDone = function newDone() {
clearTimeout(timeoutTimer);
done.apply(self, arguments);
};
var newArgs = [fn, newDone].concat(args.slice(1, -1));
if (typeof fn !== 'function') {
return done(new Error('.evaluate() fn should be a function'));
}
var timeoutTimer = setTimeout(function () {
done(new Error('Evaluation timed out after ' + self.options.executionTimeout + 'msec. Are you calling done() or resolving your promises?'));
}, self.options.executionTimeout);
this.evaluateNow.apply(this, newArgs);
};
exports.inject = function (type, file, done) {
debug('.inject()');
if (arguments.length === 2) {
done = file;
file = null;
}
var webview = this.webview;
if (type === 'js') {
var js = fs.readFileSync(file, { encoding: 'utf-8' });
this._inject(js, done);
} else if (type === 'css') {
var css = fs.readFileSync(file, { encoding: 'utf-8' });
webview.insertCSS(css);
done();
} else {
this._inject(type, done);
}
};
exports.scrollTo = function (y, x, done) {
debug('.scrollTo() ' + x + ' ' + y);
this.evaluateNow(function (y, x) {
window.scrollTo(x, y);
}, done, y, x);
};
exports.cookies = {
get: function get(name, done) {
debug('.cookies.get() ' + name);
var query = {};
switch (arguments.length) {
case 2:
query = typeof name === 'string' ? { name: name } : name;
break;
case 1:
done = name;
break;
}
var webview = this.webview;
var details = Object.assign({
url: webview.getURL()
}, query);
webview.getWebContents().session.cookies.get(details, function (error, cookies) {
if (error) return done(error);
done(null, details.name ? cookies[0] : cookies);
});
},
set: function set(name, value, done) {
debug('.cookies.set() ' + name + ' ' + value);
var cookies = [];
switch (arguments.length) {
case 3:
cookies.push({
name: name,
value: value
});
break;
case 2:
cookies = [].concat(name);
done = value;
break;
case 1:
done = name;
break;
}
var webview = this.webview;
var pending = cookies.length;
var errors = [];
for (var i = 0, cookie; cookie = cookies[i]; i++) {
var details = Object.assign({
url: webview.getURL()
}, cookie);
webview.getWebContents().session.cookies.set(details, function (error) {
if (error) errors.push(error);
if (! --pending) {
if (errors.length) return done(errors);
done();
}
});
}
},
clear: function clear(name, done) {
debug('.cookies.clear() ' + name);
var cookies = [];
switch (arguments.length) {
case 2:
cookies = [].concat(name);
break;
case 1:
done = name;
break;
}
var webview = this.webview;
var webContents = webview.getWebContents();
var url = webview.getURL();
var getCookies = function getCookies(cb) {
return cb(null, cookies);
};
if (cookies.length === 0) {
getCookies = function getCookies(cb) {
return webContents.session.cookies.get({ url: url }, function (error, cookies) {
cb(error, cookies.map(function (cookie) {
return cookie.name;
}));
});
};
}
getCookies(function (error, cookies) {
if (error) return done(error); // ?
var pending = cookies.length;
if (pending === 0) {
return done();
}
for (var i = 0, cookie; cookie = cookies[i]; i++) {
webContents.session.cookies.remove(url, cookie, function (error) {
if (error) done(error);else if (! --pending) done();
});
}
});
},
clearAll: function clearAll(done) {
debug('.cookies.clearAll()');
var webview = this.webview;
var webContents = webview.getWebContents();
webContents.session.clearStorageData({
storages: ['cookies']
}, done);
}
};
\ No newline at end of file
'use strict';
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var fs = window.require('fs');
var os = window.require('os');
var path = window.require('path');
var mkdirp = window.require('mkdirp');
var sliced = require('sliced');
var once = window.require('once');
var debug = window.require('debug')('avatar:main');
var debugSource = window.require('debug')('avatar:source');
var Events = window.require('events');
var _require = require('./javascript'),
execute = _require.execute,
inject = _require.inject;
var actions = require('./actions');
var preload = require('./preload');
var noop = function noop() {};
var keys = Object.keys;
var DEFAULT_HALT_MESSAGE = 'Avatar Halted';
var datadir = path.join(os.tmpdir(), 'avatar');
var preloadFile = path.join(datadir, 'preload.js');
mkdirp.sync(datadir);
fs.writeFileSync(preloadFile, '(' + preload.toString() + ')()');
var container = document.createElement('div');
container.style.width = container.style.height = 0;
container.style.overflow = 'hidden';
document.body.appendChild(container
// wrap all the functions in the queueing function
);function queued(name, fn) {
return function action() {
debug('queueing action "' + name + '"');
var args = [].slice.call(arguments);
this._queue.push([fn, args]);
return this;
};
}
window.__avatars__ = {};
var n = 0;
var Avatar = function (_Events) {
_inherits(Avatar, _Events);
function Avatar() {
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
_classCallCheck(this, Avatar);
var _this = _possibleConstructorReturn(this, (Avatar.__proto__ || Object.getPrototypeOf(Avatar)).call(this));
_this.state = 'initial';
_this.running = false;
_this.ending = false;
_this.ended = false;
_this._queue = [];
_this._headers = [];
var defaultOptions = {
waitTimeout: 5 * 60 * 1000, // wait超时时间
gotoTimeout: 30 * 1000, // 页面加载超时时间
pollInterval: 250, // wait检测间隔
typeInterval: 100, // type间隔
executionTimeout: 30 * 1000, // 执行超时时间
Promise: Avatar.Promise || Promise,
container: container,
url: 'about:blank'
};
window.localStorage.debug = '';
_this.nid = n++;
options = _this.options = Object.assign({}, defaultOptions, options);
_this.id = options.id || _this.nid;
if (options.debug) {
window.localStorage.debug = options.debug;
}
var webview = _this.options.webview || document.createElement('webview');
webview.style.height = webview.style.width = '100%';
_this.webview = webview;
var self = _this;
function forward(name) {
return function (event) {
if (!self._closed) {
self.emit(name, event);
}
};
}
webview.addEventListener('did-finish-load', forward('did-finish-load'));
webview.addEventListener('did-fail-load', forward('did-fail-load'));
webview.addEventListener('did-fail-provisional-load', forward('did-fail-provisional-load'));
webview.addEventListener('did-frame-finish-load', forward('did-frame-finish-load'));
webview.addEventListener('did-start-loading', forward('did-start-loading'));
webview.addEventListener('did-stop-loading', forward('did-stop-loading'));
webview.addEventListener('did-get-response-details', forward('did-get-response-details'));
webview.addEventListener('did-get-redirect-request', forward('did-get-redirect-request'));
webview.addEventListener('dom-ready', forward('dom-ready'));
webview.addEventListener('page-favicon-updated', forward('page-favicon-updated'));
webview.addEventListener('new-window', forward('new-window'));
webview.addEventListener('will-navigate', forward('will-navigate'));
webview.addEventListener('crashed', forward('crashed'));
webview.addEventListener('plugin-crashed', forward('plugin-crashed'));
webview.addEventListener('destroyed', forward('destroyed'));
webview.addEventListener('media-started-playing', forward('media-started-playing'));
webview.addEventListener('media-paused', forward('media-paused'));
webview.addEventListener('close', function () {
_this._closed = true;
forward('close');
});
debug('queuing process start');
_this._ready = new options.Promise(function (resolve) {
function didDomReady() {
webview.removeEventListener('dom-ready', didDomReady);
resolve();
}
webview.addEventListener('dom-ready', didDomReady);
webview.setAttribute('partition', 'persist:' + _this.id);
webview.setAttribute('src', options.url);
webview.setAttribute('disablewebsecurity', true);
webview.setAttribute('preload', 'file://' + preloadFile);
options.container.appendChild(webview);
});
_this.queue(function (done) {
debug(_this.id + ' start');
_this._ready.then(function () {
debug(_this.id + ' ready');
done();
});
});
Avatar.namespaces.forEach(function (name) {
if (typeof this[name] === 'function') {
this[name] = this[name]();
}
}, _this);
// TODO:
window.__avatars__[_this.id + '_' + _this.nid] = _this;
return _this;
}
_createClass(Avatar, [{
key: '_end',
value: function _end(fn) {
delete window.__avatars__[this.id + '_' + this.nid];
if (this.webview) {
this.webview.stop();
this.webview.remove();
this.webview = null;
}
this.ended = true;
fn();
}
}, {
key: 'destroy',
value: function destroy() {
this.halt();
}
}, {
key: 'run',
value: function run(fn) {
var _this2 = this;
debug('running');
var steps = this.queue();
this.running = true;
this._queue = [];
this.queue(function (done) {
_this2._ready.then(function () {
done();
});
});
var self = this;
// kick us off
next();
// next function
function next(err, res) {
var item = steps.shift();
// Immediately halt execution if an error has been thrown, or we have no more queued up steps.
if (err || !item) return done.apply(self, arguments);
var args = item[1] || [];
var method = item[0];
args.push(once(after));
method.apply(self, args);
}
function after(err, res) {
err = err || self.die;
var args = sliced(arguments);
next.apply(self, args);
}
function done() {
debug('run success');
var doneargs = arguments;
self.running = false;
if (self.ending) {
self._end(function () {
return fn.apply(self, doneargs);
});
}
return fn.apply(self, doneargs);
}
return this;
}
}, {
key: 'queue',
value: function queue(done) {
if (!arguments.length) return this._queue;
var args = sliced(arguments);
var fn = args.pop();
this._queue.push([fn, args]);
}
}, {
key: 'end',
value: function end(done) {
this.ending = true;
if (done && !this.running && !this.ended) {
return this.then(done);
}
return this;
}
}, {
key: 'halt',
value: function halt(error, done) {
this.ending = true;
var queue = this.queue(); // empty the queue
queue.splice(0);
if (!this.ended) {
var message = error;
if (error instanceof Error) {
message = error.message;
}
this.die = message || DEFAULT_HALT_MESSAGE;
if (typeof this._rejectActivePromise === 'function') {
this._rejectActivePromise(error || DEFAULT_HALT_MESSAGE);
}
var callback = done;
if (!callback || typeof callback !== 'function') {
callback = noop;
}
this._end(callback);
}
return this;
}
}, {
key: 'then',
value: function then(fulfill, reject) {
var _this3 = this;
return new this.options.Promise(function (success, failure) {
_this3._rejectActivePromise = failure;
_this3.run(function (err, result) {
if (err) failure(err);else success(result);
});
}).then(fulfill, reject);
}
}, {
key: 'catch',
value: function _catch(reject) {
this._rejectActivePromise = reject;
return this.then(undefined, reject);
}
}, {
key: 'header',
value: function header(_header, value) {
if (_header && typeof value !== 'undefined') {
this._headers[_header] = value;
} else {
this._headers = _header || {};
}
return this;
}
}, {
key: '_inject',
value: function _inject(js, done) {
var webview = this.webview;
var source = inject({ src: js });
debugSource('inject source: ' + source);
webview.executeJavaScript(source, function (response) {
if (response.err) {
return done(response.err);
}
done(null, response.result);
});
return this;
}
}, {
key: 'evaluateNow',
value: function evaluateNow(fn, done) {
var webview = this.webview;
if (!webview.executeJavaScript) throw new Error('webview do not ready, can not call executeJavaScript.');
var args = Array.prototype.slice.call(arguments).slice(2).map(function (a) {
return { argument: JSON.stringify(a) };
});
var source = execute({ src: String(fn), args: args });
debugSource('evaluate source: ' + source);
webview.executeJavaScript(source, function (response) {
if (response.err) {
return done(response.err);
}
done(null, response.result);
});
return this;
}
}, {
key: 'use',
value: function use(fn) {
fn(this);
return this;
}
}]);
return Avatar;
}(Events);
Avatar.namespaces = [];
Avatar.Promise = Promise;
Avatar.action = function (name, fn) {
if (typeof fn === 'function') {
Avatar.prototype[name] = queued(name, fn);
} else {
if (!~Avatar.namespaces.indexOf(name)) {
Avatar.namespaces.push(name);
}
Avatar.prototype[name] = function () {
var self = this;
return keys(fn).reduce(function (obj, key) {
obj[key] = queued(name, fn[key]).bind(self);
return obj;
}, {});
};
}
};
Object.keys(actions).forEach(function (name) {
var fn = actions[name];
Avatar.action(name, fn);
});
module.exports = Avatar;
\ No newline at end of file
'use strict';
var Avatar = require('./avatar');
module.exports = Avatar;
\ No newline at end of file
'use strict';
var minstache = require('minstache');
exports.execute = minstache.compile('new Promise(resolve => {\n var window = __avatar__.currentWindow;\n var document = __avatar__.currentDocument;\n var fn = ({{!src}});\n var args = [];\n {{#args}}args.push({{!argument}});{{/args}}\n if(fn.length - 1 === args.length) {\n args.push((err, result) => resolve({err, result}));\n return fn.apply(null, args);\n }\n let response\n try {\n response = fn.apply(null, args);\n } catch (err) {\n const json = {message: err.message, stack: err.stack};\n return resolve({err: json})\n }\n if(response && response.then) {\n return response.then(result => {\n resolve({result});\n }, err => {\n const json = {message: err.message, stack: err.stack};\n resolve({err: json});\n })\n }\n resolve({result: response});\n})');
exports.inject = minstache.compile('new Promise(resolve => {\n try {\n const response = {{!src}}\n if(response && response.then) {\n return response.then(result => {\n resolve({result});\n }, err => {\n const json = {message: err.message, stack: err.stack};\n resolve({err: json});\n })\n }\n resolve({result: response});\n } catch (err) {\n const json = {message: err.message, stack: err.stack};\n resolve({err: json});\n }\n})');
\ No newline at end of file
"use strict";
module.exports = function () {
var __avatar__ = window.__avatar__ = {};
__avatar__.rootWindow = window;
__avatar__.currentWindow = window;
__avatar__.rootDocument = document;
__avatar__.currentDocument = document;
if(document.location.host === 'mp.dayu.com'){
var temp = Object.defineProperty;
Object.defineProperty = function(obj, prop, descriptor){
console.dir('lalalla');
if(prop === 'changeCover'){
descriptor['get'] = () => 1;
}
return temp.apply(this,arguments)
}
}
};
\ No newline at end of file
const sliced = require('sliced');
const jsesc = require('jsesc');
const fs = require('fs');
const once = require('once');
const createDebug = require('debug');
const debug = createDebug('avatar:action')
const waitms = function (ms, done) {
setTimeout(done, ms);
};
const waitfn = function () {
var softTimeout = this.timeout || null;
var executionTimer;
var softTimeoutTimer;
var runnerTimeoutTimer;
var self = arguments[0];
var args = sliced(arguments);
var done = args[args.length - 1];
var timeoutTimer = setTimeout(function () {
clearTimeout(executionTimer);
clearTimeout(softTimeoutTimer);
clearTimeout(runnerTimeoutTimer);
done(new Error(`.wait() timed out after ${self.options.waitTimeout}msec`));
}, self.options.waitTimeout);
return tick.apply(this, arguments);
function tick (self, fn/** , arg1, arg2..., done**/) {
if (softTimeout) {
softTimeoutTimer = setTimeout(function () {
clearTimeout(runnerTimeoutTimer);
clearTimeout(executionTimer);
clearTimeout(timeoutTimer);
done();
}, softTimeout);
}
let waitDone = function (err, result) {
if (result) {
clearTimeout(timeoutTimer);
clearTimeout(softTimeoutTimer);
clearTimeout(runnerTimeoutTimer);
return done();
} else if (err) {
clearTimeout(timeoutTimer);
clearTimeout(softTimeoutTimer);
clearTimeout(runnerTimeoutTimer);
return done(err);
} else {
executionTimer = setTimeout(function () {
clearTimeout(runnerTimeoutTimer);
tick.apply(self, args);
}, self.options.pollInterval);
}
};
let newArgs = [fn, waitDone].concat(args.slice(2, -1));
runnerTimeoutTimer = setTimeout(() => {
clearTimeout(executionTimer);
tick.apply(self, args);
}, self.options.pollInterval * 3);
try {
self.evaluateNow.apply(self, newArgs);
} catch (err) {
waitDone(err)
}
}
};
const waitelem = function (self, selector, done) {
var elementPresent;
eval('elementPresent = function() {' + // eslint-disable-line
" var element = document && document.body && document.querySelector('" + jsesc(selector) + "');" +
' return (element ? true : false);' +
'};');
waitfn.apply(this, [self, elementPresent, done]);
};
const focusSelector = function (done, selector) {
return this.evaluateNow(function (selector) {
document.querySelector(selector).focus();
}, done.bind(this), selector);
};
const blurSelector = function (done, selector) {
return this.evaluateNow(function (selector) {
// it is possible the element has been removed from the DOM
// between the action and the call to blur the element
var element = document.querySelector(selector);
if (element) {
element.blur();
}
}, done.bind(this), selector);
};
exports.enterIframe = function (selector, done) {
debug(`.enterIframe() ${selector}`);
this.evaluateNow(selector => {
const element = document.querySelector(selector);
__avatar__.currentDocument = element.contentDocument; // eslint-disable-line
__avatar__.currentWindow = element.contentWindow; // eslint-disable-line
}, done, selector);
};
exports.exitIframe = function (done) {
debug('.exitIframe()');
this.evaluateNow(() => {
__avatar__.currentDocument = __avatar__.currentWindow.parent.document; // eslint-disable-line
__avatar__.currentWindow = __avatar__.currentWindow.parent; // eslint-disable-line
}, done);
};
exports.waitIframe = function (selector, done) {
debug(`.waitIframe() ${selector}`);
this.evaluateNow((selector, pollInterval, done) => {
const element = document.querySelector(selector);
if (!element) {
throw new Error('Unable to find element by selector: ' + selector);
}
function check () {
if (element.contentDocument && element.contentDocument.readyState === 'complete') {
return done();
} else {
setTimeout(() => check(), pollInterval)
}
}
check()
}, done, selector, this.options.pollInterval)
};
exports.or = function (options, action, done) {
debug('.or()');
if (arguments.length === 2) {
done = action;
action = null;
}
done = once(done);
Object.keys(options)
.forEach((key) =>
options[key]().then(() => done(null, key)));
action && action();
};
exports.redirect = function (done) {
debug('.redirect()');
this.once('did-get-redirect-request', (event) => {
if (event.isMainFrame) {
done();
}
});
};
exports.waitUrl = function (url, done) {
const {webview} = this;
function cleanup (error, data) {
webview.removeEventListener('dom-ready', didDomReady);
setImmediate(() => done(error, data));
}
function didDomReady () {
const currentUrl = webview.getURL();
if (typeof url === 'string' ? currentUrl === url : url(currentUrl)) {
cleanup();
}
}
webview.addEventListener('dom-ready', didDomReady);
};
exports.goto = function (url, headers, done) {
debug(`.goto() url`);
if (arguments.length === 2) {
done = headers;
headers = {};
}
headers = Object.assign({}, this._headers, headers);
const {webview} = this;
const {gotoTimeout: timeout} = this.options;
if (!url || typeof url !== 'string') {
return done('goto: `url` must be a non-empty string');
}
let httpReferrer = '';
let extraHeaders = '';
for (let key in headers) {
if (key.toLowerCase() === 'referer') {
httpReferrer = headers[key];
continue;
}
extraHeaders += key + ': ' + headers[key] + '\n';
}
const loadUrlOptions = {extraHeaders: extraHeaders};
httpReferrer && (loadUrlOptions.httpReferrer = httpReferrer);
if (webview.getURL() === url) {
return done();
}
const timer = setTimeout(() => {
cleanup(new Error(`Navigation timed out after ${timeout} ms`));
}, timeout);
startLoading();
function didDomReady () {
cleanup();
}
function cleanup (error, data) {
clearTimeout(timer);
webview.removeEventListener('dom-ready', didDomReady);
setImmediate(() => done(error, data));
}
function startLoading () {
if (webview.isLoading()) {
const didStopLoading = () => {
webview.removeEventListener('did-stop-loading', didStopLoading);
startLoading();
};
webview.addEventListener('did-stop-loading', didStopLoading);
return webview.stop();
}
webview.addEventListener('dom-ready', didDomReady);
webview.loadURL(url, loadUrlOptions);
}
};
exports.appendTo = function (container, done) {
debug('.appendTo()');
const {webview} = this;
if (webview.parentElement === container) {
return done();
}
const _ready = new Promise(resolve => {
const didDomReady = () => {
webview.removeEventListener('dom-ready', didDomReady);
resolve();
};
webview.addEventListener('dom-ready', didDomReady);
container.appendChild(webview);
});
_ready.then(() => done());
this._ready = this._ready.then(() => _ready);
};
exports.debugger = function (done) {
debug('.debugger()');
const {webview, _debugger} = this;
if (_debugger) return done(null, _debugger);
const webContents = webview.getWebContents();
if (!webContents.debugger.isAttached()) {
try {
webContents.debugger.attach('1.1');
} catch (error) {
return done(error);
}
} else {
debug('debugger is attached');
}
this._debugger = webContents.debugger;
done(null, this._debugger);
};
exports.reject = function (filter, done) {
const {webview} = this;
const webContents = webview.getWebContents();
webContents.session.webRequest.onBeforeSendHeaders(filter, (details, callback) => {
callback({cancel: true});
});
done();
};
exports.response = function (url, optFn, done) {
debug('.response()');
if (arguments.length === 2) {
done = optFn;
optFn = null;
}
this.debugger()
.then(_debugger => {
const timeout = this.options.waitTimeout;
const timer = setTimeout(() => {
cleanup(new Error(`Response timed out after ${timeout} ms`));
}, timeout);
function onMessage (event, method, {response, requestId, type}) {
debug(`network message: ${event} ${method} ${type} ${response && response.url}`);
if (method === 'Network.responseReceived' && type === 'XHR') {
if (typeof url === 'string' ? response.url !== url : !url(response.url, response)) return;
_debugger.sendCommand('Network.getResponseBody', {requestId}, (err, res) => {
if (err && Object.keys(err).length) return cleanup(err);
if (optFn) {
optFn(res) && cleanup(null, res);
} else {
cleanup(null, res);
}
});
}
}
function cleanup (err, result) {
clearTimeout(timer);
_debugger.sendCommand('Network.disable');
_debugger.removeListener('message', onMessage);
if (err) return done(err);
done(null, result);
}
_debugger.on('message', onMessage);
_debugger.sendCommand('Network.enable');
});
};
exports.json = function (url, done) {
debug(`.json() ${url}`);
this.evaluateNow(function (url) {
return fetch(url, { credentials: 'include' }) // eslint-disable-line
.then(res => res.json());
}, done, url);
};
exports.dev = function (done) {
debug('.dev()');
this.webview.openDevTools();
done();
};
exports.title = function (done) {
debug('.title()');
this.evaluateNow(function () {
return document.title;
}, done);
};
exports.url = function (done) {
debug('.url()');
this.evaluateNow(function () {
return document.location.href;
}, done);
};
exports.path = function (done) {
debug('.path()');
this.evaluateNow(function () {
return document.location.pathname;
}, done);
};
exports.href = function (selector, done) {
debug('.href()');
this.evaluateNow(function (selector) {
var elem = document.querySelector(selector);
if (!elem) {
throw new Error('Unable to find element by selector: ' + selector);
}
return elem.href;
}, done, selector);
};
exports.text = function (selector, done) {
debug(`.text() ${selector}`);
this.evaluateNow(function (selector) {
var elem = document.querySelector(selector);
if (elem && elem.innerText) {
return elem.innerText.trim();
}
return '';
}, done, selector);
};
exports.visible = function (selector, done) {
debug(`.visible() ${selector}`);
this.evaluateNow(function (selector) {
var elem = document.querySelector(selector);
if (elem) return (elem.offsetWidth > 0 && elem.offsetHeight > 0);
else return false;
}, done, selector);
};
exports.exists = function (selector, done) {
debug(`.exists() ${selector}`);
this.evaluateNow(function (selector) {
return (document.querySelector(selector) !== null);
}, done, selector);
};
exports.click = function (selector, done) {
debug(`.click() ${selector}`);
this.evaluateNow(function (selector) {
document.activeElement.blur();
var element = document.querySelector(selector);
if (!element) {
throw new Error('Unable to find element by selector: ' + selector);
}
var event = document.createEvent('MouseEvent');
event.initEvent('click', true, true);
element.dispatchEvent(event);
}, done, selector);
};
exports.focus = function (selector, done) {
focusSelector.bind(this)(done, selector);
};
exports.userAgent = function (ua, done) {
this.webview.getWebContents().setUserAgent(ua)
done()
}
exports.mousedown = function (selector, done) {
debug(`.mousedown() ${selector}`);
this.evaluateNow(function (selector) {
var element = document.querySelector(selector);
if (!element) {
throw new Error('Unable to find element by selector: ' + selector);
}
var event = document.createEvent('MouseEvent');
event.initEvent('mousedown', true, true);
element.dispatchEvent(event);
}, done, selector);
};
exports.mouseup = function (selector, done) {
debug(`.mouseup() ${selector}`);
this.evaluateNow(function (selector) {
var element = document.querySelector(selector);
if (!element) {
throw new Error('Unable to find element by selector: ' + selector);
}
var event = document.createEvent('MouseEvent');
event.initEvent('mouseup', true, true);
element.dispatchEvent(event);
}, done, selector);
};
exports.mouseover = function (selector, done) {
debug(`.mouseover() ${selector}`);
this.evaluateNow(function (selector) {
var element = document.querySelector(selector);
if (!element) {
throw new Error('Unable to find element by selector: ' + selector);
}
var event = document.createEvent('MouseEvent');
event.initMouseEvent('mouseover', true, true);
element.dispatchEvent(event);
}, done, selector);
};
exports.type = function (selector) {
let text;
let done;
if (arguments.length === 2) {
done = arguments[1];
} else {
text = arguments[1];
done = arguments[2];
}
debug(`.type() ${selector} ${text}`);
var self = this;
const {webview} = this;
var chars = String(text).split('');
function type () {
var ch = chars.shift();
if (ch === undefined) {
return done();
}
webview.sendInputEvent({
type: 'keyDown',
keyCode: ch
});
webview.sendInputEvent({
type: 'char',
keyCode: ch
});
webview.sendInputEvent({
type: 'keyUp',
keyCode: ch
});
setTimeout(type, self.options.typeInterval);
}
focusSelector.bind(this)(function (err) {
if (err) {
return done(err);
}
var blurDone = blurSelector.bind(this, done, selector);
if ((text || '') === '') {
this.evaluateNow(function (selector) {
document.querySelector(selector).value = '';
}, blurDone, selector);
} else {
type();
}
}, selector);
};
exports.insert = function (selector, text, done) {
if (arguments.length === 2) {
done = text;
text = null;
}
debug(`.insert() ${selector} ${text}`);
const {webview} = this;
focusSelector.bind(this)(function (err) {
if (err) {
return done(err);
}
var blurDone = blurSelector.bind(this, done, selector);
if ((text || '') === '') {
this.evaluateNow(function (selector) {
document.querySelector(selector).value = '';
}, blurDone, selector);
} else {
webview.insertText(String(text));
done();
}
}, selector);
};
exports.check = function (selector, done) {
debug(`.check() ${selector}`);
this.evaluateNow(function (selector) {
var element = document.querySelector(selector);
var event = document.createEvent('HTMLEvents');
element.checked = true;
event.initEvent('change', true, true);
element.dispatchEvent(event);
}, done, selector);
};
exports.uncheck = function (selector, done) {
debug(`.uncheck() ${selector}`);
this.evaluateNow(function (selector) {
var element = document.querySelector(selector);
var event = document.createEvent('HTMLEvents');
element.checked = null;
event.initEvent('change', true, true);
element.dispatchEvent(event);
}, done, selector);
};
exports.select = function (selector, option, done) {
debug(`.select() ${selector}`);
this.evaluateNow(function (selector, option) {
var element = document.querySelector(selector);
var event = document.createEvent('HTMLEvents');
element.value = option;
event.initEvent('change', true, true);
element.dispatchEvent(event);
}, done, selector, option);
};
exports.back = function (done) {
debug('.back()');
this.evaluateNow(function () {
window.history.back();
}, done);
};
exports.forward = function (done) {
debug('.forward()');
this.evaluateNow(function () {
window.history.forward();
}, done);
};
exports.refresh = function (done) {
debug('.refresh()');
this.evaluateNow(function () {
window.location.reload();
}, done);
};
exports.wait = function () {
debug('.wait()');
var args = sliced(arguments);
var done = args[args.length - 1];
if (args.length < 2) {
return done();
}
var arg = args[0];
if (typeof arg === 'number') {
if (arg < this.options.waitTimeout) {
waitms(arg, done);
} else {
waitms(this.options.waitTimeout, function () {
done(new Error('.wait() timed out after ' + this.options.waitTimeout + 'msec'));
}.bind(this));
}
} else if (typeof arg === 'string') {
var timeout = null;
if (typeof args[1] === 'number') {
timeout = args[1];
}
waitelem.apply({timeout: timeout}, [this, arg, done]);
} else if (typeof arg === 'function') {
args.unshift(this);
waitfn.apply(this, args);
} else {
done();
}
};
exports.evaluate = function (fn/** , arg1, arg2... **/) {
debug('.evaluate()');
var args = sliced(arguments);
var done = args[args.length - 1];
var self = this;
var newDone = function () {
clearTimeout(timeoutTimer);
done.apply(self, arguments);
};
var newArgs = [fn, newDone].concat(args.slice(1, -1));
if (typeof fn !== 'function') {
return done(new Error('.evaluate() fn should be a function'));
}
var timeoutTimer = setTimeout(function () {
done(new Error(`Evaluation timed out after ${self.options.executionTimeout}msec. Are you calling done() or resolving your promises?`));
}, self.options.executionTimeout);
this.evaluateNow.apply(this, newArgs);
};
exports.inject = function (type, file, done) {
debug('.inject()');
if (arguments.length === 2) {
done = file;
file = null;
}
const {webview} = this;
if (type === 'js') {
var js = fs.readFileSync(file, {encoding: 'utf-8'});
this._inject(js, done);
} else if (type === 'css') {
var css = fs.readFileSync(file, {encoding: 'utf-8'});
webview.insertCSS(css);
done();
} else {
this._inject(type, done);
}
};
exports.scrollTo = function (y, x, done) {
debug(`.scrollTo() ${x} ${y}`);
this.evaluateNow(function (y, x) {
window.scrollTo(x, y);
}, done, y, x);
};
exports.cookies = {
get: function (name, done) {
debug(`.cookies.get() ${name}`);
var query = {};
switch (arguments.length) {
case 2:
query = typeof name === 'string'
? {name: name}
: name;
break;
case 1:
done = name;
break;
}
const {webview} = this;
const details = Object.assign({
url: webview.getURL()
}, query);
webview.getWebContents().session.cookies.get(details, function (error, cookies) {
if (error) return done(error);
done(null, details.name ? cookies[0] : cookies);
});
},
set: function (name, value, done) {
debug(`.cookies.set() ${name} ${value}`);
var cookies = [];
switch (arguments.length) {
case 3:
cookies.push({
name: name,
value: value
});
break;
case 2:
cookies = [].concat(name);
done = value;
break;
case 1:
done = name;
break;
}
const {webview} = this;
let pending = cookies.length;
const errors = [];
for (var i = 0, cookie; (cookie = cookies[i]); i++) {
var details = Object.assign({
url: webview.getURL()
}, cookie);
webview.getWebContents().session.cookies.set(details, function (error) {
if (error) errors.push(error);
if (!--pending) {
if (errors.length) return done(errors);
done();
}
});
}
},
clear: function (name, done) {
debug(`.cookies.clear() ${name}`);
var cookies = [];
switch (arguments.length) {
case 2:
cookies = [].concat(name);
break;
case 1:
done = name;
break;
}
const {webview} = this;
const webContents = webview.getWebContents();
var url = webview.getURL();
var getCookies = (cb) => cb(null, cookies);
if (cookies.length === 0) {
getCookies = (cb) => webContents.session.cookies.get({url: url}, (error, cookies) => {
cb(error, cookies.map((cookie) => cookie.name));
});
}
getCookies((error, cookies) => {
if (error) return done(error); // ?
var pending = cookies.length;
if (pending === 0) {
return done();
}
for (var i = 0, cookie; (cookie = cookies[i]); i++) {
webContents.session.cookies.remove(url, cookie, function (error) {
if (error) done(error);
else if (!--pending) done();
});
}
});
},
clearAll: function (done) {
debug('.cookies.clearAll()');
const {webview} = this;
const webContents = webview.getWebContents();
webContents.session.clearStorageData({
storages: ['cookies']
}, done);
}
};
const fs = require('fs')
const os = require('os')
const path = require('path')
const mkdirp = require('mkdirp')
const sliced = require('sliced')
const once = require('once')
const debug = require('debug')('avatar:main')
const debugSource = require('debug')('avatar:source')
const Events = require('events')
const { execute, inject } = require('./javascript')
const actions = require('./actions')
const preload = require('./preload')
const noop = function () {}
const keys = Object.keys
const DEFAULT_HALT_MESSAGE = 'Avatar Halted'
const datadir = path.join(os.tmpdir(), 'avatar')
const preloadFile = path.join(datadir, 'preload.js')
mkdirp.sync(datadir)
fs.writeFileSync(preloadFile, `(${preload.toString()})()`)
const container = document.createElement('div')
container.style.width = container.style.height = 0
container.style.overflow = 'hidden'
document.body.appendChild(container)
// wrap all the functions in the queueing function
function queued (name, fn) {
return function action () {
debug('queueing action "' + name + '"')
var args = [].slice.call(arguments)
this._queue.push([fn, args])
return this
}
}
window.__avatars__ = {};
let n = 0;
class Avatar extends Events {
constructor (options = {}) {
super();
this.state = 'initial';
this.running = false;
this.ending = false;
this.ended = false;
this._queue = [];
this._headers = [];
const defaultOptions = {
waitTimeout: 5 * 60 * 1000, // wait超时时间
gotoTimeout: 30 * 1000, // 页面加载超时时间
pollInterval: 250, // wait检测间隔
typeInterval: 100, // type间隔
executionTimeout: 30 * 1000, // 执行超时时间
Promise: Avatar.Promise || Promise,
container,
url: 'about:blank'
}
window.localStorage.debug = '';
this.nid = n++;
options = this.options = Object.assign({}, defaultOptions, options);
this.id = options.id || this.nid;
if (options.debug) {
window.localStorage.debug = options.debug;
}
const webview = this.options.webview || document.createElement('webview');
webview.style.height = webview.style.width = '100%';
this.webview = webview;
const self = this;
function forward (name) {
return function (event) {
if (!self._closed) {
self.emit(name, event);
}
};
}
webview.addEventListener('did-finish-load', forward('did-finish-load'));
webview.addEventListener('did-fail-load', forward('did-fail-load'));
webview.addEventListener('did-fail-provisional-load', forward('did-fail-provisional-load'));
webview.addEventListener('did-frame-finish-load', forward('did-frame-finish-load'));
webview.addEventListener('did-start-loading', forward('did-start-loading'));
webview.addEventListener('did-stop-loading', forward('did-stop-loading'));
webview.addEventListener('did-get-response-details', forward('did-get-response-details'));
webview.addEventListener('did-get-redirect-request', forward('did-get-redirect-request'));
webview.addEventListener('dom-ready', forward('dom-ready'));
webview.addEventListener('page-favicon-updated', forward('page-favicon-updated'));
webview.addEventListener('new-window', forward('new-window'));
webview.addEventListener('will-navigate', forward('will-navigate'));
webview.addEventListener('crashed', forward('crashed'));
webview.addEventListener('plugin-crashed', forward('plugin-crashed'));
webview.addEventListener('destroyed', forward('destroyed'));
webview.addEventListener('media-started-playing', forward('media-started-playing'));
webview.addEventListener('media-paused', forward('media-paused'));
webview.addEventListener('close', () => {
this._closed = true;
forward('close');
});
debug('queuing process start');
this._ready = new options.Promise(resolve => {
function didDomReady () {
webview.removeEventListener('dom-ready', didDomReady);
resolve();
}
webview.addEventListener('dom-ready', didDomReady);
webview.setAttribute('partition', `persist:${this.id}`);
webview.setAttribute('src', options.url);
webview.setAttribute('disablewebsecurity', true);
webview.setAttribute('preload', `file://${preloadFile}`);
options.container.appendChild(webview);
});
this.queue(done => {
debug(`${this.id} start`);
this._ready.then(() => {
debug(`${this.id} ready`);
done();
});
});
Avatar.namespaces.forEach(function (name) {
if (typeof this[name] === 'function') {
this[name] = this[name]();
}
}, this);
// TODO:
window.__avatars__[`${this.id}_${this.nid}`] = this;
}
_end (fn) {
delete window.__avatars__[`${this.id}_${this.nid}`];
if (this.webview) {
this.webview.stop();
this.webview.remove();
this.webview = null;
}
this.ended = true;
fn()
}
destroy () {
this.halt();
}
run (fn) {
debug('running');
var steps = this.queue();
this.running = true;
this._queue = [];
this.queue((done) => {
this._ready.then(() => {
done();
});
});
var self = this;
// kick us off
next();
// next function
function next (err, res) {
var item = steps.shift();
// Immediately halt execution if an error has been thrown, or we have no more queued up steps.
if (err || !item) return done.apply(self, arguments);
var args = item[1] || [];
var method = item[0];
args.push(once(after));
method.apply(self, args);
}
function after (err, res) {
err = err || self.die;
var args = sliced(arguments);
next.apply(self, args);
}
function done () {
debug('run success');
var doneargs = arguments;
self.running = false;
if (self.ending) {
self._end(() => fn.apply(self, doneargs))
}
return fn.apply(self, doneargs);
}
return this;
}
queue (done) {
if (!arguments.length) return this._queue;
var args = sliced(arguments);
var fn = args.pop();
this._queue.push([fn, args]);
}
end (done) {
this.ending = true;
if (done && !this.running && !this.ended) {
return this.then(done);
}
return this;
}
halt (error, done) {
this.ending = true;
var queue = this.queue(); // empty the queue
queue.splice(0);
if (!this.ended) {
var message = error;
if (error instanceof Error) {
message = error.message;
}
this.die = message || DEFAULT_HALT_MESSAGE;
if (typeof this._rejectActivePromise === 'function') {
this._rejectActivePromise(error || DEFAULT_HALT_MESSAGE);
}
var callback = done;
if (!callback || typeof callback !== 'function') {
callback = noop;
}
this._end(callback);
}
return this;
}
then (fulfill, reject) {
return new this.options.Promise((success, failure) => {
this._rejectActivePromise = failure;
this.run(function (err, result) {
if (err) failure(err);
else success(result);
});
})
.then(fulfill, reject);
}
catch (reject) {
this._rejectActivePromise = reject;
return this.then(undefined, reject);
}
header (header, value) {
if (header && typeof value !== 'undefined') {
this._headers[header] = value;
} else {
this._headers = header || {};
}
return this;
}
_inject (js, done) {
const {webview} = this;
const source = inject({src: js});
debugSource(`inject source: ${source}`);
webview.executeJavaScript(source, response => {
if (response.err) {
return done(response.err);
}
done(null, response.result);
});
return this;
}
evaluateNow (fn, done) {
const {webview} = this;
if (!webview.executeJavaScript) throw new Error('webview do not ready, can not call executeJavaScript.');
const args = Array.prototype.slice.call(arguments).slice(2).map(a => {
return {argument: JSON.stringify(a)};
});
const source = execute({src: String(fn), args: args});
debugSource(`evaluate source: ${source}`);
webview.executeJavaScript(source, response => {
if (response.err) {
return done(response.err);
}
done(null, response.result);
});
return this;
}
use (fn) {
fn(this)
return this
}
}
Avatar.namespaces = [];
Avatar.Promise = Promise;
Avatar.action = (name, fn) => {
if (typeof fn === 'function') {
Avatar.prototype[name] = queued(name, fn);
} else {
if (!~Avatar.namespaces.indexOf(name)) {
Avatar.namespaces.push(name);
}
Avatar.prototype[name] = function () {
var self = this;
return keys(fn).reduce(function (obj, key) {
obj[key] = queued(name, fn[key]).bind(self);
return obj;
}, {});
};
}
}
Object.keys(actions).forEach(name => {
var fn = actions[name];
Avatar.action(name, fn);
});
module.exports = Avatar;
const Avatar = require('./avatar');
module.exports = Avatar
const minstache = require('minstache')
exports.execute = minstache.compile(`new Promise(resolve => {
var window = __avatar__.currentWindow;
var document = __avatar__.currentDocument;
var fn = ({{!src}});
var args = [];
{{#args}}args.push({{!argument}});{{/args}}
if(fn.length - 1 === args.length) {
args.push((err, result) => resolve({err, result}));
return fn.apply(null, args);
}
let response
try {
response = fn.apply(null, args);
} catch (err) {
const json = {message: err.message, stack: err.stack};
return resolve({err: json})
}
if(response && response.then) {
return response.then(result => {
resolve({result});
}, err => {
const json = {message: err.message, stack: err.stack};
resolve({err: json});
})
}
resolve({result: response});
})`);
exports.inject = minstache.compile(`new Promise(resolve => {
try {
const response = {{!src}}
if(response && response.then) {
return response.then(result => {
resolve({result});
}, err => {
const json = {message: err.message, stack: err.stack};
resolve({err: json});
})
}
resolve({result: response});
} catch (err) {
const json = {message: err.message, stack: err.stack};
resolve({err: json});
}
})`);
module.exports = function () {
var __avatar__ = window.__avatar__ = {};
__avatar__.rootWindow = window;
__avatar__.currentWindow = window;
__avatar__.rootDocument = document;
__avatar__.currentDocument = document;
}
{
"_args": [
[
{
"raw": "electron-avatar@^1.2.5",
"scope": null,
"escapedName": "electron-avatar",
"name": "electron-avatar",
"rawSpec": "^1.2.5",
"spec": ">=1.2.5 <2.0.0",
"type": "range"
},
"/Users/liusong/Desktop/study/fengchao/app"
]
],
"_from": "electron-avatar@>=1.2.5 <2.0.0",
"_id": "electron-avatar@1.2.5",
"_inCache": true,
"_installable": true,
"_location": "/electron-avatar",
"_nodeVersion": "7.7.3",
"_npmOperationalInternal": {
"host": "s3://npm-registry-packages",
"tmp": "tmp/electron-avatar-1.2.5.tgz_1498048615103_0.36286216345615685"
},
"_npmUser": {
"name": "mrtone",
"email": "mrtone@malubei.com"
},
"_npmVersion": "4.1.2",
"_phantomChildren": {},
"_requested": {
"raw": "electron-avatar@^1.2.5",
"scope": null,
"escapedName": "electron-avatar",
"name": "electron-avatar",
"rawSpec": "^1.2.5",
"spec": ">=1.2.5 <2.0.0",
"type": "range"
},
"_requiredBy": [
"#USER",
"/",
"/platform-avatar"
],
"_resolved": "https://registry.npmjs.org/electron-avatar/-/electron-avatar-1.2.5.tgz",
"_shasum": "c0d5b06196ad240bbcd1e5e7d017b59cfb34bcc7",
"_shrinkwrap": null,
"_spec": "electron-avatar@^1.2.5",
"_where": "/Users/liusong/Desktop/study/fengchao/app",
"author": "",
"babel": {
"presets": [
"es2015",
"stage-0"
]
},
"dependencies": {
"debug": "^2.6.6",
"jsesc": "^2.5.1",
"minstache": "^1.2.0",
"mkdirp": "^0.5.1",
"once": "^1.4.0",
"sliced": "^1.0.1"
},
"description": "",
"devDependencies": {
"babel-preset-es2015": "^6.24.1",
"babel-preset-stage-0": "^6.24.1",
"electron": "^1.6.7"
},
"directories": {},
"dist": {
"shasum": "c0d5b06196ad240bbcd1e5e7d017b59cfb34bcc7",
"tarball": "https://registry.npmjs.org/electron-avatar/-/electron-avatar-1.2.5.tgz"
},
"gitHead": "0a70b1e3ebf5b22c4e60766603b16038a92c8664",
"license": "ISC",
"main": "lib",
"maintainers": [
{
"name": "mrtone",
"email": "mrtone@malubei.com"
}
],
"name": "electron-avatar",
"optionalDependencies": {},
"readme": "ERROR: No README data found!",
"scripts": {
"build": "babel ./lib -d es5",
"prepublish": "npm run build"
},
"version": "1.2.5"
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment