Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/third_party/closure/goog/net/filedownloader.js
2868 views
1
// Copyright 2011 The Closure Library Authors. All Rights Reserved.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
// http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS-IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
/**
16
* @fileoverview A class for downloading remote files and storing them
17
* locally using the HTML5 FileSystem API.
18
*
19
* The directory structure is of the form /HASH/URL/BASENAME:
20
*
21
* The HASH portion is a three-character slice of the hash of the URL. Since the
22
* filesystem has a limit of about 5000 files per directory, this should divide
23
* the downloads roughly evenly among about 5000 directories, thus allowing for
24
* at most 5000^2 downloads.
25
*
26
* The URL portion is the (sanitized) full URL used for downloading the file.
27
* This is used to ensure that each file ends up in a different location, even
28
* if the HASH and BASENAME are the same.
29
*
30
* The BASENAME portion is the basename of the URL. It's used for the filename
31
* proper so that the local filesystem: URL will be downloaded to a file with a
32
* recognizable name.
33
*
34
*/
35
36
goog.provide('goog.net.FileDownloader');
37
goog.provide('goog.net.FileDownloader.Error');
38
39
goog.require('goog.Disposable');
40
goog.require('goog.asserts');
41
goog.require('goog.async.Deferred');
42
goog.require('goog.crypt.hash32');
43
goog.require('goog.debug.Error');
44
goog.require('goog.events');
45
goog.require('goog.events.EventHandler');
46
goog.require('goog.fs');
47
goog.require('goog.fs.DirectoryEntry');
48
goog.require('goog.fs.Error');
49
goog.require('goog.fs.FileSaver');
50
goog.require('goog.net.EventType');
51
goog.require('goog.net.XhrIo');
52
goog.require('goog.net.XhrIoPool');
53
goog.require('goog.object');
54
55
56
57
/**
58
* A class for downloading remote files and storing them locally using the
59
* HTML5 filesystem API.
60
*
61
* @param {!goog.fs.DirectoryEntry} dir The directory in which the downloaded
62
* files are stored. This directory should be solely managed by
63
* FileDownloader.
64
* @param {goog.net.XhrIoPool=} opt_pool The pool of XhrIo objects to use for
65
* downloading files.
66
* @constructor
67
* @extends {goog.Disposable}
68
* @final
69
*/
70
goog.net.FileDownloader = function(dir, opt_pool) {
71
goog.net.FileDownloader.base(this, 'constructor');
72
73
/**
74
* The directory in which the downloaded files are stored.
75
* @type {!goog.fs.DirectoryEntry}
76
* @private
77
*/
78
this.dir_ = dir;
79
80
/**
81
* The pool of XHRs to use for capturing.
82
* @type {!goog.net.XhrIoPool}
83
* @private
84
*/
85
this.pool_ = opt_pool || new goog.net.XhrIoPool();
86
87
/**
88
* A map from URLs to active downloads running for those URLs.
89
* @type {!Object<!goog.net.FileDownloader.Download_>}
90
* @private
91
*/
92
this.downloads_ = {};
93
94
/**
95
* The handler for URL capturing events.
96
* @type {!goog.events.EventHandler<!goog.net.FileDownloader>}
97
* @private
98
*/
99
this.eventHandler_ = new goog.events.EventHandler(this);
100
};
101
goog.inherits(goog.net.FileDownloader, goog.Disposable);
102
103
104
/**
105
* Download a remote file and save its contents to the filesystem. A given file
106
* is uniquely identified by its URL string; this means that the relative and
107
* absolute URLs for a single file are considered different for the purposes of
108
* the FileDownloader.
109
*
110
* Returns a Deferred that will contain the downloaded blob. If there's an error
111
* while downloading the URL, this Deferred will be passed the
112
* {@link goog.net.FileDownloader.Error} object as an errback.
113
*
114
* If a download is already in progress for the given URL, this will return the
115
* deferred blob for that download. If the URL has already been downloaded, this
116
* will fail once it tries to save the downloaded blob.
117
*
118
* When a download is in progress, all Deferreds returned for that download will
119
* be branches of a single parent. If all such branches are cancelled, or if one
120
* is cancelled with opt_deepCancel set, then the download will be cancelled as
121
* well.
122
*
123
* @param {string} url The URL of the file to download.
124
* @return {!goog.async.Deferred} The deferred result blob.
125
*/
126
goog.net.FileDownloader.prototype.download = function(url) {
127
if (this.isDownloading(url)) {
128
return this.downloads_[url].deferred.branch(true /* opt_propagateCancel */);
129
}
130
131
var download = new goog.net.FileDownloader.Download_(url, this);
132
this.downloads_[url] = download;
133
this.pool_.getObject(goog.bind(this.gotXhr_, this, download));
134
return download.deferred.branch(true /* opt_propagateCancel */);
135
};
136
137
138
/**
139
* Return a Deferred that will fire once no download is active for a given URL.
140
* If there's no download active for that URL when this is called, the deferred
141
* will fire immediately; otherwise, it will fire once the download is complete,
142
* whether or not it succeeds.
143
*
144
* @param {string} url The URL of the download to wait for.
145
* @return {!goog.async.Deferred} The Deferred that will fire when the download
146
* is complete.
147
*/
148
goog.net.FileDownloader.prototype.waitForDownload = function(url) {
149
var deferred = new goog.async.Deferred();
150
if (this.isDownloading(url)) {
151
this.downloads_[url].deferred.addBoth(function() {
152
deferred.callback(null);
153
}, this);
154
} else {
155
deferred.callback(null);
156
}
157
return deferred;
158
};
159
160
161
/**
162
* Returns whether or not there is an active download for a given URL.
163
*
164
* @param {string} url The URL of the download to check.
165
* @return {boolean} Whether or not there is an active download for the URL.
166
*/
167
goog.net.FileDownloader.prototype.isDownloading = function(url) {
168
return url in this.downloads_;
169
};
170
171
172
/**
173
* Load a downloaded blob from the filesystem. Will fire a deferred error if the
174
* given URL has not yet been downloaded.
175
*
176
* @param {string} url The URL of the blob to load.
177
* @return {!goog.async.Deferred} The deferred Blob object. The callback will be
178
* passed the blob. If a file API error occurs while loading the blob, that
179
* error will be passed to the errback.
180
*/
181
goog.net.FileDownloader.prototype.getDownloadedBlob = function(url) {
182
return this.getFile_(url).addCallback(function(fileEntry) {
183
return fileEntry.file();
184
});
185
};
186
187
188
/**
189
* Get the local filesystem: URL for a downloaded file. This is different from
190
* the blob: URL that's available from getDownloadedBlob(). If the end user
191
* accesses the filesystem: URL, the resulting file's name will be determined by
192
* the download filename as opposed to an arbitrary GUID. In addition, the
193
* filesystem: URL is connected to a filesystem location, so if the download is
194
* removed then that URL will become invalid.
195
*
196
* Warning: in Chrome 12, some filesystem: URLs are opened inline. This means
197
* that e.g. HTML pages given to the user via filesystem: URLs will be opened
198
* and processed by the browser.
199
*
200
* @param {string} url The URL of the file to get the URL of.
201
* @return {!goog.async.Deferred} The deferred filesystem: URL. The callback
202
* will be passed the URL. If a file API error occurs while loading the
203
* blob, that error will be passed to the errback.
204
*/
205
goog.net.FileDownloader.prototype.getLocalUrl = function(url) {
206
return this.getFile_(url).addCallback(function(fileEntry) {
207
return fileEntry.toUrl();
208
});
209
};
210
211
212
/**
213
* Return (deferred) whether or not a URL has been downloaded. Will fire a
214
* deferred error if something goes wrong when determining this.
215
*
216
* @param {string} url The URL to check.
217
* @return {!goog.async.Deferred} The deferred boolean. The callback will be
218
* passed the boolean. If a file API error occurs while checking the
219
* existence of the downloaded URL, that error will be passed to the
220
* errback.
221
*/
222
goog.net.FileDownloader.prototype.isDownloaded = function(url) {
223
var deferred = new goog.async.Deferred();
224
var blobDeferred = this.getDownloadedBlob(url);
225
blobDeferred.addCallback(function() { deferred.callback(true); });
226
blobDeferred.addErrback(function(err) {
227
if (err.name == goog.fs.Error.ErrorName.NOT_FOUND) {
228
deferred.callback(false);
229
} else {
230
deferred.errback(err);
231
}
232
});
233
return deferred;
234
};
235
236
237
/**
238
* Remove a URL from the FileDownloader.
239
*
240
* This returns a Deferred. If the removal is completed successfully, its
241
* callback will be called without any value. If the removal fails, its errback
242
* will be called with the {@link goog.fs.Error}.
243
*
244
* @param {string} url The URL to remove.
245
* @return {!goog.async.Deferred} The deferred used for registering callbacks on
246
* success or on error.
247
*/
248
goog.net.FileDownloader.prototype.remove = function(url) {
249
return this.getDir_(url, goog.fs.DirectoryEntry.Behavior.DEFAULT)
250
.addCallback(function(dir) { return dir.removeRecursively(); });
251
};
252
253
254
/**
255
* Save a blob for a given URL. This works just as through the blob were
256
* downloaded form that URL, except you specify the blob and no HTTP request is
257
* made.
258
*
259
* If the URL is currently being downloaded, it's indeterminate whether the blob
260
* being set or the blob being downloaded will end up in the filesystem.
261
* Whichever one doesn't get saved will have an error. To ensure that one or the
262
* other takes precedence, use {@link #waitForDownload} to allow the download to
263
* complete before setting the blob.
264
*
265
* @param {string} url The URL at which to set the blob.
266
* @param {!Blob} blob The blob to set.
267
* @param {string=} opt_name The name of the file. If this isn't given, it's
268
* determined from the URL.
269
* @return {!goog.async.Deferred} The deferred used for registering callbacks on
270
* success or on error. This can be cancelled just like a {@link #download}
271
* Deferred. The objects passed to the errback will be
272
* {@link goog.net.FileDownloader.Error}s.
273
*/
274
goog.net.FileDownloader.prototype.setBlob = function(url, blob, opt_name) {
275
var name = this.sanitize_(opt_name || this.urlToName_(url));
276
var download = new goog.net.FileDownloader.Download_(url, this);
277
this.downloads_[url] = download;
278
download.blob = blob;
279
this.getDir_(download.url, goog.fs.DirectoryEntry.Behavior.CREATE_EXCLUSIVE)
280
.addCallback(function(dir) {
281
return dir.getFile(
282
name, goog.fs.DirectoryEntry.Behavior.CREATE_EXCLUSIVE);
283
})
284
.addCallback(goog.bind(this.fileSuccess_, this, download))
285
.addErrback(goog.bind(this.error_, this, download));
286
return download.deferred.branch(true /* opt_propagateCancel */);
287
};
288
289
290
/**
291
* The callback called when an XHR becomes available from the XHR pool.
292
*
293
* @param {!goog.net.FileDownloader.Download_} download The download object for
294
* this download.
295
* @param {!goog.net.XhrIo} xhr The XhrIo object for downloading the page.
296
* @private
297
*/
298
goog.net.FileDownloader.prototype.gotXhr_ = function(download, xhr) {
299
if (download.cancelled) {
300
this.freeXhr_(xhr);
301
return;
302
}
303
304
this.eventHandler_.listen(
305
xhr, goog.net.EventType.SUCCESS,
306
goog.bind(this.xhrSuccess_, this, download));
307
this.eventHandler_.listen(
308
xhr, [goog.net.EventType.ERROR, goog.net.EventType.ABORT],
309
goog.bind(this.error_, this, download));
310
this.eventHandler_.listen(
311
xhr, goog.net.EventType.READY, goog.bind(this.freeXhr_, this, xhr));
312
313
download.xhr = xhr;
314
xhr.setResponseType(goog.net.XhrIo.ResponseType.ARRAY_BUFFER);
315
xhr.send(download.url);
316
};
317
318
319
/**
320
* The callback called when an XHR succeeds in downloading a remote file.
321
*
322
* @param {!goog.net.FileDownloader.Download_} download The download object for
323
* this download.
324
* @private
325
*/
326
goog.net.FileDownloader.prototype.xhrSuccess_ = function(download) {
327
if (download.cancelled) {
328
return;
329
}
330
331
var name = this.sanitize_(
332
this.getName_(
333
/** @type {!goog.net.XhrIo} */ (download.xhr)));
334
var resp = /** @type {ArrayBuffer} */ (download.xhr.getResponse());
335
if (!resp) {
336
// This should never happen - it indicates the XHR hasn't completed, has
337
// failed or has been cleaned up. If it does happen (eg. due to a bug
338
// somewhere) we don't want to pass null to getBlob - it's not valid and
339
// triggers a bug in some versions of WebKit causing it to crash.
340
this.error_(download);
341
return;
342
}
343
344
download.blob = goog.fs.getBlob(resp);
345
delete download.xhr;
346
347
this.getDir_(download.url, goog.fs.DirectoryEntry.Behavior.CREATE_EXCLUSIVE)
348
.addCallback(function(dir) {
349
return dir.getFile(
350
name, goog.fs.DirectoryEntry.Behavior.CREATE_EXCLUSIVE);
351
})
352
.addCallback(goog.bind(this.fileSuccess_, this, download))
353
.addErrback(goog.bind(this.error_, this, download));
354
};
355
356
357
/**
358
* The callback called when a file that will be used for saving a file is
359
* successfully opened.
360
*
361
* @param {!goog.net.FileDownloader.Download_} download The download object for
362
* this download.
363
* @param {!goog.fs.FileEntry} file The newly-opened file object.
364
* @private
365
*/
366
goog.net.FileDownloader.prototype.fileSuccess_ = function(download, file) {
367
if (download.cancelled) {
368
file.remove();
369
return;
370
}
371
372
download.file = file;
373
file.createWriter()
374
.addCallback(goog.bind(this.fileWriterSuccess_, this, download))
375
.addErrback(goog.bind(this.error_, this, download));
376
};
377
378
379
/**
380
* The callback called when a file writer is successfully created for writing a
381
* file to the filesystem.
382
*
383
* @param {!goog.net.FileDownloader.Download_} download The download object for
384
* this download.
385
* @param {!goog.fs.FileWriter} writer The newly-created file writer object.
386
* @private
387
*/
388
goog.net.FileDownloader.prototype.fileWriterSuccess_ = function(
389
download, writer) {
390
if (download.cancelled) {
391
download.file.remove();
392
return;
393
}
394
395
download.writer = writer;
396
writer.write(/** @type {!Blob} */ (download.blob));
397
this.eventHandler_.listenOnce(
398
writer, goog.fs.FileSaver.EventType.WRITE_END,
399
goog.bind(this.writeEnd_, this, download));
400
};
401
402
403
/**
404
* The callback called when file writing ends, whether or not it's successful.
405
*
406
* @param {!goog.net.FileDownloader.Download_} download The download object for
407
* this download.
408
* @private
409
*/
410
goog.net.FileDownloader.prototype.writeEnd_ = function(download) {
411
if (download.cancelled || download.writer.getError()) {
412
this.error_(download, download.writer.getError());
413
return;
414
}
415
416
delete this.downloads_[download.url];
417
download.deferred.callback(download.blob);
418
};
419
420
421
/**
422
* The error callback for all asynchronous operations. Ensures that all stages
423
* of a given download are cleaned up, and emits the error event.
424
*
425
* @param {!goog.net.FileDownloader.Download_} download The download object for
426
* this download.
427
* @param {goog.fs.Error=} opt_err The file error object. Only defined if the
428
* error was raised by the file API.
429
* @private
430
*/
431
goog.net.FileDownloader.prototype.error_ = function(download, opt_err) {
432
if (download.file) {
433
download.file.remove();
434
}
435
436
if (download.cancelled) {
437
return;
438
}
439
440
delete this.downloads_[download.url];
441
download.deferred.errback(
442
new goog.net.FileDownloader.Error(download, opt_err));
443
};
444
445
446
/**
447
* Abort the download of the given URL.
448
*
449
* @param {!goog.net.FileDownloader.Download_} download The download to abort.
450
* @private
451
*/
452
goog.net.FileDownloader.prototype.cancel_ = function(download) {
453
goog.dispose(download);
454
delete this.downloads_[download.url];
455
};
456
457
458
/**
459
* Get the directory for a given URL. If the directory already exists when this
460
* is called, it will contain exactly one file: the downloaded file.
461
*
462
* This not only calls the FileSystem API's getFile method, but attempts to
463
* distribute the files so that they don't overload the filesystem. The spec
464
* says directories can't contain more than 5000 files
465
* (http://www.w3.org/TR/file-system-api/#directories), so this ensures that
466
* each file is put into a subdirectory based on its SHA1 hash.
467
*
468
* All parameters are the same as in the FileSystem API's Entry#getFile method.
469
*
470
* @param {string} url The URL corresponding to the directory to get.
471
* @param {goog.fs.DirectoryEntry.Behavior} behavior The behavior to pass to the
472
* underlying method.
473
* @return {!goog.async.Deferred} The deferred DirectoryEntry object.
474
* @private
475
*/
476
goog.net.FileDownloader.prototype.getDir_ = function(url, behavior) {
477
// 3 hex digits provide 16**3 = 4096 different possible dirnames, which is
478
// less than the maximum of 5000 entries. Downloaded files should be
479
// distributed roughly evenly throughout the directories due to the hash
480
// function, allowing many more than 5000 files to be downloaded.
481
//
482
// The leading ` ensures that no illegal dirnames are accidentally used. % was
483
// previously used, but Chrome has a bug (as of 12.0.725.0 dev) where
484
// filenames are URL-decoded before checking their validity, so filenames
485
// containing e.g. '%3f' (the URL-encoding of :, an invalid character) are
486
// rejected.
487
var dirname = '`' +
488
Math.abs(goog.crypt.hash32.encodeString(url))
489
.toString(16)
490
.substring(0, 3);
491
492
return this.dir_.getDirectory(dirname, goog.fs.DirectoryEntry.Behavior.CREATE)
493
.addCallback(function(dir) {
494
return dir.getDirectory(this.sanitize_(url), behavior);
495
}, this);
496
};
497
498
499
/**
500
* Get the file for a given URL. This will only retrieve files that have already
501
* been saved; it shouldn't be used for creating the file in the first place.
502
* This is because the filename isn't necessarily determined by the URL, but by
503
* the headers of the XHR response.
504
*
505
* @param {string} url The URL corresponding to the file to get.
506
* @return {!goog.async.Deferred} The deferred FileEntry object.
507
* @private
508
*/
509
goog.net.FileDownloader.prototype.getFile_ = function(url) {
510
return this.getDir_(url, goog.fs.DirectoryEntry.Behavior.DEFAULT)
511
.addCallback(function(dir) {
512
return dir.listDirectory().addCallback(function(files) {
513
goog.asserts.assert(files.length == 1);
514
// If the filesystem somehow gets corrupted and we end up with an
515
// empty directory here, it makes sense to just return the normal
516
// file-not-found error.
517
return files[0] || dir.getFile('file');
518
});
519
});
520
};
521
522
523
/**
524
* Sanitize a string so it can be safely used as a file or directory name for
525
* the FileSystem API.
526
*
527
* @param {string} str The string to sanitize.
528
* @return {string} The sanitized string.
529
* @private
530
*/
531
goog.net.FileDownloader.prototype.sanitize_ = function(str) {
532
// Add a prefix, since certain prefixes are disallowed for paths. None of the
533
// disallowed prefixes start with '`'. We use ` rather than % for escaping the
534
// filename due to a Chrome bug (as of 12.0.725.0 dev) where filenames are
535
// URL-decoded before checking their validity, so filenames containing e.g.
536
// '%3f' (the URL-encoding of :, an invalid character) are rejected.
537
return '`' +
538
str.replace(/[\/\\<>:?*"|%`]/g, encodeURIComponent).replace(/%/g, '`');
539
};
540
541
542
/**
543
* Gets the filename specified by the XHR. This first attempts to parse the
544
* Content-Disposition header for a filename and, failing that, falls back on
545
* deriving the filename from the URL.
546
*
547
* @param {!goog.net.XhrIo} xhr The XHR containing the response headers.
548
* @return {string} The filename.
549
* @private
550
*/
551
goog.net.FileDownloader.prototype.getName_ = function(xhr) {
552
var disposition = xhr.getResponseHeader('Content-Disposition');
553
var match =
554
disposition && disposition.match(/^attachment *; *filename="(.*)"$/i);
555
if (match) {
556
// The Content-Disposition header allows for arbitrary backslash-escaped
557
// characters (usually " and \). We want to unescape them before using them
558
// in the filename.
559
return match[1].replace(/\\(.)/g, '$1');
560
}
561
562
return this.urlToName_(xhr.getLastUri());
563
};
564
565
566
/**
567
* Extracts the basename from a URL.
568
*
569
* @param {string} url The URL.
570
* @return {string} The basename.
571
* @private
572
*/
573
goog.net.FileDownloader.prototype.urlToName_ = function(url) {
574
var segments = url.split('/');
575
return segments[segments.length - 1];
576
};
577
578
579
/**
580
* Remove all event listeners for an XHR and release it back into the pool.
581
*
582
* @param {!goog.net.XhrIo} xhr The XHR to free.
583
* @private
584
*/
585
goog.net.FileDownloader.prototype.freeXhr_ = function(xhr) {
586
goog.events.removeAll(xhr);
587
this.pool_.addFreeObject(xhr);
588
};
589
590
591
/** @override */
592
goog.net.FileDownloader.prototype.disposeInternal = function() {
593
delete this.dir_;
594
goog.dispose(this.eventHandler_);
595
delete this.eventHandler_;
596
goog.object.forEach(this.downloads_, function(download) {
597
download.deferred.cancel();
598
}, this);
599
delete this.downloads_;
600
goog.dispose(this.pool_);
601
delete this.pool_;
602
603
goog.net.FileDownloader.base(this, 'disposeInternal');
604
};
605
606
607
608
/**
609
* The error object for FileDownloader download errors.
610
*
611
* @param {!goog.net.FileDownloader.Download_} download The download object for
612
* the download in question.
613
* @param {goog.fs.Error=} opt_fsErr The file error object, if this was a file
614
* error.
615
*
616
* @constructor
617
* @extends {goog.debug.Error}
618
* @final
619
*/
620
goog.net.FileDownloader.Error = function(download, opt_fsErr) {
621
goog.net.FileDownloader.Error.base(
622
this, 'constructor', 'Error capturing URL ' + download.url);
623
624
/**
625
* The URL the event relates to.
626
* @type {string}
627
*/
628
this.url = download.url;
629
630
if (download.xhr) {
631
this.xhrStatus = download.xhr.getStatus();
632
this.xhrErrorCode = download.xhr.getLastErrorCode();
633
this.message += ': XHR failed with status ' + this.xhrStatus +
634
' (error code ' + this.xhrErrorCode + ')';
635
} else if (opt_fsErr) {
636
this.fileError = opt_fsErr;
637
this.message += ': file API failed (' + opt_fsErr.message + ')';
638
}
639
};
640
goog.inherits(goog.net.FileDownloader.Error, goog.debug.Error);
641
642
643
/**
644
* The status of the XHR. Only set if the error was caused by an XHR failure.
645
* @type {number|undefined}
646
*/
647
goog.net.FileDownloader.Error.prototype.xhrStatus;
648
649
650
/**
651
* The error code of the XHR. Only set if the error was caused by an XHR
652
* failure.
653
* @type {goog.net.ErrorCode|undefined}
654
*/
655
goog.net.FileDownloader.Error.prototype.xhrErrorCode;
656
657
658
/**
659
* The file API error. Only set if the error was caused by the file API.
660
* @type {goog.fs.Error|undefined}
661
*/
662
goog.net.FileDownloader.Error.prototype.fileError;
663
664
665
666
/**
667
* A struct containing the data for a single download.
668
*
669
* @param {string} url The URL for the file being downloaded.
670
* @param {!goog.net.FileDownloader} downloader The parent FileDownloader.
671
* @extends {goog.Disposable}
672
* @constructor
673
* @private
674
*/
675
goog.net.FileDownloader.Download_ = function(url, downloader) {
676
goog.net.FileDownloader.Download_.base(this, 'constructor');
677
678
/**
679
* The URL for the file being downloaded.
680
* @type {string}
681
*/
682
this.url = url;
683
684
/**
685
* The Deferred that will be fired when the download is complete.
686
* @type {!goog.async.Deferred}
687
*/
688
this.deferred =
689
new goog.async.Deferred(goog.bind(downloader.cancel_, downloader, this));
690
691
/**
692
* Whether this download has been cancelled by the user.
693
* @type {boolean}
694
*/
695
this.cancelled = false;
696
697
/**
698
* The XhrIo object for downloading the file. Only set once it's been
699
* retrieved from the pool.
700
* @type {goog.net.XhrIo}
701
*/
702
this.xhr = null;
703
704
/**
705
* The name of the blob being downloaded. Only sey once the XHR has completed,
706
* if it completed successfully.
707
* @type {?string}
708
*/
709
this.name = null;
710
711
/**
712
* The downloaded blob. Only set once the XHR has completed, if it completed
713
* successfully.
714
* @type {Blob}
715
*/
716
this.blob = null;
717
718
/**
719
* The file entry where the blob is to be stored. Only set once it's been
720
* loaded from the filesystem.
721
* @type {goog.fs.FileEntry}
722
*/
723
this.file = null;
724
725
/**
726
* The file writer for writing the blob to the filesystem. Only set once it's
727
* been loaded from the filesystem.
728
* @type {goog.fs.FileWriter}
729
*/
730
this.writer = null;
731
};
732
goog.inherits(goog.net.FileDownloader.Download_, goog.Disposable);
733
734
735
/** @override */
736
goog.net.FileDownloader.Download_.prototype.disposeInternal = function() {
737
this.cancelled = true;
738
if (this.xhr) {
739
this.xhr.abort();
740
} else if (
741
this.writer &&
742
this.writer.getReadyState() == goog.fs.FileSaver.ReadyState.WRITING) {
743
this.writer.abort();
744
}
745
746
goog.net.FileDownloader.Download_.base(this, 'disposeInternal');
747
};
748
749