Click here to see all locales supported:
15 |
16 |
17 |
18 | - "en-US"
19 | - "it-IT"
20 | - "sv-SE"
21 | - "fr-CA"
22 | - "de-DE"
23 | - "he-IL"
24 | - "id-ID"
25 | - "en-GB"
26 | - "es-AR"
27 | - "nl-BE"
28 | - "en-scotland"
29 | - "en-US"
30 | - "ro-RO"
31 | - "pt-PT"
32 | - "es-ES"
33 | - "es-MX"
34 | - "th-TH"
35 | - "en-AU"
36 | - "ja-JP"
37 | - "sk-SK"
38 | - "hi-IN"
39 | - "it-IT"
40 | - "pt-BR"
41 | - "ar-SA"
42 | - "hu-HU"
43 | - "zh-TW"
44 | - "el-GR"
45 | - "ru-RU"
46 | - "en-IE"
47 | - "es-ES"
48 | - "nb-NO"
49 | - "es-MX"
50 | - "en-IN"
51 | - "en-US"
52 | - "da-DK"
53 | - "fi-FI"
54 | - "zh-HK"
55 | - "en-ZA"
56 | - "fr-FR"
57 | - "zh-CN"
58 | - "en-IN"
59 | - "en-US"
60 | - "nl-NL"
61 | - "tr-TR"
62 | - "ko-KR"
63 | - "ru-RU"
64 | - "pl-PL"
65 | - "cs-CZ"
66 |
67 |
68 |
69 |
70 |
71 |
72 | ## Motivation
73 | All throughout middle school and high school I was a pretty good english student, but ever since I started college I began to lose confidence over my pronounciation (as I didn't have english lessons or anyone to talk to in english). I find this really frustrating because 99.9% of my time online is me reading in english... So the goal of this extension is to practice my pronounciation english.
74 |
75 | This repo was featured in the 138th issue of Ruan YiFeng's blog: https://ruanyifeng.com/blog/2020/12/weekly-issue-138.html
76 |
77 | ## Installation
78 |
79 | ### Firefox
80 |
81 | You can download the extension [here](https://addons.mozilla.org/en-US/firefox/addon/double-click-pronounce/).
82 |
83 | ### Chrome
84 | You can download the extension [here](https://chrome.google.com/webstore/detail/double-click-pronounce/pohphjaomaaadphonldcdmfflfjhnlgf?hl=en).
85 |
86 | ## Contributing
87 |
88 | Feel free to Contribute to this project!
89 |
90 | ## Contributors
91 |
92 | - [@filipeisho](https://github.com/filipeisho)
93 | - [@guimcaballero](https://github.com/guimcaballero)
94 | - [@boncom99](https://github.com/boncom99)
95 |
96 | ## License
97 |
98 | The MIT License (MIT).
99 |
--------------------------------------------------------------------------------
/browser-polyfill.js:
--------------------------------------------------------------------------------
1 |
2 | (function (global, factory) {
3 | if (typeof define === "function" && define.amd) {
4 | define("webextension-polyfill", ["module"], factory);
5 | } else if (typeof exports !== "undefined") {
6 | factory(module);
7 | } else {
8 | var mod = {
9 | exports: {}
10 | };
11 | factory(mod);
12 | global.browser = mod.exports;
13 | }
14 | })(typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : this, function (module) {
15 | /* webextension-polyfill - v0.6.0 - Mon Dec 23 2019 12:32:53 */
16 |
17 | /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
18 |
19 | /* vim: set sts=2 sw=2 et tw=80: */
20 |
21 | /* This Source Code Form is subject to the terms of the Mozilla Public
22 | * License, v. 2.0. If a copy of the MPL was not distributed with this
23 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
24 | "use strict";
25 |
26 | if (typeof browser === "undefined" || Object.getPrototypeOf(browser) !== Object.prototype) {
27 | const CHROME_SEND_MESSAGE_CALLBACK_NO_RESPONSE_MESSAGE = "The message port closed before a response was received.";
28 | const SEND_RESPONSE_DEPRECATION_WARNING = "Returning a Promise is the preferred way to send a reply from an onMessage/onMessageExternal listener, as the sendResponse will be removed from the specs (See https://developer.mozilla.org/docs/Mozilla/Add-ons/WebExtensions/API/runtime/onMessage)"; // Wrapping the bulk of this polyfill in a one-time-use function is a minor
29 | // optimization for Firefox. Since Spidermonkey does not fully parse the
30 | // contents of a function until the first time it's called, and since it will
31 | // never actually need to be called, this allows the polyfill to be included
32 | // in Firefox nearly for free.
33 |
34 | const wrapAPIs = extensionAPIs => {
35 | // NOTE: apiMetadata is associated to the content of the api-metadata.json file
36 | // at build time by replacing the following "include" with the content of the
37 | // JSON file.
38 | const apiMetadata = {
39 | "alarms": {
40 | "clear": {
41 | "minArgs": 0,
42 | "maxArgs": 1
43 | },
44 | "clearAll": {
45 | "minArgs": 0,
46 | "maxArgs": 0
47 | },
48 | "get": {
49 | "minArgs": 0,
50 | "maxArgs": 1
51 | },
52 | "getAll": {
53 | "minArgs": 0,
54 | "maxArgs": 0
55 | }
56 | },
57 | "bookmarks": {
58 | "create": {
59 | "minArgs": 1,
60 | "maxArgs": 1
61 | },
62 | "get": {
63 | "minArgs": 1,
64 | "maxArgs": 1
65 | },
66 | "getChildren": {
67 | "minArgs": 1,
68 | "maxArgs": 1
69 | },
70 | "getRecent": {
71 | "minArgs": 1,
72 | "maxArgs": 1
73 | },
74 | "getSubTree": {
75 | "minArgs": 1,
76 | "maxArgs": 1
77 | },
78 | "getTree": {
79 | "minArgs": 0,
80 | "maxArgs": 0
81 | },
82 | "move": {
83 | "minArgs": 2,
84 | "maxArgs": 2
85 | },
86 | "remove": {
87 | "minArgs": 1,
88 | "maxArgs": 1
89 | },
90 | "removeTree": {
91 | "minArgs": 1,
92 | "maxArgs": 1
93 | },
94 | "search": {
95 | "minArgs": 1,
96 | "maxArgs": 1
97 | },
98 | "update": {
99 | "minArgs": 2,
100 | "maxArgs": 2
101 | }
102 | },
103 | "browserAction": {
104 | "disable": {
105 | "minArgs": 0,
106 | "maxArgs": 1,
107 | "fallbackToNoCallback": true
108 | },
109 | "enable": {
110 | "minArgs": 0,
111 | "maxArgs": 1,
112 | "fallbackToNoCallback": true
113 | },
114 | "getBadgeBackgroundColor": {
115 | "minArgs": 1,
116 | "maxArgs": 1
117 | },
118 | "getBadgeText": {
119 | "minArgs": 1,
120 | "maxArgs": 1
121 | },
122 | "getPopup": {
123 | "minArgs": 1,
124 | "maxArgs": 1
125 | },
126 | "getTitle": {
127 | "minArgs": 1,
128 | "maxArgs": 1
129 | },
130 | "openPopup": {
131 | "minArgs": 0,
132 | "maxArgs": 0
133 | },
134 | "setBadgeBackgroundColor": {
135 | "minArgs": 1,
136 | "maxArgs": 1,
137 | "fallbackToNoCallback": true
138 | },
139 | "setBadgeText": {
140 | "minArgs": 1,
141 | "maxArgs": 1,
142 | "fallbackToNoCallback": true
143 | },
144 | "setIcon": {
145 | "minArgs": 1,
146 | "maxArgs": 1
147 | },
148 | "setPopup": {
149 | "minArgs": 1,
150 | "maxArgs": 1,
151 | "fallbackToNoCallback": true
152 | },
153 | "setTitle": {
154 | "minArgs": 1,
155 | "maxArgs": 1,
156 | "fallbackToNoCallback": true
157 | }
158 | },
159 | "browsingData": {
160 | "remove": {
161 | "minArgs": 2,
162 | "maxArgs": 2
163 | },
164 | "removeCache": {
165 | "minArgs": 1,
166 | "maxArgs": 1
167 | },
168 | "removeCookies": {
169 | "minArgs": 1,
170 | "maxArgs": 1
171 | },
172 | "removeDownloads": {
173 | "minArgs": 1,
174 | "maxArgs": 1
175 | },
176 | "removeFormData": {
177 | "minArgs": 1,
178 | "maxArgs": 1
179 | },
180 | "removeHistory": {
181 | "minArgs": 1,
182 | "maxArgs": 1
183 | },
184 | "removeLocalStorage": {
185 | "minArgs": 1,
186 | "maxArgs": 1
187 | },
188 | "removePasswords": {
189 | "minArgs": 1,
190 | "maxArgs": 1
191 | },
192 | "removePluginData": {
193 | "minArgs": 1,
194 | "maxArgs": 1
195 | },
196 | "settings": {
197 | "minArgs": 0,
198 | "maxArgs": 0
199 | }
200 | },
201 | "commands": {
202 | "getAll": {
203 | "minArgs": 0,
204 | "maxArgs": 0
205 | }
206 | },
207 | "contextMenus": {
208 | "remove": {
209 | "minArgs": 1,
210 | "maxArgs": 1
211 | },
212 | "removeAll": {
213 | "minArgs": 0,
214 | "maxArgs": 0
215 | },
216 | "update": {
217 | "minArgs": 2,
218 | "maxArgs": 2
219 | }
220 | },
221 | "cookies": {
222 | "get": {
223 | "minArgs": 1,
224 | "maxArgs": 1
225 | },
226 | "getAll": {
227 | "minArgs": 1,
228 | "maxArgs": 1
229 | },
230 | "getAllCookieStores": {
231 | "minArgs": 0,
232 | "maxArgs": 0
233 | },
234 | "remove": {
235 | "minArgs": 1,
236 | "maxArgs": 1
237 | },
238 | "set": {
239 | "minArgs": 1,
240 | "maxArgs": 1
241 | }
242 | },
243 | "devtools": {
244 | "inspectedWindow": {
245 | "eval": {
246 | "minArgs": 1,
247 | "maxArgs": 2,
248 | "singleCallbackArg": false
249 | }
250 | },
251 | "panels": {
252 | "create": {
253 | "minArgs": 3,
254 | "maxArgs": 3,
255 | "singleCallbackArg": true
256 | }
257 | }
258 | },
259 | "downloads": {
260 | "cancel": {
261 | "minArgs": 1,
262 | "maxArgs": 1
263 | },
264 | "download": {
265 | "minArgs": 1,
266 | "maxArgs": 1
267 | },
268 | "erase": {
269 | "minArgs": 1,
270 | "maxArgs": 1
271 | },
272 | "getFileIcon": {
273 | "minArgs": 1,
274 | "maxArgs": 2
275 | },
276 | "open": {
277 | "minArgs": 1,
278 | "maxArgs": 1,
279 | "fallbackToNoCallback": true
280 | },
281 | "pause": {
282 | "minArgs": 1,
283 | "maxArgs": 1
284 | },
285 | "removeFile": {
286 | "minArgs": 1,
287 | "maxArgs": 1
288 | },
289 | "resume": {
290 | "minArgs": 1,
291 | "maxArgs": 1
292 | },
293 | "search": {
294 | "minArgs": 1,
295 | "maxArgs": 1
296 | },
297 | "show": {
298 | "minArgs": 1,
299 | "maxArgs": 1,
300 | "fallbackToNoCallback": true
301 | }
302 | },
303 | "extension": {
304 | "isAllowedFileSchemeAccess": {
305 | "minArgs": 0,
306 | "maxArgs": 0
307 | },
308 | "isAllowedIncognitoAccess": {
309 | "minArgs": 0,
310 | "maxArgs": 0
311 | }
312 | },
313 | "history": {
314 | "addUrl": {
315 | "minArgs": 1,
316 | "maxArgs": 1
317 | },
318 | "deleteAll": {
319 | "minArgs": 0,
320 | "maxArgs": 0
321 | },
322 | "deleteRange": {
323 | "minArgs": 1,
324 | "maxArgs": 1
325 | },
326 | "deleteUrl": {
327 | "minArgs": 1,
328 | "maxArgs": 1
329 | },
330 | "getVisits": {
331 | "minArgs": 1,
332 | "maxArgs": 1
333 | },
334 | "search": {
335 | "minArgs": 1,
336 | "maxArgs": 1
337 | }
338 | },
339 | "i18n": {
340 | "detectLanguage": {
341 | "minArgs": 1,
342 | "maxArgs": 1
343 | },
344 | "getAcceptLanguages": {
345 | "minArgs": 0,
346 | "maxArgs": 0
347 | }
348 | },
349 | "identity": {
350 | "launchWebAuthFlow": {
351 | "minArgs": 1,
352 | "maxArgs": 1
353 | }
354 | },
355 | "idle": {
356 | "queryState": {
357 | "minArgs": 1,
358 | "maxArgs": 1
359 | }
360 | },
361 | "management": {
362 | "get": {
363 | "minArgs": 1,
364 | "maxArgs": 1
365 | },
366 | "getAll": {
367 | "minArgs": 0,
368 | "maxArgs": 0
369 | },
370 | "getSelf": {
371 | "minArgs": 0,
372 | "maxArgs": 0
373 | },
374 | "setEnabled": {
375 | "minArgs": 2,
376 | "maxArgs": 2
377 | },
378 | "uninstallSelf": {
379 | "minArgs": 0,
380 | "maxArgs": 1
381 | }
382 | },
383 | "notifications": {
384 | "clear": {
385 | "minArgs": 1,
386 | "maxArgs": 1
387 | },
388 | "create": {
389 | "minArgs": 1,
390 | "maxArgs": 2
391 | },
392 | "getAll": {
393 | "minArgs": 0,
394 | "maxArgs": 0
395 | },
396 | "getPermissionLevel": {
397 | "minArgs": 0,
398 | "maxArgs": 0
399 | },
400 | "update": {
401 | "minArgs": 2,
402 | "maxArgs": 2
403 | }
404 | },
405 | "pageAction": {
406 | "getPopup": {
407 | "minArgs": 1,
408 | "maxArgs": 1
409 | },
410 | "getTitle": {
411 | "minArgs": 1,
412 | "maxArgs": 1
413 | },
414 | "hide": {
415 | "minArgs": 1,
416 | "maxArgs": 1,
417 | "fallbackToNoCallback": true
418 | },
419 | "setIcon": {
420 | "minArgs": 1,
421 | "maxArgs": 1
422 | },
423 | "setPopup": {
424 | "minArgs": 1,
425 | "maxArgs": 1,
426 | "fallbackToNoCallback": true
427 | },
428 | "setTitle": {
429 | "minArgs": 1,
430 | "maxArgs": 1,
431 | "fallbackToNoCallback": true
432 | },
433 | "show": {
434 | "minArgs": 1,
435 | "maxArgs": 1,
436 | "fallbackToNoCallback": true
437 | }
438 | },
439 | "permissions": {
440 | "contains": {
441 | "minArgs": 1,
442 | "maxArgs": 1
443 | },
444 | "getAll": {
445 | "minArgs": 0,
446 | "maxArgs": 0
447 | },
448 | "remove": {
449 | "minArgs": 1,
450 | "maxArgs": 1
451 | },
452 | "request": {
453 | "minArgs": 1,
454 | "maxArgs": 1
455 | }
456 | },
457 | "runtime": {
458 | "getBackgroundPage": {
459 | "minArgs": 0,
460 | "maxArgs": 0
461 | },
462 | "getPlatformInfo": {
463 | "minArgs": 0,
464 | "maxArgs": 0
465 | },
466 | "openOptionsPage": {
467 | "minArgs": 0,
468 | "maxArgs": 0
469 | },
470 | "requestUpdateCheck": {
471 | "minArgs": 0,
472 | "maxArgs": 0
473 | },
474 | "sendMessage": {
475 | "minArgs": 1,
476 | "maxArgs": 3
477 | },
478 | "sendNativeMessage": {
479 | "minArgs": 2,
480 | "maxArgs": 2
481 | },
482 | "setUninstallURL": {
483 | "minArgs": 1,
484 | "maxArgs": 1
485 | }
486 | },
487 | "sessions": {
488 | "getDevices": {
489 | "minArgs": 0,
490 | "maxArgs": 1
491 | },
492 | "getRecentlyClosed": {
493 | "minArgs": 0,
494 | "maxArgs": 1
495 | },
496 | "restore": {
497 | "minArgs": 0,
498 | "maxArgs": 1
499 | }
500 | },
501 | "storage": {
502 | "local": {
503 | "clear": {
504 | "minArgs": 0,
505 | "maxArgs": 0
506 | },
507 | "get": {
508 | "minArgs": 0,
509 | "maxArgs": 1
510 | },
511 | "getBytesInUse": {
512 | "minArgs": 0,
513 | "maxArgs": 1
514 | },
515 | "remove": {
516 | "minArgs": 1,
517 | "maxArgs": 1
518 | },
519 | "set": {
520 | "minArgs": 1,
521 | "maxArgs": 1
522 | }
523 | },
524 | "managed": {
525 | "get": {
526 | "minArgs": 0,
527 | "maxArgs": 1
528 | },
529 | "getBytesInUse": {
530 | "minArgs": 0,
531 | "maxArgs": 1
532 | }
533 | },
534 | "sync": {
535 | "clear": {
536 | "minArgs": 0,
537 | "maxArgs": 0
538 | },
539 | "get": {
540 | "minArgs": 0,
541 | "maxArgs": 1
542 | },
543 | "getBytesInUse": {
544 | "minArgs": 0,
545 | "maxArgs": 1
546 | },
547 | "remove": {
548 | "minArgs": 1,
549 | "maxArgs": 1
550 | },
551 | "set": {
552 | "minArgs": 1,
553 | "maxArgs": 1
554 | }
555 | }
556 | },
557 | "tabs": {
558 | "captureVisibleTab": {
559 | "minArgs": 0,
560 | "maxArgs": 2
561 | },
562 | "create": {
563 | "minArgs": 1,
564 | "maxArgs": 1
565 | },
566 | "detectLanguage": {
567 | "minArgs": 0,
568 | "maxArgs": 1
569 | },
570 | "discard": {
571 | "minArgs": 0,
572 | "maxArgs": 1
573 | },
574 | "duplicate": {
575 | "minArgs": 1,
576 | "maxArgs": 1
577 | },
578 | "executeScript": {
579 | "minArgs": 1,
580 | "maxArgs": 2
581 | },
582 | "get": {
583 | "minArgs": 1,
584 | "maxArgs": 1
585 | },
586 | "getCurrent": {
587 | "minArgs": 0,
588 | "maxArgs": 0
589 | },
590 | "getZoom": {
591 | "minArgs": 0,
592 | "maxArgs": 1
593 | },
594 | "getZoomSettings": {
595 | "minArgs": 0,
596 | "maxArgs": 1
597 | },
598 | "highlight": {
599 | "minArgs": 1,
600 | "maxArgs": 1
601 | },
602 | "insertCSS": {
603 | "minArgs": 1,
604 | "maxArgs": 2
605 | },
606 | "move": {
607 | "minArgs": 2,
608 | "maxArgs": 2
609 | },
610 | "query": {
611 | "minArgs": 1,
612 | "maxArgs": 1
613 | },
614 | "reload": {
615 | "minArgs": 0,
616 | "maxArgs": 2
617 | },
618 | "remove": {
619 | "minArgs": 1,
620 | "maxArgs": 1
621 | },
622 | "removeCSS": {
623 | "minArgs": 1,
624 | "maxArgs": 2
625 | },
626 | "sendMessage": {
627 | "minArgs": 2,
628 | "maxArgs": 3
629 | },
630 | "setZoom": {
631 | "minArgs": 1,
632 | "maxArgs": 2
633 | },
634 | "setZoomSettings": {
635 | "minArgs": 1,
636 | "maxArgs": 2
637 | },
638 | "update": {
639 | "minArgs": 1,
640 | "maxArgs": 2
641 | }
642 | },
643 | "topSites": {
644 | "get": {
645 | "minArgs": 0,
646 | "maxArgs": 0
647 | }
648 | },
649 | "webNavigation": {
650 | "getAllFrames": {
651 | "minArgs": 1,
652 | "maxArgs": 1
653 | },
654 | "getFrame": {
655 | "minArgs": 1,
656 | "maxArgs": 1
657 | }
658 | },
659 | "webRequest": {
660 | "handlerBehaviorChanged": {
661 | "minArgs": 0,
662 | "maxArgs": 0
663 | }
664 | },
665 | "windows": {
666 | "create": {
667 | "minArgs": 0,
668 | "maxArgs": 1
669 | },
670 | "get": {
671 | "minArgs": 1,
672 | "maxArgs": 2
673 | },
674 | "getAll": {
675 | "minArgs": 0,
676 | "maxArgs": 1
677 | },
678 | "getCurrent": {
679 | "minArgs": 0,
680 | "maxArgs": 1
681 | },
682 | "getLastFocused": {
683 | "minArgs": 0,
684 | "maxArgs": 1
685 | },
686 | "remove": {
687 | "minArgs": 1,
688 | "maxArgs": 1
689 | },
690 | "update": {
691 | "minArgs": 2,
692 | "maxArgs": 2
693 | }
694 | }
695 | };
696 |
697 | if (Object.keys(apiMetadata).length === 0) {
698 | throw new Error("api-metadata.json has not been included in browser-polyfill");
699 | }
700 | /**
701 | * A WeakMap subclass which creates and stores a value for any key which does
702 | * not exist when accessed, but behaves exactly as an ordinary WeakMap
703 | * otherwise.
704 | *
705 | * @param {function} createItem
706 | * A function which will be called in order to create the value for any
707 | * key which does not exist, the first time it is accessed. The
708 | * function receives, as its only argument, the key being created.
709 | */
710 |
711 |
712 | class DefaultWeakMap extends WeakMap {
713 | constructor(createItem, items = undefined) {
714 | super(items);
715 | this.createItem = createItem;
716 | }
717 |
718 | get(key) {
719 | if (!this.has(key)) {
720 | this.set(key, this.createItem(key));
721 | }
722 |
723 | return super.get(key);
724 | }
725 |
726 | }
727 | /**
728 | * Returns true if the given object is an object with a `then` method, and can
729 | * therefore be assumed to behave as a Promise.
730 | *
731 | * @param {*} value The value to test.
732 | * @returns {boolean} True if the value is thenable.
733 | */
734 |
735 |
736 | const isThenable = value => {
737 | return value && typeof value === "object" && typeof value.then === "function";
738 | };
739 | /**
740 | * Creates and returns a function which, when called, will resolve or reject
741 | * the given promise based on how it is called:
742 | *
743 | * - If, when called, `chrome.runtime.lastError` contains a non-null object,
744 | * the promise is rejected with that value.
745 | * - If the function is called with exactly one argument, the promise is
746 | * resolved to that value.
747 | * - Otherwise, the promise is resolved to an array containing all of the
748 | * function's arguments.
749 | *
750 | * @param {object} promise
751 | * An object containing the resolution and rejection functions of a
752 | * promise.
753 | * @param {function} promise.resolve
754 | * The promise's resolution function.
755 | * @param {function} promise.rejection
756 | * The promise's rejection function.
757 | * @param {object} metadata
758 | * Metadata about the wrapped method which has created the callback.
759 | * @param {integer} metadata.maxResolvedArgs
760 | * The maximum number of arguments which may be passed to the
761 | * callback created by the wrapped async function.
762 | *
763 | * @returns {function}
764 | * The generated callback function.
765 | */
766 |
767 |
768 | const makeCallback = (promise, metadata) => {
769 | return (...callbackArgs) => {
770 | if (extensionAPIs.runtime.lastError) {
771 | promise.reject(extensionAPIs.runtime.lastError);
772 | } else if (metadata.singleCallbackArg || callbackArgs.length <= 1 && metadata.singleCallbackArg !== false) {
773 | promise.resolve(callbackArgs[0]);
774 | } else {
775 | promise.resolve(callbackArgs);
776 | }
777 | };
778 | };
779 |
780 | const pluralizeArguments = numArgs => numArgs == 1 ? "argument" : "arguments";
781 | /**
782 | * Creates a wrapper function for a method with the given name and metadata.
783 | *
784 | * @param {string} name
785 | * The name of the method which is being wrapped.
786 | * @param {object} metadata
787 | * Metadata about the method being wrapped.
788 | * @param {integer} metadata.minArgs
789 | * The minimum number of arguments which must be passed to the
790 | * function. If called with fewer than this number of arguments, the
791 | * wrapper will raise an exception.
792 | * @param {integer} metadata.maxArgs
793 | * The maximum number of arguments which may be passed to the
794 | * function. If called with more than this number of arguments, the
795 | * wrapper will raise an exception.
796 | * @param {integer} metadata.maxResolvedArgs
797 | * The maximum number of arguments which may be passed to the
798 | * callback created by the wrapped async function.
799 | *
800 | * @returns {function(object, ...*)}
801 | * The generated wrapper function.
802 | */
803 |
804 |
805 | const wrapAsyncFunction = (name, metadata) => {
806 | return function asyncFunctionWrapper(target, ...args) {
807 | if (args.length < metadata.minArgs) {
808 | throw new Error(`Expected at least ${metadata.minArgs} ${pluralizeArguments(metadata.minArgs)} for ${name}(), got ${args.length}`);
809 | }
810 |
811 | if (args.length > metadata.maxArgs) {
812 | throw new Error(`Expected at most ${metadata.maxArgs} ${pluralizeArguments(metadata.maxArgs)} for ${name}(), got ${args.length}`);
813 | }
814 |
815 | return new Promise((resolve, reject) => {
816 | if (metadata.fallbackToNoCallback) {
817 | // This API method has currently no callback on Chrome, but it return a promise on Firefox,
818 | // and so the polyfill will try to call it with a callback first, and it will fallback
819 | // to not passing the callback if the first call fails.
820 | try {
821 | target[name](...args, makeCallback({
822 | resolve,
823 | reject
824 | }, metadata));
825 | } catch (cbError) {
826 | console.warn(`${name} API method doesn't seem to support the callback parameter, ` + "falling back to call it without a callback: ", cbError);
827 | target[name](...args); // Update the API method metadata, so that the next API calls will not try to
828 | // use the unsupported callback anymore.
829 |
830 | metadata.fallbackToNoCallback = false;
831 | metadata.noCallback = true;
832 | resolve();
833 | }
834 | } else if (metadata.noCallback) {
835 | target[name](...args);
836 | resolve();
837 | } else {
838 | target[name](...args, makeCallback({
839 | resolve,
840 | reject
841 | }, metadata));
842 | }
843 | });
844 | };
845 | };
846 | /**
847 | * Wraps an existing method of the target object, so that calls to it are
848 | * intercepted by the given wrapper function. The wrapper function receives,
849 | * as its first argument, the original `target` object, followed by each of
850 | * the arguments passed to the original method.
851 | *
852 | * @param {object} target
853 | * The original target object that the wrapped method belongs to.
854 | * @param {function} method
855 | * The method being wrapped. This is used as the target of the Proxy
856 | * object which is created to wrap the method.
857 | * @param {function} wrapper
858 | * The wrapper function which is called in place of a direct invocation
859 | * of the wrapped method.
860 | *
861 | * @returns {Proxy