fix(import): one blob URL per packed KMZ image, cap KML styles and links

A KMZ could name one large packed image in thousands of <href>s, and each
one made a fresh Blob copy for Cesium to decode. kmzImageResolver now keeps
one blob URL per archive entry, and countKmlGeometry refuses a document
with more than 2,000 <href> and <Style> elements (MAX_KML_LINKS) before
Cesium reads it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
kapturnerandClaude Opus 5.5 committed 2026-10-11 21:55:11 -05:00
1 parent d8b4b0d80f
commit 90b783c532
3 files changed
+94 -22

No files matched your search

+38
View File
@@ -14,6 +14,7 @@ import {
stripGeoJsonStyleProperties,
detectGeoFileFormat,
importDisplayName,
kmzImageResolver,
kmzImageType,
kmzMainDocument,
parseGeoJsonText,
@@ -406,12 +407,49 @@ test('KML placemarks and coordinate tuples are counted before loading', () => {
assert.deepEqual(countKmlGeometry(doc), {
features: 2,
positions: 42,
links: 0,
over: '',
});
assert.equal(countKmlGeometry(doc, { maxPositions: 41 }).over, 'positions');
assert.equal(countKmlGeometry(doc, { maxFeatures: 1 }).over, 'features');
});
test('KML styles and icon links are counted against their own limit', () => {
const style = () =>
xmlEl('Style', {}, [
xmlEl('IconStyle', {}, [xmlEl('Icon', {}, [xmlEl('href', {}, 'a.png')])]),
]);
const doc = xmlDoc(xmlEl('kml', {}, [style(), style(), style()]));
assert.equal(countKmlGeometry(doc).links, 6);
assert.equal(countKmlGeometry(doc, { maxLinks: 5 }).over, 'links');
});
test('a KMZ image gets one blob URL however many hrefs name it', () => {
const entries = new Map([
['doc.kml', new Uint8Array(0)],
['a.png', new Uint8Array(8)],
['b.png', new Uint8Array(8)],
]);
const made = [];
const kept = [];
const resolve = kmzImageResolver(
entries,
'doc.kml',
(url) => kept.push(url),
(blob) => {
made.push(blob);
return `blob:test/${made.length}`;
},
);
for (let i = 0; i < 1000; i++) assert.equal(resolve('a.png'), 'blob:test/1');
assert.equal(resolve('./b.png'), 'blob:test/2');
assert.equal(resolve('a.png'), 'blob:test/1');
assert.equal(resolve('https://example.com/a.png'), null);
assert.equal(resolve('doc.kml'), null);
assert.equal(made.length, 2);
assert.deepEqual(kept, ['blob:test/1', 'blob:test/2']);
});
test('simplestyle is dropped: no icon fetched, the row color is what draws', () => {
const fc = {
type: 'FeatureCollection',