Skip to content

Commit a6cdde4

Browse files
author
Mike Taylor
authored
Merge pull request #1253 from /issues/1248/1
Fixes #1248. Handle blobs in addition to data URIs in bugform.js
2 parents 854f30b + e4253f6 commit a6cdde4

File tree

2 files changed

+101
-28
lines changed

2 files changed

+101
-28
lines changed

tests/functional/image-uploads-non-auth.js

+70-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
* License, v. 2.0. If a copy of the MPL was not distributed with this
33
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
44

5+
/*global Promise:true*/
6+
57
define([
68
'intern',
79
'intern!object',
@@ -30,6 +32,38 @@ define([
3032
.end();
3133
},
3234

35+
'postMessaged blob preview': function() {
36+
return this.remote
37+
.setFindTimeout(intern.config.wc.pageLoadTimeout)
38+
.get(require.toUrl(url + '?open=1'))
39+
// Build up a green test square in canvas, toBlob that, and then postMessage the blob
40+
.execute(function() {
41+
return new Promise(function(res) {
42+
var c = document.createElement('canvas');
43+
c.width = 25;
44+
c.height = 25;
45+
var ctx = c.getContext('2d');
46+
ctx.fillStyle = 'rgb(0, 128, 0)';
47+
ctx.rect(0, 0, 25, 25);
48+
ctx.fill();
49+
ctx.lineWidth = 2;
50+
ctx.strokeStyle = 'rgb(0, 0, 0)';
51+
ctx.strokeRect(0, 0, 25, 25);
52+
c.toBlob(function(blob) {
53+
res(blob);
54+
});
55+
}).then(function(blob) {
56+
postMessage(blob, 'http://localhost:5000');
57+
});
58+
})
59+
.sleep(1000)
60+
.findByCssSelector('.js-image-upload-label').getAttribute('style')
61+
.then(function(inlineStyle) {
62+
assert.include(inlineStyle, 'data:image/png;base64,iVBOR', 'Base64 data shown as preview background');
63+
})
64+
.end();
65+
},
66+
3367
'postMessaged dataURI image upload worked': function() {
3468
return this.remote
3569
.setFindTimeout(intern.config.wc.pageLoadTimeout)
@@ -44,11 +78,45 @@ define([
4478
.end();
4579
},
4680

47-
'postMessaged dataURI remove button': function() {
81+
'postMessaged blob image upload worked': function() {
82+
return this.remote
83+
.setFindTimeout(intern.config.wc.pageLoadTimeout)
84+
.get(require.toUrl(url + '?open=1'))
85+
// Build up a green test square in canvas, toBlob that, and then postMessage the blob
86+
.execute(function() {
87+
return new Promise(function(res) {
88+
var c = document.createElement('canvas');
89+
c.width = 25;
90+
c.height = 25;
91+
var ctx = c.getContext('2d');
92+
ctx.fillStyle = 'rgb(0, 128, 0)';
93+
ctx.rect(0, 0, 25, 25);
94+
ctx.fill();
95+
ctx.lineWidth = 2;
96+
ctx.strokeStyle = 'rgb(0, 0, 0)';
97+
ctx.strokeRect(0, 0, 25, 25);
98+
c.toBlob(function(blob) {
99+
res(blob);
100+
});
101+
}).then(function(blob) {
102+
postMessage(blob, 'http://localhost:5000');
103+
});
104+
})
105+
.sleep(1000)
106+
.findByCssSelector('#description').getProperty('value')
107+
.then(function(val) {
108+
assert.include(val, '![Screenshot Description](http://localhost:5000/uploads/', 'The data URI was correctly uploaded and its URL was copied to the bug description.');
109+
})
110+
.end();
111+
},
112+
113+
'remove image upload button': function() {
48114
return this.remote
49115
.setFindTimeout(intern.config.wc.pageLoadTimeout)
50116
.get(require.toUrl(url + '?open=1'))
51117
// send a small base64 encoded green test square
118+
// in theory a blob should work as well, since by the time we're removing the image,
119+
// it's been converted to a data URI
52120
.execute('postMessage("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAIAAABLixI0AAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAB3RJTUUH3gYSAig452t/EQAAAClJREFUOMvtzkENAAAMg0A25ZU+E032AQEXoNcApCGFLX5paWlpaWl9dqq9AS6CKROfAAAAAElFTkSuQmCC", "http://localhost:5000")')
53121
.sleep(1000)
54122
.findByCssSelector('.js-image-upload-label .wc-UploadForm-button').isDisplayed()
@@ -65,7 +133,7 @@ define([
65133
.end()
66134
.findByCssSelector('#description').getProperty('value')
67135
.then(function(val) {
68-
assert.notInclude(val, '![Screenshot Description](http://localhost:5000/uploads/', 'The data URI was correctly uploaded and its URL was copied to the bug description.');
136+
assert.notInclude(val, '![Screenshot Description](http://localhost:5000/uploads/', 'The url to the image upload was correctly removed.');
69137
})
70138
.end();
71139
}

webcompat/static/js/lib/bugform.js

+31-26
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ function BugForm() {
99
this.reportButton = $('#js-ReportBug');
1010
this.loaderImage = $('.js-Loader');
1111
this.uploadLoader = $('.js-Upload-Loader');
12-
this.screenshotData = '';
1312
// by default, submission type is anonymous
1413
this.submitType = 'github-proxy-report';
1514
this.UPLOAD_LIMIT = 1024 * 1024 * 4;
@@ -77,23 +76,34 @@ function BugForm() {
7776
// Set up listener for message events from screenshot-enabled add-ons
7877
window.addEventListener('message', _.bind(function(event) {
7978
// Make sure the data is coming from ~*inside the house*~!
80-
// (i.e., our add-on sent it)
79+
// (i.e., our add-on or some other priviledged code sent it)
8180
if (location.origin === event.origin) {
82-
this.screenshotData = event.data;
83-
84-
// The final size of Base64-encoded binary data is ~equal to
85-
// 1.37 times the original data size + 814 bytes (for headers).
86-
// so, bytes = (encoded_string.length - 814) / 1.37)
87-
// see https://en.wikipedia.org/wiki/Base64#MIME
88-
if ((String(this.screenshotData).length - 814 / 1.37) > this.UPLOAD_LIMIT) {
89-
this.downsampleImageAndUpload(this.screenshotData);
81+
// See https://github.com/webcompat/webcompat.com/issues/1252 to track
82+
// the work of only accepting blobs, which should simplify things.
83+
if (event.data instanceof Blob) {
84+
// showUploadPreview will take care of converting from blob to
85+
// dataURI, and will send the result to resampleIfNecessaryAndUpload.
86+
this.showUploadPreview(event.data);
9087
} else {
91-
this.addPreviewBackgroundAndUpload(this.screenshotData);
88+
// ...the data is already a data URI string
89+
this.resampleIfNecessaryAndUpload(event.data);
9290
}
9391
}
9492
}, this), false);
9593
};
9694

95+
this.resampleIfNecessaryAndUpload = function(screenshotData) {
96+
// The final size of Base64-encoded binary data is ~equal to
97+
// 1.37 times the original data size + 814 bytes (for headers).
98+
// so, bytes = (encoded_string.length - 814) / 1.37)
99+
// see https://en.wikipedia.org/wiki/Base64#MIME
100+
if ((String(screenshotData).length - 814 / 1.37) > this.UPLOAD_LIMIT) {
101+
this.downsampleImageAndUpload(screenshotData);
102+
} else {
103+
this.addPreviewBackgroundAndUpload(screenshotData);
104+
}
105+
};
106+
97107
this.downsampleImageAndUpload = function(dataURI) {
98108
var img = document.createElement('img');
99109
var canvas = document.createElement('canvas');
@@ -110,16 +120,16 @@ function BugForm() {
110120
// Note: this will convert GIFs to JPEG, which breaks
111121
// animated GIFs. However, this only will happen if they
112122
// were above the upload limit size. So... sorry?
113-
this.screenshotData = canvas.toDataURL('image/jpeg', 0.8);
123+
var screenshotData = canvas.toDataURL('image/jpeg', 0.8);
114124

115125
// The limit is 4MB (which is crazy big!), so let the user know if their
116126
// file is unreasonably large at this point (after 1 round of downsampling)
117-
if (this.screenshotData > this.UPLOAD_LIMIT) {
127+
if (screenshotData > this.UPLOAD_LIMIT) {
118128
this.makeInvalid('image', {altHelp: true});
119129
return;
120130
}
121131

122-
this.addPreviewBackgroundAndUpload(this.screenshotData);
132+
this.addPreviewBackgroundAndUpload(screenshotData);
123133
img = null, canvas = null;
124134
}, this);
125135

@@ -184,7 +194,9 @@ function BugForm() {
184194
} else {
185195
this.makeValid('image');
186196
if (event) {
187-
this.showUploadPreview(event);
197+
// We can just grab the 0th one, because we only allow uploading
198+
// a single image at a time (for now)
199+
this.showUploadPreview(event.target.files[0]);
188200
}
189201
}
190202
};
@@ -312,30 +324,23 @@ function BugForm() {
312324
If the users browser understands the FileReader API, show a preview
313325
of the image they're about to load.
314326
*/
315-
this.showUploadPreview = function(event) {
327+
this.showUploadPreview = function(blobOrFile) {
316328
if (!(window.FileReader && window.File)) {
317329
return;
318330
}
319-
// We can just grab the 0th one, because we only allow uploading
320-
// a single image at a time (for now)
321-
var img = event.target.files[0];
322331

323332
// One last image type validation check.
324-
if (!img.type.match('image.*')) {
333+
if (!blobOrFile.type.match('image.*')) {
325334
this.makeInvalid('image');
326335
return;
327336
}
328337

329338
var reader = new FileReader();
330339
reader.onload = _.bind(function(event) {
331340
var dataURI = event.target.result;
332-
if ((String(dataURI).length - 814 / 1.37) > this.UPLOAD_LIMIT) {
333-
this.downsampleImageAndUpload(dataURI);
334-
} else {
335-
this.addPreviewBackgroundAndUpload(dataURI);
336-
}
341+
this.resampleIfNecessaryAndUpload(dataURI);
337342
}, this);
338-
reader.readAsDataURL(img);
343+
reader.readAsDataURL(blobOrFile);
339344
};
340345

341346
this.addPreviewBackgroundAndUpload = function(dataURI) {

0 commit comments

Comments
 (0)