Adds peer communication between MessageHandlers.

This commit is contained in:
Yury Delendik 2015-10-27 10:07:20 -05:00
parent 4b243cdd89
commit acdd49f480
3 changed files with 129 additions and 57 deletions

View File

@ -51,12 +51,49 @@ var WorkerTask = (function WorkerTaskClosure() {
})(); })();
var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { var WorkerMessageHandler = PDFJS.WorkerMessageHandler = {
setup: function wphSetup(handler) { setup: function wphSetup(handler, port) {
handler.on('test', function wphSetupTest(data) {
// check if Uint8Array can be sent to worker
if (!(data instanceof Uint8Array)) {
handler.send('test', 'main', false);
return;
}
// making sure postMessage transfers are working
var supportTransfers = data[0] === 255;
handler.postMessageTransfers = supportTransfers;
// check if the response property is supported by xhr
var xhr = new XMLHttpRequest();
var responseExists = 'response' in xhr;
// check if the property is actually implemented
try {
var dummy = xhr.responseType;
} catch (e) {
responseExists = false;
}
if (!responseExists) {
handler.send('test', false);
return;
}
handler.send('test', {
supportTypedArray: true,
supportTransfers: supportTransfers
});
});
handler.on('GetDocRequest', function wphSetupDoc(data) {
return WorkerMessageHandler.createDocumentHandler(data, port);
});
},
createDocumentHandler: function wphCreateDocumentHandler(data, port) {
var pdfManager; var pdfManager;
var terminated = false; var terminated = false;
var cancelXHRs = null; var cancelXHRs = null;
var WorkerTasks = []; var WorkerTasks = [];
var mainHandlerName = data.docId;
var workerHandlerName = data.docId + '_worker';
var handler = new MessageHandler(workerHandlerName, mainHandlerName, port);
function ensureNotTerminated() { function ensureNotTerminated() {
if (terminated) { if (terminated) {
throw new Error('Worker was terminated'); throw new Error('Worker was terminated');
@ -262,35 +299,7 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = {
return pdfManagerCapability.promise; return pdfManagerCapability.promise;
} }
handler.on('test', function wphSetupTest(data) { var setupDoc = function(data) {
// check if Uint8Array can be sent to worker
if (!(data instanceof Uint8Array)) {
handler.send('test', false);
return;
}
// making sure postMessage transfers are working
var supportTransfers = data[0] === 255;
handler.postMessageTransfers = supportTransfers;
// check if the response property is supported by xhr
var xhr = new XMLHttpRequest();
var responseExists = 'response' in xhr;
// check if the property is actually implemented
try {
var dummy = xhr.responseType;
} catch (e) {
responseExists = false;
}
if (!responseExists) {
handler.send('test', false);
return;
}
handler.send('test', {
supportTypedArray: true,
supportTransfers: supportTransfers
});
});
handler.on('GetDocRequest', function wphSetupDoc(data) {
var onSuccess = function(doc) { var onSuccess = function(doc) {
ensureNotTerminated(); ensureNotTerminated();
handler.send('GetDoc', { pdfInfo: doc }); handler.send('GetDoc', { pdfInfo: doc });
@ -366,7 +375,7 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = {
}); });
}, onFailure); }, onFailure);
}, onFailure); }, onFailure);
}); };
handler.on('GetPage', function wphSetupGetPage(data) { handler.on('GetPage', function wphSetupGetPage(data) {
return pdfManager.getPage(data.pageIndex).then(function(page) { return pdfManager.getPage(data.pageIndex).then(function(page) {
@ -548,6 +557,9 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = {
return Promise.all(waitOn).then(function () {}); return Promise.all(waitOn).then(function () {});
}); });
setupDoc(data);
return workerHandlerName;
} }
}; };
@ -557,6 +569,7 @@ var workerConsole = {
log: function log() { log: function log() {
var args = Array.prototype.slice.call(arguments); var args = Array.prototype.slice.call(arguments);
globalScope.postMessage({ globalScope.postMessage({
targetName: 'main',
action: 'console_log', action: 'console_log',
data: args data: args
}); });
@ -565,6 +578,7 @@ var workerConsole = {
error: function error() { error: function error() {
var args = Array.prototype.slice.call(arguments); var args = Array.prototype.slice.call(arguments);
globalScope.postMessage({ globalScope.postMessage({
targetName: 'main',
action: 'console_error', action: 'console_error',
data: args data: args
}); });
@ -594,11 +608,12 @@ if (typeof window === 'undefined') {
// Listen for unsupported features so we can pass them on to the main thread. // Listen for unsupported features so we can pass them on to the main thread.
PDFJS.UnsupportedManager.listen(function (msg) { PDFJS.UnsupportedManager.listen(function (msg) {
globalScope.postMessage({ globalScope.postMessage({
targetName: 'main',
action: '_unsupported_feature', action: '_unsupported_feature',
data: msg data: msg
}); });
}); });
var handler = new MessageHandler('worker_processor', this); var handler = new MessageHandler('worker', 'main', this);
WorkerMessageHandler.setup(handler); WorkerMessageHandler.setup(handler, this);
} }

View File

@ -17,7 +17,7 @@
Promise, PasswordResponses, PasswordException, InvalidPDFException, Promise, PasswordResponses, PasswordException, InvalidPDFException,
MissingPDFException, UnknownErrorException, FontFaceObject, MissingPDFException, UnknownErrorException, FontFaceObject,
loadJpegStream, createScratchCanvas, CanvasGraphics, stringToBytes, loadJpegStream, createScratchCanvas, CanvasGraphics, stringToBytes,
UnexpectedResponseException, deprecated */ UnexpectedResponseException, deprecated, UnsupportedManager */
'use strict'; 'use strict';
@ -353,6 +353,12 @@ var PDFDocumentLoadingTask = (function PDFDocumentLoadingTaskClosure() {
this._capability = createPromiseCapability(); this._capability = createPromiseCapability();
this._transport = null; this._transport = null;
/**
* Shows if loading task is destroyed.
* @type {boolean}
*/
this.destroyed = false;
/** /**
* Callback to request a password if wrong or no password was provided. * Callback to request a password if wrong or no password was provided.
* The callback receives two parameters: function that needs to be called * The callback receives two parameters: function that needs to be called
@ -383,6 +389,10 @@ var PDFDocumentLoadingTask = (function PDFDocumentLoadingTaskClosure() {
* is completed. * is completed.
*/ */
destroy: function () { destroy: function () {
this.destroyed = true;
if (!this._transport) {
return Promise.resolve();
}
return this._transport.destroy(); return this._transport.destroy();
}, },
@ -1042,8 +1052,7 @@ var WorkerTransport = (function WorkerTransportClosure() {
// Some versions of FF can't create a worker on localhost, see: // Some versions of FF can't create a worker on localhost, see:
// https://bugzilla.mozilla.org/show_bug.cgi?id=683280 // https://bugzilla.mozilla.org/show_bug.cgi?id=683280
var worker = new Worker(workerSrc); var worker = new Worker(workerSrc);
var messageHandler = new MessageHandler('main', worker); var messageHandler = new MessageHandler('main', 'worker', worker);
this.messageHandler = messageHandler;
messageHandler.on('test', function transportTest(data) { messageHandler.on('test', function transportTest(data) {
var supportTypedArray = data && data.supportTypedArray; var supportTypedArray = data && data.supportTypedArray;
@ -1052,13 +1061,23 @@ var WorkerTransport = (function WorkerTransportClosure() {
if (!data.supportTransfers) { if (!data.supportTransfers) {
PDFJS.postMessageTransfers = false; PDFJS.postMessageTransfers = false;
} }
this.setupMessageHandler(messageHandler); this.setupMainMessageHandler(messageHandler, worker);
workerInitializedCapability.resolve(); workerInitializedCapability.resolve();
} else { } else {
this.setupFakeWorker(); this.setupFakeWorker();
} }
}.bind(this)); }.bind(this));
messageHandler.on('console_log', function (data) {
console.log.apply(console, data);
});
messageHandler.on('console_error', function (data) {
console.error.apply(console, data);
});
messageHandler.on('_unsupported_feature', function (data) {
UnsupportedManager.notify(data);
});
var testObj = new Uint8Array([PDFJS.postMessageTransfers ? 255 : 0]); var testObj = new Uint8Array([PDFJS.postMessageTransfers ? 255 : 0]);
// Some versions of Opera throw a DATA_CLONE_ERR on serializing the // Some versions of Opera throw a DATA_CLONE_ERR on serializing the
// typed array. Also, checking if we can use transfers. // typed array. Also, checking if we can use transfers.
@ -1141,23 +1160,41 @@ var WorkerTransport = (function WorkerTransportClosure() {
warn('Setting up fake worker.'); warn('Setting up fake worker.');
// If we don't use a worker, just post/sendMessage to the main thread. // If we don't use a worker, just post/sendMessage to the main thread.
var fakeWorker = { var fakeWorker = {
_listeners: [],
postMessage: function WorkerTransport_postMessage(obj) { postMessage: function WorkerTransport_postMessage(obj) {
fakeWorker.onmessage({data: obj}); var e = {data: obj};
this._listeners.forEach(function (listener) {
listener.call(this, e);
}, this);
},
addEventListener: function (name, listener) {
this._listeners.push(listener);
},
removeEventListener: function (name, listener) {
var i = this._listeners.indexOf(listener);
this._listeners.splice(i, 1);
}, },
terminate: function WorkerTransport_terminate() {} terminate: function WorkerTransport_terminate() {}
}; };
var messageHandler = new MessageHandler('main', fakeWorker); var messageHandler = new MessageHandler('main', 'worker', fakeWorker);
this.setupMessageHandler(messageHandler); this.setupMainMessageHandler(messageHandler, fakeWorker);
// If the main thread is our worker, setup the handling for the messages // If the main thread is our worker, setup the handling for the messages
// the main thread sends to it self. // the main thread sends to it self.
PDFJS.WorkerMessageHandler.setup(messageHandler); var workerHandler = new MessageHandler('worker', 'main', fakeWorker);
PDFJS.WorkerMessageHandler.setup(workerHandler, fakeWorker);
this.workerInitializedCapability.resolve(); this.workerInitializedCapability.resolve();
}.bind(this)); }.bind(this));
}, },
setupMainMessageHandler:
function WorkerTransport_setupMainMessageHandler(messageHandler, port) {
this.mainMessageHandler = messageHandler;
this.port = port;
},
setupMessageHandler: setupMessageHandler:
function WorkerTransport_setupMessageHandler(messageHandler) { function WorkerTransport_setupMessageHandler(messageHandler) {
this.messageHandler = messageHandler; this.messageHandler = messageHandler;
@ -1441,7 +1478,9 @@ var WorkerTransport = (function WorkerTransportClosure() {
source.length = this.pdfDataRangeTransport.length; source.length = this.pdfDataRangeTransport.length;
source.initialData = this.pdfDataRangeTransport.initialData; source.initialData = this.pdfDataRangeTransport.initialData;
} }
this.messageHandler.send('GetDocRequest', { var docId = 'doc';
this.mainMessageHandler.sendWithPromise('GetDocRequest', {
docId: docId,
source: source, source: source,
disableRange: PDFJS.disableRange, disableRange: PDFJS.disableRange,
maxImageSize: PDFJS.maxImageSize, maxImageSize: PDFJS.maxImageSize,
@ -1450,7 +1489,16 @@ var WorkerTransport = (function WorkerTransportClosure() {
disableFontFace: PDFJS.disableFontFace, disableFontFace: PDFJS.disableFontFace,
disableCreateObjectURL: PDFJS.disableCreateObjectURL, disableCreateObjectURL: PDFJS.disableCreateObjectURL,
verbosity: PDFJS.verbosity verbosity: PDFJS.verbosity
}); }).then(function (workerId) {
if (this.destroyed) {
loadingTask._capability.reject(new Error('Loading aborted'));
this.destroyCapability.resolve();
return;
}
var messageHandler = new MessageHandler(docId, workerId, this.port);
this.setupMessageHandler(messageHandler);
}.bind(this), loadingTask._capability.reject);
}, },
getData: function WorkerTransport_getData() { getData: function WorkerTransport_getData() {

View File

@ -1518,26 +1518,20 @@ PDFJS.createObjectURL = (function createObjectURLClosure() {
}; };
})(); })();
function MessageHandler(name, comObj) { function MessageHandler(sourceName, targetName, comObj) {
this.name = name; this.sourceName = sourceName;
this.targetName = targetName;
this.comObj = comObj; this.comObj = comObj;
this.callbackIndex = 1; this.callbackIndex = 1;
this.postMessageTransfers = true; this.postMessageTransfers = true;
var callbacksCapabilities = this.callbacksCapabilities = {}; var callbacksCapabilities = this.callbacksCapabilities = {};
var ah = this.actionHandler = {}; var ah = this.actionHandler = {};
ah['console_log'] = [function ahConsoleLog(data) { this._onComObjOnMessage = function messageHandlerComObjOnMessage(event) {
console.log.apply(console, data);
}];
ah['console_error'] = [function ahConsoleError(data) {
console.error.apply(console, data);
}];
ah['_unsupported_feature'] = [function ah_unsupportedFeature(data) {
UnsupportedManager.notify(data);
}];
comObj.onmessage = function messageHandlerComObjOnMessage(event) {
var data = event.data; var data = event.data;
if (data.targetName !== this.sourceName) {
return;
}
if (data.isReply) { if (data.isReply) {
var callbackId = data.callbackId; var callbackId = data.callbackId;
if (data.callbackId in callbacksCapabilities) { if (data.callbackId in callbacksCapabilities) {
@ -1554,10 +1548,14 @@ function MessageHandler(name, comObj) {
} else if (data.action in ah) { } else if (data.action in ah) {
var action = ah[data.action]; var action = ah[data.action];
if (data.callbackId) { if (data.callbackId) {
var sourceName = this.sourceName;
var targetName = data.sourceName;
Promise.resolve().then(function () { Promise.resolve().then(function () {
return action[0].call(action[1], data.data); return action[0].call(action[1], data.data);
}).then(function (result) { }).then(function (result) {
comObj.postMessage({ comObj.postMessage({
sourceName: sourceName,
targetName: targetName,
isReply: true, isReply: true,
callbackId: data.callbackId, callbackId: data.callbackId,
data: result data: result
@ -1568,6 +1566,8 @@ function MessageHandler(name, comObj) {
reason = reason + ''; reason = reason + '';
} }
comObj.postMessage({ comObj.postMessage({
sourceName: sourceName,
targetName: targetName,
isReply: true, isReply: true,
callbackId: data.callbackId, callbackId: data.callbackId,
error: reason error: reason
@ -1579,7 +1579,8 @@ function MessageHandler(name, comObj) {
} else { } else {
error('Unknown action from worker: ' + data.action); error('Unknown action from worker: ' + data.action);
} }
}; }.bind(this);
comObj.addEventListener('message', this._onComObjOnMessage);
} }
MessageHandler.prototype = { MessageHandler.prototype = {
@ -1598,6 +1599,8 @@ MessageHandler.prototype = {
*/ */
send: function messageHandlerSend(actionName, data, transfers) { send: function messageHandlerSend(actionName, data, transfers) {
var message = { var message = {
sourceName: this.sourceName,
targetName: this.targetName,
action: actionName, action: actionName,
data: data data: data
}; };
@ -1615,6 +1618,8 @@ MessageHandler.prototype = {
function messageHandlerSendWithPromise(actionName, data, transfers) { function messageHandlerSendWithPromise(actionName, data, transfers) {
var callbackId = this.callbackIndex++; var callbackId = this.callbackIndex++;
var message = { var message = {
sourceName: this.sourceName,
targetName: this.targetName,
action: actionName, action: actionName,
data: data, data: data,
callbackId: callbackId callbackId: callbackId
@ -1640,6 +1645,10 @@ MessageHandler.prototype = {
} else { } else {
this.comObj.postMessage(message); this.comObj.postMessage(message);
} }
},
destroy: function () {
this.comObj.removeEventListener('message', this._onComObjOnMessage);
} }
}; };