diff --git a/src/source/raster_dem_tile_source.js b/src/source/raster_dem_tile_source.js index 5e7a8e6cfd6..31daad032b0 100644 --- a/src/source/raster_dem_tile_source.js +++ b/src/source/raster_dem_tile_source.js @@ -32,7 +32,7 @@ class RasterDEMTileSource extends RasterTileSource implements Source { const url = this.map._requestManager.normalizeTileURL(tile.tileID.canonical.url(this.tiles, this.scheme), false, this.tileSize); tile.request = getImage(this.map._requestManager.transformRequest(url, ResourceType.Tile), imageLoaded.bind(this)); - function imageLoaded(err, img) { + function imageLoaded(err, img, cacheControl, expires) { delete tile.request; if (tile.aborted) { tile.state = 'unloaded'; @@ -41,9 +41,7 @@ class RasterDEMTileSource extends RasterTileSource implements Source { tile.state = 'errored'; callback(err); } else if (img) { - if (this.map._refreshExpiredTiles) tile.setExpiryData(img); - delete (img: any).cacheControl; - delete (img: any).expires; + if (this.map._refreshExpiredTiles) tile.setExpiryData({cacheControl, expires}); const transfer = window.ImageBitmap && img instanceof window.ImageBitmap && offscreenCanvasSupported(); // DEMData uses 1px padding. Handle cases with image buffer of 1 and 2 pxs, the rest assume default buffer 0 // in order to keep the previous implementation working (no validation against tileSize). diff --git a/src/source/raster_tile_source.js b/src/source/raster_tile_source.js index 874cd9e1dbb..cfc7ab4b712 100644 --- a/src/source/raster_tile_source.js +++ b/src/source/raster_tile_source.js @@ -112,7 +112,7 @@ class RasterTileSource extends Evented implements Source { loadTile(tile: Tile, callback: Callback) { const use2x = browser.devicePixelRatio >= 2; const url = this.map._requestManager.normalizeTileURL(tile.tileID.canonical.url(this.tiles, this.scheme), use2x, this.tileSize); - tile.request = getImage(this.map._requestManager.transformRequest(url, ResourceType.Tile), (err, img) => { + tile.request = getImage(this.map._requestManager.transformRequest(url, ResourceType.Tile), (err, img, cacheControl, expires) => { delete tile.request; if (tile.aborted) { @@ -122,9 +122,7 @@ class RasterTileSource extends Evented implements Source { tile.state = 'errored'; callback(err); } else if (img) { - if (this.map._refreshExpiredTiles) tile.setExpiryData(img); - delete (img: any).cacheControl; - delete (img: any).expires; + if (this.map._refreshExpiredTiles) tile.setExpiryData({cacheControl, expires}); const context = this.map.painter.context; const gl = context.gl; diff --git a/src/util/ajax.js b/src/util/ajax.js index de5108c4824..67b24505ab6 100644 --- a/src/util/ajax.js +++ b/src/util/ajax.js @@ -7,7 +7,6 @@ import config from './config.js'; import assert from 'assert'; import {cacheGet, cachePut} from './tile_request_cache.js'; import webpSupported from './webp_supported.js'; -import offscreenCanvasSupported from './offscreen_canvas_supported.js'; import type {Callback} from '../types/callback.js'; import type {Cancelable} from '../types/cancelable.js'; @@ -273,7 +272,7 @@ function sameOrigin(url) { const transparentPngUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQYV2NgAAIAAAUAAarVyFEAAAAASUVORK5CYII='; -function arrayBufferToImage(data: ArrayBuffer, callback: (err: ?Error, image: ?HTMLImageElement) => void, cacheControl: ?string, expires: ?string) { +function arrayBufferToImage(data: ArrayBuffer, callback: Callback) { const img: HTMLImageElement = new window.Image(); const URL = window.URL; img.onload = () => { @@ -287,12 +286,10 @@ function arrayBufferToImage(data: ArrayBuffer, callback: (err: ?Error, image: ?H }; img.onerror = () => callback(new Error('Could not load image. Please make sure to use a supported image type such as PNG or JPEG. Note that SVGs are not supported.')); const blob: Blob = new window.Blob([new Uint8Array(data)], {type: 'image/png'}); - (img: any).cacheControl = cacheControl; - (img: any).expires = expires; img.src = data.byteLength ? URL.createObjectURL(blob) : transparentPngUrl; } -function arrayBufferToImageBitmap(data: ArrayBuffer, callback: (err: ?Error, image: ?ImageBitmap) => void) { +function arrayBufferToImageBitmap(data: ArrayBuffer, callback: Callback) { const blob: Blob = new window.Blob([new Uint8Array(data)], {type: 'image/png'}); window.createImageBitmap(blob).then((imgBitmap) => { callback(null, imgBitmap); @@ -308,7 +305,7 @@ export const resetImageRequestQueue = () => { }; resetImageRequestQueue(); -export const getImage = function(requestParameters: RequestParameters, callback: Callback): Cancelable { +export const getImage = function(requestParameters: RequestParameters, callback: ResponseCallback): Cancelable { if (webpSupported.supported) { if (!requestParameters.headers) { requestParameters.headers = {}; @@ -353,10 +350,10 @@ export const getImage = function(requestParameters: RequestParameters, callback: if (err) { callback(err); } else if (data) { - if (offscreenCanvasSupported()) { - arrayBufferToImageBitmap(data, callback); + if (window.createImageBitmap) { + arrayBufferToImageBitmap(data, (err, imgBitmap) => callback(err, imgBitmap, cacheControl, expires)); } else { - arrayBufferToImage(data, callback, cacheControl, expires); + arrayBufferToImage(data, (err, img) => callback(err, img, cacheControl, expires)); } } }); diff --git a/test/unit/util/ajax.test.js b/test/unit/util/ajax.test.js index 887f81443f5..077264f2534 100644 --- a/test/unit/util/ajax.test.js +++ b/test/unit/util/ajax.test.js @@ -213,5 +213,59 @@ test('ajax', (t) => { window.server.respond(); }); + t.test('getImage retains cache control headers when using arrayBufferToImage', (t) => { + resetImageRequestQueue(); + + const headers = { + 'Content-Type': 'image/webp', + 'Cache-Control': 'max-age=43200,s-maxage=604800', + 'Expires': 'Wed, 21 Oct 2015 07:28:00 GMT' + }; + + window.server.respondWith(request => request.respond(200, headers, '')); + + // jsdom doesn't call image onload; fake it https://github.com/jsdom/jsdom/issues/1816 + window.Image = class { + set src(src) { + setTimeout(() => { + if (this.onload) this.onload(); + }); + } + }; + + getImage({url: ''}, (err, img, cacheControl, expires) => { + if (err) t.fail(); + t.equals(cacheControl, headers['Cache-Control']); + t.equals(expires, headers['Expires']); + t.end(); + }); + + window.server.respond(); + }); + + t.test('getImage retains cache control headers when using arrayBufferToImageBitmap', (t) => { + resetImageRequestQueue(); + + const headers = { + 'Content-Type': 'image/webp', + 'Cache-Control': 'max-age=43200,s-maxage=604800', + 'Expires': 'Wed, 21 Oct 2015 07:28:00 GMT' + }; + + window.server.respondWith(request => request.respond(200, headers, '')); + + // jsdom doesn't support createImageBitmap; fake it + window.createImageBitmap = () => Promise.resolve(); + + getImage({url: ''}, (err, img, cacheControl, expires) => { + if (err) t.fail(); + t.equals(cacheControl, headers['Cache-Control']); + t.equals(expires, headers['Expires']); + t.end(); + }); + + window.server.respond(); + }); + t.end(); });