diff --git a/src/ui/map.js b/src/ui/map.js index 277df72dc83..76008dd7369 100755 --- a/src/ui/map.js +++ b/src/ui/map.js @@ -25,6 +25,7 @@ import { RGBAImage } from '../util/image'; import { Event, ErrorEvent } from '../util/evented'; import { MapMouseEvent } from './events'; import TaskQueue from '../util/task_queue'; +import webpSupported from '../util/webp_supported'; import type {PointLike} from '@mapbox/point-geometry'; import type {LngLatLike} from '../geo/lng_lat'; @@ -1539,6 +1540,8 @@ class Map extends Camera { } this.painter = new Painter(gl, this.transform); + + webpSupported.testSupport(gl); } _contextLost(event: *) { diff --git a/src/util/browser.js b/src/util/browser.js index 2ea3570a851..70036884a76 100755 --- a/src/util/browser.js +++ b/src/util/browser.js @@ -51,16 +51,7 @@ const exported = { }, hardwareConcurrency: window.navigator.hardwareConcurrency || 4, - get devicePixelRatio() { return window.devicePixelRatio; }, - supportsWebp: false + get devicePixelRatio() { return window.devicePixelRatio; } }; export default exported; - -if (window.document) { - const webpImgTest = window.document.createElement('img'); - webpImgTest.onload = function() { - exported.supportsWebp = true; - }; - webpImgTest.src = 'data:image/webp;base64,UklGRh4AAABXRUJQVlA4TBEAAAAvAQAAAAfQ//73v/+BiOh/AAA='; -} diff --git a/src/util/mapbox.js b/src/util/mapbox.js index 5c5dccea7c3..14b75347a4a 100644 --- a/src/util/mapbox.js +++ b/src/util/mapbox.js @@ -3,6 +3,7 @@ import config from './config'; import browser from './browser'; +import webpSupported from './webp_supported'; import window from './window'; import { version } from '../../package.json'; import { uuid, validateUuid, storageAvailable, warnOnce, extend } from './util'; @@ -101,7 +102,7 @@ export const normalizeTileURL = function(tileURL: string, sourceURL?: ?string, t // is appended to the tile URL. If `tileSize: 512` is specified for // a Mapbox raster source force the @2x suffix even if a non hidpi device. const suffix = browser.devicePixelRatio >= 2 || tileSize === 512 ? '@2x' : ''; - const extension = browser.supportsWebp ? '.webp' : '$1'; + const extension = webpSupported.supported ? '.webp' : '$1'; urlObject.path = urlObject.path.replace(imageExtensionRe, `${suffix}${extension}`); urlObject.path = `/v4${urlObject.path}`; diff --git a/src/util/webp_supported.js b/src/util/webp_supported.js new file mode 100755 index 00000000000..452bb10a5f9 --- /dev/null +++ b/src/util/webp_supported.js @@ -0,0 +1,61 @@ +// @flow + +import window from './window'; + +const exported = { + supported: false, + testSupport +}; + +export default exported; + +let glForTesting; +let webpCheckComplete = false; +let webpImgTest; + +if (window.document) { + webpImgTest = window.document.createElement('img'); + webpImgTest.onload = function() { + if (glForTesting) testWebpTextureUpload(glForTesting); + glForTesting = null; + }; + webpImgTest.onerror = function() { + webpCheckComplete = true; + glForTesting = null; + }; + webpImgTest.src = 'data:image/webp;base64,UklGRh4AAABXRUJQVlA4TBEAAAAvAQAAAAfQ//73v/+BiOh/AAA='; +} + +function testSupport(gl: WebGLRenderingContext) { + if (webpCheckComplete || !webpImgTest) return; + + if (!webpImgTest.complete) { + glForTesting = gl; + return; + } + + testWebpTextureUpload(gl); +} + +function testWebpTextureUpload(gl: WebGLRenderingContext) { + // Edge 18 supports WebP but not uploading a WebP image to a gl texture + // Test support for this before allowing WebP images. + // https://github.com/mapbox/mapbox-gl-js/issues/7671 + const texture = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, texture); + + try { + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, webpImgTest); + + // The error does not get triggered in Edge if the context is lost + if (gl.isContextLost()) return; + + exported.supported = true; + } catch (e) { + // Catch "Unspecified Error." in Edge 18. + } + + gl.deleteTexture(texture); + + webpCheckComplete = true; +} diff --git a/test/unit/util/browser.test.js b/test/unit/util/browser.test.js index e520f0d9016..4a65646f225 100644 --- a/test/unit/util/browser.test.js +++ b/test/unit/util/browser.test.js @@ -33,10 +33,5 @@ test('browser', (t) => { t.end(); }); - t.test('supportsWebp', (t) => { - t.equal(typeof browser.supportsWebp, 'boolean'); - t.end(); - }); - t.end(); }); diff --git a/test/unit/util/mapbox.test.js b/test/unit/util/mapbox.test.js index c3b6fc50f88..25480485395 100644 --- a/test/unit/util/mapbox.test.js +++ b/test/unit/util/mapbox.test.js @@ -1,7 +1,7 @@ import { test } from 'mapbox-gl-js-test'; import * as mapbox from '../../../src/util/mapbox'; import config from '../../../src/util/config'; -import browser from '../../../src/util/browser'; +import webpSupported from '../../../src/util/webp_supported'; import window from '../../../src/util/window'; import { uuid } from '../../../src/util/util'; import { version } from '../../../package.json'; @@ -287,7 +287,7 @@ test("mapbox", (t) => { }); t.test('.normalizeTileURL', (t) => { - browser.supportsWebp = false; + webpSupported.supported = false; t.test('does nothing on 1x devices', (t) => { config.API_URL = 'http://path.png'; @@ -321,14 +321,14 @@ test("mapbox", (t) => { }); t.test('replaces img extension with webp on supporting devices', (t) => { - browser.supportsWebp = true; + webpSupported.supported = true; config.API_URL = 'http://path.png'; config.REQUIRE_ACCESS_TOKEN = false; t.equal(mapbox.normalizeTileURL('http://path.png/tile.png', mapboxSource), 'http://path.png/v4/tile.webp'); t.equal(mapbox.normalizeTileURL('http://path.png/tile.png32', mapboxSource), 'http://path.png/v4/tile.webp'); t.equal(mapbox.normalizeTileURL('http://path.png/tile.jpg70', mapboxSource), 'http://path.png/v4/tile.webp'); t.equal(mapbox.normalizeTileURL('http://path.png/tile.png?access_token=foo', mapboxSource), 'http://path.png/v4/tile.webp?access_token=foo'); - browser.supportsWebp = false; + webpSupported.supported = false; t.end(); }); @@ -372,7 +372,7 @@ test("mapbox", (t) => { t.end(); }); - browser.supportsWebp = true; + webpSupported.supported = true; t.end(); });