|  | // Copyright 2014 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | /** | 
|  | * @fileoverview Provides a countdown-based timer implementation. | 
|  | */ | 
|  | 'use strict'; | 
|  |  | 
|  | /** | 
|  | * Constructs a new timer.  The timer has a very limited resolution, and does | 
|  | * not attempt to be millisecond accurate. Its intended use is as a | 
|  | * low-precision timer that pauses while debugging. | 
|  | * @param {!SystemTimer} sysTimer The system timer implementation. | 
|  | * @param {number=} timeoutMillis how long, in milliseconds, the countdown | 
|  | *     lasts. | 
|  | * @param {Function=} cb called back when the countdown expires. | 
|  | * @constructor | 
|  | * @implements {Countdown} | 
|  | */ | 
|  | function CountdownTimer(sysTimer, timeoutMillis, cb) { | 
|  | /** @private {!SystemTimer} */ | 
|  | this.sysTimer_ = sysTimer; | 
|  | this.remainingMillis = 0; | 
|  | this.setTimeout(timeoutMillis || 0, cb); | 
|  | } | 
|  |  | 
|  | /** Timer interval */ | 
|  | CountdownTimer.TIMER_INTERVAL_MILLIS = 200; | 
|  |  | 
|  | /** | 
|  | * Sets a new timeout for this timer. Only possible if the timer is not | 
|  | * currently active. | 
|  | * @param {number} timeoutMillis how long, in milliseconds, the countdown lasts. | 
|  | * @param {Function=} cb called back when the countdown expires. | 
|  | * @return {boolean} whether the timeout could be set. | 
|  | */ | 
|  | CountdownTimer.prototype.setTimeout = function(timeoutMillis, cb) { | 
|  | if (this.timeoutId) { | 
|  | return false; | 
|  | } | 
|  | if (!timeoutMillis || timeoutMillis < 0) { | 
|  | return false; | 
|  | } | 
|  | this.remainingMillis = timeoutMillis; | 
|  | this.cb = cb; | 
|  | if (this.remainingMillis > CountdownTimer.TIMER_INTERVAL_MILLIS) { | 
|  | this.timeoutId = this.sysTimer_.setInterval( | 
|  | this.timerTick.bind(this), CountdownTimer.TIMER_INTERVAL_MILLIS); | 
|  | } else { | 
|  | // Set a one-shot timer for the last interval. | 
|  | this.timeoutId = this.sysTimer_.setTimeout( | 
|  | this.timerTick.bind(this), this.remainingMillis); | 
|  | } | 
|  | return true; | 
|  | }; | 
|  |  | 
|  | /** Clears this timer's timeout. Timers that are cleared become expired. */ | 
|  | CountdownTimer.prototype.clearTimeout = function() { | 
|  | if (this.timeoutId) { | 
|  | this.sysTimer_.clearTimeout(this.timeoutId); | 
|  | this.timeoutId = undefined; | 
|  | } | 
|  | this.remainingMillis = 0; | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * @return {number} how many milliseconds are remaining until the timer expires. | 
|  | */ | 
|  | CountdownTimer.prototype.millisecondsUntilExpired = function() { | 
|  | return this.remainingMillis > 0 ? this.remainingMillis : 0; | 
|  | }; | 
|  |  | 
|  | /** @return {boolean} whether the timer has expired. */ | 
|  | CountdownTimer.prototype.expired = function() { | 
|  | return this.remainingMillis <= 0; | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Constructs a new clone of this timer, while overriding its callback. | 
|  | * @param {Function=} cb callback for new timer. | 
|  | * @return {!Countdown} new clone. | 
|  | */ | 
|  | CountdownTimer.prototype.clone = function(cb) { | 
|  | return new CountdownTimer(this.sysTimer_, this.remainingMillis, cb); | 
|  | }; | 
|  |  | 
|  | /** Timer callback. */ | 
|  | CountdownTimer.prototype.timerTick = function() { | 
|  | this.remainingMillis -= CountdownTimer.TIMER_INTERVAL_MILLIS; | 
|  | if (this.expired()) { | 
|  | this.sysTimer_.clearTimeout(this.timeoutId); | 
|  | this.timeoutId = undefined; | 
|  | if (this.cb) { | 
|  | this.cb(); | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * A factory for creating CountdownTimers. | 
|  | * @param {!SystemTimer} sysTimer The system timer implementation. | 
|  | * @constructor | 
|  | * @implements {CountdownFactory} | 
|  | */ | 
|  | function CountdownTimerFactory(sysTimer) { | 
|  | /** @private {!SystemTimer} */ | 
|  | this.sysTimer_ = sysTimer; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Creates a new timer. | 
|  | * @param {number} timeoutMillis How long, in milliseconds, the countdown lasts. | 
|  | * @param {function()=} opt_cb Called back when the countdown expires. | 
|  | * @return {!Countdown} The timer. | 
|  | */ | 
|  | CountdownTimerFactory.prototype.createTimer = function(timeoutMillis, opt_cb) { | 
|  | return new CountdownTimer(this.sysTimer_, timeoutMillis, opt_cb); | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Minimum timeout attenuation, below which a response couldn't be reasonably | 
|  | * guaranteed, in seconds. | 
|  | * @const | 
|  | */ | 
|  | var MINIMUM_TIMEOUT_ATTENUATION_SECONDS = 1; | 
|  |  | 
|  | /** | 
|  | * @param {number} timeoutSeconds Timeout value in seconds. | 
|  | * @param {number=} opt_attenuationSeconds Attenuation value in seconds. | 
|  | * @return {number} The timeout value, attenuated to ensure a response can be | 
|  | *     given before the timeout's expiration. | 
|  | */ | 
|  | function attenuateTimeoutInSeconds(timeoutSeconds, opt_attenuationSeconds) { | 
|  | var attenuationSeconds = | 
|  | opt_attenuationSeconds || MINIMUM_TIMEOUT_ATTENUATION_SECONDS; | 
|  | if (timeoutSeconds < attenuationSeconds) { | 
|  | return 0; | 
|  | } | 
|  | return timeoutSeconds - attenuationSeconds; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Default request timeout when none is present in the request, in seconds. | 
|  | * @const | 
|  | */ | 
|  | var DEFAULT_REQUEST_TIMEOUT_SECONDS = 30; | 
|  |  | 
|  | /** | 
|  | * Gets the timeout value from the request, if any, substituting | 
|  | * opt_defaultTimeoutSeconds or DEFAULT_REQUEST_TIMEOUT_SECONDS if the request | 
|  | * does not contain a timeout value. | 
|  | * @param {Object} request The request containing the timeout. | 
|  | * @param {number=} opt_defaultTimeoutSeconds | 
|  | * @return {number} Timeout value, in seconds. | 
|  | */ | 
|  | function getTimeoutValueFromRequest(request, opt_defaultTimeoutSeconds) { | 
|  | var timeoutValueSeconds; | 
|  | if (request.hasOwnProperty('timeoutSeconds')) { | 
|  | timeoutValueSeconds = request['timeoutSeconds']; | 
|  | } else if (request.hasOwnProperty('timeout')) { | 
|  | timeoutValueSeconds = request['timeout']; | 
|  | } else if (opt_defaultTimeoutSeconds !== undefined) { | 
|  | timeoutValueSeconds = opt_defaultTimeoutSeconds; | 
|  | } else { | 
|  | timeoutValueSeconds = DEFAULT_REQUEST_TIMEOUT_SECONDS; | 
|  | } | 
|  | return timeoutValueSeconds; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Creates a new countdown for the given timeout value, attenuated to ensure a | 
|  | * response is given prior to the countdown's expiration, using the given timer | 
|  | * factory. | 
|  | * @param {CountdownFactory} timerFactory The factory to use. | 
|  | * @param {number} timeoutValueSeconds | 
|  | * @param {number=} opt_attenuationSeconds Attenuation value in seconds. | 
|  | * @return {!Countdown} A countdown timer. | 
|  | */ | 
|  | function createAttenuatedTimer( | 
|  | timerFactory, timeoutValueSeconds, opt_attenuationSeconds) { | 
|  | timeoutValueSeconds = | 
|  | attenuateTimeoutInSeconds(timeoutValueSeconds, opt_attenuationSeconds); | 
|  | return timerFactory.createTimer(timeoutValueSeconds * 1000); | 
|  | } |