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. | |
rgustafson
2014/02/26 18:31:04
arguments
robliao
2014/02/26 18:46:17
Done.
| |
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 | |
rgustafson
2014/02/26 18:31:04
only one previous
robliao
2014/02/26 18:46:17
Done.
| |
494 * state. | |
495 * | |
496 * If p resolves, then the then calls will execute until all the 'then' | |
rgustafson
2014/02/26 18:31:04
use "'then' calls" to prevent confusion here
robliao
2014/02/26 18:46:17
Done.
| |
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. For the task manager, it also calls all callbacks | |
533 * by no-oping then first and then calling them. | |
rgustafson
2014/02/26 18:31:04
no-oping them
robliao
2014/02/26 18:46:17
Done.
| |
534 * @param {CallbackTracker} tracker Tracker to clear. | |
535 */ | |
536 function clearTracker(tracker) { | |
537 if (tracker.callbacks) { | |
538 var callbacksToClear = tracker.callbacks; | |
539 // No-ops all callbacks of this type. | |
540 tracker.callbacks = undefined; | |
541 // Do not wrap the promise then argument! | |
542 // It will call wrapped callbacks. | |
543 originalThen.call(Promise.resolve(), function() { | |
vadimt
2014/02/26 01:03:58
WOW!
| |
544 for (var i = 0; i < callbacksToClear.length; i++) { | |
545 callbacksToClear[i](); | |
546 } | |
547 }); | |
548 } | |
549 } | |
550 | |
551 /** | |
552 * Takes the argument to a 'then' or 'catch' function and applies | |
553 * a wrapping to callables consistent to ECMA promises. | |
554 * @param {*} maybeCallback Argument to 'then' or 'catch'. | |
555 * @param {CallbackTracker} sameTracker Tracker for the call type. | |
556 * Example: If the argument is from a 'then' call, use thenTracker. | |
557 * @param {CallbackTracker} otherTracker Tracker for the opposing call type. | |
558 * Example: If the argument is from a 'then' call, use catchTracker. | |
559 * @return {*} Consumable argument with necessary wrapping applied. | |
560 */ | |
561 function registerAndWrapMaybeCallback( | |
562 maybeCallback, sameTracker, otherTracker) { | |
563 // If sameTracker.callbacks is undefined, we've reached an ending state | |
564 // that means this callback will never be called back. | |
565 // We will still forward this call on to let the promise system | |
566 // handle further processing, but since this promise is in an ending state | |
567 // we can be confident it will never be called back. | |
568 if (isCallable(maybeCallback) && sameTracker.callbacks) { | |
569 var handler = wrapper.wrapCallback(function() { | |
570 if (sameTracker.callbacks) { | |
rgustafson
2014/02/26 18:31:04
This check is already done.
robliao
2014/02/26 18:46:17
This check needs to be checked again since it's in
| |
571 clearTracker(otherTracker); | |
572 maybeCallback.apply(null, arguments); | |
573 } | |
574 }, false); | |
575 sameTracker.callbacks.push(handler); | |
576 return handler; | |
577 } else { | |
578 return maybeCallback; | |
579 } | |
580 } | |
581 | |
582 /** | |
583 * Tracks then calls equivalent to Promise.prototype.then. | |
584 * @param {*} onResolved Argument to use if the promise is resolved. | |
585 * @param {*} onRejected Argument to use if the promise is rejected. | |
586 * @return {object} Promise resulting from the 'then' call. | |
587 */ | |
588 function handleThen(onResolved, onRejected) { | |
589 var resolutionHandler = | |
590 registerAndWrapMaybeCallback(onResolved, thenTracker, catchTracker); | |
591 var rejectionHandler = | |
592 registerAndWrapMaybeCallback(onRejected, catchTracker, thenTracker); | |
593 return originalThen.call(promise, resolutionHandler, rejectionHandler); | |
594 } | |
595 | |
596 /** | |
597 * Tracks then calls equivalent to Promise.prototype.catch. | |
598 * @param {*} onRejected Argument to use if the promise is rejected. | |
599 * @return {object} Promise resulting from the 'catch' call. | |
600 */ | |
601 function handleCatch(onRejected) { | |
602 var rejectionHandler = | |
603 registerAndWrapMaybeCallback(onRejected, catchTracker, thenTracker); | |
604 return originalCatch.call(promise, rejectionHandler); | |
605 } | |
606 | |
607 // Seeds this promise with at least one 'then' and 'catch' so that we always | |
608 // know which set of callbacks will not occur. | |
rgustafson
2014/02/26 18:31:04
I don't understand what this is saying at all. Cha
robliao
2014/02/26 18:46:17
Done.
| |
609 handleThen(function() {}); | |
610 handleCatch(function() {}); | |
611 | |
612 return { | |
613 handleThen: handleThen, | |
614 handleCatch: handleCatch | |
615 }; | |
616 } | |
617 } | |
618 | |
619 registerPromiseAdapter(); | |
460 | 620 |
461 /** | 621 /** |
462 * Builds the object to manage tasks (mutually exclusive chains of events). | 622 * Builds the object to manage tasks (mutually exclusive chains of events). |
463 * @param {function(string, string): boolean} areConflicting Function that | 623 * @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 | 624 * checks if a new task can't be added to a task queue that contains an |
465 * existing task. | 625 * existing task. |
466 * @return {Object} Task manager interface. | 626 * @return {Object} Task manager interface. |
467 */ | 627 */ |
468 function buildTaskManager(areConflicting) { | 628 function buildTaskManager(areConflicting) { |
469 /** | 629 /** |
(...skipping 353 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
823 // One hour is just an arbitrary amount of time chosen. | 983 // One hour is just an arbitrary amount of time chosen. |
824 chrome.alarms.create(alarmName, {periodInMinutes: 60}); | 984 chrome.alarms.create(alarmName, {periodInMinutes: 60}); |
825 | 985 |
826 return { | 986 return { |
827 addListener: addListener, | 987 addListener: addListener, |
828 getAuthToken: getAuthToken, | 988 getAuthToken: getAuthToken, |
829 isSignedIn: isSignedIn, | 989 isSignedIn: isSignedIn, |
830 removeToken: removeToken | 990 removeToken: removeToken |
831 }; | 991 }; |
832 } | 992 } |
OLD | NEW |