|
|
@ -649,41 +649,121 @@ function isPDFFunction(v) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* 'Promise' object. |
|
|
|
* The following promise implementation tries to generally implment the |
|
|
|
* Each object that is stored in PDFObjects is based on a Promise object that |
|
|
|
* Promise/A+ spec. Some notable differences from other promise libaries are: |
|
|
|
* contains the status of the object and the data. There might be situations |
|
|
|
* - There currently isn't a seperate deferred and promise object. |
|
|
|
* where a function wants to use the value of an object, but it isn't ready at |
|
|
|
* - Unhandled rejections eventually show an error if they aren't handled. |
|
|
|
* that time. To get a notification, once the object is ready to be used, s.o. |
|
|
|
* |
|
|
|
* can add a callback using the `then` method on the promise that then calls |
|
|
|
* Based off of the work in: |
|
|
|
* the callback once the object gets resolved. |
|
|
|
* https://bugzilla.mozilla.org/show_bug.cgi?id=810490
|
|
|
|
* A promise can get resolved only once and only once the data of the promise |
|
|
|
|
|
|
|
* can be set. If any of these happens twice or the data is required before |
|
|
|
|
|
|
|
* it was set, an exception is throw. |
|
|
|
|
|
|
|
*/ |
|
|
|
*/ |
|
|
|
var Promise = PDFJS.Promise = (function PromiseClosure() { |
|
|
|
var Promise = PDFJS.Promise = (function PromiseClosure() { |
|
|
|
var EMPTY_PROMISE = {}; |
|
|
|
var STATUS_PENDING = 0; |
|
|
|
|
|
|
|
var STATUS_RESOLVED = 1; |
|
|
|
|
|
|
|
var STATUS_REJECTED = 2; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// In an attempt to avoid silent exceptions, unhandled rejections are
|
|
|
|
|
|
|
|
// tracked and if they aren't handled in a certain amount of time an
|
|
|
|
|
|
|
|
// error is logged.
|
|
|
|
|
|
|
|
var REJECTION_TIMEOUT = 500; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var HandlerManager = { |
|
|
|
|
|
|
|
handlers: [], |
|
|
|
|
|
|
|
running: false, |
|
|
|
|
|
|
|
unhandledRejections: [], |
|
|
|
|
|
|
|
pendingRejectionCheck: false, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
scheduleHandlers: function scheduleHandlers(promise) { |
|
|
|
|
|
|
|
if (promise._status == STATUS_PENDING) { |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
this.handlers = this.handlers.concat(promise._handlers); |
|
|
|
* If `data` is passed in this constructor, the promise is created resolved. |
|
|
|
promise._handlers = []; |
|
|
|
* If there isn't data, it isn't resolved at the beginning. |
|
|
|
|
|
|
|
*/ |
|
|
|
if (this.running) { |
|
|
|
function Promise(name, data) { |
|
|
|
return; |
|
|
|
this.name = name; |
|
|
|
} |
|
|
|
this.isRejected = false; |
|
|
|
this.running = true; |
|
|
|
this.error = null; |
|
|
|
|
|
|
|
this.exception = null; |
|
|
|
setTimeout(this.runHandlers.bind(this), 0); |
|
|
|
// If you build a promise and pass in some data it's already resolved.
|
|
|
|
}, |
|
|
|
if (data !== null && data !== undefined) { |
|
|
|
|
|
|
|
this.isResolved = true; |
|
|
|
runHandlers: function runHandlers() { |
|
|
|
this._data = data; |
|
|
|
while (this.handlers.length > 0) { |
|
|
|
this.hasData = true; |
|
|
|
var handler = this.handlers.shift(); |
|
|
|
} else { |
|
|
|
|
|
|
|
this.isResolved = false; |
|
|
|
var nextStatus = handler.thisPromise._status; |
|
|
|
this._data = EMPTY_PROMISE; |
|
|
|
var nextValue = handler.thisPromise._value; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
if (nextStatus === STATUS_RESOLVED) { |
|
|
|
|
|
|
|
if (typeof(handler.onResolve) == 'function') { |
|
|
|
|
|
|
|
nextValue = handler.onResolve(nextValue); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else if (typeof(handler.onReject) === 'function') { |
|
|
|
|
|
|
|
nextValue = handler.onReject(nextValue); |
|
|
|
|
|
|
|
nextStatus = STATUS_RESOLVED; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (handler.thisPromise._unhandledRejection) { |
|
|
|
|
|
|
|
this.removeUnhandeledRejection(handler.thisPromise); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} catch (ex) { |
|
|
|
|
|
|
|
nextStatus = STATUS_REJECTED; |
|
|
|
|
|
|
|
nextValue = ex; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
handler.nextPromise._updateStatus(nextStatus, nextValue); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.running = false; |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
addUnhandledRejection: function addUnhandledRejection(promise) { |
|
|
|
|
|
|
|
this.unhandledRejections.push({ |
|
|
|
|
|
|
|
promise: promise, |
|
|
|
|
|
|
|
time: Date.now() |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
this.scheduleRejectionCheck(); |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
removeUnhandeledRejection: function removeUnhandeledRejection(promise) { |
|
|
|
|
|
|
|
promise._unhandledRejection = false; |
|
|
|
|
|
|
|
for (var i = 0; i < this.unhandledRejections.length; i++) { |
|
|
|
|
|
|
|
if (this.unhandledRejections[i].promise === promise) { |
|
|
|
|
|
|
|
this.unhandledRejections.splice(i); |
|
|
|
|
|
|
|
i--; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
scheduleRejectionCheck: function scheduleRejectionCheck() { |
|
|
|
|
|
|
|
if (this.pendingRejectionCheck) { |
|
|
|
|
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
this.callbacks = []; |
|
|
|
this.pendingRejectionCheck = true; |
|
|
|
this.errbacks = []; |
|
|
|
setTimeout(function rejectionCheck() { |
|
|
|
this.progressbacks = []; |
|
|
|
this.pendingRejectionCheck = false; |
|
|
|
|
|
|
|
var now = Date.now(); |
|
|
|
|
|
|
|
for (var i = 0; i < this.unhandledRejections.length; i++) { |
|
|
|
|
|
|
|
if (now - this.unhandledRejections[i].time > REJECTION_TIMEOUT) { |
|
|
|
|
|
|
|
console.error('Unhandled rejection: ' + |
|
|
|
|
|
|
|
this.unhandledRejections[i].promise._value); |
|
|
|
|
|
|
|
this.unhandledRejections.splice(i); |
|
|
|
|
|
|
|
i--; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (this.unhandledRejections.length) { |
|
|
|
|
|
|
|
this.scheduleRejectionCheck(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}.bind(this), REJECTION_TIMEOUT); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function Promise() { |
|
|
|
|
|
|
|
this._status = STATUS_PENDING; |
|
|
|
|
|
|
|
this._handlers = []; |
|
|
|
} |
|
|
|
} |
|
|
|
/** |
|
|
|
/** |
|
|
|
* Builds a promise that is resolved when all the passed in promises are |
|
|
|
* Builds a promise that is resolved when all the passed in promises are |
|
|
@ -700,7 +780,7 @@ var Promise = PDFJS.Promise = (function PromiseClosure() { |
|
|
|
return deferred; |
|
|
|
return deferred; |
|
|
|
} |
|
|
|
} |
|
|
|
function reject(reason) { |
|
|
|
function reject(reason) { |
|
|
|
if (deferred.isRejected) { |
|
|
|
if (deferred._status === STATUS_REJECTED) { |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
results = []; |
|
|
|
results = []; |
|
|
@ -710,7 +790,7 @@ var Promise = PDFJS.Promise = (function PromiseClosure() { |
|
|
|
var promise = promises[i]; |
|
|
|
var promise = promises[i]; |
|
|
|
promise.then((function(i) { |
|
|
|
promise.then((function(i) { |
|
|
|
return function(value) { |
|
|
|
return function(value) { |
|
|
|
if (deferred.isRejected) { |
|
|
|
if (deferred._status === STATUS_REJECTED) { |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
results[i] = value; |
|
|
|
results[i] = value; |
|
|
@ -722,102 +802,63 @@ var Promise = PDFJS.Promise = (function PromiseClosure() { |
|
|
|
} |
|
|
|
} |
|
|
|
return deferred; |
|
|
|
return deferred; |
|
|
|
}; |
|
|
|
}; |
|
|
|
Promise.prototype = { |
|
|
|
|
|
|
|
hasData: false, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
set data(value) { |
|
|
|
Promise.prototype = { |
|
|
|
if (value === undefined) { |
|
|
|
_status: null, |
|
|
|
|
|
|
|
_value: null, |
|
|
|
|
|
|
|
_handlers: null, |
|
|
|
|
|
|
|
_unhandledRejection: null, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_updateStatus: function Promise__updateStatus(status, value) { |
|
|
|
|
|
|
|
if (this._status === STATUS_RESOLVED || |
|
|
|
|
|
|
|
this._status === STATUS_REJECTED) { |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
if (this._data !== EMPTY_PROMISE) { |
|
|
|
|
|
|
|
error('Promise ' + this.name + |
|
|
|
|
|
|
|
': Cannot set the data of a promise twice'); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
this._data = value; |
|
|
|
|
|
|
|
this.hasData = true; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (this.onDataCallback) { |
|
|
|
|
|
|
|
this.onDataCallback(value); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
get data() { |
|
|
|
if (status == STATUS_RESOLVED && |
|
|
|
if (this._data === EMPTY_PROMISE) { |
|
|
|
value && typeof(value.then) === 'function') { |
|
|
|
error('Promise ' + this.name + ': Cannot get data that isn\'t set'); |
|
|
|
value.then(this._updateStatus.bind(this, STATUS_RESOLVED), |
|
|
|
|
|
|
|
this._updateStatus.bind(this, STATUS_REJECTED)); |
|
|
|
|
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
return this._data; |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
onData: function Promise_onData(callback) { |
|
|
|
this._status = status; |
|
|
|
if (this._data !== EMPTY_PROMISE) { |
|
|
|
this._value = value; |
|
|
|
callback(this._data); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
this.onDataCallback = callback; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
resolve: function Promise_resolve(data) { |
|
|
|
if (status === STATUS_REJECTED && this._handlers.length === 0) { |
|
|
|
if (this.isResolved) { |
|
|
|
this._unhandledRejection = true; |
|
|
|
error('A Promise can be resolved only once ' + this.name); |
|
|
|
HandlerManager.addUnhandledRejection(this); |
|
|
|
} |
|
|
|
|
|
|
|
if (this.isRejected) { |
|
|
|
|
|
|
|
error('The Promise was already rejected ' + this.name); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
this.isResolved = true; |
|
|
|
HandlerManager.scheduleHandlers(this); |
|
|
|
this.data = (typeof data !== 'undefined') ? data : null; |
|
|
|
|
|
|
|
var callbacks = this.callbacks; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (var i = 0, ii = callbacks.length; i < ii; i++) { |
|
|
|
|
|
|
|
callbacks[i].call(null, data); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
progress: function Promise_progress(data) { |
|
|
|
get isResolved() { |
|
|
|
var callbacks = this.progressbacks; |
|
|
|
return this._status === STATUS_RESOLVED; |
|
|
|
for (var i = 0, ii = callbacks.length; i < ii; i++) { |
|
|
|
|
|
|
|
callbacks[i].call(null, data); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
reject: function Promise_reject(reason, exception) { |
|
|
|
get isRejected() { |
|
|
|
if (this.isRejected) { |
|
|
|
return this._status === STATUS_REJECTED; |
|
|
|
error('A Promise can be rejected only once ' + this.name); |
|
|
|
}, |
|
|
|
} |
|
|
|
|
|
|
|
if (this.isResolved) { |
|
|
|
|
|
|
|
error('The Promise was already resolved ' + this.name); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.isRejected = true; |
|
|
|
|
|
|
|
this.error = reason || null; |
|
|
|
|
|
|
|
this.exception = exception || null; |
|
|
|
|
|
|
|
var errbacks = this.errbacks; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (var i = 0, ii = errbacks.length; i < ii; i++) { |
|
|
|
resolve: function Promise_resolve(value) { |
|
|
|
errbacks[i].call(null, reason, exception); |
|
|
|
this._updateStatus(STATUS_RESOLVED, value); |
|
|
|
} |
|
|
|
|
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
then: function Promise_then(callback, errback, progressback) { |
|
|
|
reject: function Promise_reject(reason) { |
|
|
|
// If the promise is already resolved, call the callback directly.
|
|
|
|
this._updateStatus(STATUS_REJECTED, reason); |
|
|
|
if (this.isResolved && callback) { |
|
|
|
}, |
|
|
|
var data = this.data; |
|
|
|
|
|
|
|
callback.call(null, data); |
|
|
|
|
|
|
|
} else if (this.isRejected && errback) { |
|
|
|
|
|
|
|
var error = this.error; |
|
|
|
|
|
|
|
var exception = this.exception; |
|
|
|
|
|
|
|
errback.call(null, error, exception); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
if (callback) { |
|
|
|
|
|
|
|
this.callbacks.push(callback); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (errback) { |
|
|
|
|
|
|
|
this.errbacks.push(errback); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (progressback) |
|
|
|
then: function Promise_then(onResolve, onReject) { |
|
|
|
this.progressbacks.push(progressback); |
|
|
|
var nextPromise = new Promise(); |
|
|
|
|
|
|
|
this._handlers.push({ |
|
|
|
|
|
|
|
thisPromise: this, |
|
|
|
|
|
|
|
onResolve: onResolve, |
|
|
|
|
|
|
|
onReject: onReject, |
|
|
|
|
|
|
|
nextPromise: nextPromise |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
HandlerManager.scheduleHandlers(this); |
|
|
|
|
|
|
|
return nextPromise; |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|