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 305 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
316 verify(!isInWrappedCallback, 'Re-entering instrumented callback'); | 316 verify(!isInWrappedCallback, 'Re-entering instrumented callback'); |
317 isInWrappedCallback = true; | 317 isInWrappedCallback = true; |
318 | 318 |
319 if (!opt_isEventListener) | 319 if (!opt_isEventListener) |
320 delete pendingCallbacks[callbackId]; | 320 delete pendingCallbacks[callbackId]; |
321 | 321 |
322 if (wrapperPluginInstance) | 322 if (wrapperPluginInstance) |
323 wrapperPluginInstance.prologue(); | 323 wrapperPluginInstance.prologue(); |
324 | 324 |
325 // Call the original callback. | 325 // Call the original callback. |
326 callback.apply(null, arguments); | 326 var returnValue = callback.apply(null, arguments); |
327 | 327 |
328 if (wrapperPluginInstance) | 328 if (wrapperPluginInstance) |
329 wrapperPluginInstance.epilogue(); | 329 wrapperPluginInstance.epilogue(); |
330 | 330 |
331 verify(isInWrappedCallback, | 331 verify(isInWrappedCallback, |
332 'Instrumented callback is not instrumented upon exit'); | 332 'Instrumented callback is not instrumented upon exit'); |
333 isInWrappedCallback = false; | 333 isInWrappedCallback = false; |
334 | |
335 return returnValue; | |
334 } catch (error) { | 336 } catch (error) { |
335 reportError(error); | 337 reportError(error); |
336 } | 338 } |
337 }; | 339 }; |
338 } | 340 } |
339 | 341 |
340 /** | 342 /** |
341 * Returns an instrumented function. | 343 * Returns an instrumented function. |
342 * @param {!Array.<string>} functionIdentifierParts Path to the chrome.* | 344 * @param {!Array.<string>} functionIdentifierParts Path to the chrome.* |
343 * function. | 345 * function. |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
427 checkInWrappedCallback: checkInWrappedCallback, | 429 checkInWrappedCallback: checkInWrappedCallback, |
428 debugGetStateString: debugGetStateString | 430 debugGetStateString: debugGetStateString |
429 }; | 431 }; |
430 })(); | 432 })(); |
431 | 433 |
432 wrapper.instrumentChromeApiFunction('alarms.get', 1); | 434 wrapper.instrumentChromeApiFunction('alarms.get', 1); |
433 wrapper.instrumentChromeApiFunction('alarms.onAlarm.addListener', 0); | 435 wrapper.instrumentChromeApiFunction('alarms.onAlarm.addListener', 0); |
434 wrapper.instrumentChromeApiFunction('identity.getAuthToken', 1); | 436 wrapper.instrumentChromeApiFunction('identity.getAuthToken', 1); |
435 wrapper.instrumentChromeApiFunction('identity.onSignInChanged.addListener', 0); | 437 wrapper.instrumentChromeApiFunction('identity.onSignInChanged.addListener', 0); |
436 wrapper.instrumentChromeApiFunction('identity.removeCachedAuthToken', 1); | 438 wrapper.instrumentChromeApiFunction('identity.removeCachedAuthToken', 1); |
439 wrapper.instrumentChromeApiFunction('storage.local.get', 1); | |
437 wrapper.instrumentChromeApiFunction('webstorePrivate.getBrowserLogin', 0); | 440 wrapper.instrumentChromeApiFunction('webstorePrivate.getBrowserLogin', 0); |
438 | 441 |
439 /** | 442 /** |
440 * Promise adapter for all JS promises to the task manager. | 443 * Promise adapter for all JS promises to the task manager. |
441 */ | 444 */ |
442 function registerPromiseAdapter() { | 445 function registerPromiseAdapter() { |
443 var originalThen = Promise.prototype.then; | 446 var originalThen = Promise.prototype.then; |
444 var originalCatch = Promise.prototype.catch; | 447 var originalCatch = Promise.prototype.catch; |
445 | 448 |
446 /** | 449 /** |
(...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
562 maybeCallback, sameTracker, otherTracker) { | 565 maybeCallback, sameTracker, otherTracker) { |
563 // If sameTracker.callbacks is undefined, we've reached an ending state | 566 // If sameTracker.callbacks is undefined, we've reached an ending state |
564 // that means this callback will never be called back. | 567 // that means this callback will never be called back. |
565 // We will still forward this call on to let the promise system | 568 // 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 | 569 // handle further processing, but since this promise is in an ending state |
567 // we can be confident it will never be called back. | 570 // we can be confident it will never be called back. |
568 if (isCallable(maybeCallback) && sameTracker.callbacks) { | 571 if (isCallable(maybeCallback) && sameTracker.callbacks) { |
569 var handler = wrapper.wrapCallback(function() { | 572 var handler = wrapper.wrapCallback(function() { |
570 if (sameTracker.callbacks) { | 573 if (sameTracker.callbacks) { |
571 clearTracker(otherTracker); | 574 clearTracker(otherTracker); |
572 maybeCallback.apply(null, arguments); | 575 return maybeCallback.apply(null, arguments); |
573 } | 576 } |
574 }, false); | 577 }, false); |
575 sameTracker.callbacks.push(handler); | 578 sameTracker.callbacks.push(handler); |
576 return handler; | 579 return handler; |
577 } else { | 580 } else { |
578 return maybeCallback; | 581 return maybeCallback; |
579 } | 582 } |
580 } | 583 } |
581 | 584 |
582 /** | 585 /** |
(...skipping 29 matching lines...) Expand all Loading... | |
612 return { | 615 return { |
613 handleThen: handleThen, | 616 handleThen: handleThen, |
614 handleCatch: handleCatch | 617 handleCatch: handleCatch |
615 }; | 618 }; |
616 } | 619 } |
617 } | 620 } |
618 | 621 |
619 registerPromiseAdapter(); | 622 registerPromiseAdapter(); |
620 | 623 |
621 /** | 624 /** |
625 * Control promise rejection. | |
626 * @enum {boolean} | |
627 */ | |
628 var PromiseRejection = { | |
629 /** Disallow promise rejection */ | |
630 DISALLOW: false, | |
631 /** Allow promise rejection */ | |
632 ALLOW: true | |
633 }; | |
634 | |
635 /** | |
636 * Provides the promise equivalent of instrumented.storage.local.get. | |
637 * @param {Object} defaultStorageObject Default storage object to fill. | |
638 * @param {boolean=} opt_allowPromiseRejection If PromiseRejection.ALLOW, | |
vadimt
2014/02/28 20:21:05
Should be {PromiseRejection=}
robliao
2014/02/28 20:39:49
Done.
| |
639 * allow promise rejection on errors, otherwise the default storage | |
640 * object is resolved. | |
641 * @return {Promise} A promise that fills the default storage object. On | |
642 * failure, if promise rejection is allowed, the promise is rejected, | |
643 * otherwise it is resolved to the default storage object. | |
644 */ | |
645 function fillFromChromeLocalStorage( | |
646 defaultStorageObject, | |
647 opt_allowPromiseRejection) { | |
648 return new Promise(function(resolve, reject) { | |
649 instrumented.storage.local.get(defaultStorageObject, function(items) { | |
650 if (items) { | |
651 resolve(items); | |
652 } else if (opt_allowPromiseRejection === PromiseRejection.ALLOW) { | |
653 reject(); | |
654 } else { | |
655 resolve(defaultStorageObject); | |
656 } | |
657 }); | |
658 }); | |
659 } | |
660 | |
661 /** | |
622 * Builds the object to manage tasks (mutually exclusive chains of events). | 662 * Builds the object to manage tasks (mutually exclusive chains of events). |
623 * @param {function(string, string): boolean} areConflicting Function that | 663 * @param {function(string, string): boolean} areConflicting Function that |
624 * checks if a new task can't be added to a task queue that contains an | 664 * checks if a new task can't be added to a task queue that contains an |
625 * existing task. | 665 * existing task. |
626 * @return {Object} Task manager interface. | 666 * @return {Object} Task manager interface. |
627 */ | 667 */ |
628 function buildTaskManager(areConflicting) { | 668 function buildTaskManager(areConflicting) { |
629 /** | 669 /** |
630 * Queue of scheduled tasks. The first element, if present, corresponds to the | 670 * Queue of scheduled tasks. The first element, if present, corresponds to the |
631 * currently running task. | 671 * currently running task. |
(...skipping 220 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
852 chrome.alarms.clear(alarmName); | 892 chrome.alarms.clear(alarmName); |
853 chrome.storage.local.remove(currentDelayStorageKey); | 893 chrome.storage.local.remove(currentDelayStorageKey); |
854 } | 894 } |
855 | 895 |
856 /** | 896 /** |
857 * Plans for the next attempt. | 897 * Plans for the next attempt. |
858 * @param {function()} callback Completion callback. It will be invoked after | 898 * @param {function()} callback Completion callback. It will be invoked after |
859 * the planning is done. | 899 * the planning is done. |
860 */ | 900 */ |
861 function planForNext(callback) { | 901 function planForNext(callback) { |
862 instrumented.storage.local.get(currentDelayStorageKey, function(items) { | 902 var request = {}; |
863 if (!items) { | 903 request[currentDelayStorageKey] = undefined; |
864 items = {}; | 904 fillFromChromeLocalStorage(request, PromiseRejection.ALLOW) |
865 items[currentDelayStorageKey] = maximumDelaySeconds; | 905 .catch(function() { |
866 } | 906 request[currentDelayStorageKey] = maximumDelaySeconds; |
867 console.log('planForNext-get-storage ' + JSON.stringify(items)); | 907 return Promise.resolve(request); |
868 scheduleNextAttempt(items[currentDelayStorageKey]); | 908 }).then(function(items) { |
869 callback(); | 909 console.log('planForNext-get-storage ' + JSON.stringify(items)); |
870 }); | 910 scheduleNextAttempt(items[currentDelayStorageKey]); |
911 callback(); | |
912 }); | |
871 } | 913 } |
872 | 914 |
873 instrumented.alarms.onAlarm.addListener(function(alarm) { | 915 instrumented.alarms.onAlarm.addListener(function(alarm) { |
874 if (alarm.name == alarmName) | 916 if (alarm.name == alarmName) |
875 isRunning(function(running) { | 917 isRunning(function(running) { |
876 if (running) | 918 if (running) |
877 attempt(); | 919 attempt(); |
878 }); | 920 }); |
879 }); | 921 }); |
880 | 922 |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
950 function addListener(callback) { | 992 function addListener(callback) { |
951 listeners.push(callback); | 993 listeners.push(callback); |
952 } | 994 } |
953 | 995 |
954 /** | 996 /** |
955 * Checks if the last signed in state matches the current one. | 997 * Checks if the last signed in state matches the current one. |
956 * If it doesn't, it notifies the listeners of the change. | 998 * If it doesn't, it notifies the listeners of the change. |
957 */ | 999 */ |
958 function checkAndNotifyListeners() { | 1000 function checkAndNotifyListeners() { |
959 isSignedIn().then(function(signedIn) { | 1001 isSignedIn().then(function(signedIn) { |
960 instrumented.storage.local.get('lastSignedInState', function(items) { | 1002 fillFromChromeLocalStorage({lastSignedInState: undefined}) |
961 items = items || {}; | 1003 .then(function(items) { |
962 if (items.lastSignedInState != signedIn) { | 1004 if (items.lastSignedInState != signedIn) { |
963 chrome.storage.local.set( | 1005 chrome.storage.local.set( |
964 {lastSignedInState: signedIn}); | 1006 {lastSignedInState: signedIn}); |
965 listeners.forEach(function(callback) { | 1007 listeners.forEach(function(callback) { |
966 callback(); | 1008 callback(); |
967 }); | 1009 }); |
968 } | 1010 } |
1011 }); | |
969 }); | 1012 }); |
970 }); | |
971 } | 1013 } |
972 | 1014 |
973 instrumented.identity.onSignInChanged.addListener(function() { | 1015 instrumented.identity.onSignInChanged.addListener(function() { |
974 checkAndNotifyListeners(); | 1016 checkAndNotifyListeners(); |
975 }); | 1017 }); |
976 | 1018 |
977 instrumented.alarms.onAlarm.addListener(function(alarm) { | 1019 instrumented.alarms.onAlarm.addListener(function(alarm) { |
978 if (alarm.name == alarmName) | 1020 if (alarm.name == alarmName) |
979 checkAndNotifyListeners(); | 1021 checkAndNotifyListeners(); |
980 }); | 1022 }); |
981 | 1023 |
982 // Poll for the sign in state every hour. | 1024 // Poll for the sign in state every hour. |
983 // One hour is just an arbitrary amount of time chosen. | 1025 // One hour is just an arbitrary amount of time chosen. |
984 chrome.alarms.create(alarmName, {periodInMinutes: 60}); | 1026 chrome.alarms.create(alarmName, {periodInMinutes: 60}); |
985 | 1027 |
986 return { | 1028 return { |
987 addListener: addListener, | 1029 addListener: addListener, |
988 getAuthToken: getAuthToken, | 1030 getAuthToken: getAuthToken, |
989 isSignedIn: isSignedIn, | 1031 isSignedIn: isSignedIn, |
990 removeToken: removeToken | 1032 removeToken: removeToken |
991 }; | 1033 }; |
992 } | 1034 } |
OLD | NEW |