• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5/**
6 * @fileoverview Provides a countdown-based timer implementation.
7 */
8'use strict';
9
10/**
11 * Constructs a new timer.  The timer has a very limited resolution, and does
12 * not attempt to be millisecond accurate. Its intended use is as a
13 * low-precision timer that pauses while debugging.
14 * @param {number=} timeoutMillis how long, in milliseconds, the countdown
15 *     lasts.
16 * @param {Function=} cb called back when the countdown expires.
17 * @constructor
18 * @implements {Countdown}
19 */
20function CountdownTimer(timeoutMillis, cb) {
21  this.remainingMillis = 0;
22  this.setTimeout(timeoutMillis || 0, cb);
23}
24
25/** Timer interval */
26CountdownTimer.TIMER_INTERVAL_MILLIS = 200;
27
28/**
29 * Sets a new timeout for this timer. Only possible if the timer is not
30 * currently active.
31 * @param {number} timeoutMillis how long, in milliseconds, the countdown lasts.
32 * @param {Function=} cb called back when the countdown expires.
33 * @return {boolean} whether the timeout could be set.
34 */
35CountdownTimer.prototype.setTimeout = function(timeoutMillis, cb) {
36  if (this.timeoutId)
37    return false;
38  if (!timeoutMillis || timeoutMillis < 0)
39    return false;
40  this.remainingMillis = timeoutMillis;
41  this.cb = cb;
42  if (this.remainingMillis > CountdownTimer.TIMER_INTERVAL_MILLIS) {
43    this.timeoutId =
44        window.setInterval(this.timerTick.bind(this),
45            CountdownTimer.TIMER_INTERVAL_MILLIS);
46  } else {
47    // Set a one-shot timer for the last interval.
48    this.timeoutId =
49        window.setTimeout(this.timerTick.bind(this), this.remainingMillis);
50  }
51  return true;
52};
53
54/** Clears this timer's timeout. Timers that are cleared become expired. */
55CountdownTimer.prototype.clearTimeout = function() {
56  if (this.timeoutId) {
57    window.clearTimeout(this.timeoutId);
58    this.timeoutId = undefined;
59  }
60  this.remainingMillis = 0;
61};
62
63/**
64 * @return {number} how many milliseconds are remaining until the timer expires.
65 */
66CountdownTimer.prototype.millisecondsUntilExpired = function() {
67  return this.remainingMillis > 0 ? this.remainingMillis : 0;
68};
69
70/** @return {boolean} whether the timer has expired. */
71CountdownTimer.prototype.expired = function() {
72  return this.remainingMillis <= 0;
73};
74
75/**
76 * Constructs a new clone of this timer, while overriding its callback.
77 * @param {Function=} cb callback for new timer.
78 * @return {!Countdown} new clone.
79 */
80CountdownTimer.prototype.clone = function(cb) {
81  return new CountdownTimer(this.remainingMillis, cb);
82};
83
84/** Timer callback. */
85CountdownTimer.prototype.timerTick = function() {
86  this.remainingMillis -= CountdownTimer.TIMER_INTERVAL_MILLIS;
87  if (this.expired()) {
88    window.clearTimeout(this.timeoutId);
89    this.timeoutId = undefined;
90    if (this.cb) {
91      this.cb();
92    }
93  }
94};
95
96/**
97 * A factory for creating CountdownTimers.
98 * @constructor
99 * @implements {CountdownFactory}
100 */
101function CountdownTimerFactory() {
102}
103
104/**
105 * Creates a new timer.
106 * @param {number} timeoutMillis How long, in milliseconds, the countdown lasts.
107 * @param {function()=} opt_cb Called back when the countdown expires.
108 * @return {!Countdown} The timer.
109 */
110CountdownTimerFactory.prototype.createTimer =
111    function(timeoutMillis, opt_cb) {
112  return new CountdownTimer(timeoutMillis, opt_cb);
113};
114
115/**
116 * Minimum timeout attenuation, below which a response couldn't be reasonably
117 * guaranteed, in seconds.
118 * @const
119 */
120var MINIMUM_TIMEOUT_ATTENUATION_SECONDS = 0.5;
121
122/**
123 * @param {number} timeoutSeconds Timeout value in seconds.
124 * @return {number} The timeout value, attenuated to ensure a response can be
125 *     given before the timeout's expiration.
126 * @private
127 */
128function attenuateTimeoutInSeconds_(timeoutSeconds) {
129  if (timeoutSeconds < MINIMUM_TIMEOUT_ATTENUATION_SECONDS)
130    return 0;
131  return timeoutSeconds - MINIMUM_TIMEOUT_ATTENUATION_SECONDS;
132}
133
134/**
135 * Default request timeout when none is present in the request, in seconds.
136 * @const
137 */
138var DEFAULT_REQUEST_TIMEOUT_SECONDS = 30;
139
140/**
141 * Creates a new countdown from the given request using the given timer factory,
142 * attenuated to ensure a response is given prior to the countdown's expiration.
143 * @param {CountdownFactory} timerFactory The factory to use.
144 * @param {Object} request The request containing the timeout.
145 * @param {number=} opt_defaultTimeoutSeconds
146 * @return {!Countdown} A countdown timer.
147 */
148function createTimerForRequest(timerFactory, request,
149    opt_defaultTimeoutSeconds) {
150  var timeoutValueSeconds;
151  if (request.hasOwnProperty('timeoutSeconds')) {
152    timeoutValueSeconds = request['timeoutSeconds'];
153  } else if (request.hasOwnProperty('timeout')) {
154    timeoutValueSeconds = request['timeout'];
155  } else if (opt_defaultTimeoutSeconds !== undefined) {
156    timeoutValueSeconds = opt_defaultTimeoutSeconds;
157  } else {
158    timeoutValueSeconds = DEFAULT_REQUEST_TIMEOUT_SECONDS;
159  }
160  timeoutValueSeconds = attenuateTimeoutInSeconds_(timeoutValueSeconds);
161  return timerFactory.createTimer(timeoutValueSeconds * 1000);
162}
163