Permalink
Browse files

Workaround for crbug.com/375297

Removed deletion queue + unload processing; replaced it with an
asynchronous revocation flow
  • Loading branch information...
eligrey committed Jul 21, 2014
1 parent d50456a commit 485930a7bc3b380693f9afeb7cabf144f16fa8cf
Showing with 15 additions and 22 deletions.
  1. +15 −22 FileSaver.js
@@ -1,6 +1,6 @@
/* FileSaver.js
* A saveAs() FileSaver implementation.
* 2014-05-27
* 2014-07-21
*
* By Eli Grey, http://eligrey.com
* License: X11/MIT
@@ -49,18 +49,17 @@ var saveAs = saveAs
}
, force_saveable_type = "application/octet-stream"
, fs_min_size = 0
, deletion_queue = []
, process_deletion_queue = function() {
var i = deletion_queue.length;
while (i--) {
var file = deletion_queue[i];
// See https://code.google.com/p/chromium/issues/detail?id=375297#c7 for
// the reasoning behind the timeout and revocation flow
, arbitrary_revoke_timeout = 10

This comment has been minimized.

@sjschneider

sjschneider Nov 29, 2014

The arbitrary_revoke_timeout=10 is too low for FireFox (tested on v29+). I am experiencing intermittent save failures in Firefox due to the object_url being revoked too early. Is there a reason to keep this as low as possible?

This comment has been minimized.

@eligrey

eligrey Nov 29, 2014

Owner

There was no particular reason to keep it as low as possible. I just wasn't experiencing any issues with Firefox as long as I waited 5ms with some test saves I did with 100MiB blobs, so I thought double that would be sufficient.

Thanks for reporting this. Please assist me by manually increasing arbitrary_revoke_timeout until it consistently works in your situation and tell me what kind of value would be better.

In the meantime, I'll raise it to 500ms. Hopefully that should be enough time in the general case.

, revoke = function(file) {
setTimeout(function() {
if (typeof file === "string") { // file is an object URL
get_URL().revokeObjectURL(file);
} else { // file is a File
file.remove();
}
}
deletion_queue.length = 0; // clear queue
}, arbitrary_revoke_timeout);
}
, dispatch = function(filesaver, event_types, event) {
event_types = [].concat(event_types);
@@ -84,27 +83,23 @@ var saveAs = saveAs
, blob_changed = false
, object_url
, target_view
, get_object_url = function() {
var object_url = get_URL().createObjectURL(blob);
deletion_queue.push(object_url);
return object_url;
}
, dispatch_all = function() {
dispatch(filesaver, "writestart progress write writeend".split(" "));
}
// on any filesys errors revert to saving with object URLs
, fs_error = function() {
// don't create more object URLs than needed
if (blob_changed || !object_url) {
object_url = get_object_url(blob);
object_url = get_URL().createObjectURL(blob);
}
if (target_view) {
target_view.location.href = object_url;
} else {
window.open(object_url, "_blank");
view.open(object_url, "_blank");
}
filesaver.readyState = filesaver.DONE;
dispatch_all();
revoke(object_url);
}
, abortable = function(func) {
return function() {
@@ -121,17 +116,20 @@ var saveAs = saveAs
name = "download";
}
if (can_use_save_link) {
object_url = get_object_url(blob);
object_url = get_URL().createObjectURL(blob);
save_link.href = object_url;
save_link.download = name;
click(save_link);
filesaver.readyState = filesaver.DONE;
dispatch_all();
revoke(object_url);
return;
}
// Object and web filesystem URLs have a problem saving in Google Chrome when
// viewed in a tab, so I force save with application/octet-stream
// http://code.google.com/p/chromium/issues/detail?id=91158
// Update: Google errantly closed 91158, I submitted it again:
// https://code.google.com/p/chromium/issues/detail?id=389642
if (view.chrome && type && type !== force_saveable_type) {
slice = blob.slice || blob.webkitSlice;
blob = slice.call(blob, 0, blob.size, force_saveable_type);
@@ -158,9 +156,9 @@ var saveAs = saveAs
file.createWriter(abortable(function(writer) {
writer.onwriteend = function(event) {
target_view.location.href = file.toURL();
deletion_queue.push(file);
filesaver.readyState = filesaver.DONE;
dispatch(filesaver, "writeend", event);
revoke(file);
};
writer.onerror = function() {
var error = writer.error;
@@ -217,11 +215,6 @@ var saveAs = saveAs
FS_proto.onwriteend =
null;
view.addEventListener("unload", process_deletion_queue, false);
saveAs.unload = function() {
process_deletion_queue();
view.removeEventListener("unload", process_deletion_queue, false);
};
return saveAs;
}(
typeof self !== "undefined" && self

1 comment on commit 485930a

@sjschneider

This comment has been minimized.

sjschneider commented on 485930a Nov 30, 2014

Thanks for looking at this so quickly and for the thorough reply. 20ms seems to be reliable, but I'm quite comfortable with your choice of 500ms and not cutting it too close. The cutoff seems to vary based on CPU load and whether or not the javascript console/debugger is open.

Please sign in to comment.