Chromium Code Reviews| 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 |