message_handler.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  1. /**
  2. * @licstart The following is the entire license notice for the
  3. * Javascript code in this page
  4. *
  5. * Copyright 2019 Mozilla Foundation
  6. *
  7. * Licensed under the Apache License, Version 2.0 (the "License");
  8. * you may not use this file except in compliance with the License.
  9. * You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing, software
  14. * distributed under the License is distributed on an "AS IS" BASIS,
  15. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. * See the License for the specific language governing permissions and
  17. * limitations under the License.
  18. *
  19. * @licend The above is the entire license notice for the
  20. * Javascript code in this page
  21. */
  22. "use strict";
  23. Object.defineProperty(exports, "__esModule", {
  24. value: true
  25. });
  26. exports.MessageHandler = MessageHandler;
  27. var _util = require("./util");
  28. 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); }
  29. var StreamKind = {
  30. UNKNOWN: 0,
  31. CANCEL: 1,
  32. CANCEL_COMPLETE: 2,
  33. CLOSE: 3,
  34. ENQUEUE: 4,
  35. ERROR: 5,
  36. PULL: 6,
  37. PULL_COMPLETE: 7,
  38. START_COMPLETE: 8
  39. };
  40. function wrapReason(reason) {
  41. if (_typeof(reason) !== 'object') {
  42. return reason;
  43. }
  44. switch (reason.name) {
  45. case 'AbortException':
  46. return new _util.AbortException(reason.message);
  47. case 'MissingPDFException':
  48. return new _util.MissingPDFException(reason.message);
  49. case 'UnexpectedResponseException':
  50. return new _util.UnexpectedResponseException(reason.message, reason.status);
  51. case 'UnknownErrorException':
  52. return new _util.UnknownErrorException(reason.message, reason.details);
  53. default:
  54. return new _util.UnknownErrorException(reason.message, reason.toString());
  55. }
  56. }
  57. function MessageHandler(sourceName, targetName, comObj) {
  58. var _this = this;
  59. this.sourceName = sourceName;
  60. this.targetName = targetName;
  61. this.comObj = comObj;
  62. this.callbackId = 1;
  63. this.streamId = 1;
  64. this.postMessageTransfers = true;
  65. this.streamSinks = Object.create(null);
  66. this.streamControllers = Object.create(null);
  67. var callbacksCapabilities = this.callbacksCapabilities = Object.create(null);
  68. var ah = this.actionHandler = Object.create(null);
  69. this._onComObjOnMessage = function (event) {
  70. var data = event.data;
  71. if (data.targetName !== _this.sourceName) {
  72. return;
  73. }
  74. if (data.stream) {
  75. _this._processStreamMessage(data);
  76. } else if (data.isReply) {
  77. var callbackId = data.callbackId;
  78. if (data.callbackId in callbacksCapabilities) {
  79. var callback = callbacksCapabilities[callbackId];
  80. delete callbacksCapabilities[callbackId];
  81. if ('reason' in data) {
  82. callback.reject(wrapReason(data.reason));
  83. } else {
  84. callback.resolve(data.data);
  85. }
  86. } else {
  87. throw new Error("Cannot resolve callback ".concat(callbackId));
  88. }
  89. } else if (data.action in ah) {
  90. var action = ah[data.action];
  91. if (data.callbackId) {
  92. var _sourceName = _this.sourceName;
  93. var _targetName = data.sourceName;
  94. new Promise(function (resolve) {
  95. resolve(action(data.data));
  96. }).then(function (result) {
  97. comObj.postMessage({
  98. sourceName: _sourceName,
  99. targetName: _targetName,
  100. isReply: true,
  101. callbackId: data.callbackId,
  102. data: result
  103. });
  104. }, function (reason) {
  105. comObj.postMessage({
  106. sourceName: _sourceName,
  107. targetName: _targetName,
  108. isReply: true,
  109. callbackId: data.callbackId,
  110. reason: wrapReason(reason)
  111. });
  112. });
  113. } else if (data.streamId) {
  114. _this._createStreamSink(data);
  115. } else {
  116. action(data.data);
  117. }
  118. } else {
  119. throw new Error("Unknown action from worker: ".concat(data.action));
  120. }
  121. };
  122. comObj.addEventListener('message', this._onComObjOnMessage);
  123. }
  124. MessageHandler.prototype = {
  125. on: function on(actionName, handler) {
  126. var ah = this.actionHandler;
  127. if (ah[actionName]) {
  128. throw new Error("There is already an actionName called \"".concat(actionName, "\""));
  129. }
  130. ah[actionName] = handler;
  131. },
  132. send: function send(actionName, data, transfers) {
  133. this.postMessage({
  134. sourceName: this.sourceName,
  135. targetName: this.targetName,
  136. action: actionName,
  137. data: data
  138. }, transfers);
  139. },
  140. sendWithPromise: function sendWithPromise(actionName, data, transfers) {
  141. var callbackId = this.callbackId++;
  142. var capability = (0, _util.createPromiseCapability)();
  143. this.callbacksCapabilities[callbackId] = capability;
  144. try {
  145. this.postMessage({
  146. sourceName: this.sourceName,
  147. targetName: this.targetName,
  148. action: actionName,
  149. callbackId: callbackId,
  150. data: data
  151. }, transfers);
  152. } catch (ex) {
  153. capability.reject(ex);
  154. }
  155. return capability.promise;
  156. },
  157. sendWithStream: function sendWithStream(actionName, data, queueingStrategy, transfers) {
  158. var _this2 = this;
  159. var streamId = this.streamId++;
  160. var sourceName = this.sourceName;
  161. var targetName = this.targetName;
  162. var comObj = this.comObj;
  163. return new _util.ReadableStream({
  164. start: function start(controller) {
  165. var startCapability = (0, _util.createPromiseCapability)();
  166. _this2.streamControllers[streamId] = {
  167. controller: controller,
  168. startCall: startCapability,
  169. pullCall: null,
  170. cancelCall: null,
  171. isClosed: false
  172. };
  173. _this2.postMessage({
  174. sourceName: sourceName,
  175. targetName: targetName,
  176. action: actionName,
  177. streamId: streamId,
  178. data: data,
  179. desiredSize: controller.desiredSize
  180. }, transfers);
  181. return startCapability.promise;
  182. },
  183. pull: function pull(controller) {
  184. var pullCapability = (0, _util.createPromiseCapability)();
  185. _this2.streamControllers[streamId].pullCall = pullCapability;
  186. comObj.postMessage({
  187. sourceName: sourceName,
  188. targetName: targetName,
  189. stream: StreamKind.PULL,
  190. streamId: streamId,
  191. desiredSize: controller.desiredSize
  192. });
  193. return pullCapability.promise;
  194. },
  195. cancel: function cancel(reason) {
  196. (0, _util.assert)(reason instanceof Error, 'cancel must have a valid reason');
  197. var cancelCapability = (0, _util.createPromiseCapability)();
  198. _this2.streamControllers[streamId].cancelCall = cancelCapability;
  199. _this2.streamControllers[streamId].isClosed = true;
  200. comObj.postMessage({
  201. sourceName: sourceName,
  202. targetName: targetName,
  203. stream: StreamKind.CANCEL,
  204. streamId: streamId,
  205. reason: wrapReason(reason)
  206. });
  207. return cancelCapability.promise;
  208. }
  209. }, queueingStrategy);
  210. },
  211. _createStreamSink: function _createStreamSink(data) {
  212. var self = this;
  213. var action = this.actionHandler[data.action];
  214. var streamId = data.streamId;
  215. var desiredSize = data.desiredSize;
  216. var sourceName = this.sourceName;
  217. var targetName = data.sourceName;
  218. var capability = (0, _util.createPromiseCapability)();
  219. var comObj = this.comObj;
  220. var streamSink = {
  221. enqueue: function enqueue(chunk) {
  222. var size = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
  223. var transfers = arguments.length > 2 ? arguments[2] : undefined;
  224. if (this.isCancelled) {
  225. return;
  226. }
  227. var lastDesiredSize = this.desiredSize;
  228. this.desiredSize -= size;
  229. if (lastDesiredSize > 0 && this.desiredSize <= 0) {
  230. this.sinkCapability = (0, _util.createPromiseCapability)();
  231. this.ready = this.sinkCapability.promise;
  232. }
  233. self.postMessage({
  234. sourceName: sourceName,
  235. targetName: targetName,
  236. stream: StreamKind.ENQUEUE,
  237. streamId: streamId,
  238. chunk: chunk
  239. }, transfers);
  240. },
  241. close: function close() {
  242. if (this.isCancelled) {
  243. return;
  244. }
  245. this.isCancelled = true;
  246. comObj.postMessage({
  247. sourceName: sourceName,
  248. targetName: targetName,
  249. stream: StreamKind.CLOSE,
  250. streamId: streamId
  251. });
  252. delete self.streamSinks[streamId];
  253. },
  254. error: function error(reason) {
  255. (0, _util.assert)(reason instanceof Error, 'error must have a valid reason');
  256. if (this.isCancelled) {
  257. return;
  258. }
  259. this.isCancelled = true;
  260. comObj.postMessage({
  261. sourceName: sourceName,
  262. targetName: targetName,
  263. stream: StreamKind.ERROR,
  264. streamId: streamId,
  265. reason: wrapReason(reason)
  266. });
  267. },
  268. sinkCapability: capability,
  269. onPull: null,
  270. onCancel: null,
  271. isCancelled: false,
  272. desiredSize: desiredSize,
  273. ready: null
  274. };
  275. streamSink.sinkCapability.resolve();
  276. streamSink.ready = streamSink.sinkCapability.promise;
  277. this.streamSinks[streamId] = streamSink;
  278. new Promise(function (resolve) {
  279. resolve(action(data.data, streamSink));
  280. }).then(function () {
  281. comObj.postMessage({
  282. sourceName: sourceName,
  283. targetName: targetName,
  284. stream: StreamKind.START_COMPLETE,
  285. streamId: streamId,
  286. success: true
  287. });
  288. }, function (reason) {
  289. comObj.postMessage({
  290. sourceName: sourceName,
  291. targetName: targetName,
  292. stream: StreamKind.START_COMPLETE,
  293. streamId: streamId,
  294. reason: wrapReason(reason)
  295. });
  296. });
  297. },
  298. _processStreamMessage: function _processStreamMessage(data) {
  299. var _this3 = this;
  300. var sourceName = this.sourceName;
  301. var targetName = data.sourceName;
  302. var streamId = data.streamId;
  303. var comObj = this.comObj;
  304. var deleteStreamController = function deleteStreamController() {
  305. Promise.all([_this3.streamControllers[streamId].startCall, _this3.streamControllers[streamId].pullCall, _this3.streamControllers[streamId].cancelCall].map(function (capability) {
  306. return capability && capability.promise["catch"](function () {});
  307. })).then(function () {
  308. delete _this3.streamControllers[streamId];
  309. });
  310. };
  311. switch (data.stream) {
  312. case StreamKind.START_COMPLETE:
  313. if (data.success) {
  314. this.streamControllers[streamId].startCall.resolve();
  315. } else {
  316. this.streamControllers[streamId].startCall.reject(wrapReason(data.reason));
  317. }
  318. break;
  319. case StreamKind.PULL_COMPLETE:
  320. if (data.success) {
  321. this.streamControllers[streamId].pullCall.resolve();
  322. } else {
  323. this.streamControllers[streamId].pullCall.reject(wrapReason(data.reason));
  324. }
  325. break;
  326. case StreamKind.PULL:
  327. if (!this.streamSinks[streamId]) {
  328. comObj.postMessage({
  329. sourceName: sourceName,
  330. targetName: targetName,
  331. stream: StreamKind.PULL_COMPLETE,
  332. streamId: streamId,
  333. success: true
  334. });
  335. break;
  336. }
  337. if (this.streamSinks[streamId].desiredSize <= 0 && data.desiredSize > 0) {
  338. this.streamSinks[streamId].sinkCapability.resolve();
  339. }
  340. this.streamSinks[streamId].desiredSize = data.desiredSize;
  341. var onPull = this.streamSinks[data.streamId].onPull;
  342. new Promise(function (resolve) {
  343. resolve(onPull && onPull());
  344. }).then(function () {
  345. comObj.postMessage({
  346. sourceName: sourceName,
  347. targetName: targetName,
  348. stream: StreamKind.PULL_COMPLETE,
  349. streamId: streamId,
  350. success: true
  351. });
  352. }, function (reason) {
  353. comObj.postMessage({
  354. sourceName: sourceName,
  355. targetName: targetName,
  356. stream: StreamKind.PULL_COMPLETE,
  357. streamId: streamId,
  358. reason: wrapReason(reason)
  359. });
  360. });
  361. break;
  362. case StreamKind.ENQUEUE:
  363. (0, _util.assert)(this.streamControllers[streamId], 'enqueue should have stream controller');
  364. if (this.streamControllers[streamId].isClosed) {
  365. break;
  366. }
  367. this.streamControllers[streamId].controller.enqueue(data.chunk);
  368. break;
  369. case StreamKind.CLOSE:
  370. (0, _util.assert)(this.streamControllers[streamId], 'close should have stream controller');
  371. if (this.streamControllers[streamId].isClosed) {
  372. break;
  373. }
  374. this.streamControllers[streamId].isClosed = true;
  375. this.streamControllers[streamId].controller.close();
  376. deleteStreamController();
  377. break;
  378. case StreamKind.ERROR:
  379. (0, _util.assert)(this.streamControllers[streamId], 'error should have stream controller');
  380. this.streamControllers[streamId].controller.error(wrapReason(data.reason));
  381. deleteStreamController();
  382. break;
  383. case StreamKind.CANCEL_COMPLETE:
  384. if (data.success) {
  385. this.streamControllers[streamId].cancelCall.resolve();
  386. } else {
  387. this.streamControllers[streamId].cancelCall.reject(wrapReason(data.reason));
  388. }
  389. deleteStreamController();
  390. break;
  391. case StreamKind.CANCEL:
  392. if (!this.streamSinks[streamId]) {
  393. break;
  394. }
  395. var onCancel = this.streamSinks[data.streamId].onCancel;
  396. new Promise(function (resolve) {
  397. resolve(onCancel && onCancel(wrapReason(data.reason)));
  398. }).then(function () {
  399. comObj.postMessage({
  400. sourceName: sourceName,
  401. targetName: targetName,
  402. stream: StreamKind.CANCEL_COMPLETE,
  403. streamId: streamId,
  404. success: true
  405. });
  406. }, function (reason) {
  407. comObj.postMessage({
  408. sourceName: sourceName,
  409. targetName: targetName,
  410. stream: StreamKind.CANCEL_COMPLETE,
  411. streamId: streamId,
  412. reason: wrapReason(reason)
  413. });
  414. });
  415. this.streamSinks[streamId].sinkCapability.reject(wrapReason(data.reason));
  416. this.streamSinks[streamId].isCancelled = true;
  417. delete this.streamSinks[streamId];
  418. break;
  419. default:
  420. throw new Error('Unexpected stream case');
  421. }
  422. },
  423. postMessage: function postMessage(message, transfers) {
  424. if (transfers && this.postMessageTransfers) {
  425. this.comObj.postMessage(message, transfers);
  426. } else {
  427. this.comObj.postMessage(message);
  428. }
  429. },
  430. destroy: function destroy() {
  431. this.comObj.removeEventListener('message', this._onComObjOnMessage);
  432. }
  433. };