-
Notifications
You must be signed in to change notification settings - Fork 2.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix a bug preventing map.loaded() from returning true with raster tiles. #3302
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,6 +34,9 @@ exports.getArrayBuffer = function(url, callback) { | |
callback(e); | ||
}; | ||
xhr.onload = function() { | ||
if (xhr.response.byteLength === 0 && xhr.status === 200) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. specifically, this bit |
||
return callback(new Error('http status 200 returned without content.')); | ||
} | ||
if (xhr.status >= 200 && xhr.status < 300 && xhr.response) { | ||
callback(null, xhr.response); | ||
} else { | ||
|
@@ -50,6 +53,8 @@ function sameOrigin(url) { | |
return a.protocol === window.document.location.protocol && a.host === window.document.location.host; | ||
} | ||
|
||
const transparentPngUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQYV2NgAAIAAAUAAarVyFEAAAAASUVORK5CYII='; | ||
|
||
exports.getImage = function(url, callback) { | ||
return exports.getArrayBuffer(url, (err, imgData) => { | ||
if (err) return callback(err); | ||
|
@@ -59,7 +64,11 @@ exports.getImage = function(url, callback) { | |
(window.URL || window.webkitURL).revokeObjectURL(img.src); | ||
}; | ||
const blob = new window.Blob([new Uint8Array(imgData)], { type: 'image/png' }); | ||
img.src = (window.URL || window.webkitURL).createObjectURL(blob); | ||
if (imgData.byteLength) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why did you decide to branch on There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not that familiar with the spec but I figured it was possible a response could include an empty PNG (with byteLength > 0) and still return a 204. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. According to the w3c spec, a 204 response may not include any body
My intuition is that we should keep the behaviour you have implemented in response to a 204 header and fail loudly if any other type of response contains no data. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good to know that about 204. With that said, is an empty response from a 200 still valid? My intuition is yes. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
My interpretation is that a 200 response must include a body. Per the spec, a trivial transparent PNG would be acceptable but the lack of a body would be unacceptable. In practice, a 200 response with no body is more likely a bug than intended behaviour. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @lucaswoj I updated the PR. is this better? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. LGTM! Now we need to figure out a way to test this code path. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @lucaswoj any ideas how best to do that? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is unfortunate that we don't have any unit tests for this module yet 😬. I suggest that we create a |
||
img.src = (window.URL || window.webkitURL).createObjectURL(blob); | ||
} else { | ||
img.src = transparentPngUrl; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we don't do this, the tile becomes black and webGL warnings are logged. |
||
} | ||
img.getData = function() { | ||
const canvas = window.document.createElement('canvas'); | ||
const context = canvas.getContext('2d'); | ||
|
@@ -68,7 +77,6 @@ exports.getImage = function(url, callback) { | |
context.drawImage(img, 0, 0); | ||
return context.getImageData(0, 0, img.width, img.height).data; | ||
}; | ||
return img; | ||
}); | ||
}; | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
'use strict'; | ||
|
||
const test = require('mapbox-gl-js-test').test; | ||
const ajax = require('../../../js/util/ajax'); | ||
const window = require('../../../js/util/window'); | ||
|
||
test('ajax', (t) => { | ||
t.beforeEach(callback => { | ||
window.useFakeXMLHttpRequest(); | ||
callback(); | ||
}); | ||
|
||
t.afterEach(callback => { | ||
window.restore(); | ||
callback(); | ||
}); | ||
t.test('getArrayBuffer', (t) => { | ||
const url = '/buffer-request'; | ||
window.server.respondWith(request => { | ||
request.respond(200, {'Content-Type': 'image/png'}, ''); | ||
}); | ||
ajax.getArrayBuffer(url, (error) => { | ||
t.pass('called getArrayBuffer'); | ||
t.ok(error, 'should error when the status is 200 without content.'); | ||
t.end(); | ||
}); | ||
window.server.respond(); | ||
}); | ||
t.end(); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this fixing another bug? If so, thank you! Out of curiosity: what were the symptoms of this bug? I'm guessing something along the lines of "
map.loaded()
doesn't return true if there are any tiles were aborted."There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, technically, it's a seperate bug that manifested itself in the same way (preventing loaded() from being true.)
Yes, exactly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Happy to help with some guidance on how best to write tests for this.