Workaround unexpected terrain spikes in browsers with fingerprint protection enabled
@astojilj is already working on this.
Since Dec 4, 2023.
- Dominant language
- TypeScript
- Stars
- 12.4k
- Forks
- 2.4k
- PR merge metrics
- No merged PRs in 30d
Description
The v3 migration guide [Known issues and limitations](https://docs.mapbox.com/mapbox-gl-js/guides/migrate-to-v3/#known-issues-and-limitations) refers to an issue with spikes visible on terrain when run in Safari 17 private browsing mode (edit: and Firefox with fingerprint protection enabled):
> In Safari 17 private browsing mode, Apple's Advanced Privacy Protection introduces [noise](https://developer.apple.com/documentation/safari-release-notes/safari-17-release-notes#Private-Browsing) into key fingerprinting areas like 2D Canvas and WebGL and may cause unexpected terrain spikes in GL JS v3.
The issue also exists with previous GL JS versions.
While we're working on resolving this, a feasible workaround would be to disable terrain from the application code, when it is detected that anti-fingerprinting protection introduces noise to 2D canvas operations:
```js
export function isPrivateModeSafariWithCanvasFingerprintProtection(scope: any): boolean {
if (!isSafari(scope)) return false;
// Starting from version 17, fingerprinting protection introduces fuzz to Canvas2D operations used for image decoding.
const version = scope.navigator ? scope.navigator.userAgent.match(/Version\/([0-9\._]+).*Safari/) : [];
if (!(version.length === 2 && parseInt(version[1]) >= 17)) {
return false;
}
assert(window.OffscreenCanvas);
const offscreenCanvas = new window.OffscreenCanvas(255 / 3, 1);
const offscreenCanvasContext = offscreenCanvas.getContext('2d', {willReadFrequently: true});
let inc = 0;
// getImageData is lossy with premultiplied alpha.
for (let i = 0; i < offscreenCanvas.width; ++i) {
offscreenCanvasContext.fillStyle = `rgba(${inc++},${inc++},${inc++}, 255)`;
offscreenCanvasContext.fillRect(i, 0, 1, 1);
}
const readData = offscreenCanvasContext.getImageData(0, 0, offscreenCanvas.width, offscreenCanvas.height);
inc = 0;
for (let i = 0; i < readData.data.length; ++i) {
if (i % 4 !== 3) {
if (inc++ !== readData.data[i]) {
return true;
}
}
}
return false;
}
```

Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.