| 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 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 116 return request; | 116 return request; |
| 117 } | 117 } |
| 118 | 118 |
| 119 /** | 119 /** |
| 120 * Sends an error report to the server. | 120 * Sends an error report to the server. |
| 121 * @param {Error} error Error to send. | 121 * @param {Error} error Error to send. |
| 122 */ | 122 */ |
| 123 function sendErrorReport(error) { | 123 function sendErrorReport(error) { |
| 124 // Don't remove 'error.stack.replace' below! | 124 // Don't remove 'error.stack.replace' below! |
| 125 var filteredStack = error.canSendMessageToServer ? | 125 var filteredStack = error.canSendMessageToServer ? |
| 126 error.stack : error.stack.replace(/.*\n/, '(message removed)\n'); | 126 error.stack : |
| 127 error.stack.replace(/.*\n/, '(message removed)\n'); |
| 127 var file; | 128 var file; |
| 128 var line; | 129 var line; |
| 129 var topFrameLineMatch = filteredStack.match(/\n at .*\n/); | 130 var topFrameLineMatch = filteredStack.match(/\n at .*\n/); |
| 130 var topFrame = topFrameLineMatch && topFrameLineMatch[0]; | 131 var topFrame = topFrameLineMatch && topFrameLineMatch[0]; |
| 131 if (topFrame) { | 132 if (topFrame) { |
| 132 // Examples of a frame: | 133 // Examples of a frame: |
| 133 // 1. '\n at someFunction (chrome-extension:// | 134 // 1. '\n at someFunction (chrome-extension:// |
| 134 // pafkbggdmjlpgkdkcbjmhmfcdpncadgh/background.js:915:15)\n' | 135 // pafkbggdmjlpgkdkcbjmhmfcdpncadgh/background.js:915:15)\n' |
| 135 // 2. '\n at chrome-extension://pafkbggdmjlpgkdkcbjmhmfcdpncadgh/ | 136 // 2. '\n at chrome-extension://pafkbggdmjlpgkdkcbjmhmfcdpncadgh/ |
| 136 // utility.js:269:18\n' | 137 // utility.js:269:18\n' |
| (...skipping 18 matching lines...) Expand all Loading... |
| 155 if (topFrameElements.length >= 3) { | 156 if (topFrameElements.length >= 3) { |
| 156 file = topFrameElements[topFrameElements.length - 3]; | 157 file = topFrameElements[topFrameElements.length - 3]; |
| 157 line = topFrameElements[topFrameElements.length - 2]; | 158 line = topFrameElements[topFrameElements.length - 2]; |
| 158 } | 159 } |
| 159 } | 160 } |
| 160 | 161 |
| 161 var errorText = error.name; | 162 var errorText = error.name; |
| 162 if (error.canSendMessageToServer) | 163 if (error.canSendMessageToServer) |
| 163 errorText = errorText + ': ' + error.message; | 164 errorText = errorText + ': ' + error.message; |
| 164 | 165 |
| 165 var errorObject = { | 166 var errorObject = |
| 166 message: errorText, | 167 {message: errorText, file: file, line: line, trace: filteredStack}; |
| 167 file: file, | |
| 168 line: line, | |
| 169 trace: filteredStack | |
| 170 }; | |
| 171 | 168 |
| 172 // We use relatively direct calls here because the instrumentation may be in | 169 // We use relatively direct calls here because the instrumentation may be in |
| 173 // a bad state. Wrappers and promises should not be involved in the reporting. | 170 // a bad state. Wrappers and promises should not be involved in the reporting. |
| 174 var request = buildServerRequest('POST', 'jserrors', 'application/json'); | 171 var request = buildServerRequest('POST', 'jserrors', 'application/json'); |
| 175 request.onloadend = function(event) { | 172 request.onloadend = function(event) { |
| 176 console.log('sendErrorReport status: ' + request.status); | 173 console.log('sendErrorReport status: ' + request.status); |
| 177 }; | 174 }; |
| 178 | 175 |
| 179 chrome.identity.getAuthToken({interactive: false}, function(token) { | 176 chrome.identity.getAuthToken({interactive: false}, function(token) { |
| 180 if (token) { | 177 if (token) { |
| (...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 283 function debugGetStateString() { | 280 function debugGetStateString() { |
| 284 return 'pendingCallbacks @' + Date.now() + ' = ' + | 281 return 'pendingCallbacks @' + Date.now() + ' = ' + |
| 285 JSON.stringify(pendingCallbacks); | 282 JSON.stringify(pendingCallbacks); |
| 286 } | 283 } |
| 287 | 284 |
| 288 /** | 285 /** |
| 289 * Checks that we run in a wrapped callback. | 286 * Checks that we run in a wrapped callback. |
| 290 */ | 287 */ |
| 291 function checkInWrappedCallback() { | 288 function checkInWrappedCallback() { |
| 292 if (!isInWrappedCallback) { | 289 if (!isInWrappedCallback) { |
| 293 reportError(buildErrorWithMessageForServer( | 290 reportError( |
| 294 'Not in instrumented callback')); | 291 buildErrorWithMessageForServer('Not in instrumented callback')); |
| 295 } | 292 } |
| 296 } | 293 } |
| 297 | 294 |
| 298 /** | 295 /** |
| 299 * Adds error processing to an API callback. | 296 * Adds error processing to an API callback. |
| 300 * @param {Function} callback Callback to instrument. | 297 * @param {Function} callback Callback to instrument. |
| 301 * @param {boolean=} opt_isEventListener True if the callback is a listener to | 298 * @param {boolean=} opt_isEventListener True if the callback is a listener to |
| 302 * a Chrome API event. | 299 * a Chrome API event. |
| 303 * @return {Function} Instrumented callback. | 300 * @return {Function} Instrumented callback. |
| 304 */ | 301 */ |
| (...skipping 20 matching lines...) Expand all Loading... |
| 325 | 322 |
| 326 if (wrapperPluginInstance) | 323 if (wrapperPluginInstance) |
| 327 wrapperPluginInstance.prologue(); | 324 wrapperPluginInstance.prologue(); |
| 328 | 325 |
| 329 // Call the original callback. | 326 // Call the original callback. |
| 330 var returnValue = callback.apply(null, arguments); | 327 var returnValue = callback.apply(null, arguments); |
| 331 | 328 |
| 332 if (wrapperPluginInstance) | 329 if (wrapperPluginInstance) |
| 333 wrapperPluginInstance.epilogue(); | 330 wrapperPluginInstance.epilogue(); |
| 334 | 331 |
| 335 verify(isInWrappedCallback, | 332 verify( |
| 336 'Instrumented callback is not instrumented upon exit'); | 333 isInWrappedCallback, |
| 334 'Instrumented callback is not instrumented upon exit'); |
| 337 isInWrappedCallback = false; | 335 isInWrappedCallback = false; |
| 338 | 336 |
| 339 return returnValue; | 337 return returnValue; |
| 340 } catch (error) { | 338 } catch (error) { |
| 341 reportError(error); | 339 reportError(error); |
| 342 } | 340 } |
| 343 }; | 341 }; |
| 344 } | 342 } |
| 345 | 343 |
| 346 /** | 344 /** |
| 347 * Returns an instrumented function. | 345 * Returns an instrumented function. |
| 348 * @param {!Array<string>} functionIdentifierParts Path to the chrome.* | 346 * @param {!Array<string>} functionIdentifierParts Path to the chrome.* |
| 349 * function. | 347 * function. |
| 350 * @param {string} functionName Name of the chrome API function. | 348 * @param {string} functionName Name of the chrome API function. |
| 351 * @param {number} callbackParameter Index of the callback parameter to this | 349 * @param {number} callbackParameter Index of the callback parameter to this |
| 352 * API function. | 350 * API function. |
| 353 * @return {Function} An instrumented function. | 351 * @return {Function} An instrumented function. |
| 354 */ | 352 */ |
| 355 function createInstrumentedFunction( | 353 function createInstrumentedFunction( |
| 356 functionIdentifierParts, | 354 functionIdentifierParts, functionName, callbackParameter) { |
| 357 functionName, | |
| 358 callbackParameter) { | |
| 359 return function() { | 355 return function() { |
| 360 // This is the wrapper for the API function. Pass the wrapped callback to | 356 // This is the wrapper for the API function. Pass the wrapped callback to |
| 361 // the original function. | 357 // the original function. |
| 362 var callback = arguments[callbackParameter]; | 358 var callback = arguments[callbackParameter]; |
| 363 if (typeof callback != 'function') { | 359 if (typeof callback != 'function') { |
| 364 reportError(buildErrorWithMessageForServer( | 360 reportError(buildErrorWithMessageForServer( |
| 365 'Argument ' + callbackParameter + ' of ' + | 361 'Argument ' + callbackParameter + ' of ' + |
| 366 functionIdentifierParts.join('.') + '.' + functionName + | 362 functionIdentifierParts.join('.') + '.' + functionName + |
| 367 ' is not a function')); | 363 ' is not a function')); |
| 368 } | 364 } |
| 369 arguments[callbackParameter] = wrapCallback( | 365 arguments[callbackParameter] = |
| 370 callback, functionName == 'addListener'); | 366 wrapCallback(callback, functionName == 'addListener'); |
| 371 | 367 |
| 372 var chromeContainer = chrome; | 368 var chromeContainer = chrome; |
| 373 functionIdentifierParts.forEach(function(fragment) { | 369 functionIdentifierParts.forEach(function(fragment) { |
| 374 chromeContainer = chromeContainer[fragment]; | 370 chromeContainer = chromeContainer[fragment]; |
| 375 }); | 371 }); |
| 376 return chromeContainer[functionName]. | 372 return chromeContainer[functionName].apply(chromeContainer, arguments); |
| 377 apply(chromeContainer, arguments); | |
| 378 }; | 373 }; |
| 379 } | 374 } |
| 380 | 375 |
| 381 /** | 376 /** |
| 382 * Instruments an API function to add error processing to its user | 377 * Instruments an API function to add error processing to its user |
| 383 * code-provided callback. | 378 * code-provided callback. |
| 384 * @param {string} functionIdentifier Full identifier of the function without | 379 * @param {string} functionIdentifier Full identifier of the function without |
| 385 * the 'chrome.' portion. | 380 * the 'chrome.' portion. |
| 386 * @param {number} callbackParameter Index of the callback parameter to this | 381 * @param {number} callbackParameter Index of the callback parameter to this |
| 387 * API function. | 382 * API function. |
| (...skipping 16 matching lines...) Expand all Loading... |
| 404 instrumentedContainer = instrumentedContainer[fragment]; | 399 instrumentedContainer = instrumentedContainer[fragment]; |
| 405 }); | 400 }); |
| 406 | 401 |
| 407 var targetFunction = chromeContainer[functionName]; | 402 var targetFunction = chromeContainer[functionName]; |
| 408 if (!targetFunction) { | 403 if (!targetFunction) { |
| 409 reportError(buildErrorWithMessageForServer( | 404 reportError(buildErrorWithMessageForServer( |
| 410 'Cannot instrument ' + functionIdentifier)); | 405 'Cannot instrument ' + functionIdentifier)); |
| 411 } | 406 } |
| 412 | 407 |
| 413 instrumentedContainer[functionName] = createInstrumentedFunction( | 408 instrumentedContainer[functionName] = createInstrumentedFunction( |
| 414 functionIdentifierParts, | 409 functionIdentifierParts, functionName, callbackParameter); |
| 415 functionName, | |
| 416 callbackParameter); | |
| 417 } | 410 } |
| 418 | 411 |
| 419 instrumentChromeApiFunction('runtime.onSuspend.addListener', 0); | 412 instrumentChromeApiFunction('runtime.onSuspend.addListener', 0); |
| 420 | 413 |
| 421 instrumented.runtime.onSuspend.addListener(function() { | 414 instrumented.runtime.onSuspend.addListener(function() { |
| 422 var stringifiedPendingCallbacks = JSON.stringify(pendingCallbacks); | 415 var stringifiedPendingCallbacks = JSON.stringify(pendingCallbacks); |
| 423 verify( | 416 verify( |
| 424 stringifiedPendingCallbacks == '{}', | 417 stringifiedPendingCallbacks == '{}', |
| 425 'Pending callbacks when unloading event page @' + Date.now() + ':' + | 418 'Pending callbacks when unloading event page @' + Date.now() + ':' + |
| 426 stringifiedPendingCallbacks); | 419 stringifiedPendingCallbacks); |
| 427 }); | 420 }); |
| 428 | 421 |
| 429 return { | 422 return { |
| 430 wrapCallback: wrapCallback, | 423 wrapCallback: wrapCallback, |
| 431 instrumentChromeApiFunction: instrumentChromeApiFunction, | 424 instrumentChromeApiFunction: instrumentChromeApiFunction, |
| 432 registerWrapperPluginFactory: registerWrapperPluginFactory, | 425 registerWrapperPluginFactory: registerWrapperPluginFactory, |
| 433 checkInWrappedCallback: checkInWrappedCallback, | 426 checkInWrappedCallback: checkInWrappedCallback, |
| 434 debugGetStateString: debugGetStateString | 427 debugGetStateString: debugGetStateString |
| 435 }; | 428 }; |
| 436 })(); | 429 })(); |
| (...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 559 * Takes the argument to a 'then' or 'catch' function and applies | 552 * Takes the argument to a 'then' or 'catch' function and applies |
| 560 * a wrapping to callables consistent to ECMA promises. | 553 * a wrapping to callables consistent to ECMA promises. |
| 561 * @param {*} maybeCallback Argument to 'then' or 'catch'. | 554 * @param {*} maybeCallback Argument to 'then' or 'catch'. |
| 562 * @param {CallbackTracker} sameTracker Tracker for the call type. | 555 * @param {CallbackTracker} sameTracker Tracker for the call type. |
| 563 * Example: If the argument is from a 'then' call, use thenTracker. | 556 * Example: If the argument is from a 'then' call, use thenTracker. |
| 564 * @param {CallbackTracker} otherTracker Tracker for the opposing call type. | 557 * @param {CallbackTracker} otherTracker Tracker for the opposing call type. |
| 565 * Example: If the argument is from a 'then' call, use catchTracker. | 558 * Example: If the argument is from a 'then' call, use catchTracker. |
| 566 * @return {*} Consumable argument with necessary wrapping applied. | 559 * @return {*} Consumable argument with necessary wrapping applied. |
| 567 */ | 560 */ |
| 568 function registerAndWrapMaybeCallback( | 561 function registerAndWrapMaybeCallback( |
| 569 maybeCallback, sameTracker, otherTracker) { | 562 maybeCallback, sameTracker, otherTracker) { |
| 570 // If sameTracker.callbacks is undefined, we've reached an ending state | 563 // If sameTracker.callbacks is undefined, we've reached an ending state |
| 571 // that means this callback will never be called back. | 564 // that means this callback will never be called back. |
| 572 // We will still forward this call on to let the promise system | 565 // We will still forward this call on to let the promise system |
| 573 // handle further processing, but since this promise is in an ending state | 566 // handle further processing, but since this promise is in an ending state |
| 574 // we can be confident it will never be called back. | 567 // we can be confident it will never be called back. |
| 575 if (isCallable(maybeCallback) && | 568 if (isCallable(maybeCallback) && !maybeCallback.wrappedByPromiseTracker && |
| 576 !maybeCallback.wrappedByPromiseTracker && | |
| 577 sameTracker.callbacks) { | 569 sameTracker.callbacks) { |
| 578 var handler = wrapper.wrapCallback(function() { | 570 var handler = wrapper.wrapCallback(function() { |
| 579 if (sameTracker.callbacks) { | 571 if (sameTracker.callbacks) { |
| 580 clearTracker(otherTracker); | 572 clearTracker(otherTracker); |
| 581 return maybeCallback.apply(null, arguments); | 573 return maybeCallback.apply(null, arguments); |
| 582 } | 574 } |
| 583 }, false); | 575 }, false); |
| 584 // Harmony promises' catch calls will call into handleThen, | 576 // Harmony promises' catch calls will call into handleThen, |
| 585 // double-wrapping all catch callbacks. Regular promise catch calls do | 577 // double-wrapping all catch callbacks. Regular promise catch calls do |
| 586 // not call into handleThen. Setting an attribute on the wrapped | 578 // not call into handleThen. Setting an attribute on the wrapped |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 619 } | 611 } |
| 620 | 612 |
| 621 // Register at least one resolve and reject callback so we always receive | 613 // Register at least one resolve and reject callback so we always receive |
| 622 // a callback to update the task manager and clear the callbacks | 614 // a callback to update the task manager and clear the callbacks |
| 623 // that will never occur. | 615 // that will never occur. |
| 624 // | 616 // |
| 625 // The then form is used to avoid reentrancy by handleCatch, | 617 // The then form is used to avoid reentrancy by handleCatch, |
| 626 // which ends up calling handleThen. | 618 // which ends up calling handleThen. |
| 627 handleThen(function() {}, function() {}); | 619 handleThen(function() {}, function() {}); |
| 628 | 620 |
| 629 return { | 621 return {handleThen: handleThen, handleCatch: handleCatch}; |
| 630 handleThen: handleThen, | |
| 631 handleCatch: handleCatch | |
| 632 }; | |
| 633 } | 622 } |
| 634 } | 623 } |
| 635 | 624 |
| 636 registerPromiseAdapter(); | 625 registerPromiseAdapter(); |
| 637 | 626 |
| 638 /** | 627 /** |
| 639 * Control promise rejection. | 628 * Control promise rejection. |
| 640 * @enum {number} | 629 * @enum {number} |
| 641 */ | 630 */ |
| 642 var PromiseRejection = { | 631 var PromiseRejection = { |
| 643 /** Disallow promise rejection */ | 632 /** Disallow promise rejection */ |
| 644 DISALLOW: 0, | 633 DISALLOW: 0, |
| 645 /** Allow promise rejection */ | 634 /** Allow promise rejection */ |
| 646 ALLOW: 1 | 635 ALLOW: 1 |
| 647 }; | 636 }; |
| 648 | 637 |
| 649 /** | 638 /** |
| 650 * Provides the promise equivalent of instrumented.storage.local.get. | 639 * Provides the promise equivalent of instrumented.storage.local.get. |
| 651 * @param {Object} defaultStorageObject Default storage object to fill. | 640 * @param {Object} defaultStorageObject Default storage object to fill. |
| 652 * @param {PromiseRejection=} opt_allowPromiseRejection If | 641 * @param {PromiseRejection=} opt_allowPromiseRejection If |
| 653 * PromiseRejection.ALLOW, allow promise rejection on errors, otherwise the | 642 * PromiseRejection.ALLOW, allow promise rejection on errors, otherwise the |
| 654 * default storage object is resolved. | 643 * default storage object is resolved. |
| 655 * @return {Promise} A promise that fills the default storage object. On | 644 * @return {Promise} A promise that fills the default storage object. On |
| 656 * failure, if promise rejection is allowed, the promise is rejected, | 645 * failure, if promise rejection is allowed, the promise is rejected, |
| 657 * otherwise it is resolved to the default storage object. | 646 * otherwise it is resolved to the default storage object. |
| 658 */ | 647 */ |
| 659 function fillFromChromeLocalStorage( | 648 function fillFromChromeLocalStorage( |
| 660 defaultStorageObject, | 649 defaultStorageObject, opt_allowPromiseRejection) { |
| 661 opt_allowPromiseRejection) { | |
| 662 return new Promise(function(resolve, reject) { | 650 return new Promise(function(resolve, reject) { |
| 663 // We have to create a keys array because keys with a default value | 651 // We have to create a keys array because keys with a default value |
| 664 // of undefined will cause that key to not be looked up! | 652 // of undefined will cause that key to not be looked up! |
| 665 var keysToGet = []; | 653 var keysToGet = []; |
| 666 for (var key in defaultStorageObject) { | 654 for (var key in defaultStorageObject) { |
| 667 keysToGet.push(key); | 655 keysToGet.push(key); |
| 668 } | 656 } |
| 669 instrumented.storage.local.get(keysToGet, function(items) { | 657 instrumented.storage.local.get(keysToGet, function(items) { |
| 670 if (items) { | 658 if (items) { |
| 671 // Merge the result with the default storage object to ensure all keys | 659 // Merge the result with the default storage object to ensure all keys |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 717 */ | 705 */ |
| 718 function startFirst() { | 706 function startFirst() { |
| 719 verify(queue.length >= 1, 'startFirst: queue is empty'); | 707 verify(queue.length >= 1, 'startFirst: queue is empty'); |
| 720 verify(!isInTask, 'startFirst: already in task'); | 708 verify(!isInTask, 'startFirst: already in task'); |
| 721 isInTask = true; | 709 isInTask = true; |
| 722 | 710 |
| 723 // Start the oldest queued task, but don't remove it from the queue. | 711 // Start the oldest queued task, but don't remove it from the queue. |
| 724 verify( | 712 verify( |
| 725 taskPendingCallbackCount == 0, | 713 taskPendingCallbackCount == 0, |
| 726 'tasks.startFirst: still have pending task callbacks: ' + | 714 'tasks.startFirst: still have pending task callbacks: ' + |
| 727 taskPendingCallbackCount + | 715 taskPendingCallbackCount + ', queue = ' + JSON.stringify(queue) + |
| 728 ', queue = ' + JSON.stringify(queue) + ', ' + | 716 ', ' + wrapper.debugGetStateString()); |
| 729 wrapper.debugGetStateString()); | |
| 730 var entry = queue[0]; | 717 var entry = queue[0]; |
| 731 console.log('Starting task ' + entry.name); | 718 console.log('Starting task ' + entry.name); |
| 732 | 719 |
| 733 entry.task(); | 720 entry.task(); |
| 734 | 721 |
| 735 verify(isInTask, 'startFirst: not in task at exit'); | 722 verify(isInTask, 'startFirst: not in task at exit'); |
| 736 isInTask = false; | 723 isInTask = false; |
| 737 if (taskPendingCallbackCount == 0) | 724 if (taskPendingCallbackCount == 0) |
| 738 finish(); | 725 finish(); |
| 739 } | 726 } |
| 740 | 727 |
| 741 /** | 728 /** |
| 742 * Checks if a new task can be added to the task queue. | 729 * Checks if a new task can be added to the task queue. |
| 743 * @param {string} taskName Name of the new task. | 730 * @param {string} taskName Name of the new task. |
| 744 * @return {boolean} Whether the new task can be added. | 731 * @return {boolean} Whether the new task can be added. |
| 745 */ | 732 */ |
| 746 function canQueue(taskName) { | 733 function canQueue(taskName) { |
| 747 for (var i = 0; i < queue.length; ++i) { | 734 for (var i = 0; i < queue.length; ++i) { |
| 748 if (areConflicting(taskName, queue[i].name)) { | 735 if (areConflicting(taskName, queue[i].name)) { |
| 749 console.log('Conflict: new=' + taskName + | 736 console.log( |
| 750 ', scheduled=' + queue[i].name); | 737 'Conflict: new=' + taskName + ', scheduled=' + queue[i].name); |
| 751 return false; | 738 return false; |
| 752 } | 739 } |
| 753 } | 740 } |
| 754 | 741 |
| 755 return true; | 742 return true; |
| 756 } | 743 } |
| 757 | 744 |
| 758 /** | 745 /** |
| 759 * Adds a new task. If another task is not running, runs the task immediately. | 746 * Adds a new task. If another task is not running, runs the task immediately. |
| 760 * If any task in the queue is not compatible with the task, ignores the new | 747 * If any task in the queue is not compatible with the task, ignores the new |
| (...skipping 11 matching lines...) Expand all Loading... |
| 772 | 759 |
| 773 if (queue.length == 1) { | 760 if (queue.length == 1) { |
| 774 startFirst(); | 761 startFirst(); |
| 775 } | 762 } |
| 776 } | 763 } |
| 777 | 764 |
| 778 /** | 765 /** |
| 779 * Completes the current task and starts the next queued task if available. | 766 * Completes the current task and starts the next queued task if available. |
| 780 */ | 767 */ |
| 781 function finish() { | 768 function finish() { |
| 782 verify(queue.length >= 1, | 769 verify(queue.length >= 1, 'tasks.finish: The task queue is empty'); |
| 783 'tasks.finish: The task queue is empty'); | |
| 784 console.log('Finishing task ' + queue[0].name); | 770 console.log('Finishing task ' + queue[0].name); |
| 785 queue.shift(); | 771 queue.shift(); |
| 786 | 772 |
| 787 if (queue.length >= 1) | 773 if (queue.length >= 1) |
| 788 startFirst(); | 774 startFirst(); |
| 789 } | 775 } |
| 790 | 776 |
| 791 instrumented.runtime.onSuspend.addListener(function() { | 777 instrumented.runtime.onSuspend.addListener(function() { |
| 792 verify( | 778 verify( |
| 793 queue.length == 0, | 779 queue.length == 0, 'Incomplete task when unloading event page,' + |
| 794 'Incomplete task when unloading event page,' + | 780 ' queue = ' + JSON.stringify(queue) + ', ' + |
| 795 ' queue = ' + JSON.stringify(queue) + ', ' + | 781 wrapper.debugGetStateString()); |
| 796 wrapper.debugGetStateString()); | |
| 797 }); | 782 }); |
| 798 | 783 |
| 799 | 784 |
| 800 /** | 785 /** |
| 801 * Wrapper plugin for tasks. | 786 * Wrapper plugin for tasks. |
| 802 * @constructor | 787 * @constructor |
| 803 */ | 788 */ |
| 804 function TasksWrapperPlugin() { | 789 function TasksWrapperPlugin() { |
| 805 this.isTaskCallback = isInTask; | 790 this.isTaskCallback = isInTask; |
| 806 if (this.isTaskCallback) | 791 if (this.isTaskCallback) |
| (...skipping 21 matching lines...) Expand all Loading... |
| 828 if (--taskPendingCallbackCount == 0) | 813 if (--taskPendingCallbackCount == 0) |
| 829 finish(); | 814 finish(); |
| 830 } | 815 } |
| 831 } | 816 } |
| 832 }; | 817 }; |
| 833 | 818 |
| 834 wrapper.registerWrapperPluginFactory(function() { | 819 wrapper.registerWrapperPluginFactory(function() { |
| 835 return new TasksWrapperPlugin(); | 820 return new TasksWrapperPlugin(); |
| 836 }); | 821 }); |
| 837 | 822 |
| 838 return { | 823 return {add: add}; |
| 839 add: add | |
| 840 }; | |
| 841 } | 824 } |
| 842 | 825 |
| 843 /** | 826 /** |
| 844 * Builds an object to manage retrying activities with exponential backoff. | 827 * Builds an object to manage retrying activities with exponential backoff. |
| 845 * @param {string} name Name of this attempt manager. | 828 * @param {string} name Name of this attempt manager. |
| 846 * @param {function()} attempt Activity that the manager retries until it | 829 * @param {function()} attempt Activity that the manager retries until it |
| 847 * calls 'stop' method. | 830 * calls 'stop' method. |
| 848 * @param {number} initialDelaySeconds Default first delay until first retry. | 831 * @param {number} initialDelaySeconds Default first delay until first retry. |
| 849 * @param {number} maximumDelaySeconds Maximum delay between retries. | 832 * @param {number} maximumDelaySeconds Maximum delay between retries. |
| 850 * @return {Object} Attempt manager interface. | 833 * @return {Object} Attempt manager interface. |
| (...skipping 178 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1029 function addListener(callback) { | 1012 function addListener(callback) { |
| 1030 listeners.push(callback); | 1013 listeners.push(callback); |
| 1031 } | 1014 } |
| 1032 | 1015 |
| 1033 /** | 1016 /** |
| 1034 * Checks if the last signed in state matches the current one. | 1017 * Checks if the last signed in state matches the current one. |
| 1035 * If it doesn't, it notifies the listeners of the change. | 1018 * If it doesn't, it notifies the listeners of the change. |
| 1036 */ | 1019 */ |
| 1037 function checkAndNotifyListeners() { | 1020 function checkAndNotifyListeners() { |
| 1038 isSignedIn().then(function(signedIn) { | 1021 isSignedIn().then(function(signedIn) { |
| 1039 fillFromChromeLocalStorage({lastSignedInState: undefined}) | 1022 fillFromChromeLocalStorage({ |
| 1040 .then(function(items) { | 1023 lastSignedInState: undefined |
| 1041 if (items.lastSignedInState != signedIn) { | 1024 }).then(function(items) { |
| 1042 chrome.storage.local.set( | 1025 if (items.lastSignedInState != signedIn) { |
| 1043 {lastSignedInState: signedIn}); | 1026 chrome.storage.local.set({lastSignedInState: signedIn}); |
| 1044 listeners.forEach(function(callback) { | 1027 listeners.forEach(function(callback) { |
| 1045 callback(); | 1028 callback(); |
| 1046 }); | 1029 }); |
| 1047 } | 1030 } |
| 1048 }); | |
| 1049 }); | 1031 }); |
| 1032 }); |
| 1050 } | 1033 } |
| 1051 | 1034 |
| 1052 instrumented.identity.onSignInChanged.addListener(function() { | 1035 instrumented.identity.onSignInChanged.addListener(function() { |
| 1053 checkAndNotifyListeners(); | 1036 checkAndNotifyListeners(); |
| 1054 }); | 1037 }); |
| 1055 | 1038 |
| 1056 instrumented.alarms.onAlarm.addListener(function(alarm) { | 1039 instrumented.alarms.onAlarm.addListener(function(alarm) { |
| 1057 if (alarm.name == alarmName) | 1040 if (alarm.name == alarmName) |
| 1058 checkAndNotifyListeners(); | 1041 checkAndNotifyListeners(); |
| 1059 }); | 1042 }); |
| 1060 | 1043 |
| 1061 // Poll for the sign in state every hour. | 1044 // Poll for the sign in state every hour. |
| 1062 // One hour is just an arbitrary amount of time chosen. | 1045 // One hour is just an arbitrary amount of time chosen. |
| 1063 chrome.alarms.create(alarmName, {periodInMinutes: 60}); | 1046 chrome.alarms.create(alarmName, {periodInMinutes: 60}); |
| 1064 | 1047 |
| 1065 return { | 1048 return { |
| 1066 addListener: addListener, | 1049 addListener: addListener, |
| 1067 getAuthToken: getAuthToken, | 1050 getAuthToken: getAuthToken, |
| 1068 getLogin: getLogin, | 1051 getLogin: getLogin, |
| 1069 isSignedIn: isSignedIn, | 1052 isSignedIn: isSignedIn, |
| 1070 removeToken: removeToken | 1053 removeToken: removeToken |
| 1071 }; | 1054 }; |
| 1072 } | 1055 } |
| OLD | NEW |