Skip to content
This repository has been archived by the owner on Nov 20, 2018. It is now read-only.

Commit

Permalink
fix($ios8): uploads not possible in iOS8 Safari
Browse files Browse the repository at this point in the history
No way to work around this, so we just alert
the user.
#1284
  • Loading branch information
Ray Nicholus committed Sep 17, 2014
1 parent 03e84ee commit e05a81c
Show file tree
Hide file tree
Showing 11 changed files with 387 additions and 48 deletions.
6 changes: 4 additions & 2 deletions client/js/button.js
Expand Up @@ -38,6 +38,8 @@ qq.UploadButton = function(o) {
// Called when the browser invokes the onchange handler on the `<input type="file">`
onChange: function(input) {},

ios8BrowserCrashWorkaround: true,

// **This option will be removed** in the future as the :hover CSS pseudo-class is available on all supported browsers
hoverClass: "qq-upload-button-hover",

Expand Down Expand Up @@ -147,10 +149,10 @@ qq.UploadButton = function(o) {
setMultiple: function(isMultiple, opt_input) {
var input = this.getInput() || opt_input;

// Temporary workaround for bug in in iOS8 Chrome that causes the browser to crash
// Temporary workaround for bug in in iOS8 UIWebView that causes the browser to crash
// before the file chooser appears if the file input doesn't contain a multiple attribute.
// See #1283.
if (qq.iosChrome() && !qq.ios6() && !qq.ios7()) {
if (options.ios8BrowserCrashWorkaround && qq.ios8() && (qq.iosChrome() || qq.iosSafariWebView())) {
input.setAttribute("multiple", "");
}

Expand Down
24 changes: 22 additions & 2 deletions client/js/uploader.basic.api.js
Expand Up @@ -11,6 +11,8 @@
throw new qq.Error("Blob uploading is not supported in this browser!");
}

this._maybeHandleIos8SafariWorkaround();

if (blobDataOrArray) {
var blobDataArray = [].concat(blobDataOrArray),
verifiedBlobDataList = [],
Expand Down Expand Up @@ -46,6 +48,8 @@
},

addFiles: function(filesOrInputs, params, endpoint) {
this._maybeHandleIos8SafariWorkaround();

var verifiedFilesOrInputs = [],
batchId = this._storedIds.length === 0 ? qq.getUniqueId() : this._currentBatchId,
fileOrInputIndex, fileOrInput, fileIndex;
Expand Down Expand Up @@ -563,7 +567,11 @@
function allowMultiple() {
if (qq.supportedFeatures.ajaxUploading) {
// Workaround for bug in iOS7+ (see #1039)
if (qq.ios() && !qq.ios6() && self._isAllowedExtension(allowedExtensions, ".mov")) {
if (self._options.workarounds.iosEmptyVideos &&
qq.ios() &&
!qq.ios6() &&
self._isAllowedExtension(allowedExtensions, ".mov")) {

return false;
}

Expand All @@ -587,7 +595,8 @@
self._onInputChange(input);
},
hoverClass: this._options.classes.buttonHover,
focusClass: this._options.classes.buttonFocus
focusClass: this._options.classes.buttonFocus,
ios8BrowserCrashWorkaround: this._options.workarounds.ios8BrowserCrash
});

this._disposeSupport.addDisposer(function() {
Expand Down Expand Up @@ -1176,6 +1185,17 @@
}
},

_maybeHandleIos8SafariWorkaround: function() {
var self = this;

if (this._options.workarounds.ios8SafariUploads && qq.ios8() && qq.iosSafari()) {
setTimeout(function() {
window.alert(self._options.messages.unsupportedBrowserIos8Safari);
}, 0);
throw new qq.Error(this._options.messages.unsupportedBrowserIos8Safari);
}
},

_maybeParseAndSendUploadError: function(id, name, response, xhr) {
// Assuming no one will actually set the response code to something other than 200
// and still set 'success' to true...
Expand Down
9 changes: 8 additions & 1 deletion client/js/uploader.basic.js
Expand Up @@ -78,7 +78,8 @@
minHeightImageError: "Image is not tall enough.",
minWidthImageError: "Image is not wide enough.",
retryFailTooManyItems: "Retry failed - you have reached your file limit.",
onLeave: "The files are being uploaded, if you leave now the upload will be canceled."
onLeave: "The files are being uploaded, if you leave now the upload will be canceled.",
unsupportedBrowserIos8Safari: "Unrecoverable error - this browser does not permit file uploading of any kind due to serious bugs in iOS8 Safari. Please use iOS8 Chrome until Apple fixes these issues."
},

retry: {
Expand Down Expand Up @@ -211,6 +212,12 @@

// metadata about each requested scaled version
sizes: []
},

workarounds: {
iosEmptyVideos: true,
ios8SafariUploads: true,
ios8BrowserCrash: true
}
};

Expand Down
5 changes: 4 additions & 1 deletion client/js/uploader.js
Expand Up @@ -127,7 +127,10 @@ qq.FineUploader = function(o, namespace) {
text: this._options.text
});

if (!qq.supportedFeatures.uploading || (this._options.cors.expected && !qq.supportedFeatures.uploadCors)) {
if (this._options.workarounds.ios8SafariUploads && qq.ios8() && qq.iosSafari()) {
this._templating.renderFailure(this._options.messages.unsupportedBrowserIos8Safari);
}
else if (!qq.supportedFeatures.uploading || (this._options.cors.expected && !qq.supportedFeatures.uploadCors)) {
this._templating.renderFailure(this._options.messages.unsupportedBrowser);
}
else {
Expand Down
12 changes: 12 additions & 0 deletions client/js/util.js
Expand Up @@ -522,6 +522,10 @@ var qq = function(element) {
return qq.ios() && navigator.userAgent.indexOf(" OS 7_") !== -1;
};

qq.ios8 = function() {
return qq.ios() && navigator.userAgent.indexOf(" OS 8_") !== -1;
};

qq.ios = function() {
/*jshint -W014 */
return navigator.userAgent.indexOf("iPad") !== -1
Expand All @@ -533,6 +537,14 @@ var qq = function(element) {
return qq.ios() && navigator.userAgent.indexOf("CriOS") !== -1;
};

qq.iosSafari = function() {
return qq.ios() && !qq.iosChrome() && navigator.userAgent.indexOf("Safari") !== -1;
};

qq.iosSafariWebView = function() {
return qq.ios() && !qq.iosChrome() && !qq.iosSafari();
};

//
// Events

Expand Down
9 changes: 9 additions & 0 deletions docs/api/options.jmd
Expand Up @@ -228,6 +228,15 @@ alert("The `chunking.success.endpoint` option **only** applies to traditional up
)
)
}}

{{ api_parent_option("workarounds", "workarounds", "Flags that enable or disable workarounds for browser-specific bugs.",
(
("workarounds.iosEmptyVideos", "iosEmptyVideos", "Ensures all `<input type="file">` elements tracked by Fine Uploader do NOT contain a `multiple` attribute to work around an issue present in iOS7 & 8 that otherwise results in 0-sized uploaded videos.", "Boolean", "true",),
("workarounds.ios8BrowserCrash", "ios8BrowserCrash", "Ensures all `<input type="file">` elements tracked by Fine Uploader always have a `multiple` attribute present. This only applies to iOS8 Chrome and iOS8 UIWebView, and is put in place to work around an issue that causes the browser to crash when a file input element does not contain a `multiple` attribute inside of a UIWebView container created by an iOS8 app compiled with and iOS7 SDK.", "Boolean", "true",),
("workarounds.ios8SafariUploads", "ios8SafariUploads", "Disables Fine Uploader and displays a message to the user in iOS8 Safari. Due to serious bugs in iOS8 Safari, uploading is not possible.", "Boolean", "true",)
)
)
}}
</div>
</div>

Expand Down
1 change: 1 addition & 0 deletions docs/browser-support.jmd
Expand Up @@ -359,6 +359,7 @@ Note: Any features not listed here are supported in all browsers.
</table>

<small><a style="font-size: 24px" name="ios-multiple">*</a> - iOS browsers are unable to upload multiple files when video files are allowed to be uploaded due to a long-standing iOS bug. See case <a href="https://github.com/Widen/fine-uploader/issues/990">#990</a> on our bug tracker for more details.</small>
<small><a style="font-size: 24px" name="ios-multiple">*</a> - iOS8 Safari is unable to upload any files due to bugs in Apple's code. Please see [the blog post on this topic for more details](http://blog.fineuploader.com/2014/09/10/ios8-presents-serious-issues-that-prevent-file-uploading/).</small>


### Upload size Limitations
Expand Down
7 changes: 7 additions & 0 deletions docs/index.jmd
Expand Up @@ -3,6 +3,13 @@
{% block content %}
{% markdown %}

{{ alert(
"""iOS8 contains some serious bugs that prevent uploading in some cases. Workarounds are included
in Fine Uploader in an attempt to work around these issues. Unfortunately, there are no known
workarounds for iOS8 Safari and uploading in that browser is not possible at this time
Please see [the blog post on this topic for more details](http://blog.fineuploader.com/2014/09/10/ios8-presents-serious-issues-that-prevent-file-uploading/)."""
)}}

{{ alert(
"""Version 5.0 brings some breaking changes. See the [upgrading to 5.x page](upgrading-to-5.html)
for help on upgrading from a 4.x version."""
Expand Down
57 changes: 17 additions & 40 deletions test/unit/basic.js
Expand Up @@ -2,8 +2,7 @@
describe("uploader.basic.js", function () {
"use strict";

var $fineUploader, $button, $extraButton, $extraButton2, $extraButton3,
isIos8 = qq.ios() && navigator.userAgent.indexOf(" OS 8_") !== -1;
var $fineUploader, $button, $extraButton, $extraButton2, $extraButton3;

function getFileInput($containerEl) {
return $containerEl.find("INPUT")[0];
Expand All @@ -26,22 +25,6 @@ describe("uploader.basic.js", function () {
$extraButton3 = $fixture.find("#test-button4");
});

it("Excludes the multiple attribute on the file input element by default only in iOS7+ AND when MOV files can be submitted", function() {
var uploader = new qq.FineUploaderBasic({
element: $fixture[0],
button: $button[0],
validation: {
allowedExtensions: ["gif", "mov"]
}
});

var multipleExpected = (!qq.ios() && qq.supportedFeatures.ajaxUploading) ||
qq.ios6() ||
(isIos8 && qq.iosChrome());

assert.equal(qq(getFileInput($button)).hasAttribute("multiple"), multipleExpected);
});

it("Includes the multiple attribute on the file input element by default in all supported browsers (even iOS7+) when MOV files cannot be submitted", function() {
var uploader = new qq.FineUploaderBasic({
element: $fixture[0],
Expand All @@ -54,35 +37,37 @@ describe("uploader.basic.js", function () {
assert.equal(qq(getFileInput($button)).hasAttribute("multiple"), qq.supportedFeatures.ajaxUploading);
});

it("Includes the multiple attribute on the file input element by default (where supported) except in iOS7+ with the default alloweExtensions value", function() {
it("Includes the multiple attribute on the file input element by default (where supported)", function() {
var uploader = new qq.FineUploaderBasic({
element: $fixture[0],
button: $button[0]
});

var multipleExpected = (!qq.ios() && qq.supportedFeatures.ajaxUploading) ||
qq.ios6() ||
(isIos8 && qq.iosChrome());

assert.equal(qq(getFileInput($button)).hasAttribute("multiple"), qq.supportedFeatures.ajaxUploading && !qq.ios7());
});

it("Excludes the multiple attribute on the file input element if requested, unless iOS8 Chrome", function() {
it("Excludes the multiple attribute on the file input element if requested", function() {
var uploader = new qq.FineUploaderBasic({
element: $fixture[0],
button: $button[0],
multiple: false
multiple: false,
workarounds: {
ios8BrowserCrash: false,
iosEmptyVideos: false
}
});

var multipleExpected = isIos8 && qq.iosChrome();

assert.equal(qq(getFileInput($button)).hasAttribute("multiple"), multipleExpected);
assert.ok(!qq(getFileInput($button)).hasAttribute("multiple"));
});

it("Excludes or includes the multiple attribute on 'extra' file input elements appropriately, taking OS and extraButton properties into consideration", function() {
it("Excludes or includes the multiple attribute on 'extra' file input elements appropriately, taking extraButton properties into consideration", function() {
var uploader = new qq.FineUploaderBasic({
element: $fixture[0],
button: $button[0],
workarounds: {
ios8BrowserCrash: false,
iosEmptyVideos: false
},
validation: {
allowedExtensions: ["gif", "mov"]
},
Expand All @@ -103,16 +88,8 @@ describe("uploader.basic.js", function () {
]
});

var multipleExpectedForBtn1 = (!qq.ios() && qq.supportedFeatures.ajaxUploading) ||
qq.ios6() ||
(isIos8 && qq.iosChrome());

var multipleExpectedForBtn2 = isIos8 && qq.iosChrome();

var multipleExpectedForBtn3 = qq.supportedFeatures.ajaxUploading;

assert.equal(qq(getFileInput($extraButton)).hasAttribute("multiple"), multipleExpectedForBtn1);
assert.equal(qq(getFileInput($extraButton2)).hasAttribute("multiple"), multipleExpectedForBtn2);
assert.equal(qq(getFileInput($extraButton3)).hasAttribute("multiple"), multipleExpectedForBtn3);
assert.equal(qq(getFileInput($extraButton)).hasAttribute("multiple"), true);
assert.equal(qq(getFileInput($extraButton2)).hasAttribute("multiple"), false);
assert.equal(qq(getFileInput($extraButton3)).hasAttribute("multiple"), true);
});
});
8 changes: 6 additions & 2 deletions test/unit/button.js
Expand Up @@ -86,11 +86,15 @@ describe("button.js", function () {
var input;
var button = new qq.UploadButton({
element: $button[0],
multiple: false
multiple: false,
workarounds: {
ios8BrowserCrash: false,
iosEmptyVideos: false
}
});

input = button.getInput();
assert.equal(input.hasAttribute("multiple"), qq.iosChrome() && !qq.ios6() && !qq.ios7());
assert.ok(!input.hasAttribute("multiple"));

button.setMultiple(true);
assert.ok(input.hasAttribute("multiple"));
Expand Down

0 comments on commit e05a81c

Please sign in to comment.