/** * @licstart The following is the entire license notice for the * Javascript code in this page * * Copyright 2018 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * @licend The above is the entire license notice for the * Javascript code in this page */ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MessageHandler = MessageHandler; var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator")); var _util = require("./util"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } function resolveCall(_x, _x2) { return _resolveCall.apply(this, arguments); } function _resolveCall() { _resolveCall = _asyncToGenerator( /*#__PURE__*/ _regenerator.default.mark(function _callee(fn, args) { var thisArg, _args = arguments; return _regenerator.default.wrap(function _callee$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: thisArg = _args.length > 2 && _args[2] !== undefined ? _args[2] : null; if (fn) { _context.next = 3; break; } return _context.abrupt("return"); case 3: return _context.abrupt("return", fn.apply(thisArg, args)); case 4: case "end": return _context.stop(); } } }, _callee, this); })); return _resolveCall.apply(this, arguments); } function wrapReason(reason) { if (_typeof(reason) !== 'object') { return reason; } switch (reason.name) { case 'AbortException': return new _util.AbortException(reason.message); case 'MissingPDFException': return new _util.MissingPDFException(reason.message); case 'UnexpectedResponseException': return new _util.UnexpectedResponseException(reason.message, reason.status); default: return new _util.UnknownErrorException(reason.message, reason.details); } } function makeReasonSerializable(reason) { if (!(reason instanceof Error) || reason instanceof _util.AbortException || reason instanceof _util.MissingPDFException || reason instanceof _util.UnexpectedResponseException || reason instanceof _util.UnknownErrorException) { return reason; } return new _util.UnknownErrorException(reason.message, reason.toString()); } function resolveOrReject(capability, success, reason) { if (success) { capability.resolve(); } else { capability.reject(reason); } } function finalize(promise) { return Promise.resolve(promise).catch(function () {}); } function MessageHandler(sourceName, targetName, comObj) { var _this = this; this.sourceName = sourceName; this.targetName = targetName; this.comObj = comObj; this.callbackId = 1; this.streamId = 1; this.postMessageTransfers = true; this.streamSinks = Object.create(null); this.streamControllers = Object.create(null); var callbacksCapabilities = this.callbacksCapabilities = Object.create(null); var ah = this.actionHandler = Object.create(null); this._onComObjOnMessage = function (event) { var data = event.data; if (data.targetName !== _this.sourceName) { return; } if (data.stream) { _this._processStreamMessage(data); } else if (data.isReply) { var callbackId = data.callbackId; if (data.callbackId in callbacksCapabilities) { var callback = callbacksCapabilities[callbackId]; delete callbacksCapabilities[callbackId]; if ('error' in data) { callback.reject(wrapReason(data.error)); } else { callback.resolve(data.data); } } else { throw new Error("Cannot resolve callback ".concat(callbackId)); } } 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 }); }, function (reason) { comObj.postMessage({ sourceName: _sourceName, targetName: _targetName, isReply: true, callbackId: data.callbackId, error: makeReasonSerializable(reason) }); }); } else if (data.streamId) { _this._createStreamSink(data); } else { action[0].call(action[1], data.data); } } else { throw new Error("Unknown action from worker: ".concat(data.action)); } }; comObj.addEventListener('message', this._onComObjOnMessage); } MessageHandler.prototype = { on: function on(actionName, handler, scope) { var ah = this.actionHandler; if (ah[actionName]) { throw new Error("There is already an actionName called \"".concat(actionName, "\"")); } ah[actionName] = [handler, scope]; }, send: function send(actionName, data, transfers) { var message = { sourceName: this.sourceName, targetName: this.targetName, action: actionName, data: data }; this.postMessage(message, transfers); }, sendWithPromise: function sendWithPromise(actionName, data, transfers) { var callbackId = this.callbackId++; var message = { sourceName: this.sourceName, targetName: this.targetName, action: actionName, data: data, callbackId: callbackId }; var capability = (0, _util.createPromiseCapability)(); this.callbacksCapabilities[callbackId] = capability; try { this.postMessage(message, transfers); } catch (e) { capability.reject(e); } return capability.promise; }, sendWithStream: function sendWithStream(actionName, data, queueingStrategy, transfers) { var _this2 = this; var streamId = this.streamId++; var sourceName = this.sourceName; var targetName = this.targetName; return new _util.ReadableStream({ start: function start(controller) { var startCapability = (0, _util.createPromiseCapability)(); _this2.streamControllers[streamId] = { controller: controller, startCall: startCapability, isClosed: false }; _this2.postMessage({ sourceName: sourceName, targetName: targetName, action: actionName, streamId: streamId, data: data, desiredSize: controller.desiredSize }); return startCapability.promise; }, pull: function pull(controller) { var pullCapability = (0, _util.createPromiseCapability)(); _this2.streamControllers[streamId].pullCall = pullCapability; _this2.postMessage({ sourceName: sourceName, targetName: targetName, stream: 'pull', streamId: streamId, desiredSize: controller.desiredSize }); return pullCapability.promise; }, cancel: function cancel(reason) { var cancelCapability = (0, _util.createPromiseCapability)(); _this2.streamControllers[streamId].cancelCall = cancelCapability; _this2.streamControllers[streamId].isClosed = true; _this2.postMessage({ sourceName: sourceName, targetName: targetName, stream: 'cancel', reason: reason, streamId: streamId }); return cancelCapability.promise; } }, queueingStrategy); }, _createStreamSink: function _createStreamSink(data) { var _this3 = this; var self = this; var action = this.actionHandler[data.action]; var streamId = data.streamId; var desiredSize = data.desiredSize; var sourceName = this.sourceName; var targetName = data.sourceName; var capability = (0, _util.createPromiseCapability)(); var sendStreamRequest = function sendStreamRequest(_ref) { var stream = _ref.stream, chunk = _ref.chunk, transfers = _ref.transfers, success = _ref.success, reason = _ref.reason; _this3.postMessage({ sourceName: sourceName, targetName: targetName, stream: stream, streamId: streamId, chunk: chunk, success: success, reason: reason }, transfers); }; var streamSink = { enqueue: function enqueue(chunk) { var size = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1; var transfers = arguments.length > 2 ? arguments[2] : undefined; if (this.isCancelled) { return; } var lastDesiredSize = this.desiredSize; this.desiredSize -= size; if (lastDesiredSize > 0 && this.desiredSize <= 0) { this.sinkCapability = (0, _util.createPromiseCapability)(); this.ready = this.sinkCapability.promise; } sendStreamRequest({ stream: 'enqueue', chunk: chunk, transfers: transfers }); }, close: function close() { if (this.isCancelled) { return; } this.isCancelled = true; sendStreamRequest({ stream: 'close' }); delete self.streamSinks[streamId]; }, error: function error(reason) { if (this.isCancelled) { return; } this.isCancelled = true; sendStreamRequest({ stream: 'error', reason: reason }); }, sinkCapability: capability, onPull: null, onCancel: null, isCancelled: false, desiredSize: desiredSize, ready: null }; streamSink.sinkCapability.resolve(); streamSink.ready = streamSink.sinkCapability.promise; this.streamSinks[streamId] = streamSink; resolveCall(action[0], [data.data, streamSink], action[1]).then(function () { sendStreamRequest({ stream: 'start_complete', success: true }); }, function (reason) { sendStreamRequest({ stream: 'start_complete', success: false, reason: reason }); }); }, _processStreamMessage: function _processStreamMessage(data) { var _this4 = this; var sourceName = this.sourceName; var targetName = data.sourceName; var streamId = data.streamId; var sendStreamResponse = function sendStreamResponse(_ref2) { var stream = _ref2.stream, success = _ref2.success, reason = _ref2.reason; _this4.comObj.postMessage({ sourceName: sourceName, targetName: targetName, stream: stream, success: success, streamId: streamId, reason: reason }); }; var deleteStreamController = function deleteStreamController() { Promise.all([_this4.streamControllers[data.streamId].startCall, _this4.streamControllers[data.streamId].pullCall, _this4.streamControllers[data.streamId].cancelCall].map(function (capability) { return capability && finalize(capability.promise); })).then(function () { delete _this4.streamControllers[data.streamId]; }); }; switch (data.stream) { case 'start_complete': resolveOrReject(this.streamControllers[data.streamId].startCall, data.success, wrapReason(data.reason)); break; case 'pull_complete': resolveOrReject(this.streamControllers[data.streamId].pullCall, data.success, wrapReason(data.reason)); break; case 'pull': if (!this.streamSinks[data.streamId]) { sendStreamResponse({ stream: 'pull_complete', success: true }); break; } if (this.streamSinks[data.streamId].desiredSize <= 0 && data.desiredSize > 0) { this.streamSinks[data.streamId].sinkCapability.resolve(); } this.streamSinks[data.streamId].desiredSize = data.desiredSize; resolveCall(this.streamSinks[data.streamId].onPull).then(function () { sendStreamResponse({ stream: 'pull_complete', success: true }); }, function (reason) { sendStreamResponse({ stream: 'pull_complete', success: false, reason: reason }); }); break; case 'enqueue': (0, _util.assert)(this.streamControllers[data.streamId], 'enqueue should have stream controller'); if (!this.streamControllers[data.streamId].isClosed) { this.streamControllers[data.streamId].controller.enqueue(data.chunk); } break; case 'close': (0, _util.assert)(this.streamControllers[data.streamId], 'close should have stream controller'); if (this.streamControllers[data.streamId].isClosed) { break; } this.streamControllers[data.streamId].isClosed = true; this.streamControllers[data.streamId].controller.close(); deleteStreamController(); break; case 'error': (0, _util.assert)(this.streamControllers[data.streamId], 'error should have stream controller'); this.streamControllers[data.streamId].controller.error(wrapReason(data.reason)); deleteStreamController(); break; case 'cancel_complete': resolveOrReject(this.streamControllers[data.streamId].cancelCall, data.success, wrapReason(data.reason)); deleteStreamController(); break; case 'cancel': if (!this.streamSinks[data.streamId]) { break; } resolveCall(this.streamSinks[data.streamId].onCancel, [wrapReason(data.reason)]).then(function () { sendStreamResponse({ stream: 'cancel_complete', success: true }); }, function (reason) { sendStreamResponse({ stream: 'cancel_complete', success: false, reason: reason }); }); this.streamSinks[data.streamId].sinkCapability.reject(wrapReason(data.reason)); this.streamSinks[data.streamId].isCancelled = true; delete this.streamSinks[data.streamId]; break; default: throw new Error('Unexpected stream case'); } }, postMessage: function postMessage(message, transfers) { if (transfers && this.postMessageTransfers) { this.comObj.postMessage(message, transfers); } else { this.comObj.postMessage(message); } }, destroy: function destroy() { this.comObj.removeEventListener('message', this._onComObjOnMessage); } };