| Index: chrome/browser/resources/google_now/utility.js
|
| diff --git a/chrome/browser/resources/google_now/utility.js b/chrome/browser/resources/google_now/utility.js
|
| index f2236209fc498ffa15c0b2cc10c89d254e5a8c84..e246fbd1ba4b45ed86343f047d8b3d5e6c68c9de 100644
|
| --- a/chrome/browser/resources/google_now/utility.js
|
| +++ b/chrome/browser/resources/google_now/utility.js
|
| @@ -437,26 +437,186 @@ wrapper.instrumentChromeApiFunction('identity.removeCachedAuthToken', 1);
|
| wrapper.instrumentChromeApiFunction('webstorePrivate.getBrowserLogin', 0);
|
|
|
| /**
|
| - * Add task tracking support to Promise.then.
|
| - * @override
|
| + * Promise adapter for all JS promises to the task manager.
|
| */
|
| -Promise.prototype.then = function() {
|
| +function registerPromiseAdapter() {
|
| var originalThen = Promise.prototype.then;
|
| - return function(callback) {
|
| - return originalThen.call(this, wrapper.wrapCallback(callback, false));
|
| + var originalCatch = Promise.prototype.catch;
|
| +
|
| + /**
|
| + * Takes a promise and adds the callback tracker to it.
|
| + * @param {object} promise Promise that receives the callback tracker.
|
| + */
|
| + function instrumentPromise(promise) {
|
| + if (promise.__tracker === undefined) {
|
| + promise.__tracker = createPromiseCallbackTracker(promise);
|
| + }
|
| }
|
| -}();
|
|
|
| -/**
|
| - * Add task tracking support to Promise.catch.
|
| - * @override
|
| - */
|
| -Promise.prototype.catch = function() {
|
| - var originalCatch = Promise.prototype.catch;
|
| - return function(callback) {
|
| - return originalCatch.call(this, wrapper.wrapCallback(callback, false));
|
| + Promise.prototype.then = function(onResolved, onRejected) {
|
| + instrumentPromise(this);
|
| + return this.__tracker.handleThen(onResolved, onRejected);
|
| }
|
| -}();
|
| +
|
| + Promise.prototype.catch = function(onRejected) {
|
| + instrumentPromise(this);
|
| + return this.__tracker.handleCatch(onRejected);
|
| + }
|
| +
|
| + /**
|
| + * Promise Callback Tracker.
|
| + * Handles coordination of 'then' and 'catch' callbacks in a task
|
| + * manager compatible way. For an individual promise, either the 'then'
|
| + * arguments or the 'catch' arguments will be processed, never both.
|
| + *
|
| + * Example:
|
| + * var p = new Promise([Function]);
|
| + * p.then([ThenA]);
|
| + * p.then([ThenB]);
|
| + * p.catch([CatchA]);
|
| + * On resolution, [ThenA] and [ThenB] will be used. [CatchA] is discarded.
|
| + * On rejection, vice versa.
|
| + *
|
| + * Clarification:
|
| + * Chained promises create a new promise that is tracked separately from
|
| + * the originaing promise, as the example below demonstrates:
|
| + *
|
| + * var p = new Promise([Function]));
|
| + * p.then([ThenA]).then([ThenB]).catch([CatchA]);
|
| + * ^ ^ ^
|
| + * | | + Returns a new promise.
|
| + * | + Returns a new promise.
|
| + * + Returns a new promise.
|
| + *
|
| + * Four promises exist in the above statement, each with its own
|
| + * resolution and rejection state. However, by default, this state is
|
| + * chained to the previous promise's resolution or rejection
|
| + * state.
|
| + *
|
| + * If p resolves, then the 'then' calls will execute until all the 'then'
|
| + * clauses are executed. If the result of either [ThenA] or [ThenB] is a
|
| + * promise, then that execution state will guide the remaining chain.
|
| + * Similarly, if [CatchA] returns a promise, it can also guide the
|
| + * remaining chain. In this specific case, the chain ends, so there
|
| + * is nothing left to do.
|
| + * @param {object} promise Promise being tracked.
|
| + * @return {object} A promise callback tracker.
|
| + */
|
| + function createPromiseCallbackTracker(promise) {
|
| + /**
|
| + * Callback Tracker. Holds an array of callbacks created for this promise.
|
| + * The indirection allows quick checks against the array and clearing the
|
| + * array without ugly splicing and copying.
|
| + * @typedef {{
|
| + * callback: array.<Function>=
|
| + * }}
|
| + */
|
| + var CallbackTracker;
|
| +
|
| + /** @type {CallbackTracker} */
|
| + var thenTracker = {callbacks: []};
|
| + /** @type {CallbackTracker} */
|
| + var catchTracker = {callbacks: []};
|
| +
|
| + /**
|
| + * Returns true if the specified value is callable.
|
| + * @param {*} value Value to check.
|
| + * @return {boolean} True if the value is a callable.
|
| + */
|
| + function isCallable(value) {
|
| + return typeof value === 'function';
|
| + }
|
| +
|
| + /**
|
| + * Takes a tracker and clears its callbacks in a manner consistent with
|
| + * the task manager. For the task manager, it also calls all callbacks
|
| + * by no-oping them first and then calling them.
|
| + * @param {CallbackTracker} tracker Tracker to clear.
|
| + */
|
| + function clearTracker(tracker) {
|
| + if (tracker.callbacks) {
|
| + var callbacksToClear = tracker.callbacks;
|
| + // No-ops all callbacks of this type.
|
| + tracker.callbacks = undefined;
|
| + // Do not wrap the promise then argument!
|
| + // It will call wrapped callbacks.
|
| + originalThen.call(Promise.resolve(), function() {
|
| + for (var i = 0; i < callbacksToClear.length; i++) {
|
| + callbacksToClear[i]();
|
| + }
|
| + });
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Takes the argument to a 'then' or 'catch' function and applies
|
| + * a wrapping to callables consistent to ECMA promises.
|
| + * @param {*} maybeCallback Argument to 'then' or 'catch'.
|
| + * @param {CallbackTracker} sameTracker Tracker for the call type.
|
| + * Example: If the argument is from a 'then' call, use thenTracker.
|
| + * @param {CallbackTracker} otherTracker Tracker for the opposing call type.
|
| + * Example: If the argument is from a 'then' call, use catchTracker.
|
| + * @return {*} Consumable argument with necessary wrapping applied.
|
| + */
|
| + function registerAndWrapMaybeCallback(
|
| + maybeCallback, sameTracker, otherTracker) {
|
| + // If sameTracker.callbacks is undefined, we've reached an ending state
|
| + // that means this callback will never be called back.
|
| + // We will still forward this call on to let the promise system
|
| + // handle further processing, but since this promise is in an ending state
|
| + // we can be confident it will never be called back.
|
| + if (isCallable(maybeCallback) && sameTracker.callbacks) {
|
| + var handler = wrapper.wrapCallback(function() {
|
| + if (sameTracker.callbacks) {
|
| + clearTracker(otherTracker);
|
| + maybeCallback.apply(null, arguments);
|
| + }
|
| + }, false);
|
| + sameTracker.callbacks.push(handler);
|
| + return handler;
|
| + } else {
|
| + return maybeCallback;
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Tracks then calls equivalent to Promise.prototype.then.
|
| + * @param {*} onResolved Argument to use if the promise is resolved.
|
| + * @param {*} onRejected Argument to use if the promise is rejected.
|
| + * @return {object} Promise resulting from the 'then' call.
|
| + */
|
| + function handleThen(onResolved, onRejected) {
|
| + var resolutionHandler =
|
| + registerAndWrapMaybeCallback(onResolved, thenTracker, catchTracker);
|
| + var rejectionHandler =
|
| + registerAndWrapMaybeCallback(onRejected, catchTracker, thenTracker);
|
| + return originalThen.call(promise, resolutionHandler, rejectionHandler);
|
| + }
|
| +
|
| + /**
|
| + * Tracks then calls equivalent to Promise.prototype.catch.
|
| + * @param {*} onRejected Argument to use if the promise is rejected.
|
| + * @return {object} Promise resulting from the 'catch' call.
|
| + */
|
| + function handleCatch(onRejected) {
|
| + var rejectionHandler =
|
| + registerAndWrapMaybeCallback(onRejected, catchTracker, thenTracker);
|
| + return originalCatch.call(promise, rejectionHandler);
|
| + }
|
| +
|
| + // Seeds this promise with at least one 'then' and 'catch' so we always
|
| + // receive a callback to update the task manager on the state of callbacks.
|
| + handleThen(function() {});
|
| + handleCatch(function() {});
|
| +
|
| + return {
|
| + handleThen: handleThen,
|
| + handleCatch: handleCatch
|
| + };
|
| + }
|
| +}
|
| +
|
| +registerPromiseAdapter();
|
|
|
| /**
|
| * Builds the object to manage tasks (mutually exclusive chains of events).
|
|
|