buninu-browser 0.7.0 → 0.9.1
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/AGENTS.md +5 -0
- package/README.md +90 -1
- package/buninu-browser.js +1 -1
- package/frontends/wayland/README.md +35 -2
- package/frontends/wayland/client.js +8 -6
- package/frontends/wayland/clipboard.js +1 -1
- package/frontends/wayland/index.js +1 -1
- package/frontends/wayland/input.js +1 -1
- package/frontends/wayland/resource-io.js +6 -0
- package/frontends/wayland/transport-posix.js +120 -0
- package/frontends/wayland/transport-win32.js +9 -0
- package/frontends/wayland/transport.js +3 -120
- package/frontends/wayland/win32-resources.js +80 -0
- package/frontends/wayland/win32-wire.js +62 -0
- package/lib/audio/biquad.js +25 -0
- package/lib/audio/piano.js +6 -3
- package/lib/audio/playback-buffer.js +5 -5
- package/lib/audio/pulse-client.js +24 -5
- package/lib/audio/web-audio.js +570 -0
- package/lib/browser-dialog.js +126 -0
- package/lib/cdp-server.js +43 -11
- package/lib/file-picker.js +48 -0
- package/lib/headless-shell.js +34 -12
- package/lib/layout/text-layout.js +112 -19
- package/lib/media/mp4-frame-times.js +93 -0
- package/lib/paint/direct-canvas.ts +39 -0
- package/lib/paint/display-list.js +37 -12
- package/lib/paint/raster-readback.js +16 -7
- package/lib/renderer/animation-frames.js +10 -5
- package/lib/renderer/bindings.js +69 -16
- package/lib/renderer/canvas-2d.js +265 -15
- package/lib/renderer/canvas-round-rect.js +36 -0
- package/lib/renderer/css-animations.js +173 -0
- package/lib/renderer/dialog-client.js +30 -0
- package/lib/renderer/dialog-worker.js +35 -0
- package/lib/renderer/eval-environment.js +28 -0
- package/lib/renderer/file-input.js +49 -0
- package/lib/renderer/host.js +33 -4
- package/lib/renderer/html-drag.js +77 -0
- package/lib/renderer/image-loading.js +45 -2
- package/lib/renderer/main.js +4 -0
- package/lib/renderer/module-transform.js +5 -3
- package/lib/renderer/page-builtins.js +59 -0
- package/lib/renderer/page-realm.js +177 -40
- package/lib/renderer/page-renderer.js +397 -49
- package/lib/renderer/piano-input.js +3 -1
- package/lib/renderer/raster-surface.js +18 -0
- package/lib/renderer/script-rewrite.js +165 -9
- package/lib/renderer/video-presentation.js +5 -4
- package/lib/renderer/webgl/clip.js +24 -0
- package/lib/renderer/webgl/context.js +158 -0
- package/lib/renderer/webgl/cpu-fragment.js +211 -0
- package/lib/renderer/webgl/draw-cache.js +22 -0
- package/lib/renderer/webgl/pipeline.js +70 -0
- package/lib/renderer/webgl/shaders.js +58 -0
- package/lib/renderer/webgl/textures.js +43 -0
- package/lib/renderer/window-named-properties.js +46 -0
- package/lib/screenshot-cli.js +4 -7
- package/lib/style/animation-values.js +29 -0
- package/lib/style/computed-style.js +109 -13
- package/lib/style/transform.js +44 -0
- package/lib/window-size.js +10 -0
- package/package.json +1 -1
- package/patches/happy-dom/0007-scope-content-models-to-html-namespace.patch +29 -0
- package/patches/happy-dom/README.md +16 -0
- package/test/animation-frames.test.js +15 -0
- package/test/canvas-2d.test.js +252 -4
- package/test/canvas-frame-pacing.test.js +7 -0
- package/test/cdp-viewers.test.js +36 -0
- package/test/classic-games-patches/0001-fix-game-initialization.patch +177 -0
- package/test/classic-games-patches/README.md +156 -0
- package/test/classic-games-scenarios.js +133 -0
- package/test/clipboard.test.js +16 -0
- package/test/computed-style.test.js +18 -0
- package/test/css-animation-lifecycle.test.js +28 -0
- package/test/css-animations.test.js +83 -0
- package/test/direct-canvas.test.js +39 -0
- package/test/dynamic-inline-handlers.test.js +25 -0
- package/test/eval-declarations.test.js +22 -0
- package/test/file-picker.test.js +34 -0
- package/test/fixtures/canvas-damage.js +23 -0
- package/test/fixtures/canvas-frame-pacing.js +35 -0
- package/test/fixtures/direct-canvas.js +96 -0
- package/test/fixtures/javascript-dialogs.html +8 -0
- package/test/fixtures/media-audio-lifetime.js +65 -0
- package/test/fixtures/renderer-memory.js +7 -5
- package/test/fixtures/video-black-replay.js +8 -6
- package/test/fixtures/video-slow-end.js +39 -0
- package/test/fixtures/web-audio-renderer.js +45 -0
- package/test/flex.test.js +26 -0
- package/test/grid.test.js +13 -0
- package/test/headless-shell.test.js +9 -0
- package/test/headless-window-size.test.js +28 -0
- package/test/html-rcdata.test.js +35 -0
- package/test/image-decode.test.js +18 -0
- package/test/javascript-dialog.test.js +83 -0
- package/test/manual-breakout-paddle.js +42 -0
- package/test/manual-breakout-static.js +35 -0
- package/test/manual-classic-games.js +41 -0
- package/test/manual-clumsy-bird-three-pipes.js +81 -0
- package/test/manual-clumsy-bird.js +52 -0
- package/test/manual-hextris.js +30 -0
- package/test/manual-numbered-music.js +24 -0
- package/test/manual-t-rex-runner.js +49 -0
- package/test/manual-threejs-scene.js +64 -0
- package/test/manual-webgl-benchmark.js +27 -0
- package/test/manual-webgl-cpu.html +26 -0
- package/test/media-audio-lifetime.test.js +8 -0
- package/test/mp4-frame-times.test.js +33 -0
- package/test/page-scripts.test.js +96 -1
- package/test/piano-input.test.js +15 -2
- package/test/piano.test.js +11 -2
- package/test/playback-buffer.test.js +11 -0
- package/test/pointer-drag.test.js +33 -0
- package/test/positioning.test.js +82 -0
- package/test/pulse-client.test.js +51 -2
- package/test/raster-readback.test.js +14 -0
- package/test/script-rewrite.test.js +61 -3
- package/test/video-slow-end.test.js +6 -0
- package/test/viewport-keyboard-overflow.test.js +25 -0
- package/test/wayland-win32-resources.test.js +47 -0
- package/test/wayland-win32-wire.test.js +59 -0
- package/test/web-audio-renderer.test.js +8 -0
- package/test/web-audio.test.js +264 -0
- package/test/webgl-clip.test.js +9 -0
- package/test/webgl-cpu-fragment.test.js +43 -0
- package/test/window-named-properties.test.js +41 -0
- package/vendor/happy-dom/packages/happy-dom/src/config/HTMLElementConfig.ts +2 -2
- package/vendor/happy-dom/packages/happy-dom/src/config/HTMLElementConfigContentModelEnum.ts +1 -0
- package/vendor/happy-dom/packages/happy-dom/src/html-parser/HTMLParser.ts +23 -3
- package/vendor/happy-dom/packages/happy-dom/src/query-selector/SelectorParser.ts +1 -1
- package/vendor/land-renderer/LICENSE +21 -0
- package/vendor/land-renderer/README.md +14 -0
- package/vendor/land-renderer/direct-compositor.ts +443 -0
- package/vendor/land-renderer/frame.js +88 -0
- package/vendor/land-renderer/png-fast.js +85 -0
- package/vendor/land-renderer/raw-frames.js +66 -0
- package/vendor/land-renderer/render-types.ts +7 -0
- package/vendor/land-renderer/renderer.ts +134 -0
- package/vendor/land-renderer/snapshot.js +37 -0
- package/vendor/land-renderer/viewport.js +18 -0
- package/vendor/ses/dist/ses.mjs +1 -1
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import {installPagePrimitiveBuiltins} from './page-builtins.js';
|
|
2
|
+
import {resolveAffine} from '../style/transform.js';
|
|
3
|
+
import { drainMicrotasks } from "bun:jsc";
|
|
4
|
+
import {fileReaderInterface} from './file-input.js';
|
|
5
|
+
import { evalEnvironment, declareEvalBinding } from "./eval-environment.js";
|
|
6
|
+
import { windowNamedProperties } from "./window-named-properties.js";
|
|
1
7
|
import {selectionBinding} from './document-selection.js';
|
|
2
8
|
/**
|
|
3
9
|
* The JavaScript environment of one document.
|
|
@@ -17,14 +23,14 @@ import {
|
|
|
17
23
|
} from "./bindings.js";
|
|
18
24
|
import { resolveModuleSpecifier, transformModule } from "./module-transform.js";
|
|
19
25
|
import {
|
|
20
|
-
CONSTRUCTOR_HELPER, DIRECT_EVAL_HELPER, DIRECT_EVAL_STRICT, escapeSesHtmlComments, prepareDirectEvalSource,
|
|
26
|
+
MEMBER_HELPER, CONSTRUCTOR_HELPER, DIRECT_EVAL_HELPER, DIRECT_EVAL_ENV, MAKE_EVAL_ENV, evalDeclarations, DIRECT_EVAL_STRICT, escapeSesHtmlComments, prepareDirectEvalSource,
|
|
21
27
|
DIRECT_EVAL_SCOPE_HELPER, REFERENCE_HELPER, referenceHelper, restoreSourceText, THIS_HELPER,
|
|
22
|
-
rewriteClassicScriptSource, rewriteConstructorReads, rewriteModuleSource,
|
|
28
|
+
rewriteClassicScriptSource, rewriteConstructorReads, rewriteMemberReads, rewriteModuleSource,
|
|
23
29
|
} from "./script-rewrite.js";
|
|
24
30
|
import { prepareClassicScript } from "./prepared-script-cache.js";
|
|
25
31
|
import { computeElementStyle, mediaQueryMatches, resolveVariables } from "../style/computed-style.js";
|
|
26
32
|
import { asIncumbent, createMessaging } from "./messaging.js";
|
|
27
|
-
import { canvasContext, resetCanvas, disposeDocumentCanvases, encodeCanvas } from "./canvas-2d.js";
|
|
33
|
+
import { canvasRenderingContextInterface, canvasContext, resetCanvas, disposeDocumentCanvases, encodeCanvas } from "./canvas-2d.js";
|
|
28
34
|
import { createWorkers } from "./workers.js";
|
|
29
35
|
import { createTimers } from "./timers.js";
|
|
30
36
|
import { createAnimationFrames, supportsFrameCheckpoint } from "./animation-frames.js";
|
|
@@ -37,6 +43,7 @@ import { installAttributeReactions } from "./custom-element-reactions.js";
|
|
|
37
43
|
import { createLayoutObservers } from "./layout-observers.js";
|
|
38
44
|
import { createIdleCallbacks } from "./idle-callbacks.js";
|
|
39
45
|
import { createBroadcastChannels } from "./broadcast-channel.js";
|
|
46
|
+
import { createWebAudioScope } from "../audio/web-audio.js";
|
|
40
47
|
import { createInertWindow } from "../happy-dom/parser.js";
|
|
41
48
|
import { historyBinding } from "./history.js";
|
|
42
49
|
import { installImageLoadingAttributes } from "./image-loading.js";
|
|
@@ -182,6 +189,7 @@ const INTERFACE_MEMBERS = {
|
|
|
182
189
|
options: ["HTMLSelectElement", "HTMLDataListElement"], selectedOptions: ["HTMLSelectElement"], selectedIndex: ["HTMLSelectElement"],
|
|
183
190
|
value: [...FORM_CONTROLS, "HTMLOptionElement", "HTMLOutputElement", "HTMLDataElement", "HTMLLIElement", "HTMLMeterElement",
|
|
184
191
|
"HTMLProgressElement", "HTMLParamElement"],
|
|
192
|
+
files: ["HTMLInputElement"], showPicker: ["HTMLInputElement"],
|
|
185
193
|
checked: ["HTMLInputElement"], selected: ["HTMLOptionElement"],
|
|
186
194
|
disabled: [...FORM_CONTROLS, "HTMLOptionElement", "HTMLOptGroupElement", "HTMLFieldSetElement", "HTMLLinkElement", "HTMLStyleElement"],
|
|
187
195
|
type: [...FORM_CONTROLS, "HTMLScriptElement", "HTMLLinkElement", "HTMLOListElement", "HTMLUListElement", "HTMLLIElement",
|
|
@@ -308,6 +316,7 @@ export function createPageRealm({ window, document, mode, hooks }) {
|
|
|
308
316
|
};
|
|
309
317
|
let parsingHead = false;
|
|
310
318
|
let currentScript = null;
|
|
319
|
+
let taskDepth = 0, checkingMicrotasks = false;
|
|
311
320
|
let pageGlobal = null;
|
|
312
321
|
let evaluate;
|
|
313
322
|
// Runs a classic script already prepared off the main thread (SES mode only).
|
|
@@ -333,6 +342,7 @@ export function createPageRealm({ window, document, mode, hooks }) {
|
|
|
333
342
|
/** Runs page code as a task: errors are reported, then the renderer may update. */
|
|
334
343
|
const runTask = (task) => {
|
|
335
344
|
if (disposed) return undefined;
|
|
345
|
+
taskDepth++;
|
|
336
346
|
if (mediaScope) blobUrlFallback = mediaScope;
|
|
337
347
|
try {
|
|
338
348
|
return blobUrlContext.run(mediaScope, () => asIncumbent(hooks.frames?.self, task));
|
|
@@ -340,6 +350,14 @@ export function createPageRealm({ window, document, mode, hooks }) {
|
|
|
340
350
|
reportError(error);
|
|
341
351
|
return undefined;
|
|
342
352
|
} finally {
|
|
353
|
+
// HTML's clean-up-after-running-script checkpoint happens before
|
|
354
|
+
// restoring currentScript. Nested script calls must not drain early.
|
|
355
|
+
taskDepth--;
|
|
356
|
+
if (!taskDepth && !checkingMicrotasks && supportsFrameCheckpoint()) {
|
|
357
|
+
checkingMicrotasks = true;
|
|
358
|
+
try { drainMicrotasks(); }
|
|
359
|
+
finally { checkingMicrotasks = false; }
|
|
360
|
+
}
|
|
343
361
|
hooks.afterTask();
|
|
344
362
|
}
|
|
345
363
|
};
|
|
@@ -355,12 +373,14 @@ export function createPageRealm({ window, document, mode, hooks }) {
|
|
|
355
373
|
}
|
|
356
374
|
};
|
|
357
375
|
|
|
376
|
+
const inlineHandlerSources = new WeakMap();
|
|
358
377
|
let documentSelection;
|
|
359
378
|
const realm = new BindingRealm(window, {
|
|
360
379
|
touch: (node, features) => {
|
|
361
380
|
if (!node || node === document || node.ownerDocument === document) hooks.touch(node, features);
|
|
362
381
|
},
|
|
363
382
|
boundsOf: hooks.boundsOf,
|
|
383
|
+
mouseOffset: hooks.mouseOffset,
|
|
364
384
|
computedStyle: hooks.computedStyle,
|
|
365
385
|
// The engine clock's reading when this document's timeline started (see pageNow).
|
|
366
386
|
timeOriginOffset: () => documentStarted,
|
|
@@ -381,6 +401,8 @@ export function createPageRealm({ window, document, mode, hooks }) {
|
|
|
381
401
|
mediaError: (element) => hooks.mediaError?.(element) ?? null,
|
|
382
402
|
mediaBuffered: (element) => hooks.mediaBuffered?.(element) ?? { length: 0, start: 0, end: 0 },
|
|
383
403
|
call: callPage,
|
|
404
|
+
prepareEvent: prepareInlineEvent,
|
|
405
|
+
prepareHandler: prepareInlineHandler,
|
|
384
406
|
readyState: () => readyState,
|
|
385
407
|
body: () => parsingHead ? null : document.body,
|
|
386
408
|
documentCookie: () => hooks.documentCookie?.() ?? "",
|
|
@@ -392,6 +414,9 @@ export function createPageRealm({ window, document, mode, hooks }) {
|
|
|
392
414
|
if (element.tagName === "SCRIPT") createdScripts.add(element);
|
|
393
415
|
},
|
|
394
416
|
inserted: (nodes) => startInsertedScripts(nodes),
|
|
417
|
+
execCommand: hooks.execCommand,
|
|
418
|
+
copyEnabled: hooks.copyEnabled,
|
|
419
|
+
pickFile: (element)=>hooks.pickFile?.(element),
|
|
395
420
|
activateLink: (anchor) => hooks.activateLink?.(anchor),
|
|
396
421
|
submitForm: (form, submitter) => hooks.submitForm?.(form, submitter),
|
|
397
422
|
contentWindow: (node) => messaging.contentWindow(node),
|
|
@@ -406,6 +431,10 @@ export function createPageRealm({ window, document, mode, hooks }) {
|
|
|
406
431
|
const documentBinding = realm.wrap(document);
|
|
407
432
|
const DOMStringMap = function DOMStringMap() { throw new TypeError("Illegal constructor"); };
|
|
408
433
|
realm.datasetPrototype = DOMStringMap.prototype;
|
|
434
|
+
const DOMTokenList = function DOMTokenList() { throw new TypeError('Illegal constructor'); };
|
|
435
|
+
DOMTokenList.prototype=mutableBindingPrototype(INTERFACES.DOMTokenList.prototype,Object.prototype);
|
|
436
|
+
Object.defineProperty(DOMTokenList.prototype,'constructor',{value:DOMTokenList,writable:true,configurable:true});
|
|
437
|
+
realm.tokenListPrototype=DOMTokenList.prototype;
|
|
409
438
|
const validityMembers = {};
|
|
410
439
|
for (const key of ["badInput", "customError", "patternMismatch", "rangeOverflow", "rangeUnderflow", "stepMismatch", "tooLong", "tooShort", "typeMismatch", "valid", "valueMissing"]) {
|
|
411
440
|
Object.defineProperty(validityMembers, key, { get() { return Boolean(this[key]); }, enumerable: true });
|
|
@@ -416,7 +445,7 @@ export function createPageRealm({ window, document, mode, hooks }) {
|
|
|
416
445
|
// Node.prototype inherits EventTarget.prototype (DOM §4.4), as Window.prototype does.
|
|
417
446
|
NodeInterface.prototype = mutableBindingPrototype(INTERFACES.Node.prototype, eventTargets.EventTarget.prototype);
|
|
418
447
|
const WindowInterface = function Window() { throw new TypeError("Illegal constructor"); };
|
|
419
|
-
WindowInterface.prototype = Object.create(eventTargets.EventTarget.prototype);
|
|
448
|
+
WindowInterface.prototype = Object.create(windowNamedProperties(document, realm, eventTargets.EventTarget.prototype));
|
|
420
449
|
Object.defineProperty(WindowInterface.prototype, "constructor", { value: WindowInterface, writable: true, configurable: true });
|
|
421
450
|
Object.defineProperty(WindowInterface, Symbol.hasInstance, { value: (value) => value === pageGlobal });
|
|
422
451
|
Object.defineProperty(NodeInterface.prototype, "constructor", { value: NodeInterface, writable: true, configurable: true });
|
|
@@ -426,6 +455,11 @@ export function createPageRealm({ window, document, mode, hooks }) {
|
|
|
426
455
|
}
|
|
427
456
|
realm.nodePrototype = NodeInterface.prototype;
|
|
428
457
|
const parentPrototype = mutableBindingPrototype(Object.getPrototypeOf(INTERFACES.Element.prototype), NodeInterface.prototype);
|
|
458
|
+
const elementDocumentCollections={};
|
|
459
|
+
for(const name of ['getElementsByTagName','getElementsByTagNameNS','getElementsByClassName']) {
|
|
460
|
+
elementDocumentCollections[name]=Object.getOwnPropertyDescriptor(parentPrototype,name);
|
|
461
|
+
delete parentPrototype[name];
|
|
462
|
+
}
|
|
429
463
|
// The ParentNode mixin's members (children, querySelector, append, ...) are
|
|
430
464
|
// own properties of Element.prototype, Document.prototype and
|
|
431
465
|
// DocumentFragment.prototype, as WebIDL places them: ShadyDOM saves the
|
|
@@ -440,6 +474,7 @@ export function createPageRealm({ window, document, mode, hooks }) {
|
|
|
440
474
|
};
|
|
441
475
|
const ElementInterface = function Element() { throw new TypeError("Illegal constructor"); };
|
|
442
476
|
ElementInterface.prototype = withParentNodeMembers(mutableBindingPrototype(INTERFACES.Element.prototype, parentPrototype));
|
|
477
|
+
Object.defineProperties(ElementInterface.prototype,elementDocumentCollections);
|
|
443
478
|
Object.defineProperty(ElementInterface.prototype, "constructor", { value: ElementInterface, writable: true, configurable: true });
|
|
444
479
|
const HTMLElementInterface = function HTMLElement() {
|
|
445
480
|
if (!new.target || !constructingElement) throw new TypeError("Illegal constructor");
|
|
@@ -573,6 +608,7 @@ export function createPageRealm({ window, document, mode, hooks }) {
|
|
|
573
608
|
// getContext("2d") is how pages sample colours and draw. Other elements do
|
|
574
609
|
// not inherit these.
|
|
575
610
|
realm.selection=()=>documentSelection ??= selectionBinding(window,realm,nodeOf,()=>hooks.selectionChanged?.());
|
|
611
|
+
const CanvasRenderingContext2D=canvasRenderingContextInterface();
|
|
576
612
|
const canvasPrototype = elementInterfacePrototypes.get("HTMLCanvasElement");
|
|
577
613
|
for (const name of ["width", "height"]) {
|
|
578
614
|
Object.defineProperty(canvasPrototype, name, {
|
|
@@ -595,6 +631,7 @@ export function createPageRealm({ window, document, mode, hooks }) {
|
|
|
595
631
|
writable: true,
|
|
596
632
|
value(type) {
|
|
597
633
|
return canvasContext(nodeOf(this), String(type), {
|
|
634
|
+
prototype:CanvasRenderingContext2D.prototype,
|
|
598
635
|
canvas: this, resolveImage: source => hooks.canvasImage?.(nodeOf(source)),
|
|
599
636
|
parseFont: value => hooks.canvasFont?.(nodeOf(this), value),
|
|
600
637
|
text: request => hooks.canvasText?.(nodeOf(this), request),
|
|
@@ -689,6 +726,7 @@ export function createPageRealm({ window, document, mode, hooks }) {
|
|
|
689
726
|
realm.shadowRootPrototype = mutableBindingPrototype(INTERFACES.ShadowRoot.prototype, DocumentFragmentInterface.prototype);
|
|
690
727
|
const DocumentInterface = function Document() { throw new TypeError("Illegal constructor"); };
|
|
691
728
|
DocumentInterface.prototype = withParentNodeMembers(mutableBindingPrototype(INTERFACES.Document.prototype, parentPrototype));
|
|
729
|
+
Object.defineProperties(DocumentInterface.prototype,elementDocumentCollections);
|
|
692
730
|
Object.defineProperty(DocumentInterface.prototype, "constructor", { value: DocumentInterface, writable: true, configurable: true });
|
|
693
731
|
Object.setPrototypeOf(documentBinding, DocumentInterface.prototype);
|
|
694
732
|
realm.documentPrototype = DocumentInterface.prototype;
|
|
@@ -757,6 +795,7 @@ export function createPageRealm({ window, document, mode, hooks }) {
|
|
|
757
795
|
const animationFrames = supportsFrameCheckpoint() ? createAnimationFrames({
|
|
758
796
|
invoke: (callback, timestamp) => runTask(() => callPage(callback, pageGlobal, [timestamp])),
|
|
759
797
|
now: pageNow,
|
|
798
|
+
afterFrame: () => hooks.afterAnimationFrame?.(),
|
|
760
799
|
}) : null;
|
|
761
800
|
const idleCallbacks = createIdleCallbacks({
|
|
762
801
|
now: () => hostPerformance.now(), setTimer: hostSetTimeout, clearTimer: hostClearTimeout,
|
|
@@ -1420,6 +1459,7 @@ export function createPageRealm({ window, document, mode, hooks }) {
|
|
|
1420
1459
|
// A plain function: Web IDL operations are not async functions.
|
|
1421
1460
|
fetch: function fetch(input, init = undefined) { return pageFetch(input, init); },
|
|
1422
1461
|
XMLHttpRequest,
|
|
1462
|
+
FileReader:fileReaderInterface({EventTarget:eventTargets.EventTarget,realm,window,runTask}),
|
|
1423
1463
|
WebSocket,
|
|
1424
1464
|
Headers: PageHeaders,
|
|
1425
1465
|
Request: PageRequest,
|
|
@@ -1589,7 +1629,11 @@ export function createPageRealm({ window, document, mode, hooks }) {
|
|
|
1589
1629
|
return Promise.resolve(values);
|
|
1590
1630
|
},
|
|
1591
1631
|
};
|
|
1592
|
-
installImageLoadingAttributes(elementInterfacePrototypes.get("HTMLImageElement"), nodeOf)
|
|
1632
|
+
installImageLoadingAttributes(elementInterfacePrototypes.get("HTMLImageElement"), nodeOf, node=>hooks.touch(node), node=>{
|
|
1633
|
+
// Inline replaced images can live in text fragments rather than boxes.
|
|
1634
|
+
const rect=hooks.layoutBounds?.(node)??hooks.boundsOf(node);if(!rect)return null;
|
|
1635
|
+
const style=hooks.computedStyle?.(node);return style?.display==='none'?null:rect;
|
|
1636
|
+
});
|
|
1593
1637
|
const { location, setHref } = locationBinding(document.URL, hooks.navigate);
|
|
1594
1638
|
const { History, history, pushEntry } = historyBinding(location, setHref);
|
|
1595
1639
|
const { Event, CustomEvent, ...uiEvents } = eventConstructors(realm);
|
|
@@ -1606,6 +1650,11 @@ export function createPageRealm({ window, document, mode, hooks }) {
|
|
|
1606
1650
|
EventTarget: eventTargets.EventTarget, realm, runTask, DOMException,
|
|
1607
1651
|
});
|
|
1608
1652
|
|
|
1653
|
+
const webAudio = createWebAudioScope({
|
|
1654
|
+
audio: hooks.audio, decodeAudio: hooks.decodeAudio, EventTarget: eventTargets.EventTarget, Event, DOMException,
|
|
1655
|
+
schedule: fn => hostSetTimeout(() => runTask(fn), 0), reportError,
|
|
1656
|
+
});
|
|
1657
|
+
|
|
1609
1658
|
const platform = platformObjects({
|
|
1610
1659
|
eventTargets, userAgent, userAgentDataMembers: chromeVersion ? userAgentDataMembers : null, mobile: mobileUserAgent, viewport: hooks.viewport,
|
|
1611
1660
|
performance: userTiming.performance, storages: { local: hostStorage.get(origin), session: new Map() },
|
|
@@ -1636,6 +1685,7 @@ export function createPageRealm({ window, document, mode, hooks }) {
|
|
|
1636
1685
|
|
|
1637
1686
|
/** Web APIs placed on the page's global object (bindings plus hardened host utilities). */
|
|
1638
1687
|
const api = {
|
|
1688
|
+
...webAudio.api,
|
|
1639
1689
|
document: documentBinding,
|
|
1640
1690
|
location,
|
|
1641
1691
|
navigator: platform.navigator,
|
|
@@ -1666,6 +1716,7 @@ export function createPageRealm({ window, document, mode, hooks }) {
|
|
|
1666
1716
|
return idleCallbacks.request(nextTimerId++, callback, timeout);
|
|
1667
1717
|
},
|
|
1668
1718
|
cancelIdleCallback: (handle) => idleCallbacks.cancel(Number(handle)),
|
|
1719
|
+
CanvasRenderingContext2D,
|
|
1669
1720
|
queueMicrotask: (callback) => hostQueueMicrotask(() => runTask(() => callPage(callback, pageGlobal, []))),
|
|
1670
1721
|
structuredClone: (value) => hostStructuredClone(value),
|
|
1671
1722
|
atob: (value) => hostAtob(String(value)),
|
|
@@ -1783,6 +1834,7 @@ export function createPageRealm({ window, document, mode, hooks }) {
|
|
|
1783
1834
|
},
|
|
1784
1835
|
},
|
|
1785
1836
|
...INTERFACES,
|
|
1837
|
+
DOMTokenList,
|
|
1786
1838
|
DOMStringMap,
|
|
1787
1839
|
Node: NodeInterface,
|
|
1788
1840
|
Text: TextInterface,
|
|
@@ -1808,7 +1860,7 @@ export function createPageRealm({ window, document, mode, hooks }) {
|
|
|
1808
1860
|
const style = hooks.computedStyle(node, needsLayout);
|
|
1809
1861
|
return {
|
|
1810
1862
|
style, custom: hooks.customProperties?.(node, false) ?? null,
|
|
1811
|
-
bounds: needsLayout && style && style.display !== "none" && node?.isConnected ? hooks.boundsOf(node) : null,
|
|
1863
|
+
bounds: needsLayout && style && style.display !== "none" && node?.isConnected ? (hooks.layoutBounds?.(node) ?? hooks.boundsOf(node)) : null,
|
|
1812
1864
|
element: node,
|
|
1813
1865
|
};
|
|
1814
1866
|
});
|
|
@@ -1829,9 +1881,9 @@ export function createPageRealm({ window, document, mode, hooks }) {
|
|
|
1829
1881
|
scrollTo: (x = undefined, y = undefined) => scrollArguments(hooks, x, y, false),
|
|
1830
1882
|
scroll: (x = undefined, y = undefined) => scrollArguments(hooks, x, y, false),
|
|
1831
1883
|
scrollBy: (x = undefined, y = undefined) => scrollArguments(hooks, x, y, true),
|
|
1832
|
-
alert: () => {},
|
|
1833
|
-
confirm: () =>
|
|
1834
|
-
prompt: () => null,
|
|
1884
|
+
alert: (message = "") => { hooks.dialog?.("alert", String(message), ""); },
|
|
1885
|
+
confirm: (message = "") => Boolean(hooks.dialog?.("confirm", String(message), "")),
|
|
1886
|
+
prompt: (message = "", value = "") => hooks.dialog?.("prompt", String(message), String(value)) ?? null,
|
|
1835
1887
|
print: () => {},
|
|
1836
1888
|
focus: () => {},
|
|
1837
1889
|
blur: () => {},
|
|
@@ -1918,7 +1970,58 @@ export function createPageRealm({ window, document, mode, hooks }) {
|
|
|
1918
1970
|
});
|
|
1919
1971
|
Object.defineProperty(PagePromise, "length", { value: 1, configurable: true });
|
|
1920
1972
|
pageGlobal.Promise = PagePromise;
|
|
1921
|
-
|
|
1973
|
+
// Collection polyfills may cache inherited methods on individual instances.
|
|
1974
|
+
// Mutable page-local prototypes avoid SES's frozen-inheritance assignment
|
|
1975
|
+
// trap without changing shared intrinsics or their internal slot checks.
|
|
1976
|
+
for (const name of ['Map', 'Set', 'WeakMap', 'WeakSet']) {
|
|
1977
|
+
const Base = pageGlobal[name];
|
|
1978
|
+
const Local = compartment.evaluate(`(class ${name} extends globalThis.${name} {})`);
|
|
1979
|
+
for (const key of Reflect.ownKeys(Base.prototype)) {
|
|
1980
|
+
if (key === 'constructor') continue;
|
|
1981
|
+
const descriptor = {...Object.getOwnPropertyDescriptor(Base.prototype, key), configurable:true};
|
|
1982
|
+
if ('value' in descriptor) descriptor.writable = true;
|
|
1983
|
+
Object.defineProperty(Local.prototype, key, descriptor);
|
|
1984
|
+
}
|
|
1985
|
+
Object.defineProperty(Local,'length',{value:0,configurable:true});
|
|
1986
|
+
pageGlobal[name] = Local;
|
|
1987
|
+
}
|
|
1988
|
+
|
|
1989
|
+
// Polyfills extend Array's unscopables table even when methods already exist.
|
|
1990
|
+
// Keep this mutable metadata local to the page, not SES's shared prototype.
|
|
1991
|
+
const BaseArray = pageGlobal.Array;
|
|
1992
|
+
const PageArray = compartment.evaluate('((Base) => function Array(...args) { return Reflect.construct(Base, args, new.target || Array); })(Array)');
|
|
1993
|
+
const arrayPrototype=[];
|
|
1994
|
+
for(const key of Reflect.ownKeys(BaseArray.prototype)) {
|
|
1995
|
+
if(key==='constructor'||key==='length')continue;
|
|
1996
|
+
const original=Object.getOwnPropertyDescriptor(BaseArray.prototype,key);
|
|
1997
|
+
const value=key===Symbol.unscopables?{...BaseArray.prototype[key]}:Reflect.get(BaseArray.prototype,key);
|
|
1998
|
+
Object.defineProperty(arrayPrototype,key,{value,writable:true,configurable:true,enumerable:original.enumerable});
|
|
1999
|
+
}
|
|
2000
|
+
Object.defineProperty(arrayPrototype,'constructor',{value:PageArray,writable:true,configurable:true});
|
|
2001
|
+
Object.defineProperty(PageArray,'prototype',{value:arrayPrototype});
|
|
2002
|
+
for(const key of Reflect.ownKeys(BaseArray))if(!['name','length','prototype'].includes(key))
|
|
2003
|
+
Object.defineProperty(PageArray,key,{...Object.getOwnPropertyDescriptor(BaseArray,key),configurable:true});
|
|
2004
|
+
Object.defineProperty(PageArray,Symbol.hasInstance,{value:value=>Array.isArray(value),configurable:true});
|
|
2005
|
+
pageGlobal.Array=PageArray;
|
|
2006
|
+
const BaseRegExp = pageGlobal.RegExp;
|
|
2007
|
+
const PageRegExp = function RegExp(pattern, flags) {
|
|
2008
|
+
const object=pattern!==null&&(typeof pattern==='object'||typeof pattern==='function');
|
|
2009
|
+
const matches=object?pattern[Symbol.match]:undefined;
|
|
2010
|
+
const isRegExp=matches!==undefined?Boolean(matches):object&&Object.prototype.toString.call(pattern)==='[object RegExp]';
|
|
2011
|
+
if(!new.target&&flags===undefined&&isRegExp&&(pattern.constructor===BaseRegExp||pattern.constructor===PageRegExp))return pattern;
|
|
2012
|
+
return Reflect.construct(BaseRegExp,[pattern,flags],new.target||BaseRegExp);
|
|
2013
|
+
};
|
|
2014
|
+
Object.defineProperty(PageRegExp,'prototype',{value:BaseRegExp.prototype});
|
|
2015
|
+
for(const key of Reflect.ownKeys(BaseRegExp))if(!['name','length','prototype'].includes(key))
|
|
2016
|
+
Object.defineProperty(PageRegExp,key,Object.getOwnPropertyDescriptor(BaseRegExp,key));
|
|
2017
|
+
pageGlobal.RegExp=PageRegExp;
|
|
2018
|
+
const BaseSymbol=pageGlobal.Symbol;
|
|
2019
|
+
const PageSymbol={Symbol(description){return arguments.length?BaseSymbol(description):BaseSymbol();}}.Symbol;
|
|
2020
|
+
for(const key of Reflect.ownKeys(BaseSymbol))if(!['name','length'].includes(key))
|
|
2021
|
+
Object.defineProperty(PageSymbol,key,{...Object.getOwnPropertyDescriptor(BaseSymbol,key),configurable:key!=='prototype'});
|
|
2022
|
+
pageGlobal.Symbol=PageSymbol;
|
|
2023
|
+
for(const name of ['Error','EvalError','RangeError','ReferenceError','SyntaxError','TypeError','URIError'])
|
|
2024
|
+
pageGlobal[name] = errorInterface(pageGlobal[name], hostErrorIsError);
|
|
1922
2025
|
// Namespace objects that libraries extend (reflect-metadata adds to
|
|
1923
2026
|
// Reflect, polyfills to JSON and Math): each page gets its own mutable
|
|
1924
2027
|
// copy with the same functions; the shared frozen intrinsics stay as they are.
|
|
@@ -1933,7 +2036,9 @@ export function createPageRealm({ window, document, mode, hooks }) {
|
|
|
1933
2036
|
value: Object.create(Object.getPrototypeOf(shared), descriptors), writable: true, configurable: true,
|
|
1934
2037
|
});
|
|
1935
2038
|
}
|
|
1936
|
-
const
|
|
2039
|
+
const member = installPagePrimitiveBuiltins(compartment, pageGlobal);
|
|
2040
|
+
Object.defineProperty(pageGlobal, MEMBER_HELPER, {value: member});
|
|
2041
|
+
const helper = constructorHelper(compartment, member);
|
|
1937
2042
|
Object.defineProperty(pageGlobal, CONSTRUCTOR_HELPER, { value: helper });
|
|
1938
2043
|
// Indirect eval runs code as a classic script does: in the global scope,
|
|
1939
2044
|
// its var and function declarations becoming globals, and its own direct
|
|
@@ -1943,6 +2048,7 @@ export function createPageRealm({ window, document, mode, hooks }) {
|
|
|
1943
2048
|
return code === undefined ? source : evaluate(code);
|
|
1944
2049
|
} }.eval;
|
|
1945
2050
|
Object.defineProperty(pageGlobal, "eval", { value: markNative(pageEval, "eval"), writable: true, configurable: true });
|
|
2051
|
+
Object.defineProperty(pageGlobal, MAKE_EVAL_ENV, { value: parent => evalEnvironment(pageGlobal,parent) });
|
|
1946
2052
|
Object.defineProperty(pageGlobal, DIRECT_EVAL_HELPER, { value: directEvalHelper(compartment, pageGlobal, trustedTypes.scriptText) });
|
|
1947
2053
|
// A sloppy-mode function called without a receiver must see the page
|
|
1948
2054
|
// global, never the engine's: the rewrite routes every such `this` here.
|
|
@@ -1973,7 +2079,8 @@ export function createPageRealm({ window, document, mode, hooks }) {
|
|
|
1973
2079
|
return evaluatePrepared(prepareClassicScript(source));
|
|
1974
2080
|
};
|
|
1975
2081
|
// Module code is already strict with its own scope; no global lifting.
|
|
1976
|
-
evaluateModuleCode = (code) => compartment.evaluate(escapeSesHtmlComments(code, "
|
|
2082
|
+
evaluateModuleCode = (code) => compartment.evaluate(escapeSesHtmlComments(code, "script"), {
|
|
2083
|
+
__evadeHtmlCommentTest__: true,
|
|
1977
2084
|
__evadeImportExpressionTest__: true,
|
|
1978
2085
|
__rejectSomeDirectEvalExpressions__: false,
|
|
1979
2086
|
});
|
|
@@ -2029,6 +2136,8 @@ export function createPageRealm({ window, document, mode, hooks }) {
|
|
|
2029
2136
|
// Before any page script: the Web APIs print and name themselves as built-ins,
|
|
2030
2137
|
// and the global has a browser's tags and enumerability.
|
|
2031
2138
|
markNativeApi(pageGlobal);
|
|
2139
|
+
// The legacy Web Audio alias keeps the standard constructor's name.
|
|
2140
|
+
markNative(webAudio.api.AudioContext, "AudioContext");
|
|
2032
2141
|
applyGlobalShape(pageGlobal, [Object.prototype, Function.prototype, Array.prototype, Error.prototype,
|
|
2033
2142
|
Promise.prototype, pageGlobal.Promise?.prototype, RegExp.prototype, Date.prototype, Map.prototype, Set.prototype]);
|
|
2034
2143
|
|
|
@@ -2246,30 +2355,31 @@ export function createPageRealm({ window, document, mode, hooks }) {
|
|
|
2246
2355
|
}
|
|
2247
2356
|
}
|
|
2248
2357
|
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
binding[`on${type}`] = handler;
|
|
2265
|
-
continue;
|
|
2266
|
-
}
|
|
2267
|
-
window.addEventListener(type, (event) => {
|
|
2268
|
-
const result = callPage(handler, binding, [realm.wrapEvent(event)]);
|
|
2269
|
-
if (result === false) event.preventDefault();
|
|
2270
|
-
});
|
|
2271
|
-
}
|
|
2358
|
+
// Compile lazily so innerHTML, cloned nodes and changed attributes work too.
|
|
2359
|
+
// Remember source rather than overwriting a later element.onclick assignment.
|
|
2360
|
+
function prepareInlineHandler(element, type) {
|
|
2361
|
+
if (!element?.getAttribute || !INLINE_HANDLER_EVENTS.includes(type)) return;
|
|
2362
|
+
const code = element.getAttribute(`on${type}`);
|
|
2363
|
+
let sources = inlineHandlerSources.get(element);
|
|
2364
|
+
if (sources?.has(type) && sources.get(type) === code) return;
|
|
2365
|
+
if (!sources) inlineHandlerSources.set(element, sources = new Map());
|
|
2366
|
+
sources.set(type, code);
|
|
2367
|
+
if (code === null && !sources.has(`installed:${type}`)) return;
|
|
2368
|
+
sources.set(`installed:${type}`, true);
|
|
2369
|
+
let handler = null;
|
|
2370
|
+
if (code !== null) {
|
|
2371
|
+
try { handler = evaluate(`(function (event) {\n${code}\n})`); }
|
|
2372
|
+
catch (error) { reportError(error); }
|
|
2272
2373
|
}
|
|
2374
|
+
const binding = type === 'load' && element === document.body ? pageGlobal : realm.wrap(element);
|
|
2375
|
+
binding[`on${type}`] = handler;
|
|
2376
|
+
}
|
|
2377
|
+
function prepareInlineEvent(target, type) {
|
|
2378
|
+
for (let node = target; node; node = node.parentNode) prepareInlineHandler(node, type);
|
|
2379
|
+
}
|
|
2380
|
+
function compileInlineHandlers() {
|
|
2381
|
+
for (const element of document.querySelectorAll('*'))
|
|
2382
|
+
for (const type of INLINE_HANDLER_EVENTS) prepareInlineHandler(element, type);
|
|
2273
2383
|
}
|
|
2274
2384
|
|
|
2275
2385
|
return {
|
|
@@ -2410,6 +2520,7 @@ export function createPageRealm({ window, document, mode, hooks }) {
|
|
|
2410
2520
|
dispatch(target, event) {
|
|
2411
2521
|
let prevented = false;
|
|
2412
2522
|
runTask(() => {
|
|
2523
|
+
prepareInlineEvent(target, event.type);
|
|
2413
2524
|
realm.markTrusted(event);
|
|
2414
2525
|
target.dispatchEvent(event);
|
|
2415
2526
|
prevented = event.defaultPrevented;
|
|
@@ -2475,6 +2586,7 @@ export function createPageRealm({ window, document, mode, hooks }) {
|
|
|
2475
2586
|
if (blobUrlFallback === mediaScope) blobUrlFallback = null;
|
|
2476
2587
|
messaging.dispose();
|
|
2477
2588
|
broadcasts.dispose();
|
|
2589
|
+
webAudio.dispose();
|
|
2478
2590
|
workers?.dispose();
|
|
2479
2591
|
// Leaving the document closes its WebSockets (1001 "going away").
|
|
2480
2592
|
for (const close of openSockets) close();
|
|
@@ -2572,7 +2684,7 @@ function scrollArguments(hooks, x, y, relative) {
|
|
|
2572
2684
|
* these stand-ins compile the same function text in the page's compartment
|
|
2573
2685
|
* and share the real prototypes, so instanceof and prototype checks hold.
|
|
2574
2686
|
*/
|
|
2575
|
-
function constructorHelper(compartment) {
|
|
2687
|
+
function constructorHelper(compartment, member) {
|
|
2576
2688
|
const kinds = [
|
|
2577
2689
|
[function () {}, "function"],
|
|
2578
2690
|
[async function () {}, "async function"],
|
|
@@ -2597,14 +2709,27 @@ function constructorHelper(compartment) {
|
|
|
2597
2709
|
__rejectSomeDirectEvalExpressions__: false,
|
|
2598
2710
|
});
|
|
2599
2711
|
};
|
|
2600
|
-
|
|
2712
|
+
const sharedPrototype = Object.getPrototypeOf(sample);
|
|
2713
|
+
const prototype = keyword === 'function' ? function () {} : sharedPrototype;
|
|
2714
|
+
if (prototype !== sharedPrototype) {
|
|
2715
|
+
for (const key of Reflect.ownKeys(sharedPrototype)) {
|
|
2716
|
+
if (key === 'constructor' || key === 'arguments' || key === 'caller') continue;
|
|
2717
|
+
const original=Object.getOwnPropertyDescriptor(sharedPrototype,key);
|
|
2718
|
+
const descriptor={value:Reflect.get(sharedPrototype,key),enumerable:original.enumerable,writable:true,configurable:true};
|
|
2719
|
+
Object.defineProperty(prototype,key,descriptor);
|
|
2720
|
+
}
|
|
2721
|
+
Object.defineProperty(prototype,'constructor',{value:Constructor,writable:true,configurable:true});
|
|
2722
|
+
Object.defineProperty(Constructor,Symbol.hasInstance,{value(value){return typeof value==='function';}});
|
|
2723
|
+
}
|
|
2724
|
+
Object.defineProperty(Constructor, "prototype", { value: prototype });
|
|
2601
2725
|
Object.defineProperty(Constructor, "name", { value: inert.name });
|
|
2602
2726
|
Object.defineProperty(Constructor, "length", { value: 1 });
|
|
2603
2727
|
replacements.set(inert, Constructor);
|
|
2604
2728
|
}
|
|
2605
2729
|
// call: `value.constructor(...)`, where an ordinary constructor keeps `value` as `this`.
|
|
2606
2730
|
const helper = (value, call = false) => {
|
|
2607
|
-
const
|
|
2731
|
+
const primitiveName={number:"Number",string:"String",boolean:"Boolean"}[typeof value];
|
|
2732
|
+
const constructor = primitiveName ? member(value,"constructor") : value.constructor;
|
|
2608
2733
|
const replacement = replacements.get(constructor);
|
|
2609
2734
|
if (replacement) return replacement;
|
|
2610
2735
|
return call && typeof constructor === "function" ? (...args) => Reflect.apply(constructor, value, args) : constructor;
|
|
@@ -2633,9 +2758,19 @@ function directEvalHelper(compartment, pageGlobal, scriptText) {
|
|
|
2633
2758
|
const source = typeof args[0] === "string" ? args[0] : scriptText(args[0]);
|
|
2634
2759
|
// Anything but a string or a TrustedScript is returned unchanged.
|
|
2635
2760
|
if (source === undefined) return args[0];
|
|
2761
|
+
const environment = scope[DIRECT_EVAL_ENV];
|
|
2762
|
+
const lowered = environment ? evalDeclarations(source, scope[DIRECT_EVAL_STRICT] === true) : {source,names:[]};
|
|
2763
|
+
if (environment) {
|
|
2764
|
+
for (const name of lowered.names) if (!Object.hasOwn(scope,name)) declareEvalBinding(environment,name);
|
|
2765
|
+
for (const name of new Set([...Object.keys(environment),...lowered.names])) {
|
|
2766
|
+
if (!Object.hasOwn(scope,name)) Object.defineProperty(scope,name,{
|
|
2767
|
+
get:()=>environment[name],set:value=>{environment[name]=value;},enumerable:true,configurable:true,
|
|
2768
|
+
});
|
|
2769
|
+
}
|
|
2770
|
+
}
|
|
2636
2771
|
const names = Object.keys(scope);
|
|
2637
|
-
const prepared = prepareDirectEvalSource(source, names, scope[DIRECT_EVAL_STRICT] === true);
|
|
2638
|
-
const code = escapeSesHtmlComments(rewriteConstructorReads(prepared.code).code);
|
|
2772
|
+
const prepared = prepareDirectEvalSource(lowered.source, names, scope[DIRECT_EVAL_STRICT] === true);
|
|
2773
|
+
const code = escapeSesHtmlComments(rewriteMemberReads(rewriteConstructorReads(prepared.code).code).code);
|
|
2639
2774
|
// The prepared code reaches the caller's variables only through the scope
|
|
2640
2775
|
// object, so every direct eval shares the compartment's sloppy evaluator.
|
|
2641
2776
|
const previous = currentScope;
|
|
@@ -3015,6 +3150,7 @@ function computedStyleSnapshot(style, { custom = null, bounds = null, element =
|
|
|
3015
3150
|
verticalAlign: typeof style.verticalAlign === "number" ? px(style.verticalAlign) : String(style.verticalAlign),
|
|
3016
3151
|
textOverflow: style.textOverflow,
|
|
3017
3152
|
boxSizing: style.boxSizing,
|
|
3153
|
+
aspectRatio: style.aspectRatio ? `${style.aspectRatio.auto ? "auto " : ""}${style.aspectRatio.numerator} / ${style.aspectRatio.denominator}` : "auto",
|
|
3018
3154
|
// A rendered box reads its used size; one that is not reads as computed.
|
|
3019
3155
|
width: bounds ? px(Math.max(0, bounds.width - (borderBox ? 0 : horizontal))) : length(style.width),
|
|
3020
3156
|
height: bounds ? px(Math.max(0, bounds.height - (borderBox ? 0 : vertical))) : length(style.height),
|
|
@@ -3039,6 +3175,7 @@ function computedStyleSnapshot(style, { custom = null, bounds = null, element =
|
|
|
3039
3175
|
rowGap: style.rowGapNormal ? "normal" : length(style.rowGap),
|
|
3040
3176
|
columnGap: style.columnGapNormal ? "normal" : length(style.columnGap),
|
|
3041
3177
|
transform: !transform ? "none"
|
|
3178
|
+
: transform.affine ? `matrix(${Object.values(resolveAffine(transform.affine,bounds?.width??0,bounds?.height??0)).join(", ")})`
|
|
3042
3179
|
: `matrix(${transform.scaleX ?? 1}, 0, 0, ${transform.scaleY ?? 1}, ${translate(transform.x, bounds?.width ?? 0)}, ${translate(transform.y, bounds?.height ?? 0)})`,
|
|
3043
3180
|
visibility: style.visibility ?? "visible",
|
|
3044
3181
|
opacity: String(style.opacity ?? 1),
|