Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Side by Side Diff: lib/downloader.js

Issue 29375915: Issue 4878 - Start using ESLint for adblockpluscore (Closed)
Patch Set: Rebased. Created Feb. 28, 2017, 3:55 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * This file is part of Adblock Plus <https://adblockplus.org/>, 2 * This file is part of Adblock Plus <https://adblockplus.org/>,
3 * Copyright (C) 2006-2016 Eyeo GmbH 3 * Copyright (C) 2006-2016 Eyeo GmbH
4 * 4 *
5 * Adblock Plus is free software: you can redistribute it and/or modify 5 * Adblock Plus is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 3 as 6 * it under the terms of the GNU General Public License version 3 as
7 * published by the Free Software Foundation. 7 * published by the Free Software Foundation.
8 * 8 *
9 * Adblock Plus is distributed in the hope that it will be useful, 9 * Adblock Plus is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details. 12 * GNU General Public License for more details.
13 * 13 *
14 * You should have received a copy of the GNU General Public License 14 * You should have received a copy of the GNU General Public License
15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. 15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
16 */ 16 */
17 17
18 "use strict";
19
18 /** 20 /**
19 * @fileOverview Downloads a set of URLs in regular time intervals. 21 * @fileOverview Downloads a set of URLs in regular time intervals.
20 */ 22 */
21 23
22 let {Utils} = require("utils"); 24 const {Utils} = require("utils");
Wladimir Palant 2017/03/02 14:06:43 ESLint doesn't seem to require this change. But if
kzar 2017/03/08 12:33:27 Done.
23 25
24 let MILLIS_IN_SECOND = exports.MILLIS_IN_SECOND = 1000; 26 let MILLIS_IN_SECOND = exports.MILLIS_IN_SECOND = 1000;
25 let MILLIS_IN_MINUTE = exports.MILLIS_IN_MINUTE = 60 * MILLIS_IN_SECOND; 27 let MILLIS_IN_MINUTE = exports.MILLIS_IN_MINUTE = 60 * MILLIS_IN_SECOND;
26 let MILLIS_IN_HOUR = exports.MILLIS_IN_HOUR = 60 * MILLIS_IN_MINUTE; 28 let MILLIS_IN_HOUR = exports.MILLIS_IN_HOUR = 60 * MILLIS_IN_MINUTE;
27 let MILLIS_IN_DAY = exports.MILLIS_IN_DAY = 24 * MILLIS_IN_HOUR; 29 let MILLIS_IN_DAY = exports.MILLIS_IN_DAY = 24 * MILLIS_IN_HOUR;
28 30
31 let Downloader =
29 /** 32 /**
30 * Creates a new downloader instance. 33 * Creates a new downloader instance.
31 * @param {Function} dataSource Function that will yield downloadable objects o n each check 34 * @param {Function} dataSource Function that will yield downloadable objects
32 * @param {Integer} initialDelay Number of milliseconds to wait before the firs t check 35 * on each check
33 * @param {Integer} checkInterval Interval between the checks 36 * @param {number} initialDelay Number of milliseconds to wait before the
37 * first check
38 * @param {number} checkInterval Interval between the checks
Wladimir Palant 2017/03/02 14:06:42 Nit: Indentation is messy here. I suggest avoiding
kzar 2017/03/08 12:33:28 Done.
34 * @constructor 39 * @constructor
35 */ 40 */
36 let Downloader = exports.Downloader = function Downloader(dataSource, initialDel ay, checkInterval) 41 exports.Downloader = function(dataSource, initialDelay, checkInterval)
37 { 42 {
38 this.dataSource = dataSource; 43 this.dataSource = dataSource;
39 this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); 44 this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
40 this._timer.initWithCallback(function() 45 this._timer.initWithCallback(() =>
41 { 46 {
42 this._timer.delay = checkInterval; 47 this._timer.delay = checkInterval;
43 this._doCheck(); 48 this._doCheck();
44 }.bind(this), initialDelay, Ci.nsITimer.TYPE_REPEATING_SLACK); 49 }, initialDelay, Ci.nsITimer.TYPE_REPEATING_SLACK);
45 this._downloading = Object.create(null); 50 this._downloading = Object.create(null);
46 } 51 };
47 Downloader.prototype = 52 Downloader.prototype =
48 { 53 {
49 /** 54 /**
50 * Timer triggering the downloads. 55 * Timer triggering the downloads.
51 * @type nsITimer 56 * @type {nsITimer}
52 */ 57 */
53 _timer: null, 58 _timer: null,
54 59
55 /** 60 /**
56 * Map containing the URLs of objects currently being downloaded as its keys. 61 * Map containing the URLs of objects currently being downloaded as its keys.
57 */ 62 */
58 _downloading: null, 63 _downloading: null,
59 64
60 /** 65 /**
61 * Function that will yield downloadable objects on each check. 66 * Function that will yield downloadable objects on each check.
62 * @type Function 67 * @type {Function}
63 */ 68 */
64 dataSource: null, 69 dataSource: null,
65 70
66 /** 71 /**
67 * Maximal time interval that the checks can be left out until the soft 72 * Maximal time interval that the checks can be left out until the soft
68 * expiration interval increases. 73 * expiration interval increases.
69 * @type Integer 74 * @type {number}
70 */ 75 */
71 maxAbsenceInterval: 1 * MILLIS_IN_DAY, 76 maxAbsenceInterval: 1 * MILLIS_IN_DAY,
72 77
73 /** 78 /**
74 * Minimal time interval before retrying a download after an error. 79 * Minimal time interval before retrying a download after an error.
75 * @type Integer 80 * @type {number}
76 */ 81 */
77 minRetryInterval: 1 * MILLIS_IN_DAY, 82 minRetryInterval: 1 * MILLIS_IN_DAY,
78 83
79 /** 84 /**
80 * Maximal allowed expiration interval, larger expiration intervals will be 85 * Maximal allowed expiration interval, larger expiration intervals will be
81 * corrected. 86 * corrected.
82 * @type Integer 87 * @type {number}
83 */ 88 */
84 maxExpirationInterval: 14 * MILLIS_IN_DAY, 89 maxExpirationInterval: 14 * MILLIS_IN_DAY,
85 90
86 /** 91 /**
87 * Maximal number of redirects before the download is considered as failed. 92 * Maximal number of redirects before the download is considered as failed.
88 * @type Integer 93 * @type {number}
89 */ 94 */
90 maxRedirects: 5, 95 maxRedirects: 5,
91 96
92 /** 97 /**
93 * Called whenever expiration intervals for an object need to be adapted. 98 * Called whenever expiration intervals for an object need to be adapted.
94 * @type Function 99 * @type {Function}
95 */ 100 */
96 onExpirationChange: null, 101 onExpirationChange: null,
97 102
98 /** 103 /**
99 * Callback to be triggered whenever a download starts. 104 * Callback to be triggered whenever a download starts.
100 * @type Function 105 * @type {Function}
101 */ 106 */
102 onDownloadStarted: null, 107 onDownloadStarted: null,
103 108
104 /** 109 /**
105 * Callback to be triggered whenever a download finishes successfully. The 110 * Callback to be triggered whenever a download finishes successfully. The
106 * callback can return an error code to indicate that the data is wrong. 111 * callback can return an error code to indicate that the data is wrong.
107 * @type Function 112 * @type {Function}
108 */ 113 */
109 onDownloadSuccess: null, 114 onDownloadSuccess: null,
110 115
111 /** 116 /**
112 * Callback to be triggered whenever a download fails. 117 * Callback to be triggered whenever a download fails.
113 * @type Function 118 * @type {Function}
114 */ 119 */
115 onDownloadError: null, 120 onDownloadError: null,
116 121
117 /** 122 /**
118 * Checks whether anything needs downloading. 123 * Checks whether anything needs downloading.
119 */ 124 */
120 _doCheck: function() 125 _doCheck()
121 { 126 {
122 let now = Date.now(); 127 let now = Date.now();
123 for (let downloadable of this.dataSource()) 128 for (let downloadable of this.dataSource())
124 { 129 {
125 if (downloadable.lastCheck && now - downloadable.lastCheck > this.maxAbsen ceInterval) 130 if (downloadable.lastCheck &&
131 now - downloadable.lastCheck > this.maxAbsenceInterval)
126 { 132 {
127 // No checks for a long time interval - user must have been offline, e.g . 133 // No checks for a long time interval - user must have been
128 // during a weekend. Increase soft expiration to prevent load peaks on t he 134 // offline, e.g. during a weekend. Increase soft expiration
Wladimir Palant 2017/03/02 14:06:44 Nit: "offline" should go on the previous line.
kzar 2017/03/08 12:33:26 Done.
129 // server. 135 // to prevent load peaks on the server.
130 downloadable.softExpiration += now - downloadable.lastCheck; 136 downloadable.softExpiration += now - downloadable.lastCheck;
131 } 137 }
132 downloadable.lastCheck = now; 138 downloadable.lastCheck = now;
133 139
134 // Sanity check: do expiration times make sense? Make sure people changing 140 // Sanity check: do expiration times make sense? Make sure people changing
135 // system clock don't get stuck with outdated subscriptions. 141 // system clock don't get stuck with outdated subscriptions.
136 if (downloadable.hardExpiration - now > this.maxExpirationInterval) 142 if (downloadable.hardExpiration - now > this.maxExpirationInterval)
137 downloadable.hardExpiration = now + this.maxExpirationInterval; 143 downloadable.hardExpiration = now + this.maxExpirationInterval;
138 if (downloadable.softExpiration - now > this.maxExpirationInterval) 144 if (downloadable.softExpiration - now > this.maxExpirationInterval)
139 downloadable.softExpiration = now + this.maxExpirationInterval; 145 downloadable.softExpiration = now + this.maxExpirationInterval;
140 146
141 // Notify the caller about changes to expiration parameters 147 // Notify the caller about changes to expiration parameters
142 if (this.onExpirationChange) 148 if (this.onExpirationChange)
143 this.onExpirationChange(downloadable); 149 this.onExpirationChange(downloadable);
144 150
145 // Does that object need downloading? 151 // Does that object need downloading?
146 if (downloadable.softExpiration > now && downloadable.hardExpiration > now ) 152 if (downloadable.softExpiration > now &&
153 downloadable.hardExpiration > now)
147 continue; 154 continue;
Wladimir Palant 2017/03/02 14:06:43 Nit: For multi-line conditions I suggest always ad
kzar 2017/03/08 12:33:29 Done.
148 155
149 // Do not retry downloads too often 156 // Do not retry downloads too often
150 if (downloadable.lastError && now - downloadable.lastError < this.minRetry Interval) 157 if (downloadable.lastError &&
158 now - downloadable.lastError < this.minRetryInterval)
151 continue; 159 continue;
152 160
153 this._download(downloadable, 0); 161 this._download(downloadable, 0);
154 } 162 }
155 }, 163 },
156 164
157 /** 165 /**
158 * Stops the periodic checks. 166 * Stops the periodic checks.
159 */ 167 */
160 cancel: function() 168 cancel()
161 { 169 {
162 this._timer.cancel(); 170 this._timer.cancel();
163 }, 171 },
164 172
165 /** 173 /**
166 * Checks whether an address is currently being downloaded. 174 * Checks whether an address is currently being downloaded.
175 * @param {string} url
176 * @return {boolean}
167 */ 177 */
168 isDownloading: function(/**String*/ url) /**Boolean*/ 178 isDownloading(url)
169 { 179 {
170 return url in this._downloading; 180 return url in this._downloading;
171 }, 181 },
172 182
173 /** 183 /**
174 * Starts downloading for an object. 184 * Starts downloading for an object.
185 * @param {Downloadable} downloadable
175 */ 186 */
176 download: function(/**Downloadable*/ downloadable) 187 download(downloadable)
177 { 188 {
178 // Make sure to detach download from the current execution context 189 // Make sure to detach download from the current execution context
179 Utils.runAsync(this._download.bind(this, downloadable, 0)); 190 Utils.runAsync(this._download.bind(this, downloadable, 0));
180 }, 191 },
181 192
182 /** 193 /**
183 * Generates the real download URL for an object by appending various 194 * Generates the real download URL for an object by appending various
184 * parameters. 195 * parameters.
196 * @param {Downloadable} downloadable
197 * @return {string}
185 */ 198 */
186 getDownloadUrl: function(/**Downloadable*/ downloadable) /** String*/ 199 getDownloadUrl(downloadable)
187 { 200 {
188 let {addonName, addonVersion, application, applicationVersion, platform, pla tformVersion} = require("info"); 201 const {addonName, addonVersion, application, applicationVersion,
202 platform, platformVersion} = require("info");
189 let url = downloadable.redirectURL || downloadable.url; 203 let url = downloadable.redirectURL || downloadable.url;
190 if (url.indexOf("?") >= 0) 204 if (url.indexOf("?") >= 0)
191 url += "&"; 205 url += "&";
192 else 206 else
193 url += "?"; 207 url += "?";
194 // We limit the download count to 4+ to keep the request anonymized 208 // We limit the download count to 4+ to keep the request anonymized
195 let downloadCount = downloadable.downloadCount; 209 let {downloadCount} = downloadable;
196 if (downloadCount > 4) 210 if (downloadCount > 4)
197 downloadCount = "4+"; 211 downloadCount = "4+";
198 url += "addonName=" + encodeURIComponent(addonName) + 212 url += "addonName=" + encodeURIComponent(addonName) +
199 "&addonVersion=" + encodeURIComponent(addonVersion) + 213 "&addonVersion=" + encodeURIComponent(addonVersion) +
200 "&application=" + encodeURIComponent(application) + 214 "&application=" + encodeURIComponent(application) +
201 "&applicationVersion=" + encodeURIComponent(applicationVersion) + 215 "&applicationVersion=" + encodeURIComponent(applicationVersion) +
202 "&platform=" + encodeURIComponent(platform) + 216 "&platform=" + encodeURIComponent(platform) +
203 "&platformVersion=" + encodeURIComponent(platformVersion) + 217 "&platformVersion=" + encodeURIComponent(platformVersion) +
204 "&lastVersion=" + encodeURIComponent(downloadable.lastVersion) + 218 "&lastVersion=" + encodeURIComponent(downloadable.lastVersion) +
205 "&downloadCount=" + encodeURIComponent(downloadCount); 219 "&downloadCount=" + encodeURIComponent(downloadCount);
206 return url; 220 return url;
207 }, 221 },
208 222
209 _download: function(downloadable, redirects) 223 _download(downloadable, redirects)
210 { 224 {
211 if (this.isDownloading(downloadable.url)) 225 if (this.isDownloading(downloadable.url))
212 return; 226 return;
213 227
214 let downloadUrl = this.getDownloadUrl(downloadable); 228 let downloadUrl = this.getDownloadUrl(downloadable);
215 let request = null; 229 let request = null;
216 230
217 let errorCallback = function errorCallback(error) 231 let errorCallback = function errorCallback(error)
218 { 232 {
219 let channelStatus = -1; 233 let channelStatus = -1;
220 try 234 try
221 { 235 {
222 channelStatus = request.channel.status; 236 channelStatus = request.channel.status;
223 } catch (e) {} 237 }
238 catch (e) {}
224 239
225 let responseStatus = request.status; 240 let responseStatus = request.status;
226 241
227 Cu.reportError("Adblock Plus: Downloading URL " + downloadable.url + " fai led (" + error + ")\n" + 242 Cu.reportError("Adblock Plus: Downloading URL " + downloadable.url +
243 " failed (" + error + ")\n" +
228 "Download address: " + downloadUrl + "\n" + 244 "Download address: " + downloadUrl + "\n" +
229 "Channel status: " + channelStatus + "\n" + 245 "Channel status: " + channelStatus + "\n" +
230 "Server response: " + responseStatus); 246 "Server response: " + responseStatus);
231 247
232 if (this.onDownloadError) 248 if (this.onDownloadError)
233 { 249 {
234 // Allow one extra redirect if the error handler gives us a redirect URL 250 // Allow one extra redirect if the error handler gives us a redirect URL
235 let redirectCallback = null; 251 let redirectCallback = null;
236 if (redirects <= this.maxRedirects) 252 if (redirects <= this.maxRedirects)
237 { 253 {
238 redirectCallback = function redirectCallback(url) 254 redirectCallback = (url) =>
Wladimir Palant 2017/03/02 14:06:44 Nit: Parentheses around parameter are unnecessary
kzar 2017/03/08 12:33:26 Done. (Also added the "as-needed" arrow-parens rul
239 { 255 {
240 downloadable.redirectURL = url; 256 downloadable.redirectURL = url;
241 this._download(downloadable, redirects + 1); 257 this._download(downloadable, redirects + 1);
242 }.bind(this); 258 };
243 } 259 }
244 260
245 this.onDownloadError(downloadable, downloadUrl, error, channelStatus, re sponseStatus, redirectCallback); 261 this.onDownloadError(downloadable, downloadUrl, error, channelStatus,
262 responseStatus, redirectCallback);
246 } 263 }
247 }.bind(this); 264 }.bind(this);
248 265
249 try 266 try
250 { 267 {
251 request = new XMLHttpRequest(); 268 request = new XMLHttpRequest();
252 request.mozBackgroundRequest = true; 269 request.mozBackgroundRequest = true;
253 request.open("GET", downloadUrl); 270 request.open("GET", downloadUrl);
254 } 271 }
255 catch (e) 272 catch (e)
256 { 273 {
257 errorCallback("synchronize_invalid_url"); 274 errorCallback("synchronize_invalid_url");
258 return; 275 return;
259 } 276 }
260 277
261 try { 278 try
279 {
262 request.overrideMimeType("text/plain"); 280 request.overrideMimeType("text/plain");
263 request.channel.loadFlags = request.channel.loadFlags | 281 request.channel.loadFlags = request.channel.loadFlags |
264 request.channel.INHIBIT_CACHING | 282 request.channel.INHIBIT_CACHING |
265 request.channel.VALIDATE_ALWAYS; 283 request.channel.VALIDATE_ALWAYS;
266 284
267 // Override redirect limit from preferences, user might have set it to 1 285 // Override redirect limit from preferences, user might have set it to 1
268 if (request.channel instanceof Ci.nsIHttpChannel) 286 if (request.channel instanceof Ci.nsIHttpChannel)
269 request.channel.redirectionLimit = this.maxRedirects; 287 request.channel.redirectionLimit = this.maxRedirects;
270 } 288 }
271 catch (e) 289 catch (e)
272 { 290 {
273 Cu.reportError(e) 291 Cu.reportError(e);
274 } 292 }
275 293
276 request.addEventListener("error", function(event) 294 request.addEventListener("error", event =>
277 { 295 {
278 if (onShutdown.done) 296 if (onShutdown.done)
279 return; 297 return;
280 298
281 delete this._downloading[downloadable.url]; 299 delete this._downloading[downloadable.url];
282 errorCallback("synchronize_connection_error"); 300 errorCallback("synchronize_connection_error");
283 }.bind(this), false); 301 }, false);
284 302
285 request.addEventListener("load", function(event) 303 request.addEventListener("load", event =>
286 { 304 {
287 if (onShutdown.done) 305 if (onShutdown.done)
288 return; 306 return;
289 307
290 delete this._downloading[downloadable.url]; 308 delete this._downloading[downloadable.url];
291 309
292 // Status will be 0 for non-HTTP requests 310 // Status will be 0 for non-HTTP requests
293 if (request.status && request.status != 200) 311 if (request.status && request.status != 200)
294 { 312 {
295 errorCallback("synchronize_connection_error"); 313 errorCallback("synchronize_connection_error");
296 return; 314 return;
297 } 315 }
298 316
299 downloadable.downloadCount++; 317 downloadable.downloadCount++;
300 318
301 this.onDownloadSuccess(downloadable, request.responseText, errorCallback, function redirectCallback(url) 319 this.onDownloadSuccess(
302 { 320 downloadable, request.responseText, errorCallback,
303 if (redirects >= this.maxRedirects) 321 url =>
304 errorCallback("synchronize_connection_error");
305 else
306 { 322 {
307 downloadable.redirectURL = url; 323 if (redirects >= this.maxRedirects)
308 this._download(downloadable, redirects + 1); 324 errorCallback("synchronize_connection_error");
325 else
326 {
327 downloadable.redirectURL = url;
328 this._download(downloadable, redirects + 1);
329 }
309 } 330 }
310 }.bind(this)); 331 );
311 }.bind(this), false); 332 });
312 333
313 request.send(null); 334 request.send(null);
314 335
315 this._downloading[downloadable.url] = true; 336 this._downloading[downloadable.url] = true;
316 if (this.onDownloadStarted) 337 if (this.onDownloadStarted)
317 this.onDownloadStarted(downloadable); 338 this.onDownloadStarted(downloadable);
318 }, 339 },
319 340
320 /** 341 /**
321 * Produces a soft and a hard expiration interval for a given supplied 342 * Produces a soft and a hard expiration interval for a given supplied
322 * expiration interval. 343 * expiration interval.
344 * @param {number} interval
323 * @return {Array} soft and hard expiration interval 345 * @return {Array} soft and hard expiration interval
324 */ 346 */
325 processExpirationInterval: function(/**Integer*/ interval) 347 processExpirationInterval(interval)
326 { 348 {
327 interval = Math.min(Math.max(interval, 0), this.maxExpirationInterval); 349 interval = Math.min(Math.max(interval, 0), this.maxExpirationInterval);
328 let soft = Math.round(interval * (Math.random() * 0.4 + 0.8)); 350 let soft = Math.round(interval * (Math.random() * 0.4 + 0.8));
329 let hard = interval * 2; 351 let hard = interval * 2;
330 let now = Date.now(); 352 let now = Date.now();
331 return [now + soft, now + hard]; 353 return [now + soft, now + hard];
332 } 354 }
333 }; 355 };
334 356
335 /** 357 /**
336 * An object that can be downloaded by the downloadable 358 * An object that can be downloaded by the downloadable
337 * @param {String} url URL that has to be requested for the object 359 * @param {string} url URL that has to be requested for the object
338 * @constructor 360 * @constructor
339 */ 361 */
340 let Downloadable = exports.Downloadable = function Downloadable(url) 362 let Downloadable = exports.Downloadable = function Downloadable(url)
341 { 363 {
342 this.url = url; 364 this.url = url;
343 } 365 };
344 Downloadable.prototype = 366 Downloadable.prototype =
345 { 367 {
346 /** 368 /**
347 * URL that has to be requested for the object. 369 * URL that has to be requested for the object.
348 * @type String 370 * @type {string}
349 */ 371 */
350 url: null, 372 url: null,
351 373
352 /** 374 /**
353 * URL that the download was redirected to if any. 375 * URL that the download was redirected to if any.
354 * @type String 376 * @type {string}
355 */ 377 */
356 redirectURL: null, 378 redirectURL: null,
357 379
358 /** 380 /**
359 * Time of last download error or 0 if the last download was successful. 381 * Time of last download error or 0 if the last download was successful.
360 * @type Integer 382 * @type {number}
361 */ 383 */
362 lastError: 0, 384 lastError: 0,
363 385
364 /** 386 /**
365 * Time of last check whether the object needs downloading. 387 * Time of last check whether the object needs downloading.
366 * @type Integer 388 * @type {number}
367 */ 389 */
368 lastCheck: 0, 390 lastCheck: 0,
369 391
370 /** 392 /**
371 * Object version corresponding to the last successful download. 393 * Object version corresponding to the last successful download.
372 * @type Integer 394 * @type {number}
373 */ 395 */
374 lastVersion: 0, 396 lastVersion: 0,
375 397
376 /** 398 /**
377 * Soft expiration interval, will increase if no checks are performed for a 399 * Soft expiration interval, will increase if no checks are performed for a
378 * while. 400 * while.
379 * @type Integer 401 * @type {number}
380 */ 402 */
381 softExpiration: 0, 403 softExpiration: 0,
382 404
383 /** 405 /**
384 * Hard expiration interval, this is fixed. 406 * Hard expiration interval, this is fixed.
385 * @type Integer 407 * @type {number}
386 */ 408 */
387 hardExpiration: 0, 409 hardExpiration: 0,
388 410
389 /** 411 /**
390 * Number indicating how often the object was downloaded. 412 * Number indicating how often the object was downloaded.
391 * @type Integer 413 * @type {number}
392 */ 414 */
393 downloadCount: 0, 415 downloadCount: 0
394 }; 416 };
OLDNEW

Powered by Google App Engine
This is Rietveld