OLD | NEW |
---|---|
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 'use strict'; | 5 'use strict'; |
6 | 6 |
7 /** | 7 /** |
8 * @fileoverview Utility objects and functions for Google Now extension. | 8 * @fileoverview Utility objects and functions for Google Now extension. |
9 * Most important entities here: | 9 * Most important entities here: |
10 * (1) 'wrapper' is a module used to add error handling and other services to | 10 * (1) 'wrapper' is a module used to add error handling and other services to |
(...skipping 419 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
430 })(); | 430 })(); |
431 | 431 |
432 wrapper.instrumentChromeApiFunction('alarms.get', 1); | 432 wrapper.instrumentChromeApiFunction('alarms.get', 1); |
433 wrapper.instrumentChromeApiFunction('alarms.onAlarm.addListener', 0); | 433 wrapper.instrumentChromeApiFunction('alarms.onAlarm.addListener', 0); |
434 wrapper.instrumentChromeApiFunction('identity.getAuthToken', 1); | 434 wrapper.instrumentChromeApiFunction('identity.getAuthToken', 1); |
435 wrapper.instrumentChromeApiFunction('identity.onSignInChanged.addListener', 0); | 435 wrapper.instrumentChromeApiFunction('identity.onSignInChanged.addListener', 0); |
436 wrapper.instrumentChromeApiFunction('identity.removeCachedAuthToken', 1); | 436 wrapper.instrumentChromeApiFunction('identity.removeCachedAuthToken', 1); |
437 wrapper.instrumentChromeApiFunction('webstorePrivate.getBrowserLogin', 0); | 437 wrapper.instrumentChromeApiFunction('webstorePrivate.getBrowserLogin', 0); |
438 | 438 |
439 /** | 439 /** |
440 * Add task tracking support to Promise.then. | 440 * Promise adapter for all JS promises to the task manager. |
441 * @override | |
442 */ | 441 */ |
443 Promise.prototype.then = function() { | 442 function registerPromiseAdapter() { |
444 var originalThen = Promise.prototype.then; | 443 var originalThen = Promise.prototype.then; |
445 return function(callback) { | 444 var originalCatch = Promise.prototype.catch; |
446 return originalThen.call(this, wrapper.wrapCallback(callback, false)); | 445 |
446 /** | |
447 * Takes a promise and adds the callback tracker to it. | |
448 * @param {object} promise Promise that receives the callback tracker. | |
449 */ | |
450 function instrumentPromise(promise) { | |
451 if (promise.__tracker === undefined) { | |
452 promise.__tracker = createPromiseCallbackTracker(promise); | |
453 } | |
447 } | 454 } |
448 }(); | |
449 | 455 |
450 /** | 456 Promise.prototype.then = function(onResolved, onRejected) { |
451 * Add task tracking support to Promise.catch. | 457 instrumentPromise(this); |
452 * @override | 458 return this.__tracker.handleThen(onResolved, onRejected); |
453 */ | |
454 Promise.prototype.catch = function() { | |
455 var originalCatch = Promise.prototype.catch; | |
456 return function(callback) { | |
457 return originalCatch.call(this, wrapper.wrapCallback(callback, false)); | |
458 } | 459 } |
459 }(); | 460 |
461 Promise.prototype.catch = function(onRejected) { | |
462 instrumentPromise(this); | |
463 return this.__tracker.handleCatch(onRejected); | |
464 } | |
465 | |
466 /** | |
467 * Promise Callback Tracker. | |
468 * Handles coordination of 'then' and 'catch' callbacks in a task | |
469 * manager compatible way. For an individual promise, either the 'then' | |
470 * arguments or the 'catch' arguemnts will be processed, never both. | |
471 * | |
472 * Example: | |
473 * var p = new Promise([Function]); | |
474 * p.then([ThenA]); | |
475 * p.then([ThenB]); | |
476 * p.catch([CatchA]); | |
477 * On resolution, [ThenA] and [ThenB] will be used. [CatchA] is discarded. | |
478 * On rejection, vice versa. | |
479 * | |
480 * Clarification: | |
481 * Chained promises create a new promise that is tracked separately from | |
482 * the originaing promise, as the example below demonstrates: | |
483 * | |
484 * var p = new Promise([Function])); | |
485 * p.then([ThenA]).then([ThenB]).catch([CatchA]); | |
486 * ^ ^ ^ | |
487 * | | + Returns a new promise. | |
488 * | + Returns a new promise. | |
489 * + Returns a new promise. | |
490 * | |
491 * Four promises exist in the above statement, each with its own | |
492 * resolution and rejection state. However, by default, this state is | |
493 * chained to the previous previous promise's resolution or rejection | |
494 * state. | |
495 * | |
496 * If p resolves, then the then calls will execute until all the 'then' | |
497 * clauses are executed. If the result of either [ThenA] or [ThenB] is a | |
498 * promise, then that execution state will guide the remaining chain. | |
499 * Similarly, if [CatchA] returns a promise, it can also guide the | |
500 * remaining chain. In this specific case, the chain ends, so there | |
501 * is nothing left to do. | |
502 * @param {object} promise Promise being tracked. | |
503 * @return {object} A promise callback tracker. | |
504 */ | |
505 function createPromiseCallbackTracker(promise) { | |
506 /** | |
507 * Callback Tracker. Holds an array of callbacks created for this promise. | |
508 * The indirection allows quick checks against the array and clearing the | |
509 * array without ugly splicing and copying. | |
510 * @typedef {{ | |
511 * callback: array.<Function> | |
512 * }} | |
513 */ | |
514 var CallbackTracker; | |
515 | |
516 /** @type {CallbackTracker} */ | |
517 var thenTracker = {callbacks: []}; | |
518 /** @type {CallbackTracker} */ | |
519 var catchTracker = {callbacks: []}; | |
520 | |
521 /** | |
522 * Returns true if the specified value is callable. | |
523 * @param {*} value Value to check. | |
524 * @return {boolean} True if the value is a callable. | |
525 */ | |
526 function isCallable(value) { | |
527 return typeof value === 'function'; | |
528 } | |
529 | |
530 /** | |
531 * Takes a tracker and clears its callbacks in a manner consistent with | |
532 * the task manager. | |
533 * @param {CallbackTracker} tracker Tracker to clear. | |
534 */ | |
535 function clearTracker(tracker) { | |
536 if (tracker.callbacks.length > 0) { | |
537 var callbacksToClear = tracker.callbacks; | |
538 tracker.callbacks = []; | |
vadimt
2014/02/25 23:20:22
It's worth adding a comment that this is done to a
robliao
2014/02/25 23:39:54
Done.
| |
539 // Do not wrap the setTimeout callback! | |
540 // It will call wrapped callbacks. | |
541 setTimeout(function() { | |
542 for (var i = 0; i < callbacksToClear.length; i++) { | |
543 callbacksToClear[i](); | |
vadimt
2014/02/25 22:36:49
It's not clear why we need to invoke all callbacks
robliao
2014/02/25 22:43:14
The comment says it clears the tracker in a manner
vadimt
2014/02/25 23:20:22
Cool, could you add that in the comment?
robliao
2014/02/25 23:39:54
Added to the banner comment above.
| |
544 } | |
545 }, 0); | |
546 } | |
547 } | |
548 | |
549 /** | |
550 * Takes the argument to a 'then' or 'catch' function and applies | |
551 * a wrapping to callables consistent to ECMA promises. | |
552 * @param {*} maybeCallback Argument to 'then' or 'catch'. | |
553 * @param {CallbackTracker} sameTracker Tracker for the call type. | |
554 * Example: If the argument is from a 'then' call, use thenTracker. | |
555 * @param {CallbackTracker} otherTracker Tracker for the opposing call type. | |
556 * Example: If the argument is from a 'then' call, use catchTracker. | |
557 * @return {*} Consumable argument with necessary wrapping applied. | |
558 */ | |
559 function registerAndWrapMaybeCallback( | |
560 maybeCallback, sameTracker, otherTracker) { | |
vadimt
2014/02/25 22:36:49
maybeCallback can be either function of undefined,
robliao
2014/02/25 22:43:14
maybeCallback is *, as documented above. It can be
| |
561 if (isCallable(maybeCallback)) { | |
562 var handler = wrapper.wrapCallback(function() { | |
vadimt
2014/02/25 22:36:49
Are you calling wrapCallback for each then and cat
robliao
2014/02/25 22:43:14
This is why clearTracker calls all callbacks it th
robliao
2014/02/25 22:51:13
Also, calling wrapCallback once would also miscoun
| |
563 if (sameTracker.callbacks.length > 0) { | |
vadimt
2014/02/25 23:20:22
If you place this check outside of the wrapped par
robliao
2014/02/25 23:39:54
This check must be done in the wrapped part. It is
| |
564 clearTracker(otherTracker); | |
565 maybeCallback.apply(null, arguments); | |
566 } | |
567 }, false); | |
568 sameTracker.callbacks.push(handler); | |
569 return handler; | |
570 } else { | |
571 return maybeCallback; | |
572 } | |
573 } | |
574 | |
575 /** | |
576 * Tracks then calls equivalent to Promise.prototype.then. | |
577 * @param {*} onResolved Argument to use if the promise is resolved. | |
578 * @param {*} onRejected Argument to use if the promise is rejected. | |
579 * @return {object} Promise resulting from the 'then' call. | |
580 */ | |
581 function handleThen(onResolved, onRejected) { | |
vadimt
2014/02/25 23:20:22
'handle' is not concrete enough. Add? Track? Regis
robliao
2014/02/25 23:39:54
We have precedence for this in Chromium (handleFoc
| |
582 var resolutionHandler = | |
583 registerAndWrapMaybeCallback(onResolved, thenTracker, catchTracker); | |
584 var rejectionHandler = | |
585 registerAndWrapMaybeCallback(onRejected, catchTracker, thenTracker); | |
586 return originalThen.call(promise, resolutionHandler, rejectionHandler); | |
587 } | |
588 | |
589 /** | |
590 * Tracks then calls equivalent to Promise.prototype.catch. | |
591 * @param {*} onRejected Argument to use if the promise is rejected. | |
592 * @return {object} Promise resulting from the 'catch' call. | |
593 */ | |
594 function handleCatch(onRejected) { | |
595 var rejectionHandler = | |
596 registerAndWrapMaybeCallback(onRejected, catchTracker, thenTracker); | |
597 return originalCatch.call(promise, rejectionHandler); | |
598 } | |
599 | |
600 // Seeds this promise with at least one 'then' and 'catch' so that we always | |
601 // know which set of callbacks will not occur. | |
602 handleThen(function() {}); | |
603 handleCatch(function() {}); | |
604 | |
605 return { | |
606 handleThen: handleThen, | |
607 handleCatch: handleCatch | |
608 }; | |
609 } | |
610 } | |
611 | |
612 registerPromiseAdapter(); | |
460 | 613 |
461 /** | 614 /** |
462 * Builds the object to manage tasks (mutually exclusive chains of events). | 615 * Builds the object to manage tasks (mutually exclusive chains of events). |
463 * @param {function(string, string): boolean} areConflicting Function that | 616 * @param {function(string, string): boolean} areConflicting Function that |
464 * checks if a new task can't be added to a task queue that contains an | 617 * checks if a new task can't be added to a task queue that contains an |
465 * existing task. | 618 * existing task. |
466 * @return {Object} Task manager interface. | 619 * @return {Object} Task manager interface. |
467 */ | 620 */ |
468 function buildTaskManager(areConflicting) { | 621 function buildTaskManager(areConflicting) { |
469 /** | 622 /** |
(...skipping 353 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
823 // One hour is just an arbitrary amount of time chosen. | 976 // One hour is just an arbitrary amount of time chosen. |
824 chrome.alarms.create(alarmName, {periodInMinutes: 60}); | 977 chrome.alarms.create(alarmName, {periodInMinutes: 60}); |
825 | 978 |
826 return { | 979 return { |
827 addListener: addListener, | 980 addListener: addListener, |
828 getAuthToken: getAuthToken, | 981 getAuthToken: getAuthToken, |
829 isSignedIn: isSignedIn, | 982 isSignedIn: isSignedIn, |
830 removeToken: removeToken | 983 removeToken: removeToken |
831 }; | 984 }; |
832 } | 985 } |
OLD | NEW |