gesso-core 0.6.29 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/testing.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { $h as TextMeasurer, Co as UiGestureRecognizer, Fa as UiPlatformAdapter, Fn as CanvasSurface, Ga as TouchScrollerOptions, Hm as UiNode, Ho as UiHitTester, Js as Canvas2DContext, Ka as UiTouchScroller, Mm as LayoutBox, Na as PlatformEventTarget, Nm as LayoutResult, Pa as PlatformSurface, Pn as CanvasHost, Uh as LayoutRecord, Wa as UiEditingController, Wp as UiInputDispatcher, Yi as LayoutEngine, Ys as Canvas2DGradient, bn as Canvas2DRenderer, fo as UiKeyboardController, go as ScrollSink, ho as ScrollContainerState, jm as Constraints, jp as UiFocusManager, mo as UiPointerController, po as PointerControllerOptions, sl as UiGraph, ug as UiNodeType, yo as UiWheelController } from "./index-v2fhm7Pn.js";
1
+ import { Do as ScrollContainerState, Eo as UiPointerController, Fo as UiGestureRecognizer, Ga as PlatformEventTarget, Ig as UiNodeType, Ka as PlatformSurface, Oo as ScrollSink, Rn as CanvasHost, Tg as TextMeasurer, To as PointerControllerOptions, ah as LayoutResult, es as UiHitTester, gm as UiInputDispatcher, hg as LayoutRecord, ih as LayoutBox, io as UiTouchScroller, jo as UiWheelController, mh as UiNode, nm as UiFocusManager, no as UiEditingController, oc as Canvas2DContext, qa as UiPlatformAdapter, rh as Constraints, ro as TouchScrollerOptions, sa as LayoutEngine, sc as Canvas2DGradient, wn as Canvas2DRenderer, wo as UiKeyboardController, yl as UiGraph, zn as CanvasSurface } from "./index-B5r1ETCh.js";
2
2
  //#region src/layout/LayoutTestUtils.d.ts
3
3
  /**
4
4
  * Shared helpers for layout specs: builds UiNode trees through
@@ -121,6 +121,21 @@ declare class HarnessScrollSink implements ScrollSink {
121
121
  interface RecordedCall {
122
122
  name: string;
123
123
  args: unknown[];
124
+ /** Which context made the call, when several record into one log; absent for the frame's own. */
125
+ target?: string;
126
+ }
127
+ interface RecordingOptions {
128
+ /** A log to record into, shared with other contexts; a fresh one by default. */
129
+ calls?: RecordedCall[];
130
+ /** Stamped on every call this context records into a shared log. */
131
+ target?: string;
132
+ /**
133
+ * Whether the context tracks its transform and answers `getTransform`,
134
+ * as a real one does. Off by default, because a renderer reads the
135
+ * absence as "no transform worth reporting" and most specs were
136
+ * written against that; see `Canvas2DContext.getTransform`.
137
+ */
138
+ transform?: boolean;
124
139
  }
125
140
  /**
126
141
  * The gradient a recording context hands back, carrying what it was
@@ -144,6 +159,20 @@ declare class RecordedGradient implements Canvas2DGradient {
144
159
  */
145
160
  declare class RecordingCanvasContext implements Canvas2DContext {
146
161
  readonly calls: RecordedCall[];
162
+ private readonly target;
163
+ private matrix;
164
+ private readonly matrices;
165
+ getTransform?: () => {
166
+ a: number;
167
+ b: number;
168
+ c: number;
169
+ d: number;
170
+ e: number;
171
+ f: number;
172
+ };
173
+ constructor(options?: RecordingOptions);
174
+ /** Multiplies the current matrix by another on the right, as a canvas does. */
175
+ private compose;
147
176
  private _fillStyle;
148
177
  private _strokeStyle;
149
178
  private _lineWidth;
@@ -232,10 +261,12 @@ declare function savedDepth(context: RecordingCanvasContext): number;
232
261
  */
233
262
  declare class FakeCanvasHost implements CanvasHost {
234
263
  private readonly context;
264
+ readonly label?: string | undefined;
235
265
  width: number;
236
266
  height: number;
237
267
  private contextRequests;
238
- constructor(context: Canvas2DContext | null);
268
+ /** `label` names the host for a spec that meets it again as a `drawImage` source. */
269
+ constructor(context: Canvas2DContext | null, label?: string | undefined);
239
270
  getContext(_contextId: '2d'): CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D | null;
240
271
  get contextRequestCount(): number;
241
272
  }
@@ -252,7 +283,19 @@ declare class RenderHarness {
252
283
  readonly host: FakeCanvasHost;
253
284
  readonly surface: CanvasSurface;
254
285
  readonly renderer: Canvas2DRenderer;
255
- constructor(logicalWidth?: number, logicalHeight?: number, dpr?: number);
286
+ /** The layers opacity groups were drawn in, by label; empty without `groups`. */
287
+ readonly groupLayers: Map<string, FakeCanvasHost>;
288
+ /**
289
+ * With `groups`, opacity groups get layers as they do in a browser:
290
+ * the context tracks its transform, and each layer is a recording
291
+ * context writing into the frame's log, its calls stamped with the
292
+ * layer's label (`group0`, `group1`, ...) and its host met again as
293
+ * the source of the `drawImage` that blends it. Text is not kept as
294
+ * pixels, so it is still drawn as `fillText`.
295
+ */
296
+ constructor(logicalWidth?: number, logicalHeight?: number, dpr?: number, options?: {
297
+ groups?: boolean;
298
+ });
256
299
  createNode(id: string, type: UiNodeType): UiNode;
257
300
  append(parent: UiNode, ...children: UiNode[]): void;
258
301
  layout(node: UiNode, constraints?: Constraints): LayoutResult;
package/dist/testing.js CHANGED
@@ -1,4 +1,4 @@
1
- import { $n as CharacterCountTextMeasurer, P as Constraints, Sr as UiGraph, Z as UiPlatformAdapter, at as UiGestureRecognizer, dt as UiFocusManager, ft as UiHitTester, it as UiTouchScroller, lt as UiKeyboardController, r as Canvas2DRenderer, rt as UiEditingController, st as UiWheelController, t as CanvasSurface, ut as UiPointerController, wt as UiInputDispatcher, x as LayoutEngine, yt as scrollRange } from "./CanvasSurface-BTZ3A3MN.js";
1
+ import { $ as UiPlatformAdapter, C as LayoutEngine, Et as UiInputDispatcher, I as Constraints, at as UiEditingController, dt as UiKeyboardController, ft as UiPointerController, lt as UiWheelController, mt as UiHitTester, ot as UiTouchScroller, pt as UiFocusManager, r as Canvas2DRenderer, st as UiGestureRecognizer, t as CanvasSurface, tr as CharacterCountTextMeasurer, wr as UiGraph, xt as scrollRange } from "./CanvasSurface-BpgbOku2.js";
2
2
  //#region src/layout/LayoutTestUtils.ts
3
3
  /**
4
4
  * Shared helpers for layout specs: builds UiNode trees through
@@ -263,7 +263,45 @@ var RecordedGradient = class {
263
263
  * assignment so tests can assert the exact draw sequence.
264
264
  */
265
265
  var RecordingCanvasContext = class {
266
- calls = [];
266
+ calls;
267
+ target;
268
+ matrix = [
269
+ 1,
270
+ 0,
271
+ 0,
272
+ 1,
273
+ 0,
274
+ 0
275
+ ];
276
+ matrices = [];
277
+ getTransform;
278
+ constructor(options = {}) {
279
+ this.calls = options.calls ?? [];
280
+ this.target = options.target;
281
+ if (options.transform === true) this.getTransform = () => {
282
+ const [a, b, c, d, e, f] = this.matrix;
283
+ return {
284
+ a,
285
+ b,
286
+ c,
287
+ d,
288
+ e,
289
+ f
290
+ };
291
+ };
292
+ }
293
+ /** Multiplies the current matrix by another on the right, as a canvas does. */
294
+ compose(a, b, c, d, e, f) {
295
+ const m = this.matrix;
296
+ this.matrix = [
297
+ m[0] * a + m[2] * b,
298
+ m[1] * a + m[3] * b,
299
+ m[0] * c + m[2] * d,
300
+ m[1] * c + m[3] * d,
301
+ m[0] * e + m[2] * f + m[4],
302
+ m[1] * e + m[3] * f + m[5]
303
+ ];
304
+ }
267
305
  _fillStyle = "#000";
268
306
  _strokeStyle = "#000";
269
307
  _lineWidth = 1;
@@ -389,6 +427,7 @@ var RecordingCanvasContext = class {
389
427
  shadowOffsetX: this._shadowOffsetX,
390
428
  shadowOffsetY: this._shadowOffsetY
391
429
  });
430
+ this.matrices.push(this.matrix);
392
431
  this.record("save", []);
393
432
  }
394
433
  restore() {
@@ -408,18 +447,32 @@ var RecordingCanvasContext = class {
408
447
  this._shadowOffsetX = state.shadowOffsetX;
409
448
  this._shadowOffsetY = state.shadowOffsetY;
410
449
  }
450
+ this.matrix = this.matrices.pop() ?? this.matrix;
411
451
  this.record("restore", []);
412
452
  }
413
453
  translate(x, y) {
454
+ this.compose(1, 0, 0, 1, x, y);
414
455
  this.record("translate", [x, y]);
415
456
  }
416
457
  scale(x, y) {
458
+ this.compose(x, 0, 0, y, 0, 0);
417
459
  this.record("scale", [x, y]);
418
460
  }
419
461
  rotate(angle) {
462
+ const cos = Math.cos(angle);
463
+ const sin = Math.sin(angle);
464
+ this.compose(cos, sin, -sin, cos, 0, 0);
420
465
  this.record("rotate", [angle]);
421
466
  }
422
467
  setTransform(a, b, c, d, e, f) {
468
+ this.matrix = [
469
+ a,
470
+ b,
471
+ c,
472
+ d,
473
+ e,
474
+ f
475
+ ];
423
476
  this.record("setTransform", [
424
477
  a,
425
478
  b,
@@ -518,6 +571,7 @@ var RecordingCanvasContext = class {
518
571
  ]);
519
572
  }
520
573
  transform(a, b, c, d, e, f) {
574
+ this.compose(a, b, c, d, e, f);
521
575
  this.record("transform", [
522
576
  a,
523
577
  b,
@@ -593,10 +647,12 @@ var RecordingCanvasContext = class {
593
647
  return gradient;
594
648
  }
595
649
  record(name, args) {
596
- this.calls.push({
650
+ const call = {
597
651
  name,
598
652
  args: args.map(normalizeNumber)
599
- });
653
+ };
654
+ if (this.target !== void 0) call.target = this.target;
655
+ this.calls.push(call);
600
656
  }
601
657
  };
602
658
  /**
@@ -626,11 +682,14 @@ function savedDepth(context) {
626
682
  */
627
683
  var FakeCanvasHost = class {
628
684
  context;
685
+ label;
629
686
  width = 0;
630
687
  height = 0;
631
688
  contextRequests = 0;
632
- constructor(context) {
689
+ /** `label` names the host for a spec that meets it again as a `drawImage` source. */
690
+ constructor(context, label) {
633
691
  this.context = context;
692
+ this.label = label;
634
693
  }
635
694
  getContext(_contextId) {
636
695
  this.contextRequests++;
@@ -649,11 +708,41 @@ var RenderHarness = class {
649
708
  graph = new UiGraph();
650
709
  measurer = new CharacterCountTextMeasurer();
651
710
  engine = new LayoutEngine(this.measurer);
652
- context = new RecordingCanvasContext();
653
- host = new FakeCanvasHost(this.context);
654
- surface = new CanvasSurface(this.host);
655
- renderer = new Canvas2DRenderer({ surface: this.surface });
656
- constructor(logicalWidth = 800, logicalHeight = 600, dpr = 1) {
711
+ context;
712
+ host;
713
+ surface;
714
+ renderer;
715
+ /** The layers opacity groups were drawn in, by label; empty without `groups`. */
716
+ groupLayers = /* @__PURE__ */ new Map();
717
+ /**
718
+ * With `groups`, opacity groups get layers as they do in a browser:
719
+ * the context tracks its transform, and each layer is a recording
720
+ * context writing into the frame's log, its calls stamped with the
721
+ * layer's label (`group0`, `group1`, ...) and its host met again as
722
+ * the source of the `drawImage` that blends it. Text is not kept as
723
+ * pixels, so it is still drawn as `fillText`.
724
+ */
725
+ constructor(logicalWidth = 800, logicalHeight = 600, dpr = 1, options = {}) {
726
+ const groups = options.groups === true;
727
+ this.context = new RecordingCanvasContext({ transform: groups });
728
+ this.host = new FakeCanvasHost(this.context);
729
+ this.surface = new CanvasSurface(this.host);
730
+ this.renderer = new Canvas2DRenderer(groups ? {
731
+ surface: this.surface,
732
+ textCache: false,
733
+ createLayerCanvas: (width, height) => {
734
+ const label = `group${this.groupLayers.size}`;
735
+ const host = new FakeCanvasHost(new RecordingCanvasContext({
736
+ calls: this.context.calls,
737
+ target: label,
738
+ transform: true
739
+ }), label);
740
+ host.width = width;
741
+ host.height = height;
742
+ this.groupLayers.set(label, host);
743
+ return host;
744
+ }
745
+ } : { surface: this.surface });
657
746
  this.surface.setLogicalSize(logicalWidth, logicalHeight, dpr);
658
747
  }
659
748
  createNode(id, type) {
@@ -1 +1 @@
1
- {"version":3,"file":"testing.js","names":[],"sources":["../src/layout/LayoutTestUtils.ts","../src/input/UiInputTestUtils.ts","../src/rendering/RenderTestUtils.ts","../src/media/Mp4TestUtils.ts"],"sourcesContent":["import { UiGraph } from '../graph/UiGraph';\nimport type { UiNode } from '../graph/UiNode';\nimport { UiNodeType } from '../graph/UiNodeType';\nimport { LayoutEngine } from './LayoutEngine';\nimport { Constraints } from './LayoutTypes';\nimport type { LayoutBox, LayoutResult } from './LayoutTypes';\nimport type { LayoutRecord } from './LayoutRecord';\nimport type { TextMeasurer } from './TextMeasurer';\n\n/**\n * Shared helpers for layout specs: builds UiNode trees through\n * the real graph and drives LayoutEngine.\n */\nexport class LayoutHarness {\n readonly graph = new UiGraph();\n readonly engine: LayoutEngine;\n\n constructor(textMeasurer?: TextMeasurer) {\n this.engine = new LayoutEngine(textMeasurer);\n }\n\n createNode(id: string, type: UiNodeType): UiNode {\n return this.graph.createNode(id, type);\n }\n\n append(parent: UiNode, ...children: UiNode[]): void {\n for (const child of children) {\n this.graph.appendChild(parent, child);\n }\n }\n\n layout(node: UiNode, constraints: Constraints = Constraints.unbounded()): LayoutResult {\n return this.engine.layout(node, constraints);\n }\n\n record(node: UiNode): LayoutRecord {\n const record = this.engine.recordFor(node);\n if (record === undefined) {\n throw new Error(`No layout record for '${node.id}'.`);\n }\n return record;\n }\n\n box(node: UiNode): LayoutBox {\n const record = this.record(node);\n return { x: record.x, y: record.y, width: record.width, height: record.height };\n }\n\n boxOf(node: UiNode): LayoutBox {\n return this.engine.worldBox(node);\n }\n\n /** Where the node is seen: scroll offsets and sticky shifts applied. */\n visibleBox(node: UiNode): LayoutBox {\n return this.engine.visibleBox(node);\n }\n}\n","import { UiNodeType } from '../graph/UiNodeType';\nimport type { UiNode } from '../graph/UiNode';\nimport { Constraints } from '../layout/LayoutTypes';\nimport { scrollRange } from '../layout/Scrollbars';\nimport { LayoutHarness } from '../layout/LayoutTestUtils';\nimport { UiHitTester } from './UiHitTester';\nimport { UiInputDispatcher } from './UiInputDispatcher';\nimport { UiPointerController, type PointerControllerOptions } from './UiPointerController';\nimport { UiGestureRecognizer } from './UiGestureRecognizer';\nimport { UiTouchScroller, type TouchScrollerOptions } from './UiTouchScroller';\nimport { UiFocusManager } from './UiFocusManager';\nimport { UiKeyboardController } from './UiKeyboardController';\nimport { UiEditingController, type EditingHost } from './UiEditingController';\nimport { UiWheelController, type ScrollContainerState, type ScrollSink } from './UiWheelController';\nimport { UiPlatformAdapter, type PlatformEventTarget, type PlatformSurface } from './UiPlatformAdapter';\nimport { CharacterCountTextMeasurer, type TextMeasurer } from '../layout/TextMeasurer';\n\n/**\n * Shared harness for input specs.\n *\n * Builds real UiNode trees through UiGraph + LayoutEngine so hit\n * testing, dispatch, focus and gestures are tested against actual\n * layout geometry rather than hand-written boxes.\n */\nexport class InputTestHarness {\n readonly layout: LayoutHarness;\n readonly root: UiNode;\n readonly dispatcher = new UiInputDispatcher();\n /** Shared, so a spec can read back what a controller scrolled. */\n readonly scrollSink = new HarnessScrollSink(this);\n\n /** A measurer is needed only by specs whose trees contain real text. */\n private readonly textMeasurer: TextMeasurer | undefined;\n\n constructor(width = 400, height = 400, textMeasurer?: TextMeasurer) {\n this.textMeasurer = textMeasurer;\n this.layout = new LayoutHarness(textMeasurer);\n this.root = this.layout.createNode('app', UiNodeType.Column);\n this.root.setProperty('width', width);\n this.root.setProperty('height', height);\n }\n\n node(id: string, type: UiNodeType, props: Record<string, unknown> = {}): UiNode {\n const node = this.layout.createNode(id, type);\n for (const [key, value] of Object.entries(props)) {\n node.setProperty(key, value);\n }\n return node;\n }\n\n add(parent: UiNode, ...children: UiNode[]): void {\n this.layout.append(parent, ...children);\n }\n\n layoutTree(): void {\n this.layout.layout(this.root, Constraints.loose(400, 400));\n }\n\n createHitTester(): UiHitTester {\n return new UiHitTester(this.layout.engine, this.root);\n }\n\n createPointerController(): UiPointerController {\n return new UiPointerController(this.createHitTester(), this.dispatcher);\n }\n\n createGestureRecognizer(): UiGestureRecognizer {\n return new UiGestureRecognizer(this.dispatcher);\n }\n\n createFocusManager(): UiFocusManager {\n return new UiFocusManager(this.root, this.dispatcher);\n }\n\n createKeyboardController(): UiKeyboardController {\n return new UiKeyboardController(this.dispatcher, this.createFocusManager(), this.root);\n }\n\n /**\n * An editing controller over this harness's layout and dispatcher.\n *\n * The host is the layout harness itself: everything `EditingHost`\n * asks for is something the layout already knows, which is why the\n * controller takes an interface rather than the engine.\n */\n createEditingController(focus: UiFocusManager = this.createFocusManager()): UiEditingController {\n const layout = this.layout;\n const hitTester = this.createHitTester();\n const host: EditingHost = {\n recordFor: node => layout.engine.recordFor(node),\n visibleBox: node => layout.engine.visibleBox(node),\n screenBox: (node, part) => layout.engine.screenBox(node, part),\n toLocal: (node, x, y) => hitTester.toLocal(node, x, y),\n // The controller measures only for caret geometry. A spec whose\n // tree has real text passes its own measurer to the harness; the\n // deterministic one is right for every other.\n measurer: this.textMeasurer ?? new CharacterCountTextMeasurer(),\n markDirty: () => undefined,\n reveal: () => undefined,\n now: () => 0\n };\n return new UiEditingController(host, this.dispatcher, focus);\n }\n\n createWheelController(): UiWheelController {\n return new UiWheelController(this.createHitTester(), this.dispatcher, this.scrollSink, () => this.root);\n }\n\n /**\n * A pointer controller with a gesture recognizer behind it, for the\n * specs that need a real Pan rather than a hand-made one.\n */\n createGesturePointerController(options: PointerControllerOptions = {}): UiPointerController {\n return new UiPointerController(this.createHitTester(), this.dispatcher, {\n gestures: new UiGestureRecognizer(this.dispatcher),\n scrollSink: this.scrollSink,\n ...options\n });\n }\n\n createTouchScroller(options: TouchScrollerOptions = {}): UiTouchScroller {\n return new UiTouchScroller(this.dispatcher, this.root, this.scrollSink, options);\n }\n\n createPlatformAdapter(): UiPlatformAdapter {\n return new UiPlatformAdapter({\n pointerController: this.createPointerController(),\n wheelController: this.createWheelController(),\n keyboardController: this.createKeyboardController()\n });\n }\n\n box(node: UiNode): { x: number; y: number; width: number; height: number } {\n return this.layout.boxOf(node);\n }\n}\n\n/**\n * Minimal fake event target for testing the platform adapter.\n */\nexport class FakeEventTarget implements PlatformEventTarget {\n private readonly listeners: { type: string; listener: (event: Event) => void }[] = [];\n\n addEventListener(type: string, listener: ((event: Event) => void) | null): void {\n if (listener !== null) {\n this.listeners.push({ type, listener });\n }\n }\n\n removeEventListener(type: string, listener: ((event: Event) => void) | null): void {\n if (listener === null) return;\n const index = this.listeners.findIndex(entry => entry.type === type && entry.listener === listener);\n if (index !== -1) {\n this.listeners.splice(index, 1);\n }\n }\n\n emit(type: string, event: Event): void {\n for (const entry of this.listeners) {\n if (entry.type === type) {\n entry.listener(event);\n }\n }\n }\n}\n\n/**\n * Fake PlatformSurface for tests. Emits DOM-like events from plain\n * objects so PointerEvent/WheelEvent/KeyboardEvent constructors are\n * not required in the test environment.\n */\nexport class FakePlatformSurface implements PlatformSurface {\n readonly pointerTarget = new FakeEventTarget();\n readonly keyboardTarget = new FakeEventTarget();\n localX = 0;\n localY = 0;\n /** The last `touch-action` the adapter asked for; '' before any. */\n touchAction = '';\n\n clientToLocal(_clientX: number, _clientY: number): { x: number; y: number } {\n return { x: this.localX, y: this.localY };\n }\n\n setTouchAction(value: string): void {\n this.touchAction = value;\n }\n}\n\n/**\n * ScrollSink backed by the harness LayoutEngine. Updates the\n * container's scrollX/scrollY properties, then re-lays out so the\n * engine re-reads and clamps the offset, mirroring how the app layer\n * drives scroll through node properties.\n */\nexport class HarnessScrollSink implements ScrollSink {\n /** Every scroll asked for, in order, so a spec can read the behaviour hint. */\n readonly calls: { node: UiNode; dx: number; dy: number; behavior: string }[] = [];\n /** Containers whose scrollbars were revealed. */\n readonly revealed: UiNode[] = [];\n\n constructor(private readonly harness: InputTestHarness) {}\n\n containerState(node: UiNode): ScrollContainerState | undefined {\n const record = this.harness.layout.engine.recordFor(node);\n if (record === undefined) {\n return undefined;\n }\n const horizontal = node.getProperty('direction') === 'row';\n return {\n scrollX: record.scrollX,\n scrollY: record.scrollY,\n maxScrollX: scrollRange(record, 'x'),\n maxScrollY: scrollRange(record, 'y'),\n horizontal,\n viewportWidth: record.width,\n viewportHeight: record.height\n };\n }\n\n scrollBy(node: UiNode, dx: number, dy: number, behavior: string = 'instant'): void {\n this.calls.push({ node, dx, dy, behavior });\n const record = this.harness.layout.engine.recordFor(node);\n if (record === undefined) {\n return;\n }\n node.setProperty('scrollX', clamp(record.scrollX + dx, 0, scrollRange(record, 'x')));\n node.setProperty('scrollY', clamp(record.scrollY + dy, 0, scrollRange(record, 'y')));\n this.harness.layoutTree();\n }\n\n scrollContainers(): Iterable<UiNode> {\n return this.harness.layout.engine.scrollContainers();\n }\n\n revealScrollbars(node: UiNode): void {\n this.revealed.push(node);\n }\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, value));\n}\n","import { UiGraph } from '../graph/UiGraph';\nimport { UiNodeType } from '../graph/UiNodeType';\nimport type { UiNode } from '../graph/UiNode';\nimport { LayoutEngine } from '../layout/LayoutEngine';\nimport { CharacterCountTextMeasurer } from '../layout/TextMeasurer';\nimport type { TextMeasurer } from '../layout/TextMeasurer';\nimport { Constraints } from '../layout/LayoutTypes';\nimport type { LayoutBox, LayoutResult } from '../layout/LayoutTypes';\nimport type { LayoutRecord } from '../layout/LayoutRecord';\nimport { Canvas2DRenderer } from './canvas2d/Canvas2DRenderer';\nimport type { Canvas2DContext, Canvas2DGradient } from './canvas2d/Canvas2DContext';\nimport { CanvasSurface } from './canvas2d/CanvasSurface';\nimport type { CanvasHost } from './canvas2d/CanvasSurface';\nimport type { PaintContext2D, PaintGradient } from './PaintTarget';\nimport type { PaintCanvasFactory } from './PaintPicture';\nimport type { PaintLineCap, PaintLineJoin } from './PaintSurface';\n\nexport interface RecordedCall {\n name: string;\n args: unknown[];\n}\n\n/**\n * The gradient a recording context hands back, carrying what it was\n * asked for so a test can read the whole paint rather than a `[object\n * CanvasGradient]`. A real one is opaque, which is exactly what makes\n * a gradient hard to assert on without this.\n */\nexport class RecordedGradient implements Canvas2DGradient {\n readonly stops: { offset: number; color: string }[] = [];\n\n constructor(\n readonly kind: 'linear' | 'radial',\n readonly args: readonly number[]\n ) {}\n\n addColorStop(offset: number, color: string): void {\n this.stops.push({ offset, color });\n }\n}\n\n/**\n * Deterministic Canvas2D spy: records every call and style\n * assignment so tests can assert the exact draw sequence.\n */\nexport class RecordingCanvasContext implements Canvas2DContext {\n readonly calls: RecordedCall[] = [];\n\n private _fillStyle: string | Canvas2DGradient | CanvasPattern = '#000';\n private _strokeStyle: string | Canvas2DGradient | CanvasPattern = '#000';\n private _lineWidth = 1;\n private _lineJoin: CanvasLineJoin = 'miter';\n private _globalAlpha = 1;\n private _font = '';\n private _textAlign: CanvasTextAlign = 'start';\n private _textBaseline: CanvasTextBaseline = 'alphabetic';\n private _shadowColor = 'rgba(0, 0, 0, 0)';\n private _shadowBlur = 0;\n private _shadowOffsetX = 0;\n private _shadowOffsetY = 0;\n /**\n * Tracking, which is a context property rather than part of the font\n * shorthand — and sticky, so a spec can check it was cleared as well\n * as that it was set.\n */\n letterSpacing = '0px';\n\n get fillStyle(): string | Canvas2DGradient | CanvasPattern {\n return this._fillStyle;\n }\n\n set fillStyle(value: string | Canvas2DGradient | CanvasPattern) {\n this._fillStyle = value;\n this.record('set:fillStyle', [value]);\n }\n\n get strokeStyle(): string | Canvas2DGradient | CanvasPattern {\n return this._strokeStyle;\n }\n\n set strokeStyle(value: string | Canvas2DGradient | CanvasPattern) {\n this._strokeStyle = value;\n this.record('set:strokeStyle', [value]);\n }\n\n get lineWidth(): number {\n return this._lineWidth;\n }\n\n set lineWidth(value: number) {\n this._lineWidth = value;\n this.record('set:lineWidth', [value]);\n }\n\n get lineJoin(): CanvasLineJoin {\n return this._lineJoin;\n }\n\n set lineJoin(value: CanvasLineJoin) {\n this._lineJoin = value;\n this.record('set:lineJoin', [value]);\n }\n\n get globalAlpha(): number {\n return this._globalAlpha;\n }\n\n set globalAlpha(value: number) {\n this._globalAlpha = value;\n this.record('set:globalAlpha', [value]);\n }\n\n get font(): string {\n return this._font;\n }\n\n set font(value: string) {\n this._font = value;\n this.record('set:font', [value]);\n }\n\n get textAlign(): CanvasTextAlign {\n return this._textAlign;\n }\n\n set textAlign(value: CanvasTextAlign) {\n this._textAlign = value;\n this.record('set:textAlign', [value]);\n }\n\n get textBaseline(): CanvasTextBaseline {\n return this._textBaseline;\n }\n\n set textBaseline(value: CanvasTextBaseline) {\n this._textBaseline = value;\n this.record('set:textBaseline', [value]);\n }\n\n get shadowColor(): string {\n return this._shadowColor;\n }\n\n set shadowColor(value: string) {\n this._shadowColor = value;\n this.record('set:shadowColor', [value]);\n }\n\n get shadowBlur(): number {\n return this._shadowBlur;\n }\n\n set shadowBlur(value: number) {\n this._shadowBlur = value;\n this.record('set:shadowBlur', [value]);\n }\n\n get shadowOffsetX(): number {\n return this._shadowOffsetX;\n }\n\n set shadowOffsetX(value: number) {\n this._shadowOffsetX = value;\n this.record('set:shadowOffsetX', [value]);\n }\n\n get shadowOffsetY(): number {\n return this._shadowOffsetY;\n }\n\n set shadowOffsetY(value: number) {\n this._shadowOffsetY = value;\n this.record('set:shadowOffsetY', [value]);\n }\n\n /**\n * Drawing state saved by `save`, as a real context keeps it. Without\n * it a spec that renders twice read the first frame's leftovers: the\n * renderer multiplies `globalAlpha` inside a `save`, and a double that\n * did not restore it compounded the opacity frame over frame.\n */\n private readonly saved: {\n fillStyle: string | Canvas2DGradient | CanvasPattern;\n strokeStyle: string | Canvas2DGradient | CanvasPattern;\n lineWidth: number;\n lineJoin: CanvasLineJoin;\n globalAlpha: number;\n font: string;\n textAlign: CanvasTextAlign;\n textBaseline: CanvasTextBaseline;\n letterSpacing: string;\n shadowColor: string;\n shadowBlur: number;\n shadowOffsetX: number;\n shadowOffsetY: number;\n }[] = [];\n\n save(): void {\n this.saved.push({\n fillStyle: this._fillStyle,\n strokeStyle: this._strokeStyle,\n lineWidth: this._lineWidth,\n lineJoin: this._lineJoin,\n globalAlpha: this._globalAlpha,\n font: this._font,\n textAlign: this._textAlign,\n textBaseline: this._textBaseline,\n letterSpacing: this.letterSpacing,\n shadowColor: this._shadowColor,\n shadowBlur: this._shadowBlur,\n shadowOffsetX: this._shadowOffsetX,\n shadowOffsetY: this._shadowOffsetY\n });\n this.record('save', []);\n }\n\n restore(): void {\n const state = this.saved.pop();\n if (state !== undefined) {\n this._fillStyle = state.fillStyle;\n this._strokeStyle = state.strokeStyle;\n this._lineWidth = state.lineWidth;\n this._lineJoin = state.lineJoin;\n this._globalAlpha = state.globalAlpha;\n this._font = state.font;\n this._textAlign = state.textAlign;\n this._textBaseline = state.textBaseline;\n this.letterSpacing = state.letterSpacing;\n this._shadowColor = state.shadowColor;\n this._shadowBlur = state.shadowBlur;\n this._shadowOffsetX = state.shadowOffsetX;\n this._shadowOffsetY = state.shadowOffsetY;\n }\n this.record('restore', []);\n }\n\n translate(x: number, y: number): void {\n this.record('translate', [x, y]);\n }\n\n scale(x: number, y: number): void {\n this.record('scale', [x, y]);\n }\n\n rotate(angle: number): void {\n this.record('rotate', [angle]);\n }\n\n setTransform(a: number, b: number, c: number, d: number, e: number, f: number): void {\n this.record('setTransform', [a, b, c, d, e, f]);\n }\n\n clearRect(x: number, y: number, width: number, height: number): void {\n this.record('clearRect', [x, y, width, height]);\n }\n\n fillRect(x: number, y: number, width: number, height: number): void {\n this.record('fillRect', [x, y, width, height]);\n }\n\n strokeRect(x: number, y: number, width: number, height: number): void {\n this.record('strokeRect', [x, y, width, height]);\n }\n\n beginPath(): void {\n this.record('beginPath', []);\n }\n\n moveTo(x: number, y: number): void {\n this.record('moveTo', [x, y]);\n }\n\n lineTo(x: number, y: number): void {\n this.record('lineTo', [x, y]);\n }\n\n arcTo(x1: number, y1: number, x2: number, y2: number, radius: number): void {\n this.record('arcTo', [x1, y1, x2, y2, radius]);\n }\n\n closePath(): void {\n this.record('closePath', []);\n }\n\n rect(x: number, y: number, width: number, height: number): void {\n this.record('rect', [x, y, width, height]);\n }\n\n clip(): void {\n this.record('clip', []);\n }\n\n fill(): void {\n this.record('fill', []);\n }\n\n stroke(): void {\n this.record('stroke', []);\n }\n\n fillText(text: string, x: number, y: number, maxWidth?: number): void {\n const args = maxWidth === undefined ? [text, x, y] : [text, x, y, maxWidth];\n this.record('fillText', args);\n }\n\n measureText(text: string): TextMetrics {\n this.record('measureText', [text]);\n return { width: text.length * 8 } as TextMetrics;\n }\n\n drawImage(image: ImageBitmap, dx: number, dy: number, dw: number, dh: number): void {\n this.record('drawImage', [image, dx, dy, dw, dh]);\n }\n\n // Path calls a painter's recording makes when it is replayed straight\n // onto the frame's context (`PaintPictureCache.draw`), rather than\n // onto a picture canvas of its own.\n transform(a: number, b: number, c: number, d: number, e: number, f: number): void {\n this.record('transform', [a, b, c, d, e, f]);\n }\n\n quadraticCurveTo(cx: number, cy: number, x: number, y: number): void {\n this.record('quadraticCurveTo', [cx, cy, x, y]);\n }\n\n bezierCurveTo(c1x: number, c1y: number, c2x: number, c2y: number, x: number, y: number): void {\n this.record('bezierCurveTo', [c1x, c1y, c2x, c2y, x, y]);\n }\n\n arc(x: number, y: number, radius: number, startAngle: number, endAngle: number, counterclockwise = false): void {\n this.record('arc', [x, y, radius, startAngle, endAngle, counterclockwise]);\n }\n\n setLineDash(segments: readonly number[]): void {\n this.record('setLineDash', [segments]);\n }\n\n createLinearGradient(x0: number, y0: number, x1: number, y1: number): RecordedGradient {\n const gradient = new RecordedGradient('linear', [x0, y0, x1, y1]);\n this.record('createLinearGradient', [x0, y0, x1, y1]);\n return gradient;\n }\n\n createRadialGradient(x0: number, y0: number, r0: number, x1: number, y1: number, r1: number): RecordedGradient {\n const gradient = new RecordedGradient('radial', [x0, y0, r0, x1, y1, r1]);\n this.record('createRadialGradient', [x0, y0, r0, x1, y1, r1]);\n return gradient;\n }\n\n private record(name: string, args: unknown[]): void {\n this.calls.push({ name, args: args.map(normalizeNumber) });\n }\n}\n\n/**\n * Normalizes -0 to 0 so scroll translations assert cleanly.\n */\nfunction normalizeNumber(value: unknown): unknown {\n if (typeof value === 'number' && Object.is(value, -0)) {\n return 0;\n }\n return value;\n}\n\nexport function callNames(context: RecordingCanvasContext): string[] {\n return context.calls.map(call => call.name);\n}\n\nexport function callsOf(context: RecordingCanvasContext, name: string): RecordedCall[] {\n return context.calls.filter(call => call.name === name);\n}\n\nexport function callArgs(context: RecordingCanvasContext, name: string): unknown[][] {\n return callsOf(context, name).map(call => (call.name.startsWith('set:') ? call.args[0] : call.args) as unknown[]);\n}\n\nexport function savedDepth(context: RecordingCanvasContext): number {\n let depth = 0;\n for (const call of context.calls) {\n if (call.name === 'save') {\n depth++;\n } else if (call.name === 'restore') {\n depth--;\n }\n }\n return depth;\n}\n\n/**\n * Deterministic `PaintContext2D` spy: the picture rasteriser's\n * equivalent of `RecordingCanvasContext`, so a spec can assert what a\n * painter actually drew without a browser.\n */\nexport class RecordingPaintContext implements PaintContext2D {\n readonly calls: RecordedCall[] = [];\n\n lineDashOffset = 0;\n lineWidth = 1;\n lineCap: PaintLineCap = 'butt';\n lineJoin: PaintLineJoin = 'miter';\n miterLimit = 10;\n globalAlpha = 1;\n filter = 'none';\n fillStyle: string | PaintGradient = '#000';\n strokeStyle: string | PaintGradient = '#000';\n font = '';\n textAlign: 'left' | 'center' | 'right' = 'left';\n textBaseline = 'alphabetic' as const;\n\n save(): void {\n this.record('save', []);\n }\n\n restore(): void {\n this.record('restore', []);\n }\n\n translate(x: number, y: number): void {\n this.record('translate', [x, y]);\n }\n\n scale(x: number, y: number): void {\n this.record('scale', [x, y]);\n }\n\n rotate(angle: number): void {\n this.record('rotate', [angle]);\n }\n\n transform(a: number, b: number, c: number, d: number, e: number, f: number): void {\n this.record('transform', [a, b, c, d, e, f]);\n }\n\n beginPath(): void {\n this.record('beginPath', []);\n }\n\n moveTo(x: number, y: number): void {\n this.record('moveTo', [x, y]);\n }\n\n lineTo(x: number, y: number): void {\n this.record('lineTo', [x, y]);\n }\n\n quadraticCurveTo(cx: number, cy: number, x: number, y: number): void {\n this.record('quadraticCurveTo', [cx, cy, x, y]);\n }\n\n bezierCurveTo(c1x: number, c1y: number, c2x: number, c2y: number, x: number, y: number): void {\n this.record('bezierCurveTo', [c1x, c1y, c2x, c2y, x, y]);\n }\n\n arc(x: number, y: number, radius: number, startAngle: number, endAngle: number, counterclockwise = false): void {\n this.record('arc', [x, y, radius, startAngle, endAngle, counterclockwise]);\n }\n\n arcTo(x1: number, y1: number, x2: number, y2: number, radius: number): void {\n this.record('arcTo', [x1, y1, x2, y2, radius]);\n }\n\n rect(x: number, y: number, width: number, height: number): void {\n this.record('rect', [x, y, width, height]);\n }\n\n closePath(): void {\n this.record('closePath', []);\n }\n\n fill(fillRule?: 'nonzero' | 'evenodd'): void {\n this.record('fill', [fillRule ?? 'nonzero', this.fillStyle]);\n }\n\n stroke(): void {\n this.record('stroke', [this.strokeStyle, this.lineWidth]);\n }\n\n clip(fillRule?: 'nonzero' | 'evenodd'): void {\n this.record('clip', [fillRule ?? 'nonzero']);\n }\n\n fillText(text: string, x: number, y: number): void {\n this.record('fillText', [text, x, y, this.font, this.textAlign]);\n }\n\n drawImage(image: ImageBitmap, dx: number, dy: number, dw: number, dh: number): void {\n this.record('drawImage', [image, dx, dy, dw, dh]);\n }\n\n setLineDash(segments: readonly number[]): void {\n this.record('setLineDash', [[...segments]]);\n }\n\n createLinearGradient(x0: number, y0: number, x1: number, y1: number): RecordedGradient {\n const gradient = new RecordedGradient('linear', [x0, y0, x1, y1]);\n this.record('createLinearGradient', [x0, y0, x1, y1]);\n return gradient;\n }\n\n createRadialGradient(x0: number, y0: number, r0: number, x1: number, y1: number, r1: number): RecordedGradient {\n const gradient = new RecordedGradient('radial', [x0, y0, r0, x1, y1, r1]);\n this.record('createRadialGradient', [x0, y0, r0, x1, y1, r1]);\n return gradient;\n }\n\n private record(name: string, args: unknown[]): void {\n this.calls.push({ name, args: args.map(normalizeNumber) });\n }\n}\n\n/**\n * A picture that never becomes real pixels.\n *\n * `take()` hands back a stand-in for the `ImageBitmap` a browser would\n * transfer out of an `OffscreenCanvas`, carrying only the size and a\n * `close`, which is all either renderer reads off one. Every context\n * the factory hands out is kept, so a spec can assert both what was\n * drawn and how many times anything was drawn at all.\n */\nexport class FakePaintCanvases {\n readonly contexts: RecordingPaintContext[] = [];\n readonly bitmaps: { width: number; height: number; closed: boolean }[] = [];\n\n readonly create: PaintCanvasFactory = (width, height) => {\n const context = new RecordingPaintContext();\n this.contexts.push(context);\n const bitmap = { width, height, closed: false };\n this.bitmaps.push(bitmap);\n return {\n context: () => context,\n take: () =>\n ({\n width,\n height,\n close: () => {\n bitmap.closed = true;\n }\n }) as unknown as ImageBitmap\n };\n };\n\n /** The calls made into the most recent picture. */\n get last(): RecordingPaintContext {\n const context = this.contexts.at(-1);\n if (context === undefined) {\n throw new Error('No picture has been rasterised.');\n }\n return context;\n }\n\n reset(): void {\n this.contexts.length = 0;\n this.bitmaps.length = 0;\n }\n}\n\n/**\n * A canvas host backed by a recording context.\n */\nexport class FakeCanvasHost implements CanvasHost {\n width = 0;\n height = 0;\n private contextRequests = 0;\n\n constructor(private readonly context: Canvas2DContext | null) {}\n\n getContext(_contextId: '2d'): CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D | null {\n this.contextRequests++;\n return this.context as unknown as CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D;\n }\n\n get contextRequestCount(): number {\n return this.contextRequests;\n }\n}\n\n/**\n * Shared harness: a real UiGraph + LayoutEngine + Canvas2DRenderer\n * over a recording context. Deterministic layout comes from the\n * CharacterCountTextMeasurer, shared by layout and renderer.\n */\nexport class RenderHarness {\n readonly graph = new UiGraph();\n readonly measurer: TextMeasurer = new CharacterCountTextMeasurer();\n readonly engine = new LayoutEngine(this.measurer);\n readonly context = new RecordingCanvasContext();\n readonly host = new FakeCanvasHost(this.context);\n readonly surface = new CanvasSurface(this.host);\n readonly renderer = new Canvas2DRenderer({ surface: this.surface });\n\n constructor(logicalWidth: number = 800, logicalHeight: number = 600, dpr: number = 1) {\n this.surface.setLogicalSize(logicalWidth, logicalHeight, dpr);\n }\n\n createNode(id: string, type: UiNodeType): UiNode {\n return this.graph.createNode(id, type);\n }\n\n append(parent: UiNode, ...children: UiNode[]): void {\n for (const child of children) {\n this.graph.appendChild(parent, child);\n }\n }\n\n layout(node: UiNode, constraints: Constraints = Constraints.tight(800, 600)): LayoutResult {\n return this.engine.layout(node, constraints);\n }\n\n render(node: UiNode): void {\n // Far in the future: overlay scrollbars have faded, so draw-call\n // assertions see only the scene. Pass `now` yourself to test them.\n this.renderer.render(node, { layout: this.engine, text: this.measurer, now: Number.MAX_SAFE_INTEGER });\n }\n\n record(node: UiNode): LayoutRecord {\n const record = this.engine.recordFor(node);\n if (record === undefined) {\n throw new Error(`No layout record for '${node.id}'.`);\n }\n return record;\n }\n\n box(node: UiNode): LayoutBox {\n const record = this.record(node);\n return { x: record.x, y: record.y, width: record.width, height: record.height };\n }\n}\n","/**\n * A very small MP4 writer, and a decoder that decodes nothing.\n *\n * Both exist for the same reason the demuxer's spec gives for building\n * its files by hand: a fixture `.mp4` is a better test of reality and\n * a worse test of this code, because when it fails you do not know\n * which of forty boxes was misread. Building the file means a spec can\n * name the exact field it is about, and construct the awkward cases —\n * a chunk holding several samples, composition offsets reordering\n * them, a track with no `stss` — rather than hunting for a file that\n * has one.\n *\n * In `gesso-core/testing` rather than beside a spec because two specs\n * now want it: the demuxer's, which reads these files, and the\n * resolver's, which plays them.\n */\n\nfunction u32(value: number): number[] {\n return [(value >>> 24) & 0xff, (value >>> 16) & 0xff, (value >>> 8) & 0xff, value & 0xff];\n}\n\nfunction u16(value: number): number[] {\n return [(value >>> 8) & 0xff, value & 0xff];\n}\n\nfunction ascii(text: string): number[] {\n return [...text].map(character => character.charCodeAt(0));\n}\n\n/** `size type payload`, which is every box in the format. */\nexport function box(type: string, ...payload: number[][]): number[] {\n const body = payload.flat();\n return [...u32(body.length + 8), ...ascii(type), ...body];\n}\n\n/** A full box: a version and three flag bytes before the payload. */\nexport function fullBox(type: string, version: number, ...payload: number[][]): number[] {\n return box(type, [version, 0, 0, 0], ...payload);\n}\n\n/** An AAC audio track, for a file that has sound as well as pictures. */\nexport interface AudioTrackSpec {\n timescale: number;\n deltas: { count: number; delta: number }[];\n sizes: number[];\n chunkOffsets: number[];\n chunkRuns: { firstChunk: number; samplesPerChunk: number }[];\n channels: number;\n sampleRate: number;\n /**\n * The `AudioSpecificConfig`, whose top five bits are the audio\n * object type. The default is AAC-LC at 44.1kHz stereo, which is\n * `mp4a.40.2` and what nearly every file on the web carries.\n */\n config?: number[];\n}\n\nexport interface TrackSpec {\n timescale: number;\n /** One `stts` run per entry. */\n deltas: { count: number; delta: number }[];\n compositionOffsets?: { count: number; offset: number }[];\n sizes: number[];\n chunkOffsets: number[];\n chunkRuns: { firstChunk: number; samplesPerChunk: number }[];\n /** Omitted means no `stss` at all, which means every sample is a sync sample. */\n syncSamples?: number[];\n width: number;\n height: number;\n handler?: string;\n /** The sample entry's four character code. `avc1` unless said otherwise. */\n codec?: 'avc1' | 'av01' | 'vp09';\n /** The bytes of the configuration record after any fixed header; see `configurationBox`. */\n configBytes?: number[];\n}\n\n/**\n * The codec configuration box, which differs per codec and is where a\n * codec string comes from.\n */\nfunction configurationBox(spec: TrackSpec): number[] {\n if (spec.codec === 'av01') {\n // marker+version, then seq_profile/seq_level_idx, then the tier\n // and bit depth flags.\n return box('av1C', [0x81, ...(spec.configBytes ?? [0x04, 0x00])]);\n }\n if (spec.codec === 'vp09') {\n // A full box: version and flags, then profile, level and a byte\n // whose top nibble is the bit depth.\n return fullBox('vpcC', 1, spec.configBytes ?? [0x00, 0x1f, 0x80]);\n }\n // `avc1`: length, profile, compat, level, then whatever follows.\n return box('avcC', [1, 0x64, 0x00, 0x1f, 0xff, 0xe1]);\n}\n\nfunction visualSampleEntry(spec: TrackSpec): number[] {\n return box(\n spec.codec ?? 'avc1',\n [0, 0, 0, 0, 0, 0], // reserved\n u16(1), // data_reference_index\n Array.from({ length: 16 }, () => 0), // pre_defined + reserved\n u16(spec.width),\n u16(spec.height),\n u32(0x0048_0000), // horizresolution\n u32(0x0048_0000), // vertresolution\n u32(0), // reserved\n u16(1), // frame_count\n Array.from({ length: 32 }, () => 0), // compressorname\n u16(24), // depth\n [0xff, 0xff], // pre_defined = -1\n configurationBox(spec)\n );\n}\n\n/** `esds`: the descriptor tree down to the two numbers a codec string needs. */\nfunction esds(spec: AudioTrackSpec): number[] {\n const config = spec.config ?? [0x12, 0x10];\n // DecoderSpecificInfo (tag 5)\n const dsi = [0x05, config.length, ...config];\n // DecoderConfigDescriptor (tag 4): object type 0x40 is MPEG-4 audio,\n // then a stream type, a buffer size and two bitrates.\n // Thirteen bytes of header: an object type indication, a stream\n // type, three bytes of buffer size, and two four-byte bitrates.\n const dcd = [0x04, 13 + dsi.length, 0x40, 0x15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...dsi];\n // ES_Descriptor (tag 3): an id and a flags byte with nothing set.\n const esd = [0x03, 3 + dcd.length, 0, 1, 0, ...dcd];\n return fullBox('esds', 0, esd);\n}\n\nfunction audioSampleEntry(spec: AudioTrackSpec): number[] {\n return box(\n 'mp4a',\n [0, 0, 0, 0, 0, 0], // reserved\n u16(1), // data_reference_index\n u16(0), // version\n [0, 0, 0, 0, 0, 0], // revision and vendor\n u16(spec.channels),\n u16(16), // sample size\n u16(0), // pre_defined\n u16(0), // reserved\n u16(spec.sampleRate), // 16.16 fixed, whole part\n u16(0),\n esds(spec)\n );\n}\n\nfunction audioSampleTable(spec: AudioTrackSpec): number[] {\n return box(\n 'stbl',\n fullBox('stsd', 0, u32(1), audioSampleEntry(spec)),\n fullBox('stts', 0, u32(spec.deltas.length), ...spec.deltas.map(run => [...u32(run.count), ...u32(run.delta)])),\n fullBox(\n 'stsc',\n 0,\n u32(spec.chunkRuns.length),\n ...spec.chunkRuns.map(run => [...u32(run.firstChunk), ...u32(run.samplesPerChunk), ...u32(1)])\n ),\n fullBox('stsz', 0, u32(0), u32(spec.sizes.length), ...spec.sizes.map(size => u32(size))),\n fullBox('stco', 0, u32(spec.chunkOffsets.length), ...spec.chunkOffsets.map(offset => u32(offset)))\n );\n}\n\nfunction audioTrak(spec: AudioTrackSpec): number[] {\n return box(\n 'trak',\n box(\n 'mdia',\n fullBox('mdhd', 0, u32(0), u32(0), u32(spec.timescale), u32(1000)),\n fullBox(\n 'hdlr',\n 0,\n u32(0),\n ascii('soun'),\n Array.from({ length: 12 }, () => 0),\n [0]\n ),\n box('minf', audioSampleTable(spec))\n )\n );\n}\n\nfunction sampleTable(spec: TrackSpec): number[] {\n const boxes: number[][] = [\n fullBox('stsd', 0, u32(1), visualSampleEntry(spec)),\n fullBox('stts', 0, u32(spec.deltas.length), ...spec.deltas.map(run => [...u32(run.count), ...u32(run.delta)])),\n fullBox(\n 'stsc',\n 0,\n u32(spec.chunkRuns.length),\n ...spec.chunkRuns.map(run => [...u32(run.firstChunk), ...u32(run.samplesPerChunk), ...u32(1)])\n ),\n fullBox('stsz', 0, u32(0), u32(spec.sizes.length), ...spec.sizes.map(size => u32(size))),\n fullBox('stco', 0, u32(spec.chunkOffsets.length), ...spec.chunkOffsets.map(offset => u32(offset)))\n ];\n if (spec.compositionOffsets !== undefined) {\n boxes.splice(\n 2,\n 0,\n fullBox(\n 'ctts',\n 0,\n u32(spec.compositionOffsets.length),\n ...spec.compositionOffsets.map(run => [...u32(run.count), ...u32(run.offset)])\n )\n );\n }\n if (spec.syncSamples !== undefined) {\n boxes.push(fullBox('stss', 0, u32(spec.syncSamples.length), ...spec.syncSamples.map(number => u32(number))));\n }\n return box('stbl', ...boxes);\n}\n\nexport function buildMp4(spec: TrackSpec, options: { fragmented?: boolean; audio?: AudioTrackSpec } = {}): ArrayBuffer {\n const trak = box(\n 'trak',\n box(\n 'mdia',\n fullBox('mdhd', 0, u32(0), u32(0), u32(spec.timescale), u32(1000)),\n fullBox(\n 'hdlr',\n 0,\n u32(0),\n ascii(spec.handler ?? 'vide'),\n Array.from({ length: 12 }, () => 0),\n [0]\n ),\n box('minf', sampleTable(spec))\n )\n );\n const bytes = [\n ...box('ftyp', ascii('isom'), u32(512), ascii('isomiso2avc1mp41')),\n ...box(\n 'moov',\n fullBox(\n 'mvhd',\n 0,\n Array.from({ length: 100 }, () => 0)\n ),\n trak,\n ...(options.audio === undefined ? [] : [audioTrak(options.audio)])\n ),\n ...(options.fragmented === true ? box('moof', box('mfhd', u32(1))) : []),\n // A `mdat` big enough for every offset the tables name.\n ...box(\n 'mdat',\n Array.from({ length: 512 }, () => 0x41)\n )\n ];\n return new Uint8Array(bytes).buffer;\n}\n\n/** One run of samples inside a fragment. */\nexport interface FragmentSpec {\n /** Decode time the fragment starts at, in the track's timescale. Omitted means \"carry on\". */\n baseMediaDecodeTime?: number;\n samples: { duration: number; size: number; isKey: boolean }[];\n}\n\nexport interface FragmentedSpec {\n trackId: number;\n timescale: number;\n width: number;\n height: number;\n fragments: FragmentSpec[];\n /** Written into `trex`, and used by any sample whose `trun` omits the field. */\n defaultSampleDuration?: number;\n defaultSampleSize?: number;\n /** Leave a field out of the `trun` so the default has to supply it. */\n omit?: { duration?: boolean; size?: boolean };\n}\n\nfunction tkhd(trackId: number): number[] {\n return fullBox(\n 'tkhd',\n 0,\n u32(0), // creation time\n u32(0), // modification time\n u32(trackId),\n Array.from({ length: 68 }, () => 0)\n );\n}\n\n/**\n * A fragmented MP4: a `moov` that describes the track and promises\n * fragments, then the fragments.\n *\n * The `stbl` is a shell — `stsd` and four empty tables — which is what\n * a fragmenter writes and is exactly the shape that used to make this\n * demuxer return zero samples for a file the browser played.\n */\nexport function buildFragmentedMp4(spec: FragmentedSpec): ArrayBuffer {\n const shellSpec: TrackSpec = {\n timescale: spec.timescale,\n deltas: [],\n sizes: [],\n chunkOffsets: [],\n chunkRuns: [],\n width: spec.width,\n height: spec.height\n };\n const trak = box(\n 'trak',\n tkhd(spec.trackId),\n box(\n 'mdia',\n fullBox('mdhd', 0, u32(0), u32(0), u32(spec.timescale), u32(1000)),\n fullBox(\n 'hdlr',\n 0,\n u32(0),\n ascii('vide'),\n Array.from({ length: 12 }, () => 0),\n [0]\n ),\n box('minf', sampleTable(shellSpec))\n )\n );\n const mvex = box(\n 'mvex',\n fullBox(\n 'trex',\n 0,\n u32(spec.trackId),\n u32(1), // default_sample_description_index\n u32(spec.defaultSampleDuration ?? 0),\n u32(spec.defaultSampleSize ?? 0),\n u32(0) // default_sample_flags\n )\n );\n const moov = box(\n 'moov',\n fullBox(\n 'mvhd',\n 0,\n Array.from({ length: 100 }, () => 0)\n ),\n trak,\n mvex\n );\n\n const bytes = [...box('ftyp', ascii('isom'), u32(512), ascii('isomiso6avc1mp41')), ...moov];\n\n for (const fragment of spec.fragments) {\n const payloadSize = fragment.samples.reduce((total, sample) => total + sample.size, 0);\n // Built twice: the `trun`'s data offset is relative to the start\n // of the `moof` box, and is not known until the `moof` has been\n // sized. The first pass is only for its length.\n const build = (dataOffset: number): number[] => {\n const trunFlags =\n 0x000001 | // data-offset-present\n (spec.omit?.duration === true ? 0 : 0x000100) |\n (spec.omit?.size === true ? 0 : 0x000200) |\n 0x000400; // sample-flags-present\n const entries = fragment.samples.flatMap(sample => [\n ...(spec.omit?.duration === true ? [] : u32(sample.duration)),\n ...(spec.omit?.size === true ? [] : u32(sample.size)),\n // Bit 16 set means \"not a sync sample\".\n ...u32(sample.isKey ? 0 : 0x00010000)\n ]);\n const trun = box(\n 'trun',\n [0, (trunFlags >> 16) & 0xff, (trunFlags >> 8) & 0xff, trunFlags & 0xff],\n u32(fragment.samples.length),\n u32(dataOffset),\n entries\n );\n const tfhdFlags = 0x020000; // default-base-is-moof\n const traf = box(\n 'traf',\n box('tfhd', [0, (tfhdFlags >> 16) & 0xff, (tfhdFlags >> 8) & 0xff, tfhdFlags & 0xff], u32(spec.trackId)),\n ...(fragment.baseMediaDecodeTime === undefined ? [] : [fullBox('tfdt', 0, u32(fragment.baseMediaDecodeTime))]),\n trun\n );\n return box('moof', fullBox('mfhd', 0, u32(1)), traf);\n };\n const sized = build(0);\n // The `mdat` payload begins eight bytes past the end of the `moof`.\n const moof = build(sized.length + 8);\n bytes.push(\n ...moof,\n ...box(\n 'mdat',\n Array.from({ length: payloadSize }, () => 0x42)\n )\n );\n }\n return new Uint8Array(bytes).buffer;\n}\n\n/** The offset `mdat`'s payload lands at, for a table that must point into it. */\nexport function mdatStart(data: ArrayBuffer): number {\n const view = new DataView(data);\n let at = 0;\n while (at + 8 <= data.byteLength) {\n const size = view.getUint32(at);\n const type = String.fromCharCode(\n view.getUint8(at + 4),\n view.getUint8(at + 5),\n view.getUint8(at + 6),\n view.getUint8(at + 7)\n );\n if (type === 'mdat') {\n return at + 8;\n }\n at += size;\n }\n throw new Error('no mdat');\n}\n\n/** One chunk as the fake decoder saw it. */\nexport interface DecodedChunk {\n readonly timestamp: number;\n readonly type: 'key' | 'delta';\n}\n\n/**\n * A `VideoDecoder` that records what it was asked to decode and hands\n * back a frame for each.\n *\n * What the resolver's spec is about is *which samples are submitted*:\n * a seek that starts at the wrong keyframe, or one that decodes the\n * whole clip to get somewhere, is a bug in arithmetic that has nothing\n * to do with pixels. So this decodes nothing and the frames it emits\n * carry a timestamp and a `close` and no image at all.\n *\n * Emission is synchronous, which a real decoder's never is. That makes\n * the specs readable and is the one way this lies: the ordering\n * hazards a real decoder creates — a frame arriving after the position\n * has moved past it — are `Mp4Playback`'s to handle and are asserted\n * by pushing frames in by hand rather than by waiting for a thread.\n */\nexport class FakeVideoDecoder {\n static readonly submitted: DecodedChunk[] = [];\n /** How many times a decoder was configured: a seek costs one. */\n static configures = 0;\n static resets = 0;\n /** Set to stop frames being emitted, for a spec about a queue filling up. */\n static emit = true;\n\n static install(): () => void {\n const scope = globalThis as Record<string, unknown>;\n const previous = {\n VideoDecoder: scope.VideoDecoder,\n EncodedVideoChunk: scope.EncodedVideoChunk,\n VideoFrame: scope.VideoFrame,\n createImageBitmap: scope.createImageBitmap\n };\n FakeVideoDecoder.reset();\n scope.VideoDecoder = FakeVideoDecoder;\n scope.EncodedVideoChunk = class {\n readonly timestamp: number;\n readonly type: 'key' | 'delta';\n constructor(init: { timestamp: number; type: 'key' | 'delta' }) {\n this.timestamp = init.timestamp;\n this.type = init.type;\n }\n };\n scope.VideoFrame = class {};\n // Left undefined so `MutableVideoSurface` takes the path that keeps\n // the frame itself: converting to a bitmap is asynchronous and\n // would put every assertion behind a microtask for no gain.\n scope.createImageBitmap = undefined;\n return () => Object.assign(scope, previous);\n }\n\n static reset(): void {\n FakeVideoDecoder.submitted.length = 0;\n FakeVideoDecoder.configures = 0;\n FakeVideoDecoder.resets = 0;\n FakeVideoDecoder.emit = true;\n }\n\n /** Every timestamp submitted since the last configure, in order. */\n static get sinceConfigure(): number[] {\n return FakeVideoDecoder.submitted.map(chunk => chunk.timestamp);\n }\n\n static isConfigSupported(): Promise<{ supported: boolean }> {\n return Promise.resolve({ supported: true });\n }\n\n state: 'unconfigured' | 'configured' | 'closed' = 'unconfigured';\n decodeQueueSize = 0;\n private readonly output: (frame: { timestamp: number; close(): void }) => void;\n\n constructor(init: { output: (frame: { timestamp: number; close(): void }) => void; error: (e: unknown) => void }) {\n this.output = init.output;\n }\n\n configure(): void {\n this.state = 'configured';\n FakeVideoDecoder.configures++;\n }\n\n decode(chunk: DecodedChunk): void {\n FakeVideoDecoder.submitted.push({ timestamp: chunk.timestamp, type: chunk.type });\n if (!FakeVideoDecoder.emit) {\n this.decodeQueueSize++;\n return;\n }\n this.output({ timestamp: chunk.timestamp, close: () => {} });\n }\n\n reset(): void {\n FakeVideoDecoder.resets++;\n this.decodeQueueSize = 0;\n this.state = 'unconfigured';\n }\n\n close(): void {\n this.state = 'closed';\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"testing.js","names":[],"sources":["../src/layout/LayoutTestUtils.ts","../src/input/UiInputTestUtils.ts","../src/rendering/RenderTestUtils.ts","../src/media/Mp4TestUtils.ts"],"sourcesContent":["import { UiGraph } from '../graph/UiGraph';\nimport type { UiNode } from '../graph/UiNode';\nimport { UiNodeType } from '../graph/UiNodeType';\nimport { LayoutEngine } from './LayoutEngine';\nimport { Constraints } from './LayoutTypes';\nimport type { LayoutBox, LayoutResult } from './LayoutTypes';\nimport type { LayoutRecord } from './LayoutRecord';\nimport type { TextMeasurer } from './TextMeasurer';\n\n/**\n * Shared helpers for layout specs: builds UiNode trees through\n * the real graph and drives LayoutEngine.\n */\nexport class LayoutHarness {\n readonly graph = new UiGraph();\n readonly engine: LayoutEngine;\n\n constructor(textMeasurer?: TextMeasurer) {\n this.engine = new LayoutEngine(textMeasurer);\n }\n\n createNode(id: string, type: UiNodeType): UiNode {\n return this.graph.createNode(id, type);\n }\n\n append(parent: UiNode, ...children: UiNode[]): void {\n for (const child of children) {\n this.graph.appendChild(parent, child);\n }\n }\n\n layout(node: UiNode, constraints: Constraints = Constraints.unbounded()): LayoutResult {\n return this.engine.layout(node, constraints);\n }\n\n record(node: UiNode): LayoutRecord {\n const record = this.engine.recordFor(node);\n if (record === undefined) {\n throw new Error(`No layout record for '${node.id}'.`);\n }\n return record;\n }\n\n box(node: UiNode): LayoutBox {\n const record = this.record(node);\n return { x: record.x, y: record.y, width: record.width, height: record.height };\n }\n\n boxOf(node: UiNode): LayoutBox {\n return this.engine.worldBox(node);\n }\n\n /** Where the node is seen: scroll offsets and sticky shifts applied. */\n visibleBox(node: UiNode): LayoutBox {\n return this.engine.visibleBox(node);\n }\n}\n","import { UiNodeType } from '../graph/UiNodeType';\nimport type { UiNode } from '../graph/UiNode';\nimport { Constraints } from '../layout/LayoutTypes';\nimport { scrollRange } from '../layout/Scrollbars';\nimport { LayoutHarness } from '../layout/LayoutTestUtils';\nimport { UiHitTester } from './UiHitTester';\nimport { UiInputDispatcher } from './UiInputDispatcher';\nimport { UiPointerController, type PointerControllerOptions } from './UiPointerController';\nimport { UiGestureRecognizer } from './UiGestureRecognizer';\nimport { UiTouchScroller, type TouchScrollerOptions } from './UiTouchScroller';\nimport { UiFocusManager } from './UiFocusManager';\nimport { UiKeyboardController } from './UiKeyboardController';\nimport { UiEditingController, type EditingHost } from './UiEditingController';\nimport { UiWheelController, type ScrollContainerState, type ScrollSink } from './UiWheelController';\nimport { UiPlatformAdapter, type PlatformEventTarget, type PlatformSurface } from './UiPlatformAdapter';\nimport { CharacterCountTextMeasurer, type TextMeasurer } from '../layout/TextMeasurer';\n\n/**\n * Shared harness for input specs.\n *\n * Builds real UiNode trees through UiGraph + LayoutEngine so hit\n * testing, dispatch, focus and gestures are tested against actual\n * layout geometry rather than hand-written boxes.\n */\nexport class InputTestHarness {\n readonly layout: LayoutHarness;\n readonly root: UiNode;\n readonly dispatcher = new UiInputDispatcher();\n /** Shared, so a spec can read back what a controller scrolled. */\n readonly scrollSink = new HarnessScrollSink(this);\n\n /** A measurer is needed only by specs whose trees contain real text. */\n private readonly textMeasurer: TextMeasurer | undefined;\n\n constructor(width = 400, height = 400, textMeasurer?: TextMeasurer) {\n this.textMeasurer = textMeasurer;\n this.layout = new LayoutHarness(textMeasurer);\n this.root = this.layout.createNode('app', UiNodeType.Column);\n this.root.setProperty('width', width);\n this.root.setProperty('height', height);\n }\n\n node(id: string, type: UiNodeType, props: Record<string, unknown> = {}): UiNode {\n const node = this.layout.createNode(id, type);\n for (const [key, value] of Object.entries(props)) {\n node.setProperty(key, value);\n }\n return node;\n }\n\n add(parent: UiNode, ...children: UiNode[]): void {\n this.layout.append(parent, ...children);\n }\n\n layoutTree(): void {\n this.layout.layout(this.root, Constraints.loose(400, 400));\n }\n\n createHitTester(): UiHitTester {\n return new UiHitTester(this.layout.engine, this.root);\n }\n\n createPointerController(): UiPointerController {\n return new UiPointerController(this.createHitTester(), this.dispatcher);\n }\n\n createGestureRecognizer(): UiGestureRecognizer {\n return new UiGestureRecognizer(this.dispatcher);\n }\n\n createFocusManager(): UiFocusManager {\n return new UiFocusManager(this.root, this.dispatcher);\n }\n\n createKeyboardController(): UiKeyboardController {\n return new UiKeyboardController(this.dispatcher, this.createFocusManager(), this.root);\n }\n\n /**\n * An editing controller over this harness's layout and dispatcher.\n *\n * The host is the layout harness itself: everything `EditingHost`\n * asks for is something the layout already knows, which is why the\n * controller takes an interface rather than the engine.\n */\n createEditingController(focus: UiFocusManager = this.createFocusManager()): UiEditingController {\n const layout = this.layout;\n const hitTester = this.createHitTester();\n const host: EditingHost = {\n recordFor: node => layout.engine.recordFor(node),\n visibleBox: node => layout.engine.visibleBox(node),\n screenBox: (node, part) => layout.engine.screenBox(node, part),\n toLocal: (node, x, y) => hitTester.toLocal(node, x, y),\n // The controller measures only for caret geometry. A spec whose\n // tree has real text passes its own measurer to the harness; the\n // deterministic one is right for every other.\n measurer: this.textMeasurer ?? new CharacterCountTextMeasurer(),\n markDirty: () => undefined,\n reveal: () => undefined,\n now: () => 0\n };\n return new UiEditingController(host, this.dispatcher, focus);\n }\n\n createWheelController(): UiWheelController {\n return new UiWheelController(this.createHitTester(), this.dispatcher, this.scrollSink, () => this.root);\n }\n\n /**\n * A pointer controller with a gesture recognizer behind it, for the\n * specs that need a real Pan rather than a hand-made one.\n */\n createGesturePointerController(options: PointerControllerOptions = {}): UiPointerController {\n return new UiPointerController(this.createHitTester(), this.dispatcher, {\n gestures: new UiGestureRecognizer(this.dispatcher),\n scrollSink: this.scrollSink,\n ...options\n });\n }\n\n createTouchScroller(options: TouchScrollerOptions = {}): UiTouchScroller {\n return new UiTouchScroller(this.dispatcher, this.root, this.scrollSink, options);\n }\n\n createPlatformAdapter(): UiPlatformAdapter {\n return new UiPlatformAdapter({\n pointerController: this.createPointerController(),\n wheelController: this.createWheelController(),\n keyboardController: this.createKeyboardController()\n });\n }\n\n box(node: UiNode): { x: number; y: number; width: number; height: number } {\n return this.layout.boxOf(node);\n }\n}\n\n/**\n * Minimal fake event target for testing the platform adapter.\n */\nexport class FakeEventTarget implements PlatformEventTarget {\n private readonly listeners: { type: string; listener: (event: Event) => void }[] = [];\n\n addEventListener(type: string, listener: ((event: Event) => void) | null): void {\n if (listener !== null) {\n this.listeners.push({ type, listener });\n }\n }\n\n removeEventListener(type: string, listener: ((event: Event) => void) | null): void {\n if (listener === null) return;\n const index = this.listeners.findIndex(entry => entry.type === type && entry.listener === listener);\n if (index !== -1) {\n this.listeners.splice(index, 1);\n }\n }\n\n emit(type: string, event: Event): void {\n for (const entry of this.listeners) {\n if (entry.type === type) {\n entry.listener(event);\n }\n }\n }\n}\n\n/**\n * Fake PlatformSurface for tests. Emits DOM-like events from plain\n * objects so PointerEvent/WheelEvent/KeyboardEvent constructors are\n * not required in the test environment.\n */\nexport class FakePlatformSurface implements PlatformSurface {\n readonly pointerTarget = new FakeEventTarget();\n readonly keyboardTarget = new FakeEventTarget();\n localX = 0;\n localY = 0;\n /** The last `touch-action` the adapter asked for; '' before any. */\n touchAction = '';\n\n clientToLocal(_clientX: number, _clientY: number): { x: number; y: number } {\n return { x: this.localX, y: this.localY };\n }\n\n setTouchAction(value: string): void {\n this.touchAction = value;\n }\n}\n\n/**\n * ScrollSink backed by the harness LayoutEngine. Updates the\n * container's scrollX/scrollY properties, then re-lays out so the\n * engine re-reads and clamps the offset, mirroring how the app layer\n * drives scroll through node properties.\n */\nexport class HarnessScrollSink implements ScrollSink {\n /** Every scroll asked for, in order, so a spec can read the behaviour hint. */\n readonly calls: { node: UiNode; dx: number; dy: number; behavior: string }[] = [];\n /** Containers whose scrollbars were revealed. */\n readonly revealed: UiNode[] = [];\n\n constructor(private readonly harness: InputTestHarness) {}\n\n containerState(node: UiNode): ScrollContainerState | undefined {\n const record = this.harness.layout.engine.recordFor(node);\n if (record === undefined) {\n return undefined;\n }\n const horizontal = node.getProperty('direction') === 'row';\n return {\n scrollX: record.scrollX,\n scrollY: record.scrollY,\n maxScrollX: scrollRange(record, 'x'),\n maxScrollY: scrollRange(record, 'y'),\n horizontal,\n viewportWidth: record.width,\n viewportHeight: record.height\n };\n }\n\n scrollBy(node: UiNode, dx: number, dy: number, behavior: string = 'instant'): void {\n this.calls.push({ node, dx, dy, behavior });\n const record = this.harness.layout.engine.recordFor(node);\n if (record === undefined) {\n return;\n }\n node.setProperty('scrollX', clamp(record.scrollX + dx, 0, scrollRange(record, 'x')));\n node.setProperty('scrollY', clamp(record.scrollY + dy, 0, scrollRange(record, 'y')));\n this.harness.layoutTree();\n }\n\n scrollContainers(): Iterable<UiNode> {\n return this.harness.layout.engine.scrollContainers();\n }\n\n revealScrollbars(node: UiNode): void {\n this.revealed.push(node);\n }\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, value));\n}\n","import { UiGraph } from '../graph/UiGraph';\nimport { UiNodeType } from '../graph/UiNodeType';\nimport type { UiNode } from '../graph/UiNode';\nimport { LayoutEngine } from '../layout/LayoutEngine';\nimport { CharacterCountTextMeasurer } from '../layout/TextMeasurer';\nimport type { TextMeasurer } from '../layout/TextMeasurer';\nimport { Constraints } from '../layout/LayoutTypes';\nimport type { LayoutBox, LayoutResult } from '../layout/LayoutTypes';\nimport type { LayoutRecord } from '../layout/LayoutRecord';\nimport { Canvas2DRenderer } from './canvas2d/Canvas2DRenderer';\nimport type { Canvas2DContext, Canvas2DGradient } from './canvas2d/Canvas2DContext';\nimport { CanvasSurface } from './canvas2d/CanvasSurface';\nimport type { CanvasHost } from './canvas2d/CanvasSurface';\nimport type { PaintContext2D, PaintGradient } from './PaintTarget';\nimport type { PaintCanvasFactory } from './PaintPicture';\nimport type { PaintLineCap, PaintLineJoin } from './PaintSurface';\n\nexport interface RecordedCall {\n name: string;\n args: unknown[];\n /** Which context made the call, when several record into one log; absent for the frame's own. */\n target?: string;\n}\n\nexport interface RecordingOptions {\n /** A log to record into, shared with other contexts; a fresh one by default. */\n calls?: RecordedCall[];\n /** Stamped on every call this context records into a shared log. */\n target?: string;\n /**\n * Whether the context tracks its transform and answers `getTransform`,\n * as a real one does. Off by default, because a renderer reads the\n * absence as \"no transform worth reporting\" and most specs were\n * written against that; see `Canvas2DContext.getTransform`.\n */\n transform?: boolean;\n}\n\ntype Matrix = [number, number, number, number, number, number];\n\n/**\n * The gradient a recording context hands back, carrying what it was\n * asked for so a test can read the whole paint rather than a `[object\n * CanvasGradient]`. A real one is opaque, which is exactly what makes\n * a gradient hard to assert on without this.\n */\nexport class RecordedGradient implements Canvas2DGradient {\n readonly stops: { offset: number; color: string }[] = [];\n\n constructor(\n readonly kind: 'linear' | 'radial',\n readonly args: readonly number[]\n ) {}\n\n addColorStop(offset: number, color: string): void {\n this.stops.push({ offset, color });\n }\n}\n\n/**\n * Deterministic Canvas2D spy: records every call and style\n * assignment so tests can assert the exact draw sequence.\n */\nexport class RecordingCanvasContext implements Canvas2DContext {\n readonly calls: RecordedCall[];\n private readonly target: string | undefined;\n private matrix: Matrix = [1, 0, 0, 1, 0, 0];\n private readonly matrices: Matrix[] = [];\n getTransform?: () => { a: number; b: number; c: number; d: number; e: number; f: number };\n\n constructor(options: RecordingOptions = {}) {\n this.calls = options.calls ?? [];\n this.target = options.target;\n if (options.transform === true) {\n this.getTransform = () => {\n const [a, b, c, d, e, f] = this.matrix;\n return { a, b, c, d, e, f };\n };\n }\n }\n\n /** Multiplies the current matrix by another on the right, as a canvas does. */\n private compose(a: number, b: number, c: number, d: number, e: number, f: number): void {\n const m = this.matrix;\n this.matrix = [\n m[0] * a + m[2] * b,\n m[1] * a + m[3] * b,\n m[0] * c + m[2] * d,\n m[1] * c + m[3] * d,\n m[0] * e + m[2] * f + m[4],\n m[1] * e + m[3] * f + m[5]\n ];\n }\n\n private _fillStyle: string | Canvas2DGradient | CanvasPattern = '#000';\n private _strokeStyle: string | Canvas2DGradient | CanvasPattern = '#000';\n private _lineWidth = 1;\n private _lineJoin: CanvasLineJoin = 'miter';\n private _globalAlpha = 1;\n private _font = '';\n private _textAlign: CanvasTextAlign = 'start';\n private _textBaseline: CanvasTextBaseline = 'alphabetic';\n private _shadowColor = 'rgba(0, 0, 0, 0)';\n private _shadowBlur = 0;\n private _shadowOffsetX = 0;\n private _shadowOffsetY = 0;\n /**\n * Tracking, which is a context property rather than part of the font\n * shorthand — and sticky, so a spec can check it was cleared as well\n * as that it was set.\n */\n letterSpacing = '0px';\n\n get fillStyle(): string | Canvas2DGradient | CanvasPattern {\n return this._fillStyle;\n }\n\n set fillStyle(value: string | Canvas2DGradient | CanvasPattern) {\n this._fillStyle = value;\n this.record('set:fillStyle', [value]);\n }\n\n get strokeStyle(): string | Canvas2DGradient | CanvasPattern {\n return this._strokeStyle;\n }\n\n set strokeStyle(value: string | Canvas2DGradient | CanvasPattern) {\n this._strokeStyle = value;\n this.record('set:strokeStyle', [value]);\n }\n\n get lineWidth(): number {\n return this._lineWidth;\n }\n\n set lineWidth(value: number) {\n this._lineWidth = value;\n this.record('set:lineWidth', [value]);\n }\n\n get lineJoin(): CanvasLineJoin {\n return this._lineJoin;\n }\n\n set lineJoin(value: CanvasLineJoin) {\n this._lineJoin = value;\n this.record('set:lineJoin', [value]);\n }\n\n get globalAlpha(): number {\n return this._globalAlpha;\n }\n\n set globalAlpha(value: number) {\n this._globalAlpha = value;\n this.record('set:globalAlpha', [value]);\n }\n\n get font(): string {\n return this._font;\n }\n\n set font(value: string) {\n this._font = value;\n this.record('set:font', [value]);\n }\n\n get textAlign(): CanvasTextAlign {\n return this._textAlign;\n }\n\n set textAlign(value: CanvasTextAlign) {\n this._textAlign = value;\n this.record('set:textAlign', [value]);\n }\n\n get textBaseline(): CanvasTextBaseline {\n return this._textBaseline;\n }\n\n set textBaseline(value: CanvasTextBaseline) {\n this._textBaseline = value;\n this.record('set:textBaseline', [value]);\n }\n\n get shadowColor(): string {\n return this._shadowColor;\n }\n\n set shadowColor(value: string) {\n this._shadowColor = value;\n this.record('set:shadowColor', [value]);\n }\n\n get shadowBlur(): number {\n return this._shadowBlur;\n }\n\n set shadowBlur(value: number) {\n this._shadowBlur = value;\n this.record('set:shadowBlur', [value]);\n }\n\n get shadowOffsetX(): number {\n return this._shadowOffsetX;\n }\n\n set shadowOffsetX(value: number) {\n this._shadowOffsetX = value;\n this.record('set:shadowOffsetX', [value]);\n }\n\n get shadowOffsetY(): number {\n return this._shadowOffsetY;\n }\n\n set shadowOffsetY(value: number) {\n this._shadowOffsetY = value;\n this.record('set:shadowOffsetY', [value]);\n }\n\n /**\n * Drawing state saved by `save`, as a real context keeps it. Without\n * it a spec that renders twice read the first frame's leftovers: the\n * renderer multiplies `globalAlpha` inside a `save`, and a double that\n * did not restore it compounded the opacity frame over frame.\n */\n private readonly saved: {\n fillStyle: string | Canvas2DGradient | CanvasPattern;\n strokeStyle: string | Canvas2DGradient | CanvasPattern;\n lineWidth: number;\n lineJoin: CanvasLineJoin;\n globalAlpha: number;\n font: string;\n textAlign: CanvasTextAlign;\n textBaseline: CanvasTextBaseline;\n letterSpacing: string;\n shadowColor: string;\n shadowBlur: number;\n shadowOffsetX: number;\n shadowOffsetY: number;\n }[] = [];\n\n save(): void {\n this.saved.push({\n fillStyle: this._fillStyle,\n strokeStyle: this._strokeStyle,\n lineWidth: this._lineWidth,\n lineJoin: this._lineJoin,\n globalAlpha: this._globalAlpha,\n font: this._font,\n textAlign: this._textAlign,\n textBaseline: this._textBaseline,\n letterSpacing: this.letterSpacing,\n shadowColor: this._shadowColor,\n shadowBlur: this._shadowBlur,\n shadowOffsetX: this._shadowOffsetX,\n shadowOffsetY: this._shadowOffsetY\n });\n this.matrices.push(this.matrix);\n this.record('save', []);\n }\n\n restore(): void {\n const state = this.saved.pop();\n if (state !== undefined) {\n this._fillStyle = state.fillStyle;\n this._strokeStyle = state.strokeStyle;\n this._lineWidth = state.lineWidth;\n this._lineJoin = state.lineJoin;\n this._globalAlpha = state.globalAlpha;\n this._font = state.font;\n this._textAlign = state.textAlign;\n this._textBaseline = state.textBaseline;\n this.letterSpacing = state.letterSpacing;\n this._shadowColor = state.shadowColor;\n this._shadowBlur = state.shadowBlur;\n this._shadowOffsetX = state.shadowOffsetX;\n this._shadowOffsetY = state.shadowOffsetY;\n }\n this.matrix = this.matrices.pop() ?? this.matrix;\n this.record('restore', []);\n }\n\n translate(x: number, y: number): void {\n this.compose(1, 0, 0, 1, x, y);\n this.record('translate', [x, y]);\n }\n\n scale(x: number, y: number): void {\n this.compose(x, 0, 0, y, 0, 0);\n this.record('scale', [x, y]);\n }\n\n rotate(angle: number): void {\n const cos = Math.cos(angle);\n const sin = Math.sin(angle);\n this.compose(cos, sin, -sin, cos, 0, 0);\n this.record('rotate', [angle]);\n }\n\n setTransform(a: number, b: number, c: number, d: number, e: number, f: number): void {\n this.matrix = [a, b, c, d, e, f];\n this.record('setTransform', [a, b, c, d, e, f]);\n }\n\n clearRect(x: number, y: number, width: number, height: number): void {\n this.record('clearRect', [x, y, width, height]);\n }\n\n fillRect(x: number, y: number, width: number, height: number): void {\n this.record('fillRect', [x, y, width, height]);\n }\n\n strokeRect(x: number, y: number, width: number, height: number): void {\n this.record('strokeRect', [x, y, width, height]);\n }\n\n beginPath(): void {\n this.record('beginPath', []);\n }\n\n moveTo(x: number, y: number): void {\n this.record('moveTo', [x, y]);\n }\n\n lineTo(x: number, y: number): void {\n this.record('lineTo', [x, y]);\n }\n\n arcTo(x1: number, y1: number, x2: number, y2: number, radius: number): void {\n this.record('arcTo', [x1, y1, x2, y2, radius]);\n }\n\n closePath(): void {\n this.record('closePath', []);\n }\n\n rect(x: number, y: number, width: number, height: number): void {\n this.record('rect', [x, y, width, height]);\n }\n\n clip(): void {\n this.record('clip', []);\n }\n\n fill(): void {\n this.record('fill', []);\n }\n\n stroke(): void {\n this.record('stroke', []);\n }\n\n fillText(text: string, x: number, y: number, maxWidth?: number): void {\n const args = maxWidth === undefined ? [text, x, y] : [text, x, y, maxWidth];\n this.record('fillText', args);\n }\n\n measureText(text: string): TextMetrics {\n this.record('measureText', [text]);\n return { width: text.length * 8 } as TextMetrics;\n }\n\n drawImage(image: ImageBitmap, dx: number, dy: number, dw: number, dh: number): void {\n this.record('drawImage', [image, dx, dy, dw, dh]);\n }\n\n // Path calls a painter's recording makes when it is replayed straight\n // onto the frame's context (`PaintPictureCache.draw`), rather than\n // onto a picture canvas of its own.\n transform(a: number, b: number, c: number, d: number, e: number, f: number): void {\n this.compose(a, b, c, d, e, f);\n this.record('transform', [a, b, c, d, e, f]);\n }\n\n quadraticCurveTo(cx: number, cy: number, x: number, y: number): void {\n this.record('quadraticCurveTo', [cx, cy, x, y]);\n }\n\n bezierCurveTo(c1x: number, c1y: number, c2x: number, c2y: number, x: number, y: number): void {\n this.record('bezierCurveTo', [c1x, c1y, c2x, c2y, x, y]);\n }\n\n arc(x: number, y: number, radius: number, startAngle: number, endAngle: number, counterclockwise = false): void {\n this.record('arc', [x, y, radius, startAngle, endAngle, counterclockwise]);\n }\n\n setLineDash(segments: readonly number[]): void {\n this.record('setLineDash', [segments]);\n }\n\n createLinearGradient(x0: number, y0: number, x1: number, y1: number): RecordedGradient {\n const gradient = new RecordedGradient('linear', [x0, y0, x1, y1]);\n this.record('createLinearGradient', [x0, y0, x1, y1]);\n return gradient;\n }\n\n createRadialGradient(x0: number, y0: number, r0: number, x1: number, y1: number, r1: number): RecordedGradient {\n const gradient = new RecordedGradient('radial', [x0, y0, r0, x1, y1, r1]);\n this.record('createRadialGradient', [x0, y0, r0, x1, y1, r1]);\n return gradient;\n }\n\n private record(name: string, args: unknown[]): void {\n const call: RecordedCall = { name, args: args.map(normalizeNumber) };\n if (this.target !== undefined) {\n call.target = this.target;\n }\n this.calls.push(call);\n }\n}\n\n/**\n * Normalizes -0 to 0 so scroll translations assert cleanly.\n */\nfunction normalizeNumber(value: unknown): unknown {\n if (typeof value === 'number' && Object.is(value, -0)) {\n return 0;\n }\n return value;\n}\n\nexport function callNames(context: RecordingCanvasContext): string[] {\n return context.calls.map(call => call.name);\n}\n\nexport function callsOf(context: RecordingCanvasContext, name: string): RecordedCall[] {\n return context.calls.filter(call => call.name === name);\n}\n\nexport function callArgs(context: RecordingCanvasContext, name: string): unknown[][] {\n return callsOf(context, name).map(call => (call.name.startsWith('set:') ? call.args[0] : call.args) as unknown[]);\n}\n\nexport function savedDepth(context: RecordingCanvasContext): number {\n let depth = 0;\n for (const call of context.calls) {\n if (call.name === 'save') {\n depth++;\n } else if (call.name === 'restore') {\n depth--;\n }\n }\n return depth;\n}\n\n/**\n * Deterministic `PaintContext2D` spy: the picture rasteriser's\n * equivalent of `RecordingCanvasContext`, so a spec can assert what a\n * painter actually drew without a browser.\n */\nexport class RecordingPaintContext implements PaintContext2D {\n readonly calls: RecordedCall[] = [];\n\n lineDashOffset = 0;\n lineWidth = 1;\n lineCap: PaintLineCap = 'butt';\n lineJoin: PaintLineJoin = 'miter';\n miterLimit = 10;\n globalAlpha = 1;\n filter = 'none';\n fillStyle: string | PaintGradient = '#000';\n strokeStyle: string | PaintGradient = '#000';\n font = '';\n textAlign: 'left' | 'center' | 'right' = 'left';\n textBaseline = 'alphabetic' as const;\n\n save(): void {\n this.record('save', []);\n }\n\n restore(): void {\n this.record('restore', []);\n }\n\n translate(x: number, y: number): void {\n this.record('translate', [x, y]);\n }\n\n scale(x: number, y: number): void {\n this.record('scale', [x, y]);\n }\n\n rotate(angle: number): void {\n this.record('rotate', [angle]);\n }\n\n transform(a: number, b: number, c: number, d: number, e: number, f: number): void {\n this.record('transform', [a, b, c, d, e, f]);\n }\n\n beginPath(): void {\n this.record('beginPath', []);\n }\n\n moveTo(x: number, y: number): void {\n this.record('moveTo', [x, y]);\n }\n\n lineTo(x: number, y: number): void {\n this.record('lineTo', [x, y]);\n }\n\n quadraticCurveTo(cx: number, cy: number, x: number, y: number): void {\n this.record('quadraticCurveTo', [cx, cy, x, y]);\n }\n\n bezierCurveTo(c1x: number, c1y: number, c2x: number, c2y: number, x: number, y: number): void {\n this.record('bezierCurveTo', [c1x, c1y, c2x, c2y, x, y]);\n }\n\n arc(x: number, y: number, radius: number, startAngle: number, endAngle: number, counterclockwise = false): void {\n this.record('arc', [x, y, radius, startAngle, endAngle, counterclockwise]);\n }\n\n arcTo(x1: number, y1: number, x2: number, y2: number, radius: number): void {\n this.record('arcTo', [x1, y1, x2, y2, radius]);\n }\n\n rect(x: number, y: number, width: number, height: number): void {\n this.record('rect', [x, y, width, height]);\n }\n\n closePath(): void {\n this.record('closePath', []);\n }\n\n fill(fillRule?: 'nonzero' | 'evenodd'): void {\n this.record('fill', [fillRule ?? 'nonzero', this.fillStyle]);\n }\n\n stroke(): void {\n this.record('stroke', [this.strokeStyle, this.lineWidth]);\n }\n\n clip(fillRule?: 'nonzero' | 'evenodd'): void {\n this.record('clip', [fillRule ?? 'nonzero']);\n }\n\n fillText(text: string, x: number, y: number): void {\n this.record('fillText', [text, x, y, this.font, this.textAlign]);\n }\n\n drawImage(image: ImageBitmap, dx: number, dy: number, dw: number, dh: number): void {\n this.record('drawImage', [image, dx, dy, dw, dh]);\n }\n\n setLineDash(segments: readonly number[]): void {\n this.record('setLineDash', [[...segments]]);\n }\n\n createLinearGradient(x0: number, y0: number, x1: number, y1: number): RecordedGradient {\n const gradient = new RecordedGradient('linear', [x0, y0, x1, y1]);\n this.record('createLinearGradient', [x0, y0, x1, y1]);\n return gradient;\n }\n\n createRadialGradient(x0: number, y0: number, r0: number, x1: number, y1: number, r1: number): RecordedGradient {\n const gradient = new RecordedGradient('radial', [x0, y0, r0, x1, y1, r1]);\n this.record('createRadialGradient', [x0, y0, r0, x1, y1, r1]);\n return gradient;\n }\n\n private record(name: string, args: unknown[]): void {\n this.calls.push({ name, args: args.map(normalizeNumber) });\n }\n}\n\n/**\n * A picture that never becomes real pixels.\n *\n * `take()` hands back a stand-in for the `ImageBitmap` a browser would\n * transfer out of an `OffscreenCanvas`, carrying only the size and a\n * `close`, which is all either renderer reads off one. Every context\n * the factory hands out is kept, so a spec can assert both what was\n * drawn and how many times anything was drawn at all.\n */\nexport class FakePaintCanvases {\n readonly contexts: RecordingPaintContext[] = [];\n readonly bitmaps: { width: number; height: number; closed: boolean }[] = [];\n\n readonly create: PaintCanvasFactory = (width, height) => {\n const context = new RecordingPaintContext();\n this.contexts.push(context);\n const bitmap = { width, height, closed: false };\n this.bitmaps.push(bitmap);\n return {\n context: () => context,\n take: () =>\n ({\n width,\n height,\n close: () => {\n bitmap.closed = true;\n }\n }) as unknown as ImageBitmap\n };\n };\n\n /** The calls made into the most recent picture. */\n get last(): RecordingPaintContext {\n const context = this.contexts.at(-1);\n if (context === undefined) {\n throw new Error('No picture has been rasterised.');\n }\n return context;\n }\n\n reset(): void {\n this.contexts.length = 0;\n this.bitmaps.length = 0;\n }\n}\n\n/**\n * A canvas host backed by a recording context.\n */\nexport class FakeCanvasHost implements CanvasHost {\n width = 0;\n height = 0;\n private contextRequests = 0;\n\n /** `label` names the host for a spec that meets it again as a `drawImage` source. */\n constructor(\n private readonly context: Canvas2DContext | null,\n readonly label?: string\n ) {}\n\n getContext(_contextId: '2d'): CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D | null {\n this.contextRequests++;\n return this.context as unknown as CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D;\n }\n\n get contextRequestCount(): number {\n return this.contextRequests;\n }\n}\n\n/**\n * Shared harness: a real UiGraph + LayoutEngine + Canvas2DRenderer\n * over a recording context. Deterministic layout comes from the\n * CharacterCountTextMeasurer, shared by layout and renderer.\n */\nexport class RenderHarness {\n readonly graph = new UiGraph();\n readonly measurer: TextMeasurer = new CharacterCountTextMeasurer();\n readonly engine = new LayoutEngine(this.measurer);\n readonly context: RecordingCanvasContext;\n readonly host: FakeCanvasHost;\n readonly surface: CanvasSurface;\n readonly renderer: Canvas2DRenderer;\n /** The layers opacity groups were drawn in, by label; empty without `groups`. */\n readonly groupLayers = new Map<string, FakeCanvasHost>();\n\n /**\n * With `groups`, opacity groups get layers as they do in a browser:\n * the context tracks its transform, and each layer is a recording\n * context writing into the frame's log, its calls stamped with the\n * layer's label (`group0`, `group1`, ...) and its host met again as\n * the source of the `drawImage` that blends it. Text is not kept as\n * pixels, so it is still drawn as `fillText`.\n */\n constructor(\n logicalWidth: number = 800,\n logicalHeight: number = 600,\n dpr: number = 1,\n options: { groups?: boolean } = {}\n ) {\n const groups = options.groups === true;\n this.context = new RecordingCanvasContext({ transform: groups });\n this.host = new FakeCanvasHost(this.context);\n this.surface = new CanvasSurface(this.host);\n this.renderer = new Canvas2DRenderer(\n groups\n ? {\n surface: this.surface,\n textCache: false,\n createLayerCanvas: (width, height) => {\n const label = `group${this.groupLayers.size}`;\n const context = new RecordingCanvasContext({ calls: this.context.calls, target: label, transform: true });\n const host = new FakeCanvasHost(context, label);\n host.width = width;\n host.height = height;\n this.groupLayers.set(label, host);\n return host;\n }\n }\n : { surface: this.surface }\n );\n this.surface.setLogicalSize(logicalWidth, logicalHeight, dpr);\n }\n\n createNode(id: string, type: UiNodeType): UiNode {\n return this.graph.createNode(id, type);\n }\n\n append(parent: UiNode, ...children: UiNode[]): void {\n for (const child of children) {\n this.graph.appendChild(parent, child);\n }\n }\n\n layout(node: UiNode, constraints: Constraints = Constraints.tight(800, 600)): LayoutResult {\n return this.engine.layout(node, constraints);\n }\n\n render(node: UiNode): void {\n // Far in the future: overlay scrollbars have faded, so draw-call\n // assertions see only the scene. Pass `now` yourself to test them.\n this.renderer.render(node, { layout: this.engine, text: this.measurer, now: Number.MAX_SAFE_INTEGER });\n }\n\n record(node: UiNode): LayoutRecord {\n const record = this.engine.recordFor(node);\n if (record === undefined) {\n throw new Error(`No layout record for '${node.id}'.`);\n }\n return record;\n }\n\n box(node: UiNode): LayoutBox {\n const record = this.record(node);\n return { x: record.x, y: record.y, width: record.width, height: record.height };\n }\n}\n","/**\n * A very small MP4 writer, and a decoder that decodes nothing.\n *\n * Both exist for the same reason the demuxer's spec gives for building\n * its files by hand: a fixture `.mp4` is a better test of reality and\n * a worse test of this code, because when it fails you do not know\n * which of forty boxes was misread. Building the file means a spec can\n * name the exact field it is about, and construct the awkward cases —\n * a chunk holding several samples, composition offsets reordering\n * them, a track with no `stss` — rather than hunting for a file that\n * has one.\n *\n * In `gesso-core/testing` rather than beside a spec because two specs\n * now want it: the demuxer's, which reads these files, and the\n * resolver's, which plays them.\n */\n\nfunction u32(value: number): number[] {\n return [(value >>> 24) & 0xff, (value >>> 16) & 0xff, (value >>> 8) & 0xff, value & 0xff];\n}\n\nfunction u16(value: number): number[] {\n return [(value >>> 8) & 0xff, value & 0xff];\n}\n\nfunction ascii(text: string): number[] {\n return [...text].map(character => character.charCodeAt(0));\n}\n\n/** `size type payload`, which is every box in the format. */\nexport function box(type: string, ...payload: number[][]): number[] {\n const body = payload.flat();\n return [...u32(body.length + 8), ...ascii(type), ...body];\n}\n\n/** A full box: a version and three flag bytes before the payload. */\nexport function fullBox(type: string, version: number, ...payload: number[][]): number[] {\n return box(type, [version, 0, 0, 0], ...payload);\n}\n\n/** An AAC audio track, for a file that has sound as well as pictures. */\nexport interface AudioTrackSpec {\n timescale: number;\n deltas: { count: number; delta: number }[];\n sizes: number[];\n chunkOffsets: number[];\n chunkRuns: { firstChunk: number; samplesPerChunk: number }[];\n channels: number;\n sampleRate: number;\n /**\n * The `AudioSpecificConfig`, whose top five bits are the audio\n * object type. The default is AAC-LC at 44.1kHz stereo, which is\n * `mp4a.40.2` and what nearly every file on the web carries.\n */\n config?: number[];\n}\n\nexport interface TrackSpec {\n timescale: number;\n /** One `stts` run per entry. */\n deltas: { count: number; delta: number }[];\n compositionOffsets?: { count: number; offset: number }[];\n sizes: number[];\n chunkOffsets: number[];\n chunkRuns: { firstChunk: number; samplesPerChunk: number }[];\n /** Omitted means no `stss` at all, which means every sample is a sync sample. */\n syncSamples?: number[];\n width: number;\n height: number;\n handler?: string;\n /** The sample entry's four character code. `avc1` unless said otherwise. */\n codec?: 'avc1' | 'av01' | 'vp09';\n /** The bytes of the configuration record after any fixed header; see `configurationBox`. */\n configBytes?: number[];\n}\n\n/**\n * The codec configuration box, which differs per codec and is where a\n * codec string comes from.\n */\nfunction configurationBox(spec: TrackSpec): number[] {\n if (spec.codec === 'av01') {\n // marker+version, then seq_profile/seq_level_idx, then the tier\n // and bit depth flags.\n return box('av1C', [0x81, ...(spec.configBytes ?? [0x04, 0x00])]);\n }\n if (spec.codec === 'vp09') {\n // A full box: version and flags, then profile, level and a byte\n // whose top nibble is the bit depth.\n return fullBox('vpcC', 1, spec.configBytes ?? [0x00, 0x1f, 0x80]);\n }\n // `avc1`: length, profile, compat, level, then whatever follows.\n return box('avcC', [1, 0x64, 0x00, 0x1f, 0xff, 0xe1]);\n}\n\nfunction visualSampleEntry(spec: TrackSpec): number[] {\n return box(\n spec.codec ?? 'avc1',\n [0, 0, 0, 0, 0, 0], // reserved\n u16(1), // data_reference_index\n Array.from({ length: 16 }, () => 0), // pre_defined + reserved\n u16(spec.width),\n u16(spec.height),\n u32(0x0048_0000), // horizresolution\n u32(0x0048_0000), // vertresolution\n u32(0), // reserved\n u16(1), // frame_count\n Array.from({ length: 32 }, () => 0), // compressorname\n u16(24), // depth\n [0xff, 0xff], // pre_defined = -1\n configurationBox(spec)\n );\n}\n\n/** `esds`: the descriptor tree down to the two numbers a codec string needs. */\nfunction esds(spec: AudioTrackSpec): number[] {\n const config = spec.config ?? [0x12, 0x10];\n // DecoderSpecificInfo (tag 5)\n const dsi = [0x05, config.length, ...config];\n // DecoderConfigDescriptor (tag 4): object type 0x40 is MPEG-4 audio,\n // then a stream type, a buffer size and two bitrates.\n // Thirteen bytes of header: an object type indication, a stream\n // type, three bytes of buffer size, and two four-byte bitrates.\n const dcd = [0x04, 13 + dsi.length, 0x40, 0x15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...dsi];\n // ES_Descriptor (tag 3): an id and a flags byte with nothing set.\n const esd = [0x03, 3 + dcd.length, 0, 1, 0, ...dcd];\n return fullBox('esds', 0, esd);\n}\n\nfunction audioSampleEntry(spec: AudioTrackSpec): number[] {\n return box(\n 'mp4a',\n [0, 0, 0, 0, 0, 0], // reserved\n u16(1), // data_reference_index\n u16(0), // version\n [0, 0, 0, 0, 0, 0], // revision and vendor\n u16(spec.channels),\n u16(16), // sample size\n u16(0), // pre_defined\n u16(0), // reserved\n u16(spec.sampleRate), // 16.16 fixed, whole part\n u16(0),\n esds(spec)\n );\n}\n\nfunction audioSampleTable(spec: AudioTrackSpec): number[] {\n return box(\n 'stbl',\n fullBox('stsd', 0, u32(1), audioSampleEntry(spec)),\n fullBox('stts', 0, u32(spec.deltas.length), ...spec.deltas.map(run => [...u32(run.count), ...u32(run.delta)])),\n fullBox(\n 'stsc',\n 0,\n u32(spec.chunkRuns.length),\n ...spec.chunkRuns.map(run => [...u32(run.firstChunk), ...u32(run.samplesPerChunk), ...u32(1)])\n ),\n fullBox('stsz', 0, u32(0), u32(spec.sizes.length), ...spec.sizes.map(size => u32(size))),\n fullBox('stco', 0, u32(spec.chunkOffsets.length), ...spec.chunkOffsets.map(offset => u32(offset)))\n );\n}\n\nfunction audioTrak(spec: AudioTrackSpec): number[] {\n return box(\n 'trak',\n box(\n 'mdia',\n fullBox('mdhd', 0, u32(0), u32(0), u32(spec.timescale), u32(1000)),\n fullBox(\n 'hdlr',\n 0,\n u32(0),\n ascii('soun'),\n Array.from({ length: 12 }, () => 0),\n [0]\n ),\n box('minf', audioSampleTable(spec))\n )\n );\n}\n\nfunction sampleTable(spec: TrackSpec): number[] {\n const boxes: number[][] = [\n fullBox('stsd', 0, u32(1), visualSampleEntry(spec)),\n fullBox('stts', 0, u32(spec.deltas.length), ...spec.deltas.map(run => [...u32(run.count), ...u32(run.delta)])),\n fullBox(\n 'stsc',\n 0,\n u32(spec.chunkRuns.length),\n ...spec.chunkRuns.map(run => [...u32(run.firstChunk), ...u32(run.samplesPerChunk), ...u32(1)])\n ),\n fullBox('stsz', 0, u32(0), u32(spec.sizes.length), ...spec.sizes.map(size => u32(size))),\n fullBox('stco', 0, u32(spec.chunkOffsets.length), ...spec.chunkOffsets.map(offset => u32(offset)))\n ];\n if (spec.compositionOffsets !== undefined) {\n boxes.splice(\n 2,\n 0,\n fullBox(\n 'ctts',\n 0,\n u32(spec.compositionOffsets.length),\n ...spec.compositionOffsets.map(run => [...u32(run.count), ...u32(run.offset)])\n )\n );\n }\n if (spec.syncSamples !== undefined) {\n boxes.push(fullBox('stss', 0, u32(spec.syncSamples.length), ...spec.syncSamples.map(number => u32(number))));\n }\n return box('stbl', ...boxes);\n}\n\nexport function buildMp4(spec: TrackSpec, options: { fragmented?: boolean; audio?: AudioTrackSpec } = {}): ArrayBuffer {\n const trak = box(\n 'trak',\n box(\n 'mdia',\n fullBox('mdhd', 0, u32(0), u32(0), u32(spec.timescale), u32(1000)),\n fullBox(\n 'hdlr',\n 0,\n u32(0),\n ascii(spec.handler ?? 'vide'),\n Array.from({ length: 12 }, () => 0),\n [0]\n ),\n box('minf', sampleTable(spec))\n )\n );\n const bytes = [\n ...box('ftyp', ascii('isom'), u32(512), ascii('isomiso2avc1mp41')),\n ...box(\n 'moov',\n fullBox(\n 'mvhd',\n 0,\n Array.from({ length: 100 }, () => 0)\n ),\n trak,\n ...(options.audio === undefined ? [] : [audioTrak(options.audio)])\n ),\n ...(options.fragmented === true ? box('moof', box('mfhd', u32(1))) : []),\n // A `mdat` big enough for every offset the tables name.\n ...box(\n 'mdat',\n Array.from({ length: 512 }, () => 0x41)\n )\n ];\n return new Uint8Array(bytes).buffer;\n}\n\n/** One run of samples inside a fragment. */\nexport interface FragmentSpec {\n /** Decode time the fragment starts at, in the track's timescale. Omitted means \"carry on\". */\n baseMediaDecodeTime?: number;\n samples: { duration: number; size: number; isKey: boolean }[];\n}\n\nexport interface FragmentedSpec {\n trackId: number;\n timescale: number;\n width: number;\n height: number;\n fragments: FragmentSpec[];\n /** Written into `trex`, and used by any sample whose `trun` omits the field. */\n defaultSampleDuration?: number;\n defaultSampleSize?: number;\n /** Leave a field out of the `trun` so the default has to supply it. */\n omit?: { duration?: boolean; size?: boolean };\n}\n\nfunction tkhd(trackId: number): number[] {\n return fullBox(\n 'tkhd',\n 0,\n u32(0), // creation time\n u32(0), // modification time\n u32(trackId),\n Array.from({ length: 68 }, () => 0)\n );\n}\n\n/**\n * A fragmented MP4: a `moov` that describes the track and promises\n * fragments, then the fragments.\n *\n * The `stbl` is a shell — `stsd` and four empty tables — which is what\n * a fragmenter writes and is exactly the shape that used to make this\n * demuxer return zero samples for a file the browser played.\n */\nexport function buildFragmentedMp4(spec: FragmentedSpec): ArrayBuffer {\n const shellSpec: TrackSpec = {\n timescale: spec.timescale,\n deltas: [],\n sizes: [],\n chunkOffsets: [],\n chunkRuns: [],\n width: spec.width,\n height: spec.height\n };\n const trak = box(\n 'trak',\n tkhd(spec.trackId),\n box(\n 'mdia',\n fullBox('mdhd', 0, u32(0), u32(0), u32(spec.timescale), u32(1000)),\n fullBox(\n 'hdlr',\n 0,\n u32(0),\n ascii('vide'),\n Array.from({ length: 12 }, () => 0),\n [0]\n ),\n box('minf', sampleTable(shellSpec))\n )\n );\n const mvex = box(\n 'mvex',\n fullBox(\n 'trex',\n 0,\n u32(spec.trackId),\n u32(1), // default_sample_description_index\n u32(spec.defaultSampleDuration ?? 0),\n u32(spec.defaultSampleSize ?? 0),\n u32(0) // default_sample_flags\n )\n );\n const moov = box(\n 'moov',\n fullBox(\n 'mvhd',\n 0,\n Array.from({ length: 100 }, () => 0)\n ),\n trak,\n mvex\n );\n\n const bytes = [...box('ftyp', ascii('isom'), u32(512), ascii('isomiso6avc1mp41')), ...moov];\n\n for (const fragment of spec.fragments) {\n const payloadSize = fragment.samples.reduce((total, sample) => total + sample.size, 0);\n // Built twice: the `trun`'s data offset is relative to the start\n // of the `moof` box, and is not known until the `moof` has been\n // sized. The first pass is only for its length.\n const build = (dataOffset: number): number[] => {\n const trunFlags =\n 0x000001 | // data-offset-present\n (spec.omit?.duration === true ? 0 : 0x000100) |\n (spec.omit?.size === true ? 0 : 0x000200) |\n 0x000400; // sample-flags-present\n const entries = fragment.samples.flatMap(sample => [\n ...(spec.omit?.duration === true ? [] : u32(sample.duration)),\n ...(spec.omit?.size === true ? [] : u32(sample.size)),\n // Bit 16 set means \"not a sync sample\".\n ...u32(sample.isKey ? 0 : 0x00010000)\n ]);\n const trun = box(\n 'trun',\n [0, (trunFlags >> 16) & 0xff, (trunFlags >> 8) & 0xff, trunFlags & 0xff],\n u32(fragment.samples.length),\n u32(dataOffset),\n entries\n );\n const tfhdFlags = 0x020000; // default-base-is-moof\n const traf = box(\n 'traf',\n box('tfhd', [0, (tfhdFlags >> 16) & 0xff, (tfhdFlags >> 8) & 0xff, tfhdFlags & 0xff], u32(spec.trackId)),\n ...(fragment.baseMediaDecodeTime === undefined ? [] : [fullBox('tfdt', 0, u32(fragment.baseMediaDecodeTime))]),\n trun\n );\n return box('moof', fullBox('mfhd', 0, u32(1)), traf);\n };\n const sized = build(0);\n // The `mdat` payload begins eight bytes past the end of the `moof`.\n const moof = build(sized.length + 8);\n bytes.push(\n ...moof,\n ...box(\n 'mdat',\n Array.from({ length: payloadSize }, () => 0x42)\n )\n );\n }\n return new Uint8Array(bytes).buffer;\n}\n\n/** The offset `mdat`'s payload lands at, for a table that must point into it. */\nexport function mdatStart(data: ArrayBuffer): number {\n const view = new DataView(data);\n let at = 0;\n while (at + 8 <= data.byteLength) {\n const size = view.getUint32(at);\n const type = String.fromCharCode(\n view.getUint8(at + 4),\n view.getUint8(at + 5),\n view.getUint8(at + 6),\n view.getUint8(at + 7)\n );\n if (type === 'mdat') {\n return at + 8;\n }\n at += size;\n }\n throw new Error('no mdat');\n}\n\n/** One chunk as the fake decoder saw it. */\nexport interface DecodedChunk {\n readonly timestamp: number;\n readonly type: 'key' | 'delta';\n}\n\n/**\n * A `VideoDecoder` that records what it was asked to decode and hands\n * back a frame for each.\n *\n * What the resolver's spec is about is *which samples are submitted*:\n * a seek that starts at the wrong keyframe, or one that decodes the\n * whole clip to get somewhere, is a bug in arithmetic that has nothing\n * to do with pixels. So this decodes nothing and the frames it emits\n * carry a timestamp and a `close` and no image at all.\n *\n * Emission is synchronous, which a real decoder's never is. That makes\n * the specs readable and is the one way this lies: the ordering\n * hazards a real decoder creates — a frame arriving after the position\n * has moved past it — are `Mp4Playback`'s to handle and are asserted\n * by pushing frames in by hand rather than by waiting for a thread.\n */\nexport class FakeVideoDecoder {\n static readonly submitted: DecodedChunk[] = [];\n /** How many times a decoder was configured: a seek costs one. */\n static configures = 0;\n static resets = 0;\n /** Set to stop frames being emitted, for a spec about a queue filling up. */\n static emit = true;\n\n static install(): () => void {\n const scope = globalThis as Record<string, unknown>;\n const previous = {\n VideoDecoder: scope.VideoDecoder,\n EncodedVideoChunk: scope.EncodedVideoChunk,\n VideoFrame: scope.VideoFrame,\n createImageBitmap: scope.createImageBitmap\n };\n FakeVideoDecoder.reset();\n scope.VideoDecoder = FakeVideoDecoder;\n scope.EncodedVideoChunk = class {\n readonly timestamp: number;\n readonly type: 'key' | 'delta';\n constructor(init: { timestamp: number; type: 'key' | 'delta' }) {\n this.timestamp = init.timestamp;\n this.type = init.type;\n }\n };\n scope.VideoFrame = class {};\n // Left undefined so `MutableVideoSurface` takes the path that keeps\n // the frame itself: converting to a bitmap is asynchronous and\n // would put every assertion behind a microtask for no gain.\n scope.createImageBitmap = undefined;\n return () => Object.assign(scope, previous);\n }\n\n static reset(): void {\n FakeVideoDecoder.submitted.length = 0;\n FakeVideoDecoder.configures = 0;\n FakeVideoDecoder.resets = 0;\n FakeVideoDecoder.emit = true;\n }\n\n /** Every timestamp submitted since the last configure, in order. */\n static get sinceConfigure(): number[] {\n return FakeVideoDecoder.submitted.map(chunk => chunk.timestamp);\n }\n\n static isConfigSupported(): Promise<{ supported: boolean }> {\n return Promise.resolve({ supported: true });\n }\n\n state: 'unconfigured' | 'configured' | 'closed' = 'unconfigured';\n decodeQueueSize = 0;\n private readonly output: (frame: { timestamp: number; close(): void }) => void;\n\n constructor(init: { output: (frame: { timestamp: number; close(): void }) => void; error: (e: unknown) => void }) {\n this.output = init.output;\n }\n\n configure(): void {\n this.state = 'configured';\n FakeVideoDecoder.configures++;\n }\n\n decode(chunk: DecodedChunk): void {\n FakeVideoDecoder.submitted.push({ timestamp: chunk.timestamp, type: chunk.type });\n if (!FakeVideoDecoder.emit) {\n this.decodeQueueSize++;\n return;\n }\n this.output({ timestamp: chunk.timestamp, close: () => {} });\n }\n\n reset(): void {\n FakeVideoDecoder.resets++;\n this.decodeQueueSize = 0;\n this.state = 'unconfigured';\n }\n\n close(): void {\n this.state = 'closed';\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gesso-core",
3
- "version": "0.6.29",
3
+ "version": "0.7.0",
4
4
  "description": "The retained UI graph, the layout engine, the two renderers and input — everything below a component.",
5
5
  "license": "MIT",
6
6
  "author": "Kevin Baker",