12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Copyright (c) 2013 The Chromium Authors. All rights reserved. 22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file. 42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 5868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)'use strict'; 6c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/** 82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @fileoverview Utility objects and functions for Google Now extension. 9424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * Most important entities here: 10424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * (1) 'wrapper' is a module used to add error handling and other services to 11424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * callbacks for HTML and Chrome functions and Chrome event listeners. 12424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * Chrome invokes extension code through event listeners. Once entered via 13424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * an event listener, the extension may call a Chrome/HTML API method 14424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * passing a callback (and so forth), and that callback must occur later, 15424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * otherwise, we generate an error. Chrome may unload event pages waiting 16424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * for an event. When the event fires, Chrome will reload the event page. We 17424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * don't require event listeners to fire because they are generally not 18a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * predictable (like a button clicked event). 19424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * (2) Task Manager (built with buildTaskManager() call) provides controlling 20424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * mutually excluding chains of callbacks called tasks. Task Manager uses 21424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * WrapperPlugins to add instrumentation code to 'wrapper' to determine 22424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * when a task completes. 232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */ 242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 258bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)// TODO(vadimt): Use server name in the manifest. 263240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch 2790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)/** 2890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * Notification server URL. 2990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) */ 300f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)var NOTIFICATION_CARDS_URL = 'https://www.googleapis.com/chromenow/v1'; 3190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)/** 335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * Returns true if debug mode is enabled. 345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * localStorage returns items as strings, which means if we store a boolean, 355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * it returns a string. Use this function to compare against true. 365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * @return {boolean} Whether debug mode is enabled. 375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) */ 385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)function isInDebugMode() { 395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return localStorage.debug_mode === 'true'; 405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 413240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch 423240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch/** 438bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles) * Initializes for debug or release modes of operation. 448bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles) */ 458bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)function initializeDebug() { 465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (isInDebugMode()) { 478bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles) NOTIFICATION_CARDS_URL = 488bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles) localStorage['server_url'] || NOTIFICATION_CARDS_URL; 498bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles) } 508bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)} 518bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles) 528bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)initializeDebug(); 538bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles) 540f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)/** 555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * Conditionally allow console.log output based off of the debug mode. 565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) */ 575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)console.log = function() { 585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) var originalConsoleLog = console.log; 595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return function() { 605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (isInDebugMode()) { 615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) originalConsoleLog.apply(console, arguments); 625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) }; 645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}(); 655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)/** 675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * Explanation Card Storage. 680f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) */ 695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)if (localStorage['explanatoryCardsShown'] === undefined) 705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) localStorage['explanatoryCardsShown'] = 0; 715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)/** 735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * Location Card Count Cleanup. 745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) */ 755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)if (localStorage.locationCardsShown !== undefined) 765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) localStorage.removeItem('locationCardsShown'); 770f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) 788bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)/** 793551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) * Builds an error object with a message that may be sent to the server. 803551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) * @param {string} message Error message. This message may be sent to the 813551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) * server. 823551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) * @return {Error} Error object. 833240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch */ 843551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)function buildErrorWithMessageForServer(message) { 853551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) var error = new Error(message); 863551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) error.canSendMessageToServer = true; 873551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) return error; 883240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch} 893240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch 902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/** 912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * Checks for internal errors. 922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {boolean} condition Condition that must be true. 932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {string} message Diagnostic message for the case when the condition is 942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * false. 952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */ 962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)function verify(condition, message) { 97c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (!condition) 983551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) throw buildErrorWithMessageForServer('ASSERT: ' + message); 992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/** 10290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * Builds a request to the notification server. 1034e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * @param {string} method Request method. 10490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * @param {string} handlerName Server handler to send the request to. 105a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * @param {string=} opt_contentType Value for the Content-type header. 10690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * @return {XMLHttpRequest} Server request. 10790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) */ 108a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)function buildServerRequest(method, handlerName, opt_contentType) { 10990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) var request = new XMLHttpRequest(); 11090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 11190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) request.responseType = 'text'; 1124e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) request.open(method, NOTIFICATION_CARDS_URL + '/' + handlerName, true); 113a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (opt_contentType) 114a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) request.setRequestHeader('Content-type', opt_contentType); 11590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 11690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) return request; 11790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)} 11890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 11990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)/** 120424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * Sends an error report to the server. 121424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * @param {Error} error Error to send. 1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */ 123424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)function sendErrorReport(error) { 124424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) // Don't remove 'error.stack.replace' below! 125424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) var filteredStack = error.canSendMessageToServer ? 126424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) error.stack : error.stack.replace(/.*\n/, '(message removed)\n'); 127424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) var file; 128424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) var line; 129424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) var topFrameLineMatch = filteredStack.match(/\n at .*\n/); 130424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) var topFrame = topFrameLineMatch && topFrameLineMatch[0]; 131424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) if (topFrame) { 132424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) // Examples of a frame: 133424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) // 1. '\n at someFunction (chrome-extension:// 1348bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles) // pafkbggdmjlpgkdkcbjmhmfcdpncadgh/background.js:915:15)\n' 1358bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles) // 2. '\n at chrome-extension://pafkbggdmjlpgkdkcbjmhmfcdpncadgh/ 136424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) // utility.js:269:18\n' 137424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) // 3. '\n at Function.target.(anonymous function) (extensions:: 138424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) // SafeBuiltins:19:14)\n' 139424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) // 4. '\n at Event.dispatchToListener (event_bindings:382:22)\n' 140424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) var errorLocation; 141424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) // Find the the parentheses at the end of the line, if any. 142424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) var parenthesesMatch = topFrame.match(/\(.*\)\n/); 143424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) if (parenthesesMatch && parenthesesMatch[0]) { 144424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) errorLocation = 145424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) parenthesesMatch[0].substring(1, parenthesesMatch[0].length - 2); 146424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) } else { 147424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) errorLocation = topFrame; 148424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) } 1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 150424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) var topFrameElements = errorLocation.split(':'); 151424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) // topFrameElements is an array that ends like: 1528bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles) // [N-3] //pafkbggdmjlpgkdkcbjmhmfcdpncadgh/utility.js 153424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) // [N-2] 308 154424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) // [N-1] 19 155424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) if (topFrameElements.length >= 3) { 156424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) file = topFrameElements[topFrameElements.length - 3]; 157424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) line = topFrameElements[topFrameElements.length - 2]; 158424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) } 159424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) } 160558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch 16158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) var errorText = error.name; 16258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) if (error.canSendMessageToServer) 16358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) errorText = errorText + ': ' + error.message; 16458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) 1654e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) var errorObject = { 1664e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) message: errorText, 1674e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) file: file, 1684e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) line: line, 1694e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) trace: filteredStack 1704e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) }; 1714e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 172a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // We use relatively direct calls here because the instrumentation may be in 173a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // a bad state. Wrappers and promises should not be involved in the reporting. 1744e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) var request = buildServerRequest('POST', 'jserrors', 'application/json'); 175424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) request.onloadend = function(event) { 176424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) console.log('sendErrorReport status: ' + request.status); 177424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) }; 1781e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 1791e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) chrome.identity.getAuthToken({interactive: false}, function(token) { 1801e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (token) { 1811e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) request.setRequestHeader('Authorization', 'Bearer ' + token); 1821e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) request.send(JSON.stringify(errorObject)); 1831e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 1841e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) }); 185424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)} 186558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch 187424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)// Limiting 1 error report per background page load. 188424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)var errorReported = false; 189424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 190424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)/** 191424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * Reports an error to the server and the user, as appropriate. 192424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * @param {Error} error Error to report. 193424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) */ 194424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)function reportError(error) { 195424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) var message = 'Critical error:\n' + error.stack; 1965c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (isInDebugMode()) 1975c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu console.error(message); 1985c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 199424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) if (!errorReported) { 200424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) errorReported = true; 201424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) chrome.metricsPrivate.getIsCrashReportingEnabled(function(isEnabled) { 202424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) if (isEnabled) 203424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) sendErrorReport(error); 2045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (isInDebugMode()) 205424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) alert(message); 206424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) }); 207424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) } 208424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)} 2092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 210424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)// Partial mirror of chrome.* for all instrumented functions. 211424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)var instrumented = {}; 212424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 213424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)/** 214424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * Wrapper plugin. These plugins extend instrumentation added by 215424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * wrapper.wrapCallback by adding code that executes before and after the call 216424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * to the original callback provided by the extension. 217424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * 218424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * @typedef {{ 219424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * prologue: function (), 220424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * epilogue: function () 221424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * }} 222424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) */ 223424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)var WrapperPlugin; 224424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 225424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)/** 226424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * Wrapper for callbacks. Used to add error handling and other services to 227424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * callbacks for HTML and Chrome functions and events. 228424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) */ 229424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)var wrapper = (function() { 2302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) /** 231424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * Factory for wrapper plugins. If specified, it's used to generate an 232424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * instance of WrapperPlugin each time we wrap a callback (which corresponds 233424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * to addListener call for Chrome events, and to every API call that specifies 234424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * a callback). WrapperPlugin's lifetime ends when the callback for which it 235424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * was generated, exits. It's possible to have several instances of 236424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * WrapperPlugin at the same time. 237424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * An instance of WrapperPlugin can have state that can be shared by its 238424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * constructor, prologue() and epilogue(). Also WrapperPlugins can change 239424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * state of other objects, for example, to do refcounting. 240a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * @type {?function(): WrapperPlugin} 241a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) */ 242424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) var wrapperPluginFactory = null; 243a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) 244a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) /** 245424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * Registers a wrapper plugin factory. 246424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * @param {function(): WrapperPlugin} factory Wrapper plugin factory. 247a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) */ 248424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) function registerWrapperPluginFactory(factory) { 249424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) if (wrapperPluginFactory) { 2503551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) reportError(buildErrorWithMessageForServer( 251424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 'registerWrapperPluginFactory: factory is already registered.')); 252a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) } 253558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch 254424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) wrapperPluginFactory = factory; 2552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 2562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 2572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) /** 258424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * True if currently executed code runs in a callback or event handler that 259424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * was instrumented by wrapper.wrapCallback() call. 260424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * @type {boolean} 2612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */ 262424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) var isInWrappedCallback = false; 2632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 2642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) /** 265424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * Required callbacks that are not yet called. Includes both task and non-task 266424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * callbacks. This is a map from unique callback id to the stack at the moment 267424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * when the callback was wrapped. This stack identifies the callback. 268424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * Used only for diagnostics. 269424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * @type {Object.<number, string>} 2702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */ 271424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) var pendingCallbacks = {}; 2722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 2732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) /** 274424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * Unique ID of the next callback. 275424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * @type {number} 2762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */ 277424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) var nextCallbackId = 0; 27890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 27990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) /** 280424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * Gets diagnostic string with the status of the wrapper. 281424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * @return {string} Diagnostic string. 28290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) */ 283424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) function debugGetStateString() { 284f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return 'pendingCallbacks @' + Date.now() + ' = ' + 285f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) JSON.stringify(pendingCallbacks); 28690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 287c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 288c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) /** 289424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * Checks that we run in a wrapped callback. 2903551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) */ 291424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) function checkInWrappedCallback() { 292424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) if (!isInWrappedCallback) { 293424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) reportError(buildErrorWithMessageForServer( 294424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 'Not in instrumented callback')); 2953551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) } 2963551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) } 2973551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) 2983551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) /** 299c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) * Adds error processing to an API callback. 300c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) * @param {Function} callback Callback to instrument. 301424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * @param {boolean=} opt_isEventListener True if the callback is a listener to 302424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * a Chrome API event. 303c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) * @return {Function} Instrumented callback. 304c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) */ 305a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) function wrapCallback(callback, opt_isEventListener) { 306558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch var callbackId = nextCallbackId++; 307424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 308a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) if (!opt_isEventListener) { 309424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) checkInWrappedCallback(); 310f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) pendingCallbacks[callbackId] = new Error().stack + ' @' + Date.now(); 311a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) } 312558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch 313424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) // wrapperPluginFactory may be null before task manager is built, and in 314424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) // tests. 315424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) var wrapperPluginInstance = wrapperPluginFactory && wrapperPluginFactory(); 316424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 317c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) return function() { 318c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // This is the wrapper for the callback. 319c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) try { 320424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) verify(!isInWrappedCallback, 'Re-entering instrumented callback'); 321424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) isInWrappedCallback = true; 322bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch 323a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) if (!opt_isEventListener) 324558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch delete pendingCallbacks[callbackId]; 325558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch 326424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) if (wrapperPluginInstance) 327424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) wrapperPluginInstance.prologue(); 328424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 329558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch // Call the original callback. 330a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) var returnValue = callback.apply(null, arguments); 331558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch 332424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) if (wrapperPluginInstance) 333424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) wrapperPluginInstance.epilogue(); 334bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch 335424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) verify(isInWrappedCallback, 336bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch 'Instrumented callback is not instrumented upon exit'); 337424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) isInWrappedCallback = false; 338a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 339a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return returnValue; 340c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) } catch (error) { 3413551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) reportError(error); 342c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) } 343c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) }; 344c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) } 345c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 346c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) /** 347ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch * Returns an instrumented function. 348424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * @param {!Array.<string>} functionIdentifierParts Path to the chrome.* 349424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * function. 350ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch * @param {string} functionName Name of the chrome API function. 351c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) * @param {number} callbackParameter Index of the callback parameter to this 352c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) * API function. 353424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * @return {Function} An instrumented function. 354c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) */ 355ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch function createInstrumentedFunction( 356ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch functionIdentifierParts, 357ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch functionName, 358ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch callbackParameter) { 359ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch return function() { 360c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // This is the wrapper for the API function. Pass the wrapped callback to 361c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // the original function. 362c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) var callback = arguments[callbackParameter]; 363c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (typeof callback != 'function') { 3643551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) reportError(buildErrorWithMessageForServer( 3653551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) 'Argument ' + callbackParameter + ' of ' + 3663551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) functionIdentifierParts.join('.') + '.' + functionName + 3673551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) ' is not a function')); 368c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) } 369558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch arguments[callbackParameter] = wrapCallback( 370558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch callback, functionName == 'addListener'); 371ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch 372ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch var chromeContainer = chrome; 373424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) functionIdentifierParts.forEach(function(fragment) { 374ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch chromeContainer = chromeContainer[fragment]; 375ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch }); 376ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch return chromeContainer[functionName]. 377ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch apply(chromeContainer, arguments); 378c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) }; 379c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) } 380c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 381ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch /** 382ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch * Instruments an API function to add error processing to its user 383ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch * code-provided callback. 384ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch * @param {string} functionIdentifier Full identifier of the function without 385ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch * the 'chrome.' portion. 386ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch * @param {number} callbackParameter Index of the callback parameter to this 387ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch * API function. 388ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch */ 389ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch function instrumentChromeApiFunction(functionIdentifier, callbackParameter) { 390ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch var functionIdentifierParts = functionIdentifier.split('.'); 391ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch var functionName = functionIdentifierParts.pop(); 392ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch var chromeContainer = chrome; 393ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch var instrumentedContainer = instrumented; 394424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) functionIdentifierParts.forEach(function(fragment) { 395ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch chromeContainer = chromeContainer[fragment]; 3963551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) if (!chromeContainer) { 3973551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) reportError(buildErrorWithMessageForServer( 3983551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) 'Cannot instrument ' + functionIdentifier)); 3993551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) } 4003551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) 401ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch if (!(fragment in instrumentedContainer)) 402ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch instrumentedContainer[fragment] = {}; 403ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch 404ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch instrumentedContainer = instrumentedContainer[fragment]; 405ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch }); 406ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch 407ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch var targetFunction = chromeContainer[functionName]; 4083551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) if (!targetFunction) { 4093551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) reportError(buildErrorWithMessageForServer( 4103551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) 'Cannot instrument ' + functionIdentifier)); 4113551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) } 412ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch 413ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch instrumentedContainer[functionName] = createInstrumentedFunction( 414ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch functionIdentifierParts, 415ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch functionName, 416ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch callbackParameter); 417ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch } 418ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch 419ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch instrumentChromeApiFunction('runtime.onSuspend.addListener', 0); 420c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 421424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) instrumented.runtime.onSuspend.addListener(function() { 422558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch var stringifiedPendingCallbacks = JSON.stringify(pendingCallbacks); 423c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) verify( 424424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) stringifiedPendingCallbacks == '{}', 425f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 'Pending callbacks when unloading event page @' + Date.now() + ':' + 426424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) stringifiedPendingCallbacks); 4272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) }); 4282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 4292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return { 430424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) wrapCallback: wrapCallback, 431ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch instrumentChromeApiFunction: instrumentChromeApiFunction, 432424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) registerWrapperPluginFactory: registerWrapperPluginFactory, 433424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) checkInWrappedCallback: checkInWrappedCallback, 434424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) debugGetStateString: debugGetStateString 435424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) }; 436424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)})(); 437424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 438424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)wrapper.instrumentChromeApiFunction('alarms.get', 1); 439424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)wrapper.instrumentChromeApiFunction('alarms.onAlarm.addListener', 0); 440424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)wrapper.instrumentChromeApiFunction('identity.getAuthToken', 1); 4411e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)wrapper.instrumentChromeApiFunction('identity.onSignInChanged.addListener', 0); 442424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)wrapper.instrumentChromeApiFunction('identity.removeCachedAuthToken', 1); 443a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)wrapper.instrumentChromeApiFunction('storage.local.get', 1); 4441e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)wrapper.instrumentChromeApiFunction('webstorePrivate.getBrowserLogin', 0); 445424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 446424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)/** 447a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * Promise adapter for all JS promises to the task manager. 4485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) */ 449a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)function registerPromiseAdapter() { 4505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) var originalThen = Promise.prototype.then; 451a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) var originalCatch = Promise.prototype.catch; 452a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 453a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) /** 454a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * Takes a promise and adds the callback tracker to it. 455a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * @param {object} promise Promise that receives the callback tracker. 456a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) */ 457a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) function instrumentPromise(promise) { 458a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (promise.__tracker === undefined) { 459a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) promise.__tracker = createPromiseCallbackTracker(promise); 460a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 4615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 462a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 463a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) Promise.prototype.then = function(onResolved, onRejected) { 464a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) instrumentPromise(this); 465a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return this.__tracker.handleThen(onResolved, onRejected); 4665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) }; 467a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 468a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) Promise.prototype.catch = function(onRejected) { 469a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) instrumentPromise(this); 470a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return this.__tracker.handleCatch(onRejected); 4715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) }; 472a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 473a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) /** 474a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * Promise Callback Tracker. 475a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * Handles coordination of 'then' and 'catch' callbacks in a task 476a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * manager compatible way. For an individual promise, either the 'then' 477a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * arguments or the 'catch' arguments will be processed, never both. 478a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * 479a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * Example: 480a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * var p = new Promise([Function]); 481a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * p.then([ThenA]); 482a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * p.then([ThenB]); 483a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * p.catch([CatchA]); 484a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * On resolution, [ThenA] and [ThenB] will be used. [CatchA] is discarded. 485a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * On rejection, vice versa. 486a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * 487a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * Clarification: 488a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * Chained promises create a new promise that is tracked separately from 489a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * the originaing promise, as the example below demonstrates: 490a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * 491a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * var p = new Promise([Function])); 492a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * p.then([ThenA]).then([ThenB]).catch([CatchA]); 493a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * ^ ^ ^ 494a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * | | + Returns a new promise. 495a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * | + Returns a new promise. 496a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * + Returns a new promise. 497a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * 498a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * Four promises exist in the above statement, each with its own 499a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * resolution and rejection state. However, by default, this state is 500a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * chained to the previous promise's resolution or rejection 501a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * state. 502a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * 503a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * If p resolves, then the 'then' calls will execute until all the 'then' 504a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * clauses are executed. If the result of either [ThenA] or [ThenB] is a 505a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * promise, then that execution state will guide the remaining chain. 506a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * Similarly, if [CatchA] returns a promise, it can also guide the 507a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * remaining chain. In this specific case, the chain ends, so there 508a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * is nothing left to do. 509a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * @param {object} promise Promise being tracked. 510a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * @return {object} A promise callback tracker. 511a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) */ 512a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) function createPromiseCallbackTracker(promise) { 513a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) /** 514a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * Callback Tracker. Holds an array of callbacks created for this promise. 515a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * The indirection allows quick checks against the array and clearing the 516a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * array without ugly splicing and copying. 517a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * @typedef {{ 518a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * callback: array.<Function>= 519a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * }} 520a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) */ 521a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) var CallbackTracker; 522a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 523a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) /** @type {CallbackTracker} */ 524a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) var thenTracker = {callbacks: []}; 525a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) /** @type {CallbackTracker} */ 526a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) var catchTracker = {callbacks: []}; 527a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 528a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) /** 529a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * Returns true if the specified value is callable. 530a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * @param {*} value Value to check. 531a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * @return {boolean} True if the value is a callable. 532a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) */ 533a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) function isCallable(value) { 534a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return typeof value === 'function'; 535a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 536a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 537a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) /** 538a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * Takes a tracker and clears its callbacks in a manner consistent with 539a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * the task manager. For the task manager, it also calls all callbacks 540a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * by no-oping them first and then calling them. 541a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * @param {CallbackTracker} tracker Tracker to clear. 542a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) */ 543a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) function clearTracker(tracker) { 544a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (tracker.callbacks) { 545a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) var callbacksToClear = tracker.callbacks; 546a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // No-ops all callbacks of this type. 547a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) tracker.callbacks = undefined; 548a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // Do not wrap the promise then argument! 549a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // It will call wrapped callbacks. 550a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) originalThen.call(Promise.resolve(), function() { 551a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) for (var i = 0; i < callbacksToClear.length; i++) { 552a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) callbacksToClear[i](); 553a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 554a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) }); 555a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 556a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 557a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 558a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) /** 559a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * Takes the argument to a 'then' or 'catch' function and applies 560a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * a wrapping to callables consistent to ECMA promises. 561a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * @param {*} maybeCallback Argument to 'then' or 'catch'. 562a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * @param {CallbackTracker} sameTracker Tracker for the call type. 563a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * Example: If the argument is from a 'then' call, use thenTracker. 564a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * @param {CallbackTracker} otherTracker Tracker for the opposing call type. 565a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * Example: If the argument is from a 'then' call, use catchTracker. 566a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * @return {*} Consumable argument with necessary wrapping applied. 567a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) */ 568a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) function registerAndWrapMaybeCallback( 569a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) maybeCallback, sameTracker, otherTracker) { 570a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // If sameTracker.callbacks is undefined, we've reached an ending state 571a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // that means this callback will never be called back. 572a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // We will still forward this call on to let the promise system 573a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // handle further processing, but since this promise is in an ending state 574a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // we can be confident it will never be called back. 575c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch if (isCallable(maybeCallback) && 576c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch !maybeCallback.wrappedByPromiseTracker && 577c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch sameTracker.callbacks) { 578a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) var handler = wrapper.wrapCallback(function() { 579a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (sameTracker.callbacks) { 580a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) clearTracker(otherTracker); 581a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return maybeCallback.apply(null, arguments); 582a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 583a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) }, false); 584c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch // Harmony promises' catch calls will call into handleThen, 585c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch // double-wrapping all catch callbacks. Regular promise catch calls do 586c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch // not call into handleThen. Setting an attribute on the wrapped 587c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch // function is compatible with both promise implementations. 588c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch handler.wrappedByPromiseTracker = true; 589a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) sameTracker.callbacks.push(handler); 590a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return handler; 591a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } else { 592a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return maybeCallback; 593a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 594a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 595a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 596a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) /** 597a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * Tracks then calls equivalent to Promise.prototype.then. 598a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * @param {*} onResolved Argument to use if the promise is resolved. 599a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * @param {*} onRejected Argument to use if the promise is rejected. 600a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * @return {object} Promise resulting from the 'then' call. 601a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) */ 602a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) function handleThen(onResolved, onRejected) { 603a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) var resolutionHandler = 604a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) registerAndWrapMaybeCallback(onResolved, thenTracker, catchTracker); 605a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) var rejectionHandler = 606a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) registerAndWrapMaybeCallback(onRejected, catchTracker, thenTracker); 607a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return originalThen.call(promise, resolutionHandler, rejectionHandler); 608a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 609a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 610a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) /** 611a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * Tracks then calls equivalent to Promise.prototype.catch. 612a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * @param {*} onRejected Argument to use if the promise is rejected. 613a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * @return {object} Promise resulting from the 'catch' call. 614a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) */ 615a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) function handleCatch(onRejected) { 616a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) var rejectionHandler = 617a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) registerAndWrapMaybeCallback(onRejected, catchTracker, thenTracker); 618a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return originalCatch.call(promise, rejectionHandler); 619a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 620a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 621c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch // Register at least one resolve and reject callback so we always receive 622c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch // a callback to update the task manager and clear the callbacks 623c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch // that will never occur. 624c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch // 625c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch // The then form is used to avoid reentrancy by handleCatch, 626c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch // which ends up calling handleThen. 627c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch handleThen(function() {}, function() {}); 628a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 629a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return { 630a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) handleThen: handleThen, 631a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) handleCatch: handleCatch 632a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) }; 633a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 634a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)} 635a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 636a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)registerPromiseAdapter(); 637a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 638a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)/** 639a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * Control promise rejection. 640a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * @enum {number} 641a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) */ 642a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)var PromiseRejection = { 643a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) /** Disallow promise rejection */ 644a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) DISALLOW: 0, 645a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) /** Allow promise rejection */ 646a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) ALLOW: 1 647a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}; 648a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 649a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)/** 650a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * Provides the promise equivalent of instrumented.storage.local.get. 651a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * @param {Object} defaultStorageObject Default storage object to fill. 652a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * @param {PromiseRejection=} opt_allowPromiseRejection If 653a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * PromiseRejection.ALLOW, allow promise rejection on errors, otherwise the 654a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * default storage object is resolved. 655a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * @return {Promise} A promise that fills the default storage object. On 656a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * failure, if promise rejection is allowed, the promise is rejected, 657a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * otherwise it is resolved to the default storage object. 658a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) */ 659a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)function fillFromChromeLocalStorage( 660a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) defaultStorageObject, 661a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) opt_allowPromiseRejection) { 662a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return new Promise(function(resolve, reject) { 663effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch // We have to create a keys array because keys with a default value 664effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch // of undefined will cause that key to not be looked up! 665effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch var keysToGet = []; 666effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch for (var key in defaultStorageObject) { 667effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch keysToGet.push(key); 668effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch } 669effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch instrumented.storage.local.get(keysToGet, function(items) { 670a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (items) { 671effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch // Merge the result with the default storage object to ensure all keys 672effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch // requested have either the default value or the retrieved storage 673effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch // value. 674effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch var result = {}; 675effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch for (var key in defaultStorageObject) { 676effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch result[key] = (key in items) ? items[key] : defaultStorageObject[key]; 677effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch } 678effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch resolve(result); 679a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } else if (opt_allowPromiseRejection === PromiseRejection.ALLOW) { 680a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) reject(); 681a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } else { 682a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) resolve(defaultStorageObject); 683a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 684a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) }); 685a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) }); 686a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)} 6875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 6885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)/** 689424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * Builds the object to manage tasks (mutually exclusive chains of events). 690424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * @param {function(string, string): boolean} areConflicting Function that 691424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * checks if a new task can't be added to a task queue that contains an 692424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * existing task. 693424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * @return {Object} Task manager interface. 694424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) */ 695424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)function buildTaskManager(areConflicting) { 696424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) /** 697424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * Queue of scheduled tasks. The first element, if present, corresponds to the 698424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * currently running task. 6994e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * @type {Array.<Object.<string, function()>>} 700424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) */ 701424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) var queue = []; 702424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 703424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) /** 704424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * Count of unfinished callbacks of the current task. 705424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * @type {number} 706424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) */ 707424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) var taskPendingCallbackCount = 0; 708424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 709424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) /** 710424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * True if currently executed code is a part of a task. 711424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * @type {boolean} 712424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) */ 713424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) var isInTask = false; 714424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 715424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) /** 716424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * Starts the first queued task. 717424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) */ 718424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) function startFirst() { 719424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) verify(queue.length >= 1, 'startFirst: queue is empty'); 720424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) verify(!isInTask, 'startFirst: already in task'); 721424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) isInTask = true; 722424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 723424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) // Start the oldest queued task, but don't remove it from the queue. 724424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) verify( 725424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) taskPendingCallbackCount == 0, 726424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 'tasks.startFirst: still have pending task callbacks: ' + 727424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) taskPendingCallbackCount + 728424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) ', queue = ' + JSON.stringify(queue) + ', ' + 729424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) wrapper.debugGetStateString()); 730424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) var entry = queue[0]; 731424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) console.log('Starting task ' + entry.name); 732424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 7334e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) entry.task(); 734424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 735424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) verify(isInTask, 'startFirst: not in task at exit'); 736424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) isInTask = false; 737424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) if (taskPendingCallbackCount == 0) 738424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) finish(); 739424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) } 740424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 741424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) /** 742424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * Checks if a new task can be added to the task queue. 743424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * @param {string} taskName Name of the new task. 744424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * @return {boolean} Whether the new task can be added. 745424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) */ 746424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) function canQueue(taskName) { 747424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) for (var i = 0; i < queue.length; ++i) { 748424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) if (areConflicting(taskName, queue[i].name)) { 749424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) console.log('Conflict: new=' + taskName + 750424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) ', scheduled=' + queue[i].name); 751424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) return false; 752424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) } 753424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) } 754424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 755424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) return true; 756424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) } 757424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 758424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) /** 759424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * Adds a new task. If another task is not running, runs the task immediately. 760424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * If any task in the queue is not compatible with the task, ignores the new 761424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * task. Otherwise, stores the task for future execution. 762424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * @param {string} taskName Name of the task. 7634e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * @param {function()} task Function to run. 764424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) */ 765424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) function add(taskName, task) { 766424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) wrapper.checkInWrappedCallback(); 767424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) console.log('Adding task ' + taskName); 768424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) if (!canQueue(taskName)) 769424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) return; 770424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 771424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) queue.push({name: taskName, task: task}); 772424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 773424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) if (queue.length == 1) { 774424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) startFirst(); 775424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) } 776424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) } 777424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 778424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) /** 779424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * Completes the current task and starts the next queued task if available. 780424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) */ 781424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) function finish() { 782424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) verify(queue.length >= 1, 783424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 'tasks.finish: The task queue is empty'); 784424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) console.log('Finishing task ' + queue[0].name); 785424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) queue.shift(); 786424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 787424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) if (queue.length >= 1) 788424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) startFirst(); 789424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) } 790424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 791424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) instrumented.runtime.onSuspend.addListener(function() { 792424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) verify( 793424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) queue.length == 0, 794424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 'Incomplete task when unloading event page,' + 795424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) ' queue = ' + JSON.stringify(queue) + ', ' + 796424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) wrapper.debugGetStateString()); 797424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) }); 798424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 799424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 800424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) /** 801424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * Wrapper plugin for tasks. 802424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * @constructor 803424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) */ 804424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) function TasksWrapperPlugin() { 805424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) this.isTaskCallback = isInTask; 806424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) if (this.isTaskCallback) 807424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) ++taskPendingCallbackCount; 808424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) } 809424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 810424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) TasksWrapperPlugin.prototype = { 811424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) /** 812424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * Plugin code to be executed before invoking the original callback. 813424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) */ 814424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) prologue: function() { 815424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) if (this.isTaskCallback) { 816424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) verify(!isInTask, 'TasksWrapperPlugin.prologue: already in task'); 817424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) isInTask = true; 818424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) } 819424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) }, 820424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 821424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) /** 822424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * Plugin code to be executed after invoking the original callback. 823424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) */ 824424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) epilogue: function() { 825424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) if (this.isTaskCallback) { 826424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) verify(isInTask, 'TasksWrapperPlugin.epilogue: not in task at exit'); 827424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) isInTask = false; 828424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) if (--taskPendingCallbackCount == 0) 829424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) finish(); 830424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) } 831424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) } 832424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) }; 833424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 834424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) wrapper.registerWrapperPluginFactory(function() { 835424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) return new TasksWrapperPlugin(); 836424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) }); 837424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 838424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) return { 8394e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) add: add 8402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) }; 8412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 84290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 84390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)/** 84490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * Builds an object to manage retrying activities with exponential backoff. 84590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * @param {string} name Name of this attempt manager. 84690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * @param {function()} attempt Activity that the manager retries until it 84790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * calls 'stop' method. 84890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * @param {number} initialDelaySeconds Default first delay until first retry. 84990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * @param {number} maximumDelaySeconds Maximum delay between retries. 85090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * @return {Object} Attempt manager interface. 85190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) */ 85290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)function buildAttemptManager( 85390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) name, attempt, initialDelaySeconds, maximumDelaySeconds) { 854558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch var alarmName = 'attempt-scheduler-' + name; 855558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch var currentDelayStorageKey = 'current-delay-' + name; 85690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 85790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) /** 85890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * Creates an alarm for the next attempt. The alarm is repeating for the case 85990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * when the next attempt crashes before registering next alarm. 86090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * @param {number} delaySeconds Delay until next retry. 86190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) */ 86290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) function createAlarm(delaySeconds) { 86390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) var alarmInfo = { 86490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) delayInMinutes: delaySeconds / 60, 86590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) periodInMinutes: maximumDelaySeconds / 60 86690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) }; 86790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) chrome.alarms.create(alarmName, alarmInfo); 86890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 86990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 87090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) /** 871a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) * Indicates if this attempt manager has started. 872a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) * @param {function(boolean)} callback The function's boolean parameter is 873a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) * true if the attempt manager has started, false otherwise. 874a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) */ 875a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) function isRunning(callback) { 876ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch instrumented.alarms.get(alarmName, function(alarmInfo) { 877a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) callback(!!alarmInfo); 878a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) }); 879a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) } 880a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) 881a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) /** 882010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * Schedules the alarm with a random factor to reduce the chance that all 883010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * clients will fire their timers at the same time. 884010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {number} durationSeconds Number of seconds before firing the alarm. 88590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) */ 886010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) function scheduleAlarm(durationSeconds) { 8875f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) durationSeconds = Math.min(durationSeconds, maximumDelaySeconds); 8885f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) var randomizedRetryDuration = durationSeconds * (1 + 0.2 * Math.random()); 88990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 890010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) createAlarm(randomizedRetryDuration); 89190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 89290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) var items = {}; 893010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) items[currentDelayStorageKey] = randomizedRetryDuration; 894ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch chrome.storage.local.set(items); 89590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 89690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 89790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) /** 89890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * Starts repeated attempts. 89990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * @param {number=} opt_firstDelaySeconds Time until the first attempt, if 90090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * specified. Otherwise, initialDelaySeconds will be used for the first 90190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * attempt. 90290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) */ 90390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) function start(opt_firstDelaySeconds) { 90490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) if (opt_firstDelaySeconds) { 90590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) createAlarm(opt_firstDelaySeconds); 906ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch chrome.storage.local.remove(currentDelayStorageKey); 90790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } else { 908010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) scheduleAlarm(initialDelaySeconds); 90990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 91090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 91190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 91290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) /** 91390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * Stops repeated attempts. 91490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) */ 91590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) function stop() { 91690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) chrome.alarms.clear(alarmName); 917ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch chrome.storage.local.remove(currentDelayStorageKey); 91890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 91990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 92090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) /** 921010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * Schedules an exponential backoff retry. 922010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @return {Promise} A promise to schedule the retry. 92390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) */ 924010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) function scheduleRetry() { 925a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) var request = {}; 926a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) request[currentDelayStorageKey] = undefined; 927010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) return fillFromChromeLocalStorage(request, PromiseRejection.ALLOW) 928a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) .catch(function() { 929a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) request[currentDelayStorageKey] = maximumDelaySeconds; 930a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return Promise.resolve(request); 931010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) }) 932010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) .then(function(items) { 933010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) console.log('scheduleRetry-get-storage ' + JSON.stringify(items)); 934010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) var retrySeconds = initialDelaySeconds; 935010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) if (items[currentDelayStorageKey]) { 936010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) retrySeconds = items[currentDelayStorageKey] * 2; 937010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) } 938010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) scheduleAlarm(retrySeconds); 939a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) }); 94090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 94190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 942ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch instrumented.alarms.onAlarm.addListener(function(alarm) { 94390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) if (alarm.name == alarmName) 944a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) isRunning(function(running) { 945a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) if (running) 946a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) attempt(); 947a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) }); 94890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) }); 94990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 95090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) return { 95190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) start: start, 952010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) scheduleRetry: scheduleRetry, 953a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) stop: stop, 954a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) isRunning: isRunning 955a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) }; 956a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)} 957a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) 9581e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)// TODO(robliao): Use signed-in state change watch API when it's available. 959a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)/** 960a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) * Wraps chrome.identity to provide limited listening support for 961a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) * the sign in state by polling periodically for the auth token. 962a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) * @return {Object} The Authentication Manager interface. 963a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) */ 964a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)function buildAuthenticationManager() { 965a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) var alarmName = 'sign-in-alarm'; 966a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) 967a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) /** 9681e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) * Gets an OAuth2 access token. 969a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * @return {Promise} A promise to get the authentication token. If there is 970a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * no token, the request is rejected. 971a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) */ 972a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) function getAuthToken() { 973a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return new Promise(function(resolve, reject) { 974a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) instrumented.identity.getAuthToken({interactive: false}, function(token) { 975a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (chrome.runtime.lastError || !token) { 976a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) reject(); 977a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } else { 978a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) resolve(token); 979a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 980a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) }); 9811e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) }); 9821e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 9831e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 9841e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) /** 9851e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) * Determines whether there is an account attached to the profile. 986a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * @return {Promise} A promise to determine if there is an account attached 987a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * to the profile. 9881e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) */ 989a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) function isSignedIn() { 990a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return new Promise(function(resolve) { 991a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) instrumented.webstorePrivate.getBrowserLogin(function(accountInfo) { 992a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) resolve(!!accountInfo.login); 993a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) }); 994a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) }); 995a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) } 996a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) 997a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) /** 998a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) * Removes the specified cached token. 999a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) * @param {string} token Authentication Token to remove from the cache. 1000a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * @return {Promise} A promise that resolves on completion. 1001a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) */ 1002a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) function removeToken(token) { 1003a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return new Promise(function(resolve) { 1004a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) instrumented.identity.removeCachedAuthToken({token: token}, function() { 1005a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // Let Chrome know about a possible problem with the token. 1006a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) getAuthToken(); 1007a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) resolve(); 1008a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) }); 1009a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) }); 1010a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) } 1011a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) 1012a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) var listeners = []; 1013a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) 1014a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) /** 1015a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) * Registers a listener that gets called back when the signed in state 1016a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) * is found to be changed. 1017a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * @param {function()} callback Called when the answer to isSignedIn changes. 1018a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) */ 1019a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) function addListener(callback) { 1020a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) listeners.push(callback); 1021a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) } 1022a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) 1023424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) /** 10241e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) * Checks if the last signed in state matches the current one. 1025424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) * If it doesn't, it notifies the listeners of the change. 1026424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) */ 10271e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) function checkAndNotifyListeners() { 1028a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) isSignedIn().then(function(signedIn) { 1029a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) fillFromChromeLocalStorage({lastSignedInState: undefined}) 1030a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) .then(function(items) { 1031a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (items.lastSignedInState != signedIn) { 1032a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) chrome.storage.local.set( 1033a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) {lastSignedInState: signedIn}); 1034a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) listeners.forEach(function(callback) { 1035a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) callback(); 1036a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) }); 1037a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 1038a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) }); 10391e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) }); 1040a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) } 1041a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) 10421e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) instrumented.identity.onSignInChanged.addListener(function() { 10431e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) checkAndNotifyListeners(); 10441e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) }); 10451e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 1046ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch instrumented.alarms.onAlarm.addListener(function(alarm) { 1047a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) if (alarm.name == alarmName) 10481e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) checkAndNotifyListeners(); 1049a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) }); 1050a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) 1051a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) // Poll for the sign in state every hour. 1052a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) // One hour is just an arbitrary amount of time chosen. 1053a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) chrome.alarms.create(alarmName, {periodInMinutes: 60}); 1054a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) 1055a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) return { 1056a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) addListener: addListener, 10571e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) getAuthToken: getAuthToken, 1058a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) isSignedIn: isSignedIn, 1059a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) removeToken: removeToken 106090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) }; 106190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)} 1062