Skip to content

Commit 912e738

Browse files
committed
Fix: mon code pour l'issue 7149
1 parent c2e92ec commit 912e738

File tree

11 files changed

+677
-48
lines changed

11 files changed

+677
-48
lines changed

src/source/raster_tile_source.test.ts

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ import {fakeServer, type FakeServer} from 'nise';
77
import {type Tile} from '../tile/tile';
88
import {stubAjaxGetImage, waitForEvent} from '../util/test/util';
99
import {type MapSourceDataEvent} from '../ui/events';
10+
import * as loadTileJSONModule from './load_tilejson';
11+
12+
function isAbortPendingTileRequestsEvent(e: any): boolean {
13+
return e?.abortPendingTileRequests === true || e?.data?.abortPendingTileRequests === true;
14+
}
1015

1116
function createSource(options, transformCallback?) {
1217
const source = new RasterTileSource('id', options, {send() {}} as any as Dispatcher, options.eventedParent);
@@ -31,6 +36,7 @@ describe('RasterTileSource', () => {
3136

3237
afterEach(() => {
3338
server.restore();
39+
vi.restoreAllMocks();
3440
});
3541

3642
test('transforms request for TileJSON URL', () => {
@@ -339,4 +345,118 @@ describe('RasterTileSource', () => {
339345
await expect(loadPromise).resolves.toBeUndefined();
340346
expect(tile.state).toBe('unloaded');
341347
});
348+
349+
test('loads tile after previous abort flag was set', async () => {
350+
server.respondWith('/source.json', JSON.stringify({
351+
minzoom: 0,
352+
maxzoom: 22,
353+
attribution: 'MapLibre',
354+
tiles: ['http://example.com/{z}/{x}/{y}.png'],
355+
bounds: [-47, -7, -45, -5]
356+
}));
357+
server.respondWith('http://example.com/10/5/5.png',
358+
[200, {'Content-Type': 'image/png', 'Content-Length': 1}, '0']
359+
);
360+
361+
const source = createSource({url: '/source.json'});
362+
source.map.painter = {context: {}, getTileTexture: () => ({update: () => {}})} as any;
363+
364+
const sourcePromise = waitForEvent(source, 'data', (e: MapSourceDataEvent) => e.sourceDataType === 'metadata');
365+
server.respond();
366+
await sourcePromise;
367+
368+
const tile = {
369+
tileID: new OverscaledTileID(10, 0, 10, 5, 5),
370+
state: 'loading',
371+
aborted: true,
372+
setExpiryData() {}
373+
} as any as Tile;
374+
375+
expect(tile.aborted).toBe(true);
376+
377+
const tilePromise = source.loadTile(tile);
378+
server.respond();
379+
await tilePromise;
380+
381+
expect(tile.state).toBe('loaded');
382+
expect(tile.aborted).toBe(false);
383+
});
384+
385+
test('setSourceProperty emits abortPendingTileRequests before callback and load(true)', () => {
386+
const source = createSource({
387+
tiles: ['http://example.com/{z}/{x}/{y}.png']
388+
});
389+
const onData = vi.fn();
390+
source.on('data', onData);
391+
392+
const callback = vi.fn();
393+
const loadSpy = vi.spyOn(source, 'load');
394+
395+
source.setSourceProperty(callback);
396+
397+
const abortEventIndex = onData.mock.calls.findIndex(([e]) => isAbortPendingTileRequestsEvent(e));
398+
expect(abortEventIndex).toBeGreaterThan(-1);
399+
400+
const abortEventOrder = onData.mock.invocationCallOrder[abortEventIndex];
401+
const callbackOrder = callback.mock.invocationCallOrder[0];
402+
const loadOrder = loadSpy.mock.invocationCallOrder[0];
403+
404+
expect(abortEventOrder).toBeLessThan(callbackOrder);
405+
expect(callbackOrder).toBeLessThan(loadOrder);
406+
407+
expect(callback).toHaveBeenCalledTimes(1);
408+
expect(loadSpy).toHaveBeenCalledTimes(1);
409+
expect(loadSpy).toHaveBeenCalledWith(true);
410+
});
411+
412+
test('setUrl emits abortPendingTileRequests and calls load(true)', () => {
413+
const source = createSource({
414+
tiles: ['http://example.com/{z}/{x}/{y}.png']
415+
});
416+
417+
const loadSpy = vi.spyOn(source, 'load');
418+
const events: Array<any> = [];
419+
source.on('data', (e: any) => events.push(e));
420+
421+
source.setUrl('http://localhost:2900/source2.json');
422+
423+
expect(
424+
events.some((e) => isAbortPendingTileRequestsEvent(e))
425+
).toBe(true);
426+
expect(source.url).toBe('http://localhost:2900/source2.json');
427+
expect(loadSpy).toHaveBeenCalledWith(true);
428+
});
429+
430+
test('load ignores AbortError from TileJSON request', async () => {
431+
const source = createSource({
432+
tiles: ['http://example.com/{z}/{x}/{y}.png']
433+
});
434+
435+
const abortError = new Error('aborted');
436+
(abortError as any).name = 'AbortError';
437+
vi.spyOn(loadTileJSONModule, 'loadTileJson').mockRejectedValueOnce(abortError);
438+
439+
const onError = vi.fn();
440+
source.on('error', onError);
441+
442+
await source.load(true);
443+
444+
expect(onError).not.toHaveBeenCalled();
445+
});
446+
447+
test('load emits error event when TileJSON is malformed (parser rejection)', async () => {
448+
const source = createSource({
449+
tiles: ['http://example.com/{z}/{x}/{y}.png']
450+
});
451+
452+
vi.spyOn(loadTileJSONModule, 'loadTileJson').mockRejectedValueOnce(new Error('Invalid TileJSON payload'));
453+
454+
const onError = vi.fn();
455+
source.on('error', onError);
456+
457+
await source.load(true);
458+
459+
expect(onError).toHaveBeenCalledTimes(1);
460+
});
461+
342462
});

src/source/raster_tile_source.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,8 +139,13 @@ export class RasterTileSource extends Evented implements Source {
139139
this._tileJSONRequest = null;
140140
}
141141

142-
callback();
142+
// Request to cancel tiles in flight, without depending on map.style.tileManagers
143+
this.fire(new Event('data', {
144+
dataType: 'source',
145+
abortPendingTileRequests: true
146+
} ));
143147

148+
callback();
144149
this.load(true);
145150
}
146151

@@ -181,6 +186,8 @@ export class RasterTileSource extends Evented implements Source {
181186

182187
async loadTile(tile: Tile): Promise<void> {
183188
const url = tile.tileID.canonical.url(this.tiles, this.map.getPixelRatio(), this.scheme);
189+
// A tile can be reused after a previous abort; clear stale abort state before a new request.
190+
tile.aborted = false;
184191
tile.abortController = new AbortController();
185192
try {
186193
const response = await ImageRequest.getImage(this.map._requestManager.transformRequest(url, ResourceType.Tile), tile.abortController, this.map._refreshExpiredTiles);

0 commit comments

Comments
 (0)