From acdd49f48097ffcb27332673a8b51cd653f6de88 Mon Sep 17 00:00:00 2001 From: Yury Delendik Date: Tue, 27 Oct 2015 10:07:20 -0500 Subject: [PATCH] Adds peer communication between MessageHandlers. --- src/core/worker.js | 81 +++++++++++++++++++++++++++------------------- src/display/api.js | 68 ++++++++++++++++++++++++++++++++------ src/shared/util.js | 37 +++++++++++++-------- 3 files changed, 129 insertions(+), 57 deletions(-) diff --git a/src/core/worker.js b/src/core/worker.js index 1e80d5aab..82a4946f0 100644 --- a/src/core/worker.js +++ b/src/core/worker.js @@ -51,12 +51,49 @@ var WorkerTask = (function WorkerTaskClosure() { })(); 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 terminated = false; var cancelXHRs = null; var WorkerTasks = []; + var mainHandlerName = data.docId; + var workerHandlerName = data.docId + '_worker'; + var handler = new MessageHandler(workerHandlerName, mainHandlerName, port); + function ensureNotTerminated() { if (terminated) { throw new Error('Worker was terminated'); @@ -262,35 +299,7 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { return pdfManagerCapability.promise; } - handler.on('test', function wphSetupTest(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 setupDoc = function(data) { var onSuccess = function(doc) { ensureNotTerminated(); handler.send('GetDoc', { pdfInfo: doc }); @@ -366,7 +375,7 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { }); }, onFailure); }, onFailure); - }); + }; handler.on('GetPage', function wphSetupGetPage(data) { return pdfManager.getPage(data.pageIndex).then(function(page) { @@ -548,6 +557,9 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { return Promise.all(waitOn).then(function () {}); }); + + setupDoc(data); + return workerHandlerName; } }; @@ -557,6 +569,7 @@ var workerConsole = { log: function log() { var args = Array.prototype.slice.call(arguments); globalScope.postMessage({ + targetName: 'main', action: 'console_log', data: args }); @@ -565,6 +578,7 @@ var workerConsole = { error: function error() { var args = Array.prototype.slice.call(arguments); globalScope.postMessage({ + targetName: 'main', action: 'console_error', data: args }); @@ -594,11 +608,12 @@ if (typeof window === 'undefined') { // Listen for unsupported features so we can pass them on to the main thread. PDFJS.UnsupportedManager.listen(function (msg) { globalScope.postMessage({ + targetName: 'main', action: '_unsupported_feature', data: msg }); }); - var handler = new MessageHandler('worker_processor', this); - WorkerMessageHandler.setup(handler); + var handler = new MessageHandler('worker', 'main', this); + WorkerMessageHandler.setup(handler, this); } diff --git a/src/display/api.js b/src/display/api.js index c9034fb48..7e65ba6f2 100644 --- a/src/display/api.js +++ b/src/display/api.js @@ -17,7 +17,7 @@ Promise, PasswordResponses, PasswordException, InvalidPDFException, MissingPDFException, UnknownErrorException, FontFaceObject, loadJpegStream, createScratchCanvas, CanvasGraphics, stringToBytes, - UnexpectedResponseException, deprecated */ + UnexpectedResponseException, deprecated, UnsupportedManager */ 'use strict'; @@ -353,6 +353,12 @@ var PDFDocumentLoadingTask = (function PDFDocumentLoadingTaskClosure() { this._capability = createPromiseCapability(); 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. * The callback receives two parameters: function that needs to be called @@ -383,6 +389,10 @@ var PDFDocumentLoadingTask = (function PDFDocumentLoadingTaskClosure() { * is completed. */ destroy: function () { + this.destroyed = true; + if (!this._transport) { + return Promise.resolve(); + } return this._transport.destroy(); }, @@ -1042,8 +1052,7 @@ var WorkerTransport = (function WorkerTransportClosure() { // Some versions of FF can't create a worker on localhost, see: // https://bugzilla.mozilla.org/show_bug.cgi?id=683280 var worker = new Worker(workerSrc); - var messageHandler = new MessageHandler('main', worker); - this.messageHandler = messageHandler; + var messageHandler = new MessageHandler('main', 'worker', worker); messageHandler.on('test', function transportTest(data) { var supportTypedArray = data && data.supportTypedArray; @@ -1052,13 +1061,23 @@ var WorkerTransport = (function WorkerTransportClosure() { if (!data.supportTransfers) { PDFJS.postMessageTransfers = false; } - this.setupMessageHandler(messageHandler); + this.setupMainMessageHandler(messageHandler, worker); workerInitializedCapability.resolve(); } else { this.setupFakeWorker(); } }.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]); // Some versions of Opera throw a DATA_CLONE_ERR on serializing the // typed array. Also, checking if we can use transfers. @@ -1141,23 +1160,41 @@ var WorkerTransport = (function WorkerTransportClosure() { warn('Setting up fake worker.'); // If we don't use a worker, just post/sendMessage to the main thread. var fakeWorker = { + _listeners: [], 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() {} }; - var messageHandler = new MessageHandler('main', fakeWorker); - this.setupMessageHandler(messageHandler); + var messageHandler = new MessageHandler('main', 'worker', fakeWorker); + this.setupMainMessageHandler(messageHandler, fakeWorker); // If the main thread is our worker, setup the handling for the messages // 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(); }.bind(this)); }, + setupMainMessageHandler: + function WorkerTransport_setupMainMessageHandler(messageHandler, port) { + this.mainMessageHandler = messageHandler; + this.port = port; + }, + setupMessageHandler: function WorkerTransport_setupMessageHandler(messageHandler) { this.messageHandler = messageHandler; @@ -1441,7 +1478,9 @@ var WorkerTransport = (function WorkerTransportClosure() { source.length = this.pdfDataRangeTransport.length; source.initialData = this.pdfDataRangeTransport.initialData; } - this.messageHandler.send('GetDocRequest', { + var docId = 'doc'; + this.mainMessageHandler.sendWithPromise('GetDocRequest', { + docId: docId, source: source, disableRange: PDFJS.disableRange, maxImageSize: PDFJS.maxImageSize, @@ -1450,7 +1489,16 @@ var WorkerTransport = (function WorkerTransportClosure() { disableFontFace: PDFJS.disableFontFace, disableCreateObjectURL: PDFJS.disableCreateObjectURL, 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() { diff --git a/src/shared/util.js b/src/shared/util.js index 6845a5fd4..299baf2d7 100644 --- a/src/shared/util.js +++ b/src/shared/util.js @@ -1518,26 +1518,20 @@ PDFJS.createObjectURL = (function createObjectURLClosure() { }; })(); -function MessageHandler(name, comObj) { - this.name = name; +function MessageHandler(sourceName, targetName, comObj) { + this.sourceName = sourceName; + this.targetName = targetName; this.comObj = comObj; this.callbackIndex = 1; this.postMessageTransfers = true; var callbacksCapabilities = this.callbacksCapabilities = {}; var ah = this.actionHandler = {}; - ah['console_log'] = [function ahConsoleLog(data) { - 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) { + this._onComObjOnMessage = function messageHandlerComObjOnMessage(event) { var data = event.data; + if (data.targetName !== this.sourceName) { + return; + } if (data.isReply) { var callbackId = data.callbackId; if (data.callbackId in callbacksCapabilities) { @@ -1554,10 +1548,14 @@ function MessageHandler(name, comObj) { } else if (data.action in ah) { var action = ah[data.action]; if (data.callbackId) { + var sourceName = this.sourceName; + var targetName = data.sourceName; Promise.resolve().then(function () { return action[0].call(action[1], data.data); }).then(function (result) { comObj.postMessage({ + sourceName: sourceName, + targetName: targetName, isReply: true, callbackId: data.callbackId, data: result @@ -1568,6 +1566,8 @@ function MessageHandler(name, comObj) { reason = reason + ''; } comObj.postMessage({ + sourceName: sourceName, + targetName: targetName, isReply: true, callbackId: data.callbackId, error: reason @@ -1579,7 +1579,8 @@ function MessageHandler(name, comObj) { } else { error('Unknown action from worker: ' + data.action); } - }; + }.bind(this); + comObj.addEventListener('message', this._onComObjOnMessage); } MessageHandler.prototype = { @@ -1598,6 +1599,8 @@ MessageHandler.prototype = { */ send: function messageHandlerSend(actionName, data, transfers) { var message = { + sourceName: this.sourceName, + targetName: this.targetName, action: actionName, data: data }; @@ -1615,6 +1618,8 @@ MessageHandler.prototype = { function messageHandlerSendWithPromise(actionName, data, transfers) { var callbackId = this.callbackIndex++; var message = { + sourceName: this.sourceName, + targetName: this.targetName, action: actionName, data: data, callbackId: callbackId @@ -1640,6 +1645,10 @@ MessageHandler.prototype = { } else { this.comObj.postMessage(message); } + }, + + destroy: function () { + this.comObj.removeEventListener('message', this._onComObjOnMessage); } };