From 01b3cbe3329875da2a0373002989201b1a67c1b2 Mon Sep 17 00:00:00 2001 From: Evan Date: Tue, 28 Apr 2026 09:53:22 -0600 Subject: [PATCH] Set crossOrigin = "anonymous" on canvas-bound icon images (#3789) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description: StructureLayer.loadIcon and StructureDrawingUtils.loadIcon hand-roll new Image() and feed the result into a canvas. With assets now served from a cross-origin CDN, the default no-cors fetch tainted the canvas, and WebGL's texImage2D rejected the upload `Uncaught SecurityError: Tainted canvases may not be loaded`. Setting crossOrigin = "anonymous" before src switches to a CORS-checked fetch (R2 already returns ACAO), so the canvas stays clean and the texture upload succeeds. Other new Image() and sites in the codebase don't need the change — they're either DOM-only or read naturalWidth/naturalHeight only. ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: evan --- src/client/graphics/layers/StructureDrawingUtils.ts | 4 ++++ src/client/graphics/layers/StructureLayer.ts | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/client/graphics/layers/StructureDrawingUtils.ts b/src/client/graphics/layers/StructureDrawingUtils.ts index 9c3eb6ce5..1ccad7c2f 100644 --- a/src/client/graphics/layers/StructureDrawingUtils.ts +++ b/src/client/graphics/layers/StructureDrawingUtils.ts @@ -89,6 +89,10 @@ export class SpriteFactory { unitType: UnitType, ) { const image = new Image(); + // crossOrigin must be set before src so the fetch is CORS-checked. + // Without this, an icon served from CDN_BASE taints structureCanvas + // and PIXI.Texture.from rejects the upload to WebGL. + image.crossOrigin = "anonymous"; image.src = unitInfo.iconPath; image.onload = () => { unitInfo.image = image; diff --git a/src/client/graphics/layers/StructureLayer.ts b/src/client/graphics/layers/StructureLayer.ts index 5bc165b92..f26a7dfbd 100644 --- a/src/client/graphics/layers/StructureLayer.ts +++ b/src/client/graphics/layers/StructureLayer.ts @@ -87,6 +87,10 @@ export class StructureLayer implements Layer { private loadIcon(unitType: string, config: UnitRenderConfig) { const image = new Image(); + // crossOrigin must be set before src so the fetch is CORS-checked. + // Without this, an icon served from CDN_BASE taints any canvas/texture + // it's drawn into, and WebGL refuses to upload it via texImage2D. + image.crossOrigin = "anonymous"; image.src = config.icon; image.onload = () => { this.unitIcons.set(unitType, image);