fix(street-level): camera ownership, mesh refresh, panel CSP (Sameh's review)

- Photo framing and FOLLOW claim the camera through the application's
  navigation, injected with attachNavigation({ run, subscribeHandoff }):
  aircraft and satellite tracking is released first, the cockpit refuses,
  and FOLLOW stops when another feature takes the camera. No UI imports
  in the layer.
- The mesh sampler follows the mesh floor's rules: it waits for the
  visible tileset to finish streaming (visibleTilesetLoaded, now exposed
  by the mesh floor service), needs a real bare-earth prior and keeps to
  the shared meshFloorSampleWithinPrior window, which cameraFollow now
  uses too. A cell is probed again once the camera is half as far from
  it (down to 40 m), so a coarse early sample is replaced and redrawn.
- The MCP globe panel's CSP allows the Mapillary Graph API and its image
  CDN (https://*.xx.fbcdn.net).
- The tile proxy reads bodies with the shared readResponseBytesCapped.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Said TurkseverandClaude Opus 5.5 committed 2026-10-06 23:04:33 +01:00
1 parent 630a1abdae
commit 1fa129ea8e
13 files changed
+459 -79

No files matched your search

+6 -2
View File
@@ -14,8 +14,9 @@ export const PANEL_WORKER_PRELUDE_PATH = 'cesium/worker-prelude.js';
/**
* Sites the app's browser code loads from directly: map imagery, 3D tiles,
* terrain and fonts. Everything from the app's own server comes through the
* MCP server.
* terrain, fonts, and Street Level's Mapillary Graph API and image CDN (its
* coverage tiles come through the app's server). Everything from the app's
* own server comes through the MCP server.
*/
const PROVIDER_ORIGINS = Object.freeze([
'https://tile.googleapis.com',
@@ -35,6 +36,9 @@ const PROVIDER_ORIGINS = Object.freeze([
'https://gibs.earthdata.nasa.gov',
'https://fonts.googleapis.com',
'https://fonts.gstatic.com',
'https://graph.mapillary.com',
// Mapillary photos come from regional hosts, e.g. scontent-man2-1.xx.fbcdn.net.
'https://*.xx.fbcdn.net',
]);
export const MCP_APP_MIME_TYPE = 'text/html;profile=mcp-app';
+36
View File
@@ -7,6 +7,21 @@ import {
} from './globePanel.js';
import { panelRuntime } from '../app/globePanelRuntime.js';
import { composeCatalog, coreTools } from './index.js';
import { MAPILLARY_GRAPH_HOST } from '../layers/streetLevel/providers/mapillary/policy.js';
/** Whether a CSP source list allows a URL (exact origin or `https://*.host`). */
function allows(sources, url) {
const { origin, protocol, hostname } = new URL(url);
return sources.some((source) => {
if (source === origin) return true;
const wildcard = source.match(/^(https:)\/\/\*\.(.+)$/);
return (
Boolean(wildcard) &&
protocol === wildcard[1] &&
hostname.endsWith(`.${wildcard[2]}`)
);
});
}
test('the globe panel is an MCP Apps resource that loads the app through its server', () => {
const resource = createGlobePanelResource({
@@ -45,6 +60,27 @@ test('the globe panel is an MCP Apps resource that loads the app through its ser
assert.doesNotThrow(() => new Function(script));
});
test('the panel lets Street Level reach Mapillary from inside the embed', () => {
const { csp } = createGlobePanelResource({
runtime: panelRuntime,
panelKey: 'test-key',
})._meta.ui;
// Graph API lookups (nearest image, sequences) and MapillaryJS photo loads.
for (const url of [
`${MAPILLARY_GRAPH_HOST}/images?fields=id&bbox=1,2,3,4`,
'https://scontent-man2-1.xx.fbcdn.net/m1/v/t6/photo.jpg',
'https://scontent-iad3-2.xx.fbcdn.net/m1/v/t6/photo.jpg',
]) {
assert.ok(allows(csp.connectDomains, url), `connect-src allows ${url}`);
assert.ok(allows(csp.resourceDomains, url), `img-src allows ${url}`);
}
assert.equal(allows(csp.connectDomains, 'https://evil.example/x'), false);
assert.equal(
allows(csp.connectDomains, 'https://fbcdn.net.evil.example/'),
false,
);
});
test('the globe panel needs a request key', () => {
for (const panelKey of [undefined, '', 42])
assert.throws(