diff --git a/Cargo.lock b/Cargo.lock
index 655b21708e4f06..36dc887fad29d6 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -192,6 +192,15 @@ version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
+[[package]]
+name = "approx"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6"
+dependencies = [
+ "num-traits",
+]
+
[[package]]
name = "arbitrary"
version = "1.3.2"
@@ -1772,6 +1781,16 @@ dependencies = [
"windows-sys 0.59.0",
]
+[[package]]
+name = "deno_geometry"
+version = "0.1.0"
+dependencies = [
+ "deno_core",
+ "deno_error",
+ "nalgebra",
+ "thiserror 2.0.3",
+]
+
[[package]]
name = "deno_graph"
version = "0.87.0"
@@ -2330,6 +2349,7 @@ dependencies = [
"deno_fetch",
"deno_ffi",
"deno_fs",
+ "deno_geometry",
"deno_http",
"deno_io",
"deno_kv",
@@ -5018,6 +5038,16 @@ version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
+[[package]]
+name = "matrixmultiply"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7574c1cf36da4798ab73da5b215bbf444f50718207754cb522201d78d1cd0ff2"
+dependencies = [
+ "autocfg",
+ "rawpointer",
+]
+
[[package]]
name = "md-5"
version = "0.10.6"
@@ -5175,6 +5205,21 @@ dependencies = [
"unicode-xid",
]
+[[package]]
+name = "nalgebra"
+version = "0.33.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26aecdf64b707efd1310e3544d709c5c0ac61c13756046aaaba41be5c4f66a3b"
+dependencies = [
+ "approx",
+ "matrixmultiply",
+ "num-complex",
+ "num-rational",
+ "num-traits",
+ "simba",
+ "typenum",
+]
+
[[package]]
name = "napi-build"
version = "1.2.1"
@@ -5357,6 +5402,15 @@ dependencies = [
"zeroize",
]
+[[package]]
+name = "num-complex"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6"
+dependencies = [
+ "num-traits",
+]
+
[[package]]
name = "num-conv"
version = "0.1.0"
@@ -5383,6 +5437,17 @@ dependencies = [
"num-traits",
]
+[[package]]
+name = "num-rational"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
+dependencies = [
+ "num-bigint",
+ "num-integer",
+ "num-traits",
+]
+
[[package]]
name = "num-traits"
version = "0.2.18"
@@ -6321,6 +6386,12 @@ version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8cc3bcbdb1ddfc11e700e62968e6b4cc9c75bb466464ad28fb61c5b2c964418b"
+[[package]]
+name = "rawpointer"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3"
+
[[package]]
name = "rayon"
version = "1.10.0"
@@ -6785,6 +6856,15 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad97d4ce1560a5e27cec89519dc8300d1aa6035b099821261c651486a19e44d5"
+[[package]]
+name = "safe_arch"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f398075ce1e6a179b46f51bd88d0598b92b00d3551f1a2d4ac49e771b56ac354"
+dependencies = [
+ "bytemuck",
+]
+
[[package]]
name = "saffron"
version = "0.1.0"
@@ -7112,6 +7192,19 @@ dependencies = [
"rand_core",
]
+[[package]]
+name = "simba"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b3a386a501cd104797982c15ae17aafe8b9261315b5d07e3ec803f2ea26be0fa"
+dependencies = [
+ "approx",
+ "num-complex",
+ "num-traits",
+ "paste",
+ "wide",
+]
+
[[package]]
name = "simd-abstraction"
version = "0.7.1"
@@ -9006,6 +9099,16 @@ dependencies = [
"web-sys",
]
+[[package]]
+name = "wide"
+version = "0.7.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81a1851a719f11d1d2fea40e15c72f6c00de8c142d7ac47c1441cc7e4d0d5bc6"
+dependencies = [
+ "bytemuck",
+ "safe_arch",
+]
+
[[package]]
name = "widestring"
version = "1.1.0"
diff --git a/Cargo.toml b/Cargo.toml
index 449a9ebc51bcbe..3067c39fbd5805 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -17,6 +17,7 @@ members = [
"ext/fetch",
"ext/ffi",
"ext/fs",
+ "ext/geometry",
"ext/http",
"ext/io",
"ext/kv",
@@ -81,6 +82,7 @@ deno_crypto = { version = "0.201.0", path = "./ext/crypto" }
deno_fetch = { version = "0.211.0", path = "./ext/fetch" }
deno_ffi = { version = "0.174.0", path = "./ext/ffi" }
deno_fs = { version = "0.97.0", path = "./ext/fs" }
+deno_geometry = { version = "0.1.0", path = "./ext/geometry" }
deno_http = { version = "0.185.0", path = "./ext/http" }
deno_io = { version = "0.97.0", path = "./ext/io" }
deno_kv = { version = "0.95.0", path = "./ext/kv" }
@@ -236,6 +238,10 @@ opentelemetry_sdk = "0.27.0"
hkdf = "0.12.3"
rsa = { version = "0.9.3", default-features = false, features = ["std", "pem", "hazmat"] } # hazmat needed for PrehashSigner in ext/node
+# geometry
+# TODO(petamoriken): Prefer to use glam as well as wgpu, but glam is not sufficient for mutable operations
+nalgebra = { version = "0.33.2", default-features = false, features = ["std"] }
+
# webgpu
raw-window-handle = "0.6.0"
wgpu-core = "0.21.1"
@@ -301,6 +307,8 @@ opt-level = 3
opt-level = 3
[profile.release.package.deno_ffi]
opt-level = 3
+[profile.release.package.deno_geometry]
+opt-level = 3
[profile.release.package.deno_http]
opt-level = 3
[profile.release.package.deno_napi]
diff --git a/cli/build.rs b/cli/build.rs
index 742f227ec9dc5c..6e1632ce0a8bb6 100644
--- a/cli/build.rs
+++ b/cli/build.rs
@@ -153,6 +153,7 @@ mod ts {
op_crate_libs.insert("deno.url", deno_url::get_declaration());
op_crate_libs.insert("deno.web", deno_web::get_declaration());
op_crate_libs.insert("deno.fetch", deno_fetch::get_declaration());
+ op_crate_libs.insert("deno.geometry", deno_geometry::get_declaration());
op_crate_libs.insert("deno.webgpu", deno_webgpu_get_declaration());
op_crate_libs.insert("deno.websocket", deno_websocket::get_declaration());
op_crate_libs.insert("deno.webstorage", deno_webstorage::get_declaration());
diff --git a/cli/tsc/dts/lib.deno.shared_globals.d.ts b/cli/tsc/dts/lib.deno.shared_globals.d.ts
index a469525270b588..d8ef7794055080 100644
--- a/cli/tsc/dts/lib.deno.shared_globals.d.ts
+++ b/cli/tsc/dts/lib.deno.shared_globals.d.ts
@@ -11,6 +11,7 @@
///
///
///
+///
///
///
///
diff --git a/cli/tsc/mod.rs b/cli/tsc/mod.rs
index 868610cb40a535..f8f69a009b5f31 100644
--- a/cli/tsc/mod.rs
+++ b/cli/tsc/mod.rs
@@ -102,6 +102,7 @@ pub fn get_types_declaration_file_text() -> String {
"deno.webstorage",
"deno.canvas",
"deno.crypto",
+ "deno.geometry",
"deno.broadcast_channel",
"deno.net",
"deno.shared_globals",
diff --git a/ext/geometry/00_init.js b/ext/geometry/00_init.js
new file mode 100644
index 00000000000000..ea5e20870f2c87
--- /dev/null
+++ b/ext/geometry/00_init.js
@@ -0,0 +1,27 @@
+// Copyright 2018-2025 the Deno authors. MIT license.
+
+import { core } from "ext:core/mod.js";
+
+const lazyLoad = core.createLazyLoader("ext:deno_geometry/01_geometry.js");
+
+let geometry;
+
+/**
+ * @param {(transformList: string, prefix: string) => { matrix: Float64Array, is2D: boolean }} transformListParser
+ * @param {boolean} enableWindowFeatures
+ */
+export function createGeometryLoader(
+ transformListParser,
+ enableWindowFeatures,
+) {
+ return () => {
+ if (geometry !== undefined) {
+ return geometry;
+ }
+
+ geometry = lazyLoad();
+ geometry.init(transformListParser, enableWindowFeatures);
+
+ return geometry;
+ };
+}
diff --git a/ext/geometry/01_geometry.js b/ext/geometry/01_geometry.js
new file mode 100644
index 00000000000000..81ed2c5f18c0db
--- /dev/null
+++ b/ext/geometry/01_geometry.js
@@ -0,0 +1,1531 @@
+// Copyright 2018-2025 the Deno authors. MIT license.
+
+import { primordials } from "ext:core/mod.js";
+import {
+ DOMMatrixInner,
+ DOMPointInner,
+ DOMQuadInner,
+ DOMRectInner,
+} from "ext:core/ops";
+const {
+ ArrayPrototypeJoin,
+ Float32Array,
+ Float64Array,
+ ObjectDefineProperty,
+ ObjectPrototypeIsPrototypeOf,
+ Symbol,
+ SymbolFor,
+ SymbolIterator,
+ TypeError,
+ TypedArrayPrototypeJoin,
+} = primordials;
+
+import { createFilteredInspectProxy } from "ext:deno_console/01_console.js";
+import * as webidl from "ext:deno_webidl/00_webidl.js";
+import { DOMException } from "ext:deno_web/01_dom_exception.js";
+
+const _inner = Symbol("[[inner]]");
+// Property to prevent writing values when an immutable instance is changed to
+// a mutable instance by Object.setPrototypeOf
+// TODO(petamoriken): Implementing resistance to Object.setPrototypeOf in the WebIDL layer
+const _writable = Symbol("[[writable]]");
+const _brand = webidl.brand;
+
+class DOMPointReadOnly {
+ [_writable] = false;
+ /** @type {DOMPointInner} */
+ [_inner];
+
+ constructor(x = 0, y = 0, z = 0, w = 1) {
+ this[_inner] = new DOMPointInner(x, y, z, w);
+ this[_brand] = _brand;
+ }
+
+ static fromPoint(other = { __proto__: null }) {
+ const point = webidl.createBranded(DOMPointReadOnly);
+ point[_writable] = false;
+ point[_inner] = DOMPointInner.fromPoint(other);
+ return point;
+ }
+
+ get x() {
+ webidl.assertBranded(this, DOMPointReadOnlyPrototype);
+ return this[_inner].x;
+ }
+
+ get y() {
+ webidl.assertBranded(this, DOMPointReadOnlyPrototype);
+ return this[_inner].y;
+ }
+
+ get z() {
+ webidl.assertBranded(this, DOMPointReadOnlyPrototype);
+ return this[_inner].z;
+ }
+
+ get w() {
+ webidl.assertBranded(this, DOMPointReadOnlyPrototype);
+ return this[_inner].w;
+ }
+
+ matrixTransform(matrix = { __proto__: null }) {
+ webidl.assertBranded(this, DOMPointReadOnlyPrototype);
+ let matrixInner;
+ // fast path for DOMMatrix or DOMMatrixReadOnly
+ if (
+ matrix !== null &&
+ ObjectPrototypeIsPrototypeOf(DOMMatrixReadOnlyPrototype, matrix)
+ ) {
+ matrixInner = matrix[_inner];
+ } else {
+ matrixInner = DOMMatrixInner.fromMatrix(matrix);
+ }
+ const point = webidl.createBranded(DOMPoint);
+ point[_writable] = true;
+ point[_inner] = this[_inner].matrixTransform(matrixInner);
+ return point;
+ }
+
+ toJSON() {
+ webidl.assertBranded(this, DOMPointReadOnlyPrototype);
+ const { x, y, z, w } = this[_inner];
+ return { x, y, z, w };
+ }
+
+ [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ return inspect(
+ createFilteredInspectProxy({
+ object: this,
+ evaluate: ObjectPrototypeIsPrototypeOf(DOMPointReadOnlyPrototype, this),
+ keys: [
+ "x",
+ "y",
+ "z",
+ "w",
+ ],
+ }),
+ inspectOptions,
+ );
+ }
+}
+
+webidl.configureInterface(DOMPointReadOnly);
+const DOMPointReadOnlyPrototype = DOMPointReadOnly.prototype;
+
+class DOMPoint extends DOMPointReadOnly {
+ [_writable] = true;
+
+ static fromPoint(other = { __proto__: null }) {
+ const point = webidl.createBranded(DOMPoint);
+ point[_writable] = true;
+ point[_inner] = DOMPointInner.fromPoint(other);
+ return point;
+ }
+
+ get x() {
+ webidl.assertBranded(this, DOMPointPrototype);
+ return this[_inner].x;
+ }
+ set x(value) {
+ webidl.assertBranded(this, DOMPointPrototype);
+ assertWritable(this);
+ this[_inner].x = value;
+ }
+
+ get y() {
+ webidl.assertBranded(this, DOMPointPrototype);
+ return this[_inner].y;
+ }
+ set y(value) {
+ webidl.assertBranded(this, DOMPointPrototype);
+ assertWritable(this);
+ this[_inner].y = value;
+ }
+
+ get z() {
+ webidl.assertBranded(this, DOMPointPrototype);
+ return this[_inner].z;
+ }
+ set z(value) {
+ webidl.assertBranded(this, DOMPointPrototype);
+ assertWritable(this);
+ this[_inner].z = value;
+ }
+
+ get w() {
+ webidl.assertBranded(this, DOMPointPrototype);
+ return this[_inner].w;
+ }
+ set w(value) {
+ webidl.assertBranded(this, DOMPointPrototype);
+ assertWritable(this);
+ this[_inner].w = value;
+ }
+
+ [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ return inspect(
+ createFilteredInspectProxy({
+ object: this,
+ evaluate: ObjectPrototypeIsPrototypeOf(DOMPointPrototype, this),
+ keys: [
+ "x",
+ "y",
+ "z",
+ "w",
+ ],
+ }),
+ inspectOptions,
+ );
+ }
+}
+
+webidl.configureInterface(DOMPoint);
+const DOMPointPrototype = DOMPoint.prototype;
+
+class DOMRectReadOnly {
+ [_writable] = false;
+ /** @type {DOMRectInner} */
+ [_inner];
+
+ constructor(x = 0, y = 0, width = 0, height = 0) {
+ this[_inner] = new DOMRectInner(x, y, width, height);
+ this[_brand] = _brand;
+ }
+
+ static fromRect(other = { __proto__: null }) {
+ const rect = webidl.createBranded(DOMRectReadOnly);
+ rect[_writable] = false;
+ rect[_inner] = DOMRectInner.fromRect(other);
+ return rect;
+ }
+
+ get x() {
+ webidl.assertBranded(this, DOMRectReadOnlyPrototype);
+ return this[_inner].x;
+ }
+
+ get y() {
+ webidl.assertBranded(this, DOMRectReadOnlyPrototype);
+ return this[_inner].y;
+ }
+
+ get width() {
+ webidl.assertBranded(this, DOMRectReadOnlyPrototype);
+ return this[_inner].width;
+ }
+
+ get height() {
+ webidl.assertBranded(this, DOMRectReadOnlyPrototype);
+ return this[_inner].height;
+ }
+
+ get top() {
+ webidl.assertBranded(this, DOMRectReadOnlyPrototype);
+ return this[_inner].top;
+ }
+
+ get right() {
+ webidl.assertBranded(this, DOMRectReadOnlyPrototype);
+ return this[_inner].right;
+ }
+
+ get bottom() {
+ webidl.assertBranded(this, DOMRectReadOnlyPrototype);
+ return this[_inner].bottom;
+ }
+
+ get left() {
+ webidl.assertBranded(this, DOMRectReadOnlyPrototype);
+ return this[_inner].left;
+ }
+
+ toJSON() {
+ webidl.assertBranded(this, DOMRectReadOnlyPrototype);
+ const { x, y, width, height, top, right, bottom, left } = this[_inner];
+ return { x, y, width, height, top, right, bottom, left };
+ }
+
+ [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ return inspect(
+ createFilteredInspectProxy({
+ object: this,
+ evaluate: ObjectPrototypeIsPrototypeOf(DOMRectReadOnlyPrototype, this),
+ keys: [
+ "x",
+ "y",
+ "width",
+ "height",
+ "top",
+ "right",
+ "bottom",
+ "left",
+ ],
+ }),
+ inspectOptions,
+ );
+ }
+}
+
+webidl.configureInterface(DOMRectReadOnly);
+const DOMRectReadOnlyPrototype = DOMRectReadOnly.prototype;
+
+class DOMRect extends DOMRectReadOnly {
+ [_writable] = true;
+
+ static fromRect(other = { __proto__: null }) {
+ const rect = webidl.createBranded(DOMRect);
+ rect[_writable] = true;
+ rect[_inner] = DOMRectInner.fromRect(other);
+ return rect;
+ }
+
+ get x() {
+ webidl.assertBranded(this, DOMRectPrototype);
+ return this[_inner].x;
+ }
+ set x(value) {
+ webidl.assertBranded(this, DOMRectPrototype);
+ assertWritable(this);
+ this[_inner].x = value;
+ }
+
+ get y() {
+ webidl.assertBranded(this, DOMRectPrototype);
+ return this[_inner].y;
+ }
+ set y(value) {
+ webidl.assertBranded(this, DOMRectPrototype);
+ assertWritable(this);
+ this[_inner].y = value;
+ }
+
+ get width() {
+ webidl.assertBranded(this, DOMRectPrototype);
+ return this[_inner].width;
+ }
+ set width(value) {
+ webidl.assertBranded(this, DOMRectPrototype);
+ assertWritable(this);
+ this[_inner].width = value;
+ }
+
+ get height() {
+ webidl.assertBranded(this, DOMRectPrototype);
+ return this[_inner].height;
+ }
+ set height(value) {
+ webidl.assertBranded(this, DOMRectPrototype);
+ assertWritable(this);
+ this[_inner].height = value;
+ }
+
+ [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ return inspect(
+ createFilteredInspectProxy({
+ object: this,
+ evaluate: ObjectPrototypeIsPrototypeOf(DOMRectPrototype, this),
+ keys: [
+ "x",
+ "y",
+ "width",
+ "height",
+ "top",
+ "right",
+ "bottom",
+ "left",
+ ],
+ }),
+ inspectOptions,
+ );
+ }
+}
+
+webidl.configureInterface(DOMRect);
+const DOMRectPrototype = DOMRect.prototype;
+
+const _p1 = Symbol("[[p1]]");
+const _p2 = Symbol("[[p2]]");
+const _p3 = Symbol("[[p3]]");
+const _p4 = Symbol("[[p4]]");
+
+class DOMQuad {
+ /** @type {DOMQuadInner} */
+ [_inner];
+ /** @type {DOMPoint=} */
+ [_p1];
+ /** @type {DOMPoint=} */
+ [_p2];
+ /** @type {DOMPoint=} */
+ [_p3];
+ /** @type {DOMPoint=} */
+ [_p4];
+
+ constructor(
+ p1 = { __proto__: null },
+ p2 = { __proto__: null },
+ p3 = { __proto__: null },
+ p4 = { __proto__: null },
+ ) {
+ this[_inner] = new DOMQuadInner(p1, p2, p3, p4);
+ this[_brand] = _brand;
+ }
+
+ static fromRect(other = { __proto__: null }) {
+ const quad = webidl.createBranded(DOMQuad);
+ quad[_inner] = DOMQuadInner.fromRect(other);
+ quad[_p1] = undefined;
+ quad[_p2] = undefined;
+ quad[_p3] = undefined;
+ quad[_p4] = undefined;
+ return quad;
+ }
+
+ static fromQuad(other = { __proto__: null }) {
+ const quad = webidl.createBranded(DOMQuad);
+ quad[_inner] = DOMQuadInner.fromQuad(other);
+ quad[_p1] = undefined;
+ quad[_p2] = undefined;
+ quad[_p3] = undefined;
+ quad[_p4] = undefined;
+ return quad;
+ }
+
+ get p1() {
+ webidl.assertBranded(this, DOMQuadPrototype);
+ if (this[_p1] !== undefined) {
+ return this[_p1];
+ }
+ const point = webidl.createBranded(DOMPoint);
+ point[_writable] = true;
+ point[_inner] = this[_inner].p1;
+ this[_p1] = point;
+ return point;
+ }
+
+ get p2() {
+ webidl.assertBranded(this, DOMQuadPrototype);
+ if (this[_p2] !== undefined) {
+ return this[_p2];
+ }
+ const point = webidl.createBranded(DOMPoint);
+ point[_writable] = true;
+ point[_inner] = this[_inner].p2;
+ this[_p2] = point;
+ return point;
+ }
+
+ get p3() {
+ webidl.assertBranded(this, DOMQuadPrototype);
+ if (this[_p3] !== undefined) {
+ return this[_p3];
+ }
+ const point = webidl.createBranded(DOMPoint);
+ point[_writable] = true;
+ point[_inner] = this[_inner].p3;
+ this[_p3] = point;
+ return point;
+ }
+
+ get p4() {
+ webidl.assertBranded(this, DOMQuadPrototype);
+ if (this[_p4] !== undefined) {
+ return this[_p4];
+ }
+ const point = webidl.createBranded(DOMPoint);
+ point[_writable] = true;
+ point[_inner] = this[_inner].p4;
+ this[_p4] = point;
+ return point;
+ }
+
+ getBounds() {
+ webidl.assertBranded(this, DOMQuadPrototype);
+ const bounds = webidl.createBranded(DOMRect);
+ bounds[_writable] = true;
+ bounds[_inner] = this[_inner].getBounds();
+ return bounds;
+ }
+
+ toJSON() {
+ webidl.assertBranded(this, DOMQuadPrototype);
+ return {
+ p1: this[_p1],
+ p2: this[_p2],
+ p3: this[_p3],
+ p4: this[_p4],
+ };
+ }
+
+ [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ return inspect(
+ createFilteredInspectProxy({
+ object: this,
+ evaluate: ObjectPrototypeIsPrototypeOf(DOMQuadPrototype, this),
+ keys: [
+ "p1",
+ "p2",
+ "p3",
+ "p4",
+ ],
+ }),
+ inspectOptions,
+ );
+ }
+}
+
+webidl.configureInterface(DOMQuad);
+const DOMQuadPrototype = DOMQuad.prototype;
+
+class DOMMatrixReadOnly {
+ [_writable] = false;
+ /** @type {DOMMatrixInner} */
+ [_inner];
+
+ constructor(init = undefined) {
+ const prefix = `Failed to construct '${this.constructor.name}'`;
+ this[_brand] = _brand;
+ if (init === undefined) {
+ this[_inner] = DOMMatrixInner.identity();
+ } else if (
+ webidl.type(init) === "Object" && init[SymbolIterator] !== undefined
+ ) {
+ init = webidl.converters["sequence"](
+ init,
+ prefix,
+ "Argument 1",
+ );
+ initMatrixFromSequence(this, init, prefix);
+ } else {
+ init = webidl.converters.DOMString(
+ init,
+ prefix,
+ "Argument 1",
+ );
+ const { matrix, is2D } = parseTransformList(init, prefix);
+ this[_inner] = new DOMMatrixInner(matrix, is2D);
+ }
+ }
+
+ static fromMatrix(other = { __proto__: null }) {
+ const matrix = webidl.createBranded(DOMMatrixReadOnly);
+ matrix[_writable] = false;
+ // fast path for DOMMatrix or DOMMatrixReadOnly
+ if (
+ other !== null &&
+ ObjectPrototypeIsPrototypeOf(DOMMatrixReadOnlyPrototype, other)
+ ) {
+ matrix[_inner] = other[_inner].clone();
+ } else {
+ matrix[_inner] = DOMMatrixInner.fromMatrix(other);
+ }
+ return matrix;
+ }
+
+ static fromFloat32Array(float32) {
+ const prefix = "Failed to execute 'DOMMatrixReadOnly.fromFloat32Array'";
+ webidl.requiredArguments(arguments.length, 1, prefix);
+ float32 = webidl.converters.Float32Array(float32, prefix, "Argument 1");
+ const matrix = webidl.createBranded(DOMMatrixReadOnly);
+ matrix[_writable] = false;
+ initMatrixFromSequence(matrix, float32, prefix);
+ return matrix;
+ }
+
+ static fromFloat64Array(float64) {
+ const prefix = "Failed to execute 'DOMMatrixReadOnly.fromFloat64Array'";
+ webidl.requiredArguments(arguments.length, 1, prefix);
+ float64 = webidl.converters.Float64Array(float64, prefix, "Argument 1");
+ const matrix = webidl.createBranded(DOMMatrixReadOnly);
+ matrix[_writable] = false;
+ initMatrixFromSequence(matrix, float64, prefix);
+ return matrix;
+ }
+
+ get a() {
+ webidl.assertBranded(this, DOMMatrixReadOnlyPrototype);
+ return this[_inner].a;
+ }
+
+ get b() {
+ webidl.assertBranded(this, DOMMatrixReadOnlyPrototype);
+ return this[_inner].b;
+ }
+
+ get c() {
+ webidl.assertBranded(this, DOMMatrixReadOnlyPrototype);
+ return this[_inner].c;
+ }
+
+ get d() {
+ webidl.assertBranded(this, DOMMatrixReadOnlyPrototype);
+ return this[_inner].d;
+ }
+
+ get e() {
+ webidl.assertBranded(this, DOMMatrixReadOnlyPrototype);
+ return this[_inner].e;
+ }
+
+ get f() {
+ webidl.assertBranded(this, DOMMatrixReadOnlyPrototype);
+ return this[_inner].f;
+ }
+
+ get m11() {
+ webidl.assertBranded(this, DOMMatrixReadOnlyPrototype);
+ return this[_inner].m11;
+ }
+
+ get m12() {
+ webidl.assertBranded(this, DOMMatrixReadOnlyPrototype);
+ return this[_inner].m12;
+ }
+
+ get m13() {
+ webidl.assertBranded(this, DOMMatrixReadOnlyPrototype);
+ return this[_inner].m13;
+ }
+
+ get m14() {
+ webidl.assertBranded(this, DOMMatrixReadOnlyPrototype);
+ return this[_inner].m14;
+ }
+
+ get m21() {
+ webidl.assertBranded(this, DOMMatrixReadOnlyPrototype);
+ return this[_inner].m21;
+ }
+
+ get m22() {
+ webidl.assertBranded(this, DOMMatrixReadOnlyPrototype);
+ return this[_inner].m22;
+ }
+
+ get m23() {
+ webidl.assertBranded(this, DOMMatrixReadOnlyPrototype);
+ return this[_inner].m23;
+ }
+
+ get m24() {
+ webidl.assertBranded(this, DOMMatrixReadOnlyPrototype);
+ return this[_inner].m24;
+ }
+
+ get m31() {
+ webidl.assertBranded(this, DOMMatrixReadOnlyPrototype);
+ return this[_inner].m31;
+ }
+
+ get m32() {
+ webidl.assertBranded(this, DOMMatrixReadOnlyPrototype);
+ return this[_inner].m32;
+ }
+
+ get m33() {
+ webidl.assertBranded(this, DOMMatrixReadOnlyPrototype);
+ return this[_inner].m33;
+ }
+
+ get m34() {
+ webidl.assertBranded(this, DOMMatrixReadOnlyPrototype);
+ return this[_inner].m34;
+ }
+
+ get m41() {
+ webidl.assertBranded(this, DOMMatrixReadOnlyPrototype);
+ return this[_inner].m41;
+ }
+
+ get m42() {
+ webidl.assertBranded(this, DOMMatrixReadOnlyPrototype);
+ return this[_inner].m42;
+ }
+
+ get m43() {
+ webidl.assertBranded(this, DOMMatrixReadOnlyPrototype);
+ return this[_inner].m43;
+ }
+
+ get m44() {
+ webidl.assertBranded(this, DOMMatrixReadOnlyPrototype);
+ return this[_inner].m44;
+ }
+
+ get is2D() {
+ webidl.assertBranded(this, DOMMatrixReadOnlyPrototype);
+ return this[_inner].is2D;
+ }
+
+ get isIdentity() {
+ webidl.assertBranded(this, DOMMatrixReadOnlyPrototype);
+ return this[_inner].isIdentity;
+ }
+
+ translate(tx = 0, ty = 0, tz = 0) {
+ webidl.assertBranded(this, DOMMatrixReadOnlyPrototype);
+ const matrix = webidl.createBranded(DOMMatrix);
+ matrix[_writable] = true;
+ matrix[_inner] = this[_inner].translate(tx, ty, tz);
+ return matrix;
+ }
+
+ scale(
+ scaleX = 1,
+ scaleY = scaleX,
+ scaleZ = 1,
+ originX = 0,
+ originY = 0,
+ originZ = 0,
+ ) {
+ webidl.assertBranded(this, DOMMatrixReadOnlyPrototype);
+ const matrix = webidl.createBranded(DOMMatrix);
+ matrix[_writable] = true;
+ if (originX === 0 && originY === 0 && originZ === 0) {
+ matrix[_inner] = this[_inner].scaleWithoutOrigin(
+ scaleX,
+ scaleY,
+ scaleZ,
+ );
+ } else {
+ matrix[_inner] = this[_inner].scaleWithOrigin(
+ scaleX,
+ scaleY,
+ scaleZ,
+ originX,
+ originY,
+ originZ,
+ );
+ }
+ return matrix;
+ }
+
+ scaleNonUniform(scaleX = 1, scaleY = 1) {
+ webidl.assertBranded(this, DOMMatrixReadOnlyPrototype);
+ const matrix = webidl.createBranded(DOMMatrix);
+ matrix[_writable] = true;
+ matrix[_inner] = this[_inner].scaleWithoutOrigin(
+ scaleX,
+ scaleY,
+ 1,
+ );
+ return matrix;
+ }
+
+ scale3d(scale = 1, originX = 0, originY = 0, originZ = 0) {
+ webidl.assertBranded(this, DOMMatrixReadOnlyPrototype);
+ const matrix = webidl.createBranded(DOMMatrix);
+ matrix[_writable] = true;
+ if (originX === 0 && originY === 0 && originZ === 0) {
+ matrix[_inner] = this[_inner].scaleWithoutOrigin(
+ scale,
+ scale,
+ scale,
+ );
+ } else {
+ matrix[_inner] = this[_inner].scaleWithOrigin(
+ scale,
+ scale,
+ scale,
+ originX,
+ originY,
+ originZ,
+ );
+ }
+ return matrix;
+ }
+
+ rotate(rotX = 0, rotY, rotZ) {
+ webidl.assertBranded(this, DOMMatrixReadOnlyPrototype);
+ if (rotY === undefined && rotZ === undefined) {
+ rotZ = rotX;
+ rotX = 0;
+ rotY = 0;
+ } else {
+ rotY = rotY !== undefined ? rotY : 0;
+ rotZ = rotZ !== undefined ? rotZ : 0;
+ }
+ const matrix = webidl.createBranded(DOMMatrix);
+ matrix[_writable] = true;
+ matrix[_inner] = this[_inner].rotate(
+ rotX,
+ rotY,
+ rotZ,
+ );
+ return matrix;
+ }
+
+ rotateFromVector(x = 0, y = 0) {
+ webidl.assertBranded(this, DOMMatrixReadOnlyPrototype);
+ const matrix = webidl.createBranded(DOMMatrix);
+ matrix[_writable] = true;
+ matrix[_inner] = this[_inner].rotateFromVector(x, y);
+ return matrix;
+ }
+
+ rotateAxisAngle(x = 0, y = 0, z = 0, angle = 0) {
+ webidl.assertBranded(this, DOMMatrixReadOnlyPrototype);
+ const matrix = webidl.createBranded(DOMMatrix);
+ matrix[_writable] = true;
+ matrix[_inner] = this[_inner].rotateAxisAngle(
+ x,
+ y,
+ z,
+ angle,
+ );
+ return matrix;
+ }
+
+ skewX(sx = 0) {
+ webidl.assertBranded(this, DOMMatrixReadOnlyPrototype);
+ const matrix = webidl.createBranded(DOMMatrix);
+ matrix[_writable] = true;
+ matrix[_inner] = this[_inner].skewX(sx);
+ return matrix;
+ }
+
+ skewY(sy = 0) {
+ webidl.assertBranded(this, DOMMatrixReadOnlyPrototype);
+ const matrix = webidl.createBranded(DOMMatrix);
+ matrix[_writable] = true;
+ matrix[_inner] = this[_inner].skewY(sy);
+ return matrix;
+ }
+
+ multiply(other = { __proto__: null }) {
+ webidl.assertBranded(this, DOMMatrixReadOnlyPrototype);
+ let otherInner;
+ // fast path for DOMMatrix or DOMMatrixReadOnly
+ if (
+ other !== null &&
+ ObjectPrototypeIsPrototypeOf(DOMMatrixReadOnlyPrototype, other)
+ ) {
+ otherInner = other[_inner];
+ } else {
+ otherInner = DOMMatrixInner.fromMatrix(other);
+ }
+ const matrix = webidl.createBranded(DOMMatrix);
+ matrix[_writable] = true;
+ matrix[_inner] = this[_inner].multiply(otherInner);
+ return matrix;
+ }
+
+ flipX() {
+ webidl.assertBranded(this, DOMMatrixReadOnlyPrototype);
+ const matrix = webidl.createBranded(DOMMatrix);
+ matrix[_writable] = true;
+ matrix[_inner] = this[_inner].flipX();
+ return matrix;
+ }
+
+ flipY() {
+ webidl.assertBranded(this, DOMMatrixReadOnlyPrototype);
+ const matrix = webidl.createBranded(DOMMatrix);
+ matrix[_writable] = true;
+ matrix[_inner] = this[_inner].flipY();
+ return matrix;
+ }
+
+ inverse() {
+ webidl.assertBranded(this, DOMMatrixReadOnlyPrototype);
+ const matrix = webidl.createBranded(DOMMatrix);
+ matrix[_writable] = true;
+ matrix[_inner] = this[_inner].inverse();
+ return matrix;
+ }
+
+ transformPoint(point = { __proto__: null }) {
+ webidl.assertBranded(this, DOMMatrixReadOnlyPrototype);
+ let pointInner;
+ // fast path for DOMPoint or DOMPointReadOnly
+ if (
+ point !== null &&
+ ObjectPrototypeIsPrototypeOf(DOMPointReadOnlyPrototype, point)
+ ) {
+ pointInner = point[_inner];
+ } else {
+ pointInner = DOMPointInner.fromPoint(point);
+ }
+ const result = webidl.createBranded(DOMPoint);
+ result[_writable] = true;
+ result[_inner] = this[_inner].transformPoint(pointInner);
+ return result;
+ }
+
+ toFloat32Array() {
+ webidl.assertBranded(this, DOMMatrixReadOnlyPrototype);
+ return new Float32Array(
+ new Float64Array(
+ this[_inner].toBuffer(),
+ ),
+ );
+ }
+
+ toFloat64Array() {
+ webidl.assertBranded(this, DOMMatrixReadOnlyPrototype);
+ return new Float64Array(this[_inner].toBuffer());
+ }
+
+ toJSON() {
+ webidl.assertBranded(this, DOMMatrixReadOnlyPrototype);
+ const {
+ a,
+ b,
+ c,
+ d,
+ e,
+ f,
+ m11,
+ m12,
+ m13,
+ m14,
+ m21,
+ m22,
+ m23,
+ m24,
+ m31,
+ m32,
+ m33,
+ m34,
+ m41,
+ m42,
+ m43,
+ m44,
+ is2D,
+ isIdentity,
+ } = this[_inner];
+ return {
+ a,
+ b,
+ c,
+ d,
+ e,
+ f,
+ m11,
+ m12,
+ m13,
+ m14,
+ m21,
+ m22,
+ m23,
+ m24,
+ m31,
+ m32,
+ m33,
+ m34,
+ m41,
+ m42,
+ m43,
+ m44,
+ is2D,
+ isIdentity,
+ };
+ }
+
+ [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ return inspect(
+ createFilteredInspectProxy({
+ object: this,
+ evaluate: ObjectPrototypeIsPrototypeOf(
+ DOMMatrixReadOnlyPrototype,
+ this,
+ ),
+ keys: [
+ "a",
+ "b",
+ "c",
+ "d",
+ "e",
+ "f",
+ "m11",
+ "m12",
+ "m13",
+ "m14",
+ "m21",
+ "m22",
+ "m23",
+ "m24",
+ "m31",
+ "m32",
+ "m33",
+ "m34",
+ "m41",
+ "m42",
+ "m43",
+ "m44",
+ "is2D",
+ "isIdentity",
+ ],
+ }),
+ inspectOptions,
+ );
+ }
+}
+
+webidl.configureInterface(DOMMatrixReadOnly);
+const DOMMatrixReadOnlyPrototype = DOMMatrixReadOnly.prototype;
+
+class DOMMatrix extends DOMMatrixReadOnly {
+ [_writable] = true;
+
+ static fromMatrix(other = { __proto__: null }) {
+ const matrix = webidl.createBranded(DOMMatrix);
+ matrix[_writable] = true;
+ // fast path for DOMMatrix or DOMMatrixReadOnly
+ if (
+ other !== null &&
+ ObjectPrototypeIsPrototypeOf(DOMMatrixReadOnlyPrototype, other)
+ ) {
+ matrix[_inner] = other[_inner].clone();
+ } else {
+ matrix[_inner] = DOMMatrixInner.fromMatrix(other);
+ }
+ return matrix;
+ }
+
+ static fromFloat32Array(float32) {
+ const prefix = "Failed to execute 'DOMMatrix.fromFloat32Array'";
+ webidl.requiredArguments(arguments.length, 1, prefix);
+ float32 = webidl.converters.Float32Array(float32, prefix, "Argument 1");
+ const matrix = webidl.createBranded(DOMMatrix);
+ matrix[_writable] = true;
+ initMatrixFromSequence(matrix, float32, prefix);
+ return matrix;
+ }
+
+ static fromFloat64Array(float64) {
+ const prefix = "Failed to execute 'DOMMatrix.fromFloat64Array'";
+ webidl.requiredArguments(arguments.length, 1, prefix);
+ float64 = webidl.converters.Float64Array(float64, prefix, "Argument 1");
+ const matrix = webidl.createBranded(DOMMatrix);
+ matrix[_writable] = true;
+ initMatrixFromSequence(matrix, float64, prefix);
+ return matrix;
+ }
+
+ get a() {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ return this[_inner].a;
+ }
+ set a(value) {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ assertWritable(this);
+ this[_inner].a = value;
+ }
+
+ get b() {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ return this[_inner].b;
+ }
+ set b(value) {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ assertWritable(this);
+ this[_inner].b = value;
+ }
+
+ get c() {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ return this[_inner].c;
+ }
+ set c(value) {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ assertWritable(this);
+ this[_inner].c = value;
+ }
+
+ get d() {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ return this[_inner].d;
+ }
+ set d(value) {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ assertWritable(this);
+ this[_inner].d = value;
+ }
+
+ get e() {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ return this[_inner].e;
+ }
+ set e(value) {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ assertWritable(this);
+ this[_inner].e = value;
+ }
+
+ get f() {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ return this[_inner].f;
+ }
+ set f(value) {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ assertWritable(this);
+ this[_inner].f = value;
+ }
+
+ get m11() {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ return this[_inner].m11;
+ }
+ set m11(value) {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ assertWritable(this);
+ this[_inner].m11 = value;
+ }
+
+ get m12() {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ return this[_inner].m12;
+ }
+ set m12(value) {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ assertWritable(this);
+ this[_inner].m12 = value;
+ }
+
+ get m13() {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ return this[_inner].m13;
+ }
+ set m13(value) {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ assertWritable(this);
+ this[_inner].m13 = value;
+ }
+
+ get m14() {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ return this[_inner].m14;
+ }
+ set m14(value) {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ assertWritable(this);
+ this[_inner].m14 = value;
+ }
+
+ get m21() {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ return this[_inner].m21;
+ }
+ set m21(value) {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ assertWritable(this);
+ this[_inner].m21 = value;
+ }
+
+ get m22() {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ return this[_inner].m22;
+ }
+ set m22(value) {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ assertWritable(this);
+ this[_inner].m22 = value;
+ }
+
+ get m23() {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ return this[_inner].m23;
+ }
+ set m23(value) {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ assertWritable(this);
+ this[_inner].m23 = value;
+ }
+
+ get m24() {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ return this[_inner].m24;
+ }
+ set m24(value) {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ assertWritable(this);
+ this[_inner].m24 = value;
+ }
+
+ get m31() {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ return this[_inner].m31;
+ }
+ set m31(value) {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ assertWritable(this);
+ this[_inner].m31 = value;
+ }
+
+ get m32() {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ return this[_inner].m32;
+ }
+ set m32(value) {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ assertWritable(this);
+ this[_inner].m32 = value;
+ }
+
+ get m33() {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ return this[_inner].m33;
+ }
+ set m33(value) {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ assertWritable(this);
+ this[_inner].m33 = value;
+ }
+
+ get m34() {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ return this[_inner].m34;
+ }
+ set m34(value) {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ assertWritable(this);
+ this[_inner].m34 = value;
+ }
+
+ get m41() {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ return this[_inner].m41;
+ }
+ set m41(value) {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ assertWritable(this);
+ this[_inner].m41 = value;
+ }
+
+ get m42() {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ return this[_inner].m42;
+ }
+ set m42(value) {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ assertWritable(this);
+ this[_inner].m42 = value;
+ }
+
+ get m43() {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ return this[_inner].m43;
+ }
+ set m43(value) {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ assertWritable(this);
+ this[_inner].m43 = value;
+ }
+
+ get m44() {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ return this[_inner].m44;
+ }
+ set m44(value) {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ assertWritable(this);
+ this[_inner].m44 = value;
+ }
+
+ multiplySelf(other = { __proto__: null }) {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ assertWritable(this);
+ let otherInner;
+ // fast path for DOMMatrix or DOMMatrixReadOnly
+ if (
+ other !== null &&
+ ObjectPrototypeIsPrototypeOf(DOMMatrixReadOnlyPrototype, other)
+ ) {
+ otherInner = other[_inner];
+ } else {
+ otherInner = DOMMatrixInner.fromMatrix(other);
+ }
+ this[_inner].multiplySelf(otherInner);
+ return this;
+ }
+
+ preMultiplySelf(other = { __proto__: null }) {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ assertWritable(this);
+ let otherInner;
+ // fast path for DOMMatrix or DOMMatrixReadOnly
+ if (
+ other !== null &&
+ ObjectPrototypeIsPrototypeOf(DOMMatrixReadOnlyPrototype, other)
+ ) {
+ otherInner = other[_inner];
+ } else {
+ otherInner = DOMMatrixInner.fromMatrix(other);
+ }
+ this[_inner].preMultiplySelf(otherInner);
+ return this;
+ }
+
+ translateSelf(tx = 0, ty = 0, tz = 0) {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ assertWritable(this);
+ this[_inner].translateSelf(tx, ty, tz);
+ return this;
+ }
+
+ scaleSelf(
+ scaleX = 1,
+ scaleY = scaleX,
+ scaleZ = 1,
+ originX = 0,
+ originY = 0,
+ originZ = 0,
+ ) {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ assertWritable(this);
+ if (originX === 0 && originY === 0 && originZ === 0) {
+ this[_inner].scaleWithoutOriginSelf(scaleX, scaleY, scaleZ);
+ } else {
+ this[_inner].scaleWithOriginSelf(
+ scaleX,
+ scaleY,
+ scaleZ,
+ originX,
+ originY,
+ originZ,
+ );
+ }
+ return this;
+ }
+
+ scale3dSelf(scale = 1, originX = 0, originY = 0, originZ = 0) {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ assertWritable(this);
+ if (originX === 0 && originY === 0 && originZ === 0) {
+ this[_inner].scaleWithoutOriginSelf(scale, scale, scale);
+ } else {
+ this[_inner].scaleWithOriginSelf(
+ scale,
+ scale,
+ scale,
+ originX,
+ originY,
+ originZ,
+ );
+ }
+ return this;
+ }
+
+ rotateSelf(rotX = 0, rotY, rotZ) {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ assertWritable(this);
+ if (rotY === undefined && rotZ === undefined) {
+ rotZ = rotX;
+ rotX = 0;
+ rotY = 0;
+ } else {
+ rotY = rotY !== undefined ? rotY : 0;
+ rotZ = rotZ !== undefined ? rotZ : 0;
+ }
+ this[_inner].rotateSelf(
+ rotX,
+ rotY,
+ rotZ,
+ );
+ return this;
+ }
+
+ rotateFromVectorSelf(x = 0, y = 0) {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ assertWritable(this);
+ this[_inner].rotateFromVectorSelf(x, y);
+ return this;
+ }
+
+ rotateAxisAngleSelf(x = 0, y = 0, z = 0, angle = 0) {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ assertWritable(this);
+ this[_inner].rotateAxisAngleSelf(
+ x,
+ y,
+ z,
+ angle,
+ );
+ return this;
+ }
+
+ skewXSelf(sx = 0) {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ assertWritable(this);
+ this[_inner].skewXSelf(sx);
+ return this;
+ }
+
+ skewYSelf(sy = 0) {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ assertWritable(this);
+ this[_inner].skewYSelf(sy);
+ return this;
+ }
+
+ invertSelf() {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ assertWritable(this);
+ this[_inner].invertSelf();
+ return this;
+ }
+
+ [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ return inspect(
+ createFilteredInspectProxy({
+ object: this,
+ evaluate: ObjectPrototypeIsPrototypeOf(DOMMatrixPrototype, this),
+ keys: [
+ "a",
+ "b",
+ "c",
+ "d",
+ "e",
+ "f",
+ "m11",
+ "m12",
+ "m13",
+ "m14",
+ "m21",
+ "m22",
+ "m23",
+ "m24",
+ "m31",
+ "m32",
+ "m33",
+ "m34",
+ "m41",
+ "m42",
+ "m43",
+ "m44",
+ "is2D",
+ "isIdentity",
+ ],
+ }),
+ inspectOptions,
+ );
+ }
+}
+
+webidl.configureInterface(DOMMatrix);
+const DOMMatrixPrototype = DOMMatrix.prototype;
+
+/**
+ * TODO(petamoriken): Support this by updating WebIDL's brand features
+ * @param {DOMRect | DOMPoint | DOMMatrix} self
+ */
+function assertWritable(self) {
+ if (self[_writable] !== true) {
+ throw new TypeError("Illegal invocation");
+ }
+}
+
+/**
+ * @param {object} target
+ * @param {number[] | Float32Array | Float64Array} seq
+ * @param {string} prefix
+ */
+function initMatrixFromSequence(target, seq, prefix) {
+ if (seq.length === 6) {
+ const { 0: a, 1: b, 2: c, 3: d, 4: e, 5: f } = seq;
+ // deno-fmt-ignore
+ target[_inner] = new DOMMatrixInner(new Float64Array([
+ a, b, 0, 0,
+ c, d, 0, 0,
+ 0, 0, 1, 0,
+ e, f, 0, 1,
+ ]), /* is2D */ true);
+ } else if (seq.length === 16) {
+ target[_inner] = new DOMMatrixInner(
+ new Float64Array(seq),
+ /* is2D */ false,
+ );
+ } else {
+ throw new TypeError(
+ `${prefix}: The sequence must contain 6 elements for a 2D matrix or 16 elements for a 3D matrix`,
+ );
+ }
+}
+
+/**
+ * CSS parser
+ * @type {((transformList: string, prefix: string) => { matrix: Float64Array, is2D: boolean })}
+ */
+let parseTransformList;
+
+/**
+ * @param {(transformList: string, prefix: string) => { matrix: Float64Array, is2D: boolean }} transformListParser
+ * @param {boolean} enableWindowFeatures
+ */
+function init(transformListParser, enableWindowFeatures) {
+ parseTransformList = transformListParser;
+
+ if (enableWindowFeatures) {
+ // https://drafts.fxtf.org/geometry/#dommatrixreadonly-stringification-behavior
+ ObjectDefineProperty(DOMMatrixReadOnlyPrototype, "toString", {
+ __proto__: null,
+ value: function toString() {
+ webidl.assertBranded(this, DOMMatrixReadOnlyPrototype);
+ const inner = this[_inner];
+ if (!inner.isFinite) {
+ throw new DOMException(
+ "Failed to execute 'toString' on 'DOMMatrixReadOnly': Cannot be serialized with NaN or Infinity values",
+ "InvalidStateError",
+ );
+ }
+ if (inner.is2D) {
+ return `matrix(${
+ ArrayPrototypeJoin([
+ inner.a,
+ inner.b,
+ inner.c,
+ inner.d,
+ inner.e,
+ inner.f,
+ ], ", ")
+ })`;
+ } else {
+ return `matrix3d(${
+ TypedArrayPrototypeJoin(new Float64Array(inner.toBuffer()), ", ")
+ })`;
+ }
+ },
+ writable: true,
+ enumerable: true,
+ configurable: true,
+ });
+
+ // https://drafts.fxtf.org/geometry/#dom-dommatrix-setmatrixvalue
+ ObjectDefineProperty(DOMMatrixPrototype, "setMatrixValue", {
+ __proto__: null,
+ value: function setMatrixValue(transformList) {
+ webidl.assertBranded(this, DOMMatrixPrototype);
+ const prefix = "Failed to execute 'setMatrixValue' on 'DOMMatrix'";
+ webidl.requiredArguments(arguments.length, 1, prefix);
+ transformList = webidl.converters.DOMString(
+ transformList,
+ prefix,
+ "Argument 1",
+ );
+ const { matrix, is2D } = parseTransformList(transformList, prefix);
+ this[_inner] = new DOMMatrixInner(matrix, is2D);
+ },
+ writable: true,
+ enumerable: true,
+ configurable: true,
+ });
+ }
+}
+
+export {
+ DOMMatrix,
+ DOMMatrixPrototype,
+ DOMMatrixReadOnly,
+ DOMMatrixReadOnlyPrototype,
+ DOMPoint,
+ DOMPointPrototype,
+ DOMPointReadOnly,
+ DOMPointReadOnlyPrototype,
+ DOMQuad,
+ DOMQuadPrototype,
+ DOMRect,
+ DOMRectPrototype,
+ DOMRectReadOnly,
+ DOMRectReadOnlyPrototype,
+ init,
+};
diff --git a/ext/geometry/Cargo.toml b/ext/geometry/Cargo.toml
new file mode 100644
index 00000000000000..eeca9d2f4a0076
--- /dev/null
+++ b/ext/geometry/Cargo.toml
@@ -0,0 +1,20 @@
+# Copyright 2018-2025 the Deno authors. MIT license.
+
+[package]
+name = "deno_geometry"
+version = "0.1.0"
+authors.workspace = true
+edition.workspace = true
+license.workspace = true
+readme = "README.md"
+repository.workspace = true
+description = "Geometry Interfaces Module API implementation for Deno"
+
+[lib]
+path = "lib.rs"
+
+[dependencies]
+deno_core.workspace = true
+deno_error.workspace = true
+nalgebra.workspace = true
+thiserror.workspace = true
diff --git a/ext/geometry/README.md b/ext/geometry/README.md
new file mode 100644
index 00000000000000..8c2f421b10d5e4
--- /dev/null
+++ b/ext/geometry/README.md
@@ -0,0 +1,98 @@
+# deno_geometry
+
+This crate implements the Geometry Interfaces Module API.
+
+Spec: https://drafts.fxtf.org/geometry/
+
+## Usage Example
+
+From javascript, include the extension's source:
+
+```javascript
+import { core } from "ext:core/mod.js";
+import { createGeometryLoader } from "ext:deno_geometry/00_init.js";
+```
+
+For environments that do not have a CSS `` parser, such as Web
+Worker, configure as follows:
+
+```javascript
+const loadGeometry = createGeometryLoader((_transformList, prefix) => {
+ throw new TypeError(
+ `${prefix}: Cannot parse CSS on Workers`,
+ );
+}, /* enableWindowFeatures */ false);
+```
+
+On the other hand, in environments with a CSS `` parser, you can
+configure as follows:
+
+```javascript
+const loadGeometry = createGeometryLoader((transformList, prefix) => {
+ try {
+ // parse by yourself
+ const { sequence, is2D } = parse(transformList);
+ return {
+ matrix: new Float64Array(sequence),
+ is2D,
+ };
+ } catch {
+ throw new TypeError(
+ `${prefix}: Invalid string: ${transformList}`,
+ );
+ }
+}, /* enableWindowFeatures */ true);
+```
+
+Then define to globalThis:
+
+```javascript
+Object.defineProperties(globalThis, {
+ DOMMatrix: core.propNonEnumerableLazyLoaded(
+ (geometry) => geometry.DOMMatrix,
+ loadGeometry,
+ ),
+ DOMMatrixReadOnly: core.propNonEnumerableLazyLoaded(
+ (geometry) => geometry.DOMMatrixReadOnly,
+ loadGeometry,
+ ),
+ DOMPoint: core.propNonEnumerableLazyLoaded(
+ (geometry) => geometry.DOMPoint,
+ loadGeometry,
+ ),
+ DOMPointReadOnly: core.propNonEnumerableLazyLoaded(
+ (geometry) => geometry.DOMPointReadOnly,
+ loadGeometry,
+ ),
+ DOMQuad: core.propNonEnumerableLazyLoaded(
+ (geometry) => geometry.DOMQuad,
+ loadGeometry,
+ ),
+ DOMRect: core.propNonEnumerableLazyLoaded(
+ (geometry) => geometry.DOMRect,
+ loadGeometry,
+ ),
+ DOMRectReadOnly: core.propNonEnumerableLazyLoaded(
+ (geometry) => geometry.DOMRectReadOnly,
+ loadGeometry,
+ ),
+});
+```
+
+Then from rust, provide: `deno_geometry::deno_geometry::init_ops_and_esm()` in
+the `extensions` field of your `RuntimeOptions`
+
+## Dependencies
+
+- **deno_webidl**: Provided by the `deno_webidl` crate
+- **deno_web**: Provided by the `deno_web` crate
+- **deno_console**: Provided by the `deno_console` crate
+
+## Provided ops
+
+Following ops are provided, which can be accessed through `Deno.ops`:
+
+- DOMMatrixInner
+- DOMPointInner
+- DOMQuadInner
+- DOMRectInner
diff --git a/ext/geometry/lib.deno_geometry.d.ts b/ext/geometry/lib.deno_geometry.d.ts
new file mode 100644
index 00000000000000..800993ee4f3976
--- /dev/null
+++ b/ext/geometry/lib.deno_geometry.d.ts
@@ -0,0 +1,602 @@
+// Copyright 2018-2025 the Deno authors. MIT license.
+
+// deno-lint-ignore-file no-var
+
+///
+///
+
+/** @category Geometry Interfaces Module API */
+interface DOMMatrix2DInit {
+ a?: number;
+ b?: number;
+ c?: number;
+ d?: number;
+ e?: number;
+ f?: number;
+ m11?: number;
+ m12?: number;
+ m21?: number;
+ m22?: number;
+ m41?: number;
+ m42?: number;
+}
+
+/** @category Geometry Interfaces Module API */
+interface DOMMatrixInit extends DOMMatrix2DInit {
+ is2D?: boolean;
+ m13?: number;
+ m14?: number;
+ m23?: number;
+ m24?: number;
+ m31?: number;
+ m32?: number;
+ m33?: number;
+ m34?: number;
+ m43?: number;
+ m44?: number;
+}
+
+/**
+ * A 4×4 matrix (column-major order), suitable for 2D and 3D operations including rotation and translation.
+ * [MDN](https://developer.mozilla.org/docs/Web/API/DOMMatrix)
+ *
+ * ```
+ * | m11 m21 m31 m41 |
+ * | m12 m22 m32 m42 |
+ * | m13 m23 m33 m43 |
+ * | m14 m24 m34 m44 |
+ * ```
+ *
+ * @category Geometry Interfaces Module API
+ */
+interface DOMMatrix extends DOMMatrixReadOnly {
+ a: number;
+ b: number;
+ c: number;
+ d: number;
+ e: number;
+ f: number;
+ m11: number;
+ m12: number;
+ m13: number;
+ m14: number;
+ m21: number;
+ m22: number;
+ m23: number;
+ m24: number;
+ m31: number;
+ m32: number;
+ m33: number;
+ m34: number;
+ m41: number;
+ m42: number;
+ m43: number;
+ m44: number;
+ /**
+ * Modifies the matrix by inverting it.
+ * If the matrix can't be inverted, its components are all set to `NaN`, and is2D property is set to `false`.
+ */
+ invertSelf(): DOMMatrix;
+ /**
+ * Modifies the matrix by post-multiplying it with the specified DOMMatrix.
+ * This is equivalent to the dot product `A⋅B`, where matrix `A` is the source matrix and `B` is the matrix given as an input to the method.
+ *
+ * @param other
+ */
+ multiplySelf(other?: DOMMatrixInit): DOMMatrix;
+ /**
+ * Modifies the matrix by pre-multiplying it with the specified DOMMatrix.
+ * This is equivalent to the dot product B⋅A, where matrix `A` is the source matrix and `B` is the matrix given as an input to the method.
+ *
+ * @param other
+ */
+ preMultiplySelf(other?: DOMMatrixInit): DOMMatrix;
+ /**
+ * Modifies the matrix by rotating it by the specified angle around the given vector.
+ *
+ * @param x
+ * @param y
+ * @param z
+ * @param angle in degrees
+ */
+ rotateAxisAngleSelf(
+ x?: number,
+ y?: number,
+ z?: number,
+ angle?: number,
+ ): DOMMatrix;
+ /**
+ * Modifies the matrix by rotating it by the angle between the specified vector and `(1, 0)`.
+ *
+ * @param x
+ * @param y
+ */
+ rotateFromVectorSelf(x?: number, y?: number): DOMMatrix;
+ /**
+ * Modifies the matrix by rotating itself around each axis by the specified number of degrees.
+ *
+ * @param rotZ yaw angle in degrees
+ */
+ rotateSelf(rotZ?: number): DOMMatrix;
+ /**
+ * Modifies the matrix by rotating itself around each axis by the specified number of degrees.
+ *
+ * @param rotX roll angle in degrees
+ * @param rotY pitch angle in degrees
+ * @param rotZ yaw angle in degrees
+ */
+ rotateSelf(rotX?: number, rotY?: number, rotZ?: number): DOMMatrix;
+ /**
+ * Modifies the matrix by applying the specified scaling factor to all three axes, centered on the given origin.
+ *
+ * @param scale
+ * @param originX
+ * @param originY
+ * @param originZ
+ */
+ scale3dSelf(
+ scale?: number,
+ originX?: number,
+ originY?: number,
+ originZ?: number,
+ ): DOMMatrix;
+ /**
+ * Modifies the matrix by applying the specified scaling factors, with the center located at the specified origin. Also returns itself.
+ * By default, the X and Z axes are scaled by `1` and the Y axis is given the same scaling value as the X axis.
+ * The default origin is `(0, 0, 0)`.
+ *
+ * @param scaleX
+ * @param scaleY
+ * @param scaleZ
+ * @param originX
+ * @param originY
+ * @param originZ
+ */
+ scaleSelf(
+ scaleX?: number,
+ scaleY?: number,
+ scaleZ?: number,
+ originX?: number,
+ originY?: number,
+ originZ?: number,
+ ): DOMMatrix;
+ /**
+ * NOTE: Not available in Worker
+ *
+ * Replaces the contents of the matrix with the matrix described by the specified transform or transforms.
+ *
+ * @param transformList
+ */
+ setMatrixValue(transformList: string): DOMMatrix;
+ /**
+ * Modifies the matrix by applying the specified skew transformation along the X-axis.
+ *
+ * @param sx in degrees
+ */
+ skewXSelf(sx?: number): DOMMatrix;
+ /**
+ * Modifies the matrix by applying the specified skew transformation along the Y-axis.
+ *
+ * @param sy in degrees
+ */
+ skewYSelf(sy?: number): DOMMatrix;
+ /**
+ * Modifies the matrix by applying the specified vector. The default vector is `(0, 0, 0)`.
+ *
+ * @param tx
+ * @param ty
+ * @param tz
+ */
+ translateSelf(tx?: number, ty?: number, tz?: number): DOMMatrix;
+}
+
+/**
+ * A 4×4 matrix (column-major order), suitable for 2D and 3D operations including rotation and translation.
+ * [MDN](https://developer.mozilla.org/docs/Web/API/DOMMatrix)
+ *
+ * ```
+ * | m11 m21 m31 m41 |
+ * | m12 m22 m32 m42 |
+ * | m13 m23 m33 m43 |
+ * | m14 m24 m34 m44 |
+ * ```
+ *
+ * @category Geometry Interfaces Module API
+ */
+declare var DOMMatrix: {
+ prototype: DOMMatrix;
+ new (init?: number[]): DOMMatrix;
+ new (init: DOMMatrix | DOMMatrixReadOnly): DOMMatrix;
+ /** NOTE: Not available in Worker */
+ new (init: string): DOMMatrix;
+ fromFloat32Array(array32: Float32Array): DOMMatrix;
+ fromFloat64Array(array64: Float64Array): DOMMatrix;
+ fromMatrix(other?: DOMMatrixInit): DOMMatrix;
+};
+
+/**
+ * A read-only 4×4 matrix (column-major order), suitable for 2D and 3D operations including rotation and translation.
+ * [MDN](https://developer.mozilla.org/docs/Web/API/DOMMatrixReadOnly)
+ *
+ * ```
+ * | m11 m21 m31 m41 |
+ * | m12 m22 m32 m42 |
+ * | m13 m23 m33 m43 |
+ * | m14 m24 m34 m44 |
+ * ```
+ *
+ * @category Geometry Interfaces Module API
+ */
+interface DOMMatrixReadOnly {
+ readonly a: number;
+ readonly b: number;
+ readonly c: number;
+ readonly d: number;
+ readonly e: number;
+ readonly f: number;
+ readonly is2D: boolean;
+ readonly isIdentity: boolean;
+ readonly m11: number;
+ readonly m12: number;
+ readonly m13: number;
+ readonly m14: number;
+ readonly m21: number;
+ readonly m22: number;
+ readonly m23: number;
+ readonly m24: number;
+ readonly m31: number;
+ readonly m32: number;
+ readonly m33: number;
+ readonly m34: number;
+ readonly m41: number;
+ readonly m42: number;
+ readonly m43: number;
+ readonly m44: number;
+ /** Returns a new `DOMMatrix` created by flipping the source matrix around its X-axis. */
+ flipX(): DOMMatrix;
+ /** Returns a new `DOMMatrix` created by flipping the source matrix around its Y-axis. */
+ flipY(): DOMMatrix;
+ /**
+ * Returns a new `DOMMatrix` created by inverting the source matrix.
+ * If the matrix cannot be inverted, the new matrix's components are all set to `NaN` and its is2D property is set to `false`.
+ */
+ inverse(): DOMMatrix;
+ /**
+ * Returns a new `DOMMatrix` created by computing the dot product of the source matrix and the specified matrix: `A⋅B`.
+ *
+ * @param other
+ */
+ multiply(other?: DOMMatrixInit): DOMMatrix;
+ /**
+ * Returns a new `DOMMatrix` created by rotating the source matrix around each of its axes by the specified number of degrees.
+ *
+ * @param rotZ yaw angle in degrees
+ */
+ rotate(rotZ?: number): DOMMatrix;
+ /**
+ * Returns a new `DOMMatrix` created by rotating the source matrix around each of its axes by the specified number of degrees.
+ *
+ * @param rotX roll angle in degrees
+ * @param rotY pitch angle in degrees
+ * @param rotZ yaw angle in degrees
+ */
+ rotate(rotX?: number, rotY?: number, rotZ?: number): DOMMatrix;
+ /**
+ * Returns a new DOMMatrix created by rotating the source matrix by the given angle around the specified vector.
+ *
+ * @param x
+ * @param y
+ * @param z
+ * @param angle in degrees
+ */
+ rotateAxisAngle(
+ x?: number,
+ y?: number,
+ z?: number,
+ angle?: number,
+ ): DOMMatrix;
+ /**
+ * Returns a new `DOMMatrix` created by rotating the source matrix by the angle between the specified vector and `(1, 0)`.
+ *
+ * @param x
+ * @param y
+ */
+ rotateFromVector(x?: number, y?: number): DOMMatrix;
+ /**
+ * Returns a new `DOMMatrix` created by scaling the source matrix by the amount specified for each axis, centered on the given origin.
+ * By default, the X and Z axes are scaled by `1` and the Y axis is given the same scaling value as the X axis.
+ * The default origin is `(0, 0, 0)`.
+ *
+ * @param scaleX
+ * @param scaleY
+ * @param scaleZ
+ * @param originX
+ * @param originY
+ * @param originZ
+ */
+ scale(
+ scaleX?: number,
+ scaleY?: number,
+ scaleZ?: number,
+ originX?: number,
+ originY?: number,
+ originZ?: number,
+ ): DOMMatrix;
+ /**
+ * Returns a new `DOMMatrix` created by scaling the source 3D matrix by the given factor along all its axes, centered on the specified origin point.
+ * The default origin is `(0, 0, 0)`.
+ *
+ * @param scale
+ * @param originX
+ * @param originY
+ * @param originZ
+ */
+ scale3d(
+ scale?: number,
+ originX?: number,
+ originY?: number,
+ originZ?: number,
+ ): DOMMatrix;
+ /**
+ * Returns a new `DOMMatrix` created by applying the specified scaling on the X, Y, and Z axes, centered at the given origin.
+ * By default, the X and Y axes' scaling factors are both `1`.
+ *
+ * @deprecated Supported for legacy reasons to be compatible with `SVGMatrix` as defined in SVG 1.1. Use `scale()` instead.
+ *
+ * @param scaleX
+ * @param scaleY
+ */
+ scaleNonUniform(scaleX?: number, scaleY?: number): DOMMatrix;
+ /**
+ * Returns a new DOMMatrix created by applying the specified skew transformation to the source matrix along its X-axis.
+ *
+ * @param sx in degrees
+ */
+ skewX(sx?: number): DOMMatrix;
+ /**
+ * Returns a new DOMMatrix created by applying the specified skew transformation to the source matrix along its Y-axis.
+ *
+ * @param sy in degrees
+ */
+ skewY(sy?: number): DOMMatrix;
+ toFloat32Array(): Float32Array;
+ toFloat64Array(): Float64Array;
+ toJSON(): {
+ a: number;
+ b: number;
+ c: number;
+ d: number;
+ e: number;
+ f: number;
+ is2D: boolean;
+ isIdentity: boolean;
+ m11: number;
+ m12: number;
+ m13: number;
+ m14: number;
+ m21: number;
+ m22: number;
+ m23: number;
+ m24: number;
+ m31: number;
+ m32: number;
+ m33: number;
+ m34: number;
+ m41: number;
+ m42: number;
+ m43: number;
+ m44: number;
+ };
+ transformPoint(point?: DOMPointInit): DOMPoint;
+ translate(tx?: number, ty?: number, tz?: number): DOMMatrix;
+ /** NOTE: Not available in Worker */
+ toString(): string;
+}
+
+/**
+ * A read-only 4×4 matrix (column-major order), suitable for 2D and 3D operations including rotation and translation.
+ * [MDN](https://developer.mozilla.org/docs/Web/API/DOMMatrixReadOnly)
+ *
+ * ```
+ * | m11 m21 m31 m41 |
+ * | m12 m22 m32 m42 |
+ * | m13 m23 m33 m43 |
+ * | m14 m24 m34 m44 |
+ * ```
+ *
+ * @category Geometry Interfaces Module API
+ */
+declare var DOMMatrixReadOnly: {
+ prototype: DOMMatrixReadOnly;
+ new (init?: number[]): DOMMatrixReadOnly;
+ new (init: DOMMatrix | DOMMatrixReadOnly): DOMMatrixReadOnly;
+ /** Not available in Worker */
+ new (init: string): DOMMatrixReadOnly;
+ fromFloat32Array(array32: Float32Array): DOMMatrixReadOnly;
+ fromFloat64Array(array64: Float64Array): DOMMatrixReadOnly;
+ fromMatrix(other?: DOMMatrixInit): DOMMatrixReadOnly;
+};
+
+/** @category Geometry Interfaces Module API */
+interface DOMPointInit {
+ w?: number;
+ x?: number;
+ y?: number;
+ z?: number;
+}
+
+/**
+ * A object represents a 2D or 3D point in a coordinate system; it includes values for the coordinates in up to three dimensions, as well as an optional perspective value.
+ * [MDN](https://developer.mozilla.org/docs/Web/API/DOMPoint)
+ *
+ * @category Geometry Interfaces Module API
+ */
+interface DOMPoint extends DOMPointReadOnly {
+ w: number;
+ x: number;
+ y: number;
+ z: number;
+}
+
+/**
+ * A object represents a 2D or 3D point in a coordinate system; it includes values for the coordinates in up to three dimensions, as well as an optional perspective value.
+ * [MDN](https://developer.mozilla.org/docs/Web/API/DOMPoint)
+ *
+ * @category Geometry Interfaces Module API
+ */
+declare var DOMPoint: {
+ prototype: DOMPoint;
+ new (x?: number, y?: number, z?: number, w?: number): DOMPoint;
+ fromPoint(other?: DOMPointInit): DOMPoint;
+};
+
+/**
+ * A read-only object represents a 2D or 3D point in a coordinate system; it includes values for the coordinates in up to three dimensions, as well as an optional perspective value.
+ * [MDN](https://developer.mozilla.org/docs/Web/API/DOMPointReadOnly)
+ *
+ * @category Geometry Interfaces Module API
+ */
+interface DOMPointReadOnly {
+ readonly w: number;
+ readonly x: number;
+ readonly y: number;
+ readonly z: number;
+ matrixTransform(matrix?: DOMMatrixInit): DOMPoint;
+ toJSON(): {
+ w: number;
+ x: number;
+ y: number;
+ z: number;
+ };
+}
+
+/**
+ * A read-only object represents a 2D or 3D point in a coordinate system; it includes values for the coordinates in up to three dimensions, as well as an optional perspective value.
+ * [MDN](https://developer.mozilla.org/docs/Web/API/DOMPointReadOnly)
+ *
+ * @category Geometry Interfaces Module API
+ */
+declare var DOMPointReadOnly: {
+ prototype: DOMPointReadOnly;
+ new (x?: number, y?: number, z?: number, w?: number): DOMPointReadOnly;
+ fromPoint(other?: DOMPointInit): DOMPointReadOnly;
+};
+
+/** @category Geometry Interfaces Module API */
+interface DOMQuadInit {
+ p1?: DOMPointInit;
+ p2?: DOMPointInit;
+ p3?: DOMPointInit;
+ p4?: DOMPointInit;
+}
+
+/**
+ * A collection of four DOMPoints defining the corners of an arbitrary quadrilateral.
+ * [MDN](https://developer.mozilla.org/docs/Web/API/DOMQuad)
+ *
+ * @category Geometry Interfaces Module API
+ */
+interface DOMQuad {
+ readonly p1: DOMPoint;
+ readonly p2: DOMPoint;
+ readonly p3: DOMPoint;
+ readonly p4: DOMPoint;
+ getBounds(): DOMRect;
+ toJSON(): {
+ p1: DOMPoint;
+ p2: DOMPoint;
+ p3: DOMPoint;
+ p4: DOMPoint;
+ };
+}
+
+/**
+ * A collection of four DOMPoints defining the corners of an arbitrary quadrilateral.
+ * [MDN](https://developer.mozilla.org/docs/Web/API/DOMQuad)
+ *
+ * @category Geometry Interfaces Module API
+ */
+declare var DOMQuad: {
+ prototype: DOMQuad;
+ new (
+ p1?: DOMPointInit,
+ p2?: DOMPointInit,
+ p3?: DOMPointInit,
+ p4?: DOMPointInit,
+ ): DOMQuad;
+ fromQuad(other?: DOMQuadInit): DOMQuad;
+ fromRect(other?: DOMRectInit): DOMQuad;
+};
+
+/** @category Geometry Interfaces Module API */
+interface DOMRectInit {
+ height?: number;
+ width?: number;
+ x?: number;
+ y?: number;
+}
+
+/**
+ * [MDN](https://developer.mozilla.org/docs/Web/API/DOMRect)
+ *
+ * @category Geometry Interfaces Module API
+ */
+interface DOMRect extends DOMRectReadOnly {
+ height: number;
+ width: number;
+ x: number;
+ y: number;
+}
+
+/**
+ * [MDN](https://developer.mozilla.org/docs/Web/API/DOMRect)
+ *
+ * @category Geometry Interfaces Module API
+ */
+declare var DOMRect: {
+ prototype: DOMRect;
+ new (x?: number, y?: number, width?: number, height?: number): DOMRect;
+ fromRect(other?: DOMRectInit): DOMRect;
+};
+
+/**
+ * [MDN](https://developer.mozilla.org/docs/Web/API/DOMRectReadOnly)
+ *
+ * @category Geometry Interfaces Module API
+ */
+interface DOMRectReadOnly {
+ readonly bottom: number;
+ readonly height: number;
+ readonly left: number;
+ readonly right: number;
+ readonly top: number;
+ readonly width: number;
+ readonly x: number;
+ readonly y: number;
+ toJSON(): {
+ bottom: number;
+ height: number;
+ left: number;
+ right: number;
+ top: number;
+ width: number;
+ x: number;
+ y: number;
+ };
+}
+
+/**
+ * [MDN](https://developer.mozilla.org/docs/Web/API/DOMRectReadOnly)
+ *
+ * @category Geometry Interfaces Module API
+ */
+declare var DOMRectReadOnly: {
+ prototype: DOMRectReadOnly;
+ new (
+ x?: number,
+ y?: number,
+ width?: number,
+ height?: number,
+ ): DOMRectReadOnly;
+ fromRect(other?: DOMRectInit): DOMRectReadOnly;
+};
diff --git a/ext/geometry/lib.rs b/ext/geometry/lib.rs
new file mode 100644
index 00000000000000..b12b9a24a502ca
--- /dev/null
+++ b/ext/geometry/lib.rs
@@ -0,0 +1,1559 @@
+// Copyright 2018-2025 the Deno authors. MIT license.
+
+use std::cell::Cell;
+use std::cell::RefCell;
+use std::mem;
+use std::path::PathBuf;
+use std::slice;
+
+use deno_core::cppgc;
+use deno_core::cppgc::SameObject;
+use deno_core::op2;
+use deno_core::v8;
+use deno_core::webidl;
+use deno_core::GarbageCollected;
+use deno_core::WebIDL;
+use nalgebra::Matrix3;
+use nalgebra::Matrix4;
+use nalgebra::Matrix4x2;
+use nalgebra::Matrix4x3;
+use nalgebra::Rotation3;
+use nalgebra::UnitVector3;
+use nalgebra::Vector3;
+use nalgebra::Vector4;
+
+deno_core::extension!(
+ deno_geometry,
+ deps = [deno_webidl, deno_web, deno_console],
+ objects = [DOMPointInner, DOMRectInner, DOMQuadInner, DOMMatrixInner],
+ esm = ["00_init.js"],
+ lazy_loaded_esm = ["01_geometry.js"],
+);
+
+pub fn get_declaration() -> PathBuf {
+ PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("lib.deno_geometry.d.ts")
+}
+
+#[derive(Debug, thiserror::Error, deno_error::JsError)]
+pub enum GeometryError {
+ #[class(type)]
+ #[error("Inconsistent 2d matrix value")]
+ Inconsistent2DMatrix,
+}
+
+#[derive(WebIDL, Debug)]
+#[webidl(dictionary)]
+pub struct DOMPointInit {
+ #[webidl(default = webidl::UnrestrictedDouble(0.0))]
+ x: webidl::UnrestrictedDouble,
+ #[webidl(default = webidl::UnrestrictedDouble(0.0))]
+ y: webidl::UnrestrictedDouble,
+ #[webidl(default = webidl::UnrestrictedDouble(0.0))]
+ z: webidl::UnrestrictedDouble,
+ #[webidl(default = webidl::UnrestrictedDouble(1.0))]
+ w: webidl::UnrestrictedDouble,
+}
+
+#[derive(Debug)]
+pub struct DOMPointInner {
+ inner: RefCell>,
+}
+
+impl GarbageCollected for DOMPointInner {}
+
+#[op2]
+impl DOMPointInner {
+ #[constructor]
+ #[cppgc]
+ pub fn constructor(
+ #[webidl] x: webidl::UnrestrictedDouble,
+ #[webidl] y: webidl::UnrestrictedDouble,
+ #[webidl] z: webidl::UnrestrictedDouble,
+ #[webidl] w: webidl::UnrestrictedDouble,
+ ) -> DOMPointInner {
+ DOMPointInner {
+ inner: RefCell::new(Vector4::new(*x, *y, *z, *w)),
+ }
+ }
+
+ #[reentrant]
+ #[static_method]
+ #[cppgc]
+ pub fn from_point(#[webidl] init: DOMPointInit) -> DOMPointInner {
+ DOMPointInner {
+ inner: RefCell::new(Vector4::new(*init.x, *init.y, *init.z, *init.w)),
+ }
+ }
+
+ #[fast]
+ #[getter]
+ pub fn x(&self) -> f64 {
+ self.inner.borrow().x
+ }
+
+ #[setter]
+ pub fn x(&self, #[webidl] value: webidl::UnrestrictedDouble) {
+ self.inner.borrow_mut().x = *value
+ }
+
+ #[fast]
+ #[getter]
+ pub fn y(&self) -> f64 {
+ self.inner.borrow().y
+ }
+
+ #[setter]
+ pub fn y(&self, #[webidl] value: webidl::UnrestrictedDouble) {
+ self.inner.borrow_mut().y = *value
+ }
+
+ #[fast]
+ #[getter]
+ pub fn z(&self) -> f64 {
+ self.inner.borrow().z
+ }
+
+ #[setter]
+ pub fn z(&self, #[webidl] value: webidl::UnrestrictedDouble) {
+ self.inner.borrow_mut().z = *value
+ }
+
+ #[fast]
+ #[getter]
+ pub fn w(&self) -> f64 {
+ self.inner.borrow().w
+ }
+
+ #[setter]
+ pub fn w(&self, #[webidl] value: webidl::UnrestrictedDouble) {
+ self.inner.borrow_mut().w = *value
+ }
+
+ #[cppgc]
+ pub fn matrix_transform(
+ &self,
+ #[cppgc] matrix: &DOMMatrixInner,
+ ) -> DOMPointInner {
+ let out = DOMPointInner {
+ inner: RefCell::new(Vector4::zeros()),
+ };
+ matrix_transform_point(matrix, self, &out);
+ out
+ }
+}
+
+#[derive(WebIDL, Debug)]
+#[webidl(dictionary)]
+pub struct DOMRectInit {
+ #[webidl(default = webidl::UnrestrictedDouble(0.0))]
+ x: webidl::UnrestrictedDouble,
+ #[webidl(default = webidl::UnrestrictedDouble(0.0))]
+ y: webidl::UnrestrictedDouble,
+ #[webidl(default = webidl::UnrestrictedDouble(0.0))]
+ width: webidl::UnrestrictedDouble,
+ #[webidl(default = webidl::UnrestrictedDouble(0.0))]
+ height: webidl::UnrestrictedDouble,
+}
+
+#[derive(Debug)]
+pub struct DOMRectInner {
+ x: Cell,
+ y: Cell,
+ width: Cell,
+ height: Cell,
+}
+
+impl GarbageCollected for DOMRectInner {}
+
+#[op2]
+impl DOMRectInner {
+ #[constructor]
+ #[cppgc]
+ pub fn constructor(
+ #[webidl] x: webidl::UnrestrictedDouble,
+ #[webidl] y: webidl::UnrestrictedDouble,
+ #[webidl] width: webidl::UnrestrictedDouble,
+ #[webidl] height: webidl::UnrestrictedDouble,
+ ) -> DOMRectInner {
+ DOMRectInner {
+ x: Cell::new(*x),
+ y: Cell::new(*y),
+ width: Cell::new(*width),
+ height: Cell::new(*height),
+ }
+ }
+
+ #[reentrant]
+ #[static_method]
+ #[cppgc]
+ pub fn from_rect(#[webidl] init: DOMRectInit) -> DOMRectInner {
+ DOMRectInner {
+ x: Cell::new(*init.x),
+ y: Cell::new(*init.y),
+ width: Cell::new(*init.width),
+ height: Cell::new(*init.height),
+ }
+ }
+
+ #[fast]
+ #[getter]
+ pub fn x(&self) -> f64 {
+ self.x.get()
+ }
+
+ #[setter]
+ pub fn x(&self, #[webidl] value: webidl::UnrestrictedDouble) {
+ self.x.set(*value)
+ }
+
+ #[fast]
+ #[getter]
+ pub fn y(&self) -> f64 {
+ self.y.get()
+ }
+
+ #[setter]
+ pub fn y(&self, #[webidl] value: webidl::UnrestrictedDouble) {
+ self.y.set(*value)
+ }
+
+ #[fast]
+ #[getter]
+ pub fn width(&self) -> f64 {
+ self.width.get()
+ }
+
+ #[setter]
+ pub fn width(&self, #[webidl] value: webidl::UnrestrictedDouble) {
+ self.width.set(*value)
+ }
+
+ #[fast]
+ #[getter]
+ pub fn height(&self) -> f64 {
+ self.height.get()
+ }
+
+ #[setter]
+ pub fn height(&self, #[webidl] value: webidl::UnrestrictedDouble) {
+ self.height.set(*value)
+ }
+
+ #[fast]
+ #[getter]
+ pub fn top(&self) -> f64 {
+ let y = self.y.get();
+ let height = self.height.get();
+ minimum(y, y + height)
+ }
+
+ #[fast]
+ #[getter]
+ pub fn right(&self) -> f64 {
+ let x = self.x.get();
+ let width = self.width.get();
+ maximum(x, x + width)
+ }
+
+ #[fast]
+ #[getter]
+ pub fn bottom(&self) -> f64 {
+ let y = self.y.get();
+ let height = self.height.get();
+ maximum(y, y + height)
+ }
+
+ #[fast]
+ #[getter]
+ pub fn left(&self) -> f64 {
+ let x = self.x.get();
+ let width = self.width.get();
+ minimum(x, x + width)
+ }
+}
+
+#[derive(WebIDL, Debug)]
+#[webidl(dictionary)]
+pub struct DOMQuadInit {
+ p1: DOMPointInit,
+ p2: DOMPointInit,
+ p3: DOMPointInit,
+ p4: DOMPointInit,
+}
+
+pub struct DOMQuadInner {
+ p1: SameObject,
+ p2: SameObject,
+ p3: SameObject,
+ p4: SameObject,
+}
+
+impl GarbageCollected for DOMQuadInner {}
+
+#[op2]
+impl DOMQuadInner {
+ #[constructor]
+ #[reentrant]
+ #[cppgc]
+ pub fn constructor(
+ scope: &mut v8::HandleScope,
+ #[webidl] p1: DOMPointInit,
+ #[webidl] p2: DOMPointInit,
+ #[webidl] p3: DOMPointInit,
+ #[webidl] p4: DOMPointInit,
+ ) -> DOMQuadInner {
+ #[inline]
+ fn from_point(
+ scope: &mut v8::HandleScope,
+ point: DOMPointInit,
+ ) -> SameObject {
+ let obj = SameObject::new();
+ obj.get(scope, |_| DOMPointInner {
+ inner: RefCell::new(Vector4::new(
+ *point.x, *point.y, *point.z, *point.w,
+ )),
+ });
+ obj
+ }
+
+ DOMQuadInner {
+ p1: from_point(scope, p1),
+ p2: from_point(scope, p2),
+ p3: from_point(scope, p3),
+ p4: from_point(scope, p4),
+ }
+ }
+
+ #[reentrant]
+ #[static_method]
+ #[cppgc]
+ pub fn from_rect(
+ scope: &mut v8::HandleScope,
+ #[webidl] rect: DOMRectInit,
+ ) -> DOMQuadInner {
+ #[inline]
+ fn create_point(
+ scope: &mut v8::HandleScope,
+ x: f64,
+ y: f64,
+ z: f64,
+ w: f64,
+ ) -> SameObject {
+ let obj = SameObject::new();
+ obj.get(scope, |_| DOMPointInner {
+ inner: RefCell::new(Vector4::new(x, y, z, w)),
+ });
+ obj
+ }
+
+ let DOMRectInit {
+ x,
+ y,
+ width,
+ height,
+ } = rect;
+ DOMQuadInner {
+ p1: create_point(scope, *x, *y, 0.0, 1.0),
+ p2: create_point(scope, *x + *width, *y, 0.0, 1.0),
+ p3: create_point(scope, *x + *width, *y + *height, 0.0, 1.0),
+ p4: create_point(scope, *x, *y + *height, 0.0, 1.0),
+ }
+ }
+
+ #[reentrant]
+ #[static_method]
+ #[cppgc]
+ pub fn from_quad(
+ scope: &mut v8::HandleScope,
+ #[webidl] quad: DOMQuadInit,
+ ) -> DOMQuadInner {
+ #[inline]
+ fn from_point(
+ scope: &mut v8::HandleScope,
+ point: DOMPointInit,
+ ) -> SameObject {
+ let obj = SameObject::new();
+ obj.get(scope, |_| DOMPointInner {
+ inner: RefCell::new(Vector4::new(
+ *point.x, *point.y, *point.z, *point.w,
+ )),
+ });
+ obj
+ }
+
+ DOMQuadInner {
+ p1: from_point(scope, quad.p1),
+ p2: from_point(scope, quad.p2),
+ p3: from_point(scope, quad.p3),
+ p4: from_point(scope, quad.p4),
+ }
+ }
+
+ #[getter]
+ #[global]
+ pub fn p1(&self, scope: &mut v8::HandleScope) -> v8::Global {
+ self.p1.get(scope, |_| unreachable!())
+ }
+
+ #[getter]
+ #[global]
+ pub fn p2(&self, scope: &mut v8::HandleScope) -> v8::Global {
+ self.p2.get(scope, |_| unreachable!())
+ }
+
+ #[getter]
+ #[global]
+ pub fn p3(&self, scope: &mut v8::HandleScope) -> v8::Global {
+ self.p3.get(scope, |_| unreachable!())
+ }
+
+ #[getter]
+ #[global]
+ pub fn p4(&self, scope: &mut v8::HandleScope) -> v8::Global {
+ self.p4.get(scope, |_| unreachable!())
+ }
+
+ #[cppgc]
+ pub fn get_bounds(&self, scope: &mut v8::HandleScope) -> DOMRectInner {
+ #[inline]
+ fn get_ptr(
+ scope: &mut v8::HandleScope,
+ value: &SameObject,
+ ) -> cppgc::Ptr {
+ let value = value.get(scope, |_| unreachable!());
+ let value = v8::Local::new(scope, value);
+ cppgc::try_unwrap_cppgc_object::(scope, value.cast())
+ .unwrap()
+ }
+
+ let p1 = get_ptr(scope, &self.p1);
+ let p2 = get_ptr(scope, &self.p2);
+ let p3 = get_ptr(scope, &self.p3);
+ let p4 = get_ptr(scope, &self.p4);
+ let p1 = *p1.inner.borrow();
+ let p2 = *p2.inner.borrow();
+ let p3 = *p3.inner.borrow();
+ let p4 = *p4.inner.borrow();
+ let left = minimum(minimum(p1.x, p2.x), minimum(p3.x, p4.x));
+ let top = minimum(minimum(p1.y, p2.y), minimum(p3.y, p4.y));
+ let right = maximum(maximum(p1.x, p2.x), maximum(p3.x, p4.x));
+ let bottom = maximum(maximum(p1.y, p2.y), maximum(p3.y, p4.y));
+ DOMRectInner {
+ x: Cell::new(left),
+ y: Cell::new(top),
+ width: Cell::new(right - left),
+ height: Cell::new(bottom - top),
+ }
+ }
+}
+
+#[derive(WebIDL, Debug)]
+#[webidl(dictionary)]
+pub struct DOMMatrixInit {
+ #[webidl(default = None)]
+ a: Option,
+ #[webidl(default = None)]
+ b: Option,
+ #[webidl(default = None)]
+ c: Option,
+ #[webidl(default = None)]
+ d: Option,
+ #[webidl(default = None)]
+ e: Option,
+ #[webidl(default = None)]
+ f: Option,
+ #[webidl(default = None)]
+ m11: Option,
+ #[webidl(default = None)]
+ m12: Option,
+ #[webidl(default = webidl::UnrestrictedDouble(0.0))]
+ m13: webidl::UnrestrictedDouble,
+ #[webidl(default = webidl::UnrestrictedDouble(0.0))]
+ m14: webidl::UnrestrictedDouble,
+ #[webidl(default = None)]
+ m21: Option,
+ #[webidl(default = None)]
+ m22: Option,
+ #[webidl(default = webidl::UnrestrictedDouble(0.0))]
+ m23: webidl::UnrestrictedDouble,
+ #[webidl(default = webidl::UnrestrictedDouble(0.0))]
+ m24: webidl::UnrestrictedDouble,
+ #[webidl(default = webidl::UnrestrictedDouble(0.0))]
+ m31: webidl::UnrestrictedDouble,
+ #[webidl(default = webidl::UnrestrictedDouble(0.0))]
+ m32: webidl::UnrestrictedDouble,
+ #[webidl(default = webidl::UnrestrictedDouble(1.0))]
+ m33: webidl::UnrestrictedDouble,
+ #[webidl(default = webidl::UnrestrictedDouble(0.0))]
+ m34: webidl::UnrestrictedDouble,
+ #[webidl(default = None)]
+ m41: Option,
+ #[webidl(default = None)]
+ m42: Option,
+ #[webidl(default = webidl::UnrestrictedDouble(0.0))]
+ m43: webidl::UnrestrictedDouble,
+ #[webidl(default = webidl::UnrestrictedDouble(1.0))]
+ m44: webidl::UnrestrictedDouble,
+ #[webidl(default = None)]
+ is_2d: Option,
+}
+
+#[derive(Debug, Clone)]
+pub struct DOMMatrixInner {
+ inner: RefCell>,
+ is_2d: Cell,
+}
+
+impl GarbageCollected for DOMMatrixInner {}
+
+/*
+ * NOTE: column-major order
+ *
+ * For a 2D 3x2 matrix, the index of properties in
+ * | a c 0 e | | 0 4 _ 12 |
+ * | b d 0 f | | 1 5 _ 13 |
+ * | 0 0 1 0 | is | _ _ _ _ |
+ * | 0 0 0 1 | | _ _ _ _ |
+ */
+const INDEX_A: usize = 0;
+const INDEX_B: usize = 1;
+const INDEX_C: usize = 4;
+const INDEX_D: usize = 5;
+const INDEX_E: usize = 12;
+const INDEX_F: usize = 13;
+
+/*
+ * NOTE: column-major order
+ *
+ * The index of properties in
+ * | m11 m21 m31 m41 | | 0 4 8 12 |
+ * | m12 m22 m32 m42 | | 1 5 9 13 |
+ * | m13 m23 m33 m43 | is | 2 6 10 14 |
+ * | m14 m24 m34 m44 | | 3 7 11 15 |
+ */
+const INDEX_M11: usize = 0;
+const INDEX_M12: usize = 1;
+const INDEX_M13: usize = 2;
+const INDEX_M14: usize = 3;
+const INDEX_M21: usize = 4;
+const INDEX_M22: usize = 5;
+const INDEX_M23: usize = 6;
+const INDEX_M24: usize = 7;
+const INDEX_M31: usize = 8;
+const INDEX_M32: usize = 9;
+const INDEX_M33: usize = 10;
+const INDEX_M34: usize = 11;
+const INDEX_M41: usize = 12;
+const INDEX_M42: usize = 13;
+const INDEX_M43: usize = 14;
+const INDEX_M44: usize = 15;
+
+#[op2]
+impl DOMMatrixInner {
+ #[constructor]
+ #[cppgc]
+ pub fn constructor(#[buffer] buffer: &[f64], is_2d: bool) -> DOMMatrixInner {
+ DOMMatrixInner {
+ inner: RefCell::new(Matrix4::from_column_slice(buffer)),
+ is_2d: Cell::new(is_2d),
+ }
+ }
+
+ #[reentrant]
+ #[static_method]
+ #[cppgc]
+ pub fn from_matrix(
+ #[webidl] init: DOMMatrixInit,
+ ) -> Result {
+ macro_rules! fixup {
+ ($value3d:expr, $value2d:expr, $default:expr) => {{
+ if let Some(value3d) = $value3d {
+ if let Some(value2d) = $value2d {
+ if !(*value3d == *value2d || value3d.is_nan() && value2d.is_nan()) {
+ return Err(GeometryError::Inconsistent2DMatrix);
+ }
+ }
+ value3d
+ } else if let Some(value2d) = $value2d {
+ value2d
+ } else {
+ webidl::UnrestrictedDouble($default)
+ }
+ }};
+ }
+
+ let m11 = fixup!(init.m11, init.a, 1.0);
+ let m12 = fixup!(init.m12, init.b, 0.0);
+ let m21 = fixup!(init.m21, init.c, 0.0);
+ let m22 = fixup!(init.m22, init.d, 1.0);
+ let m41 = fixup!(init.m41, init.e, 0.0);
+ let m42 = fixup!(init.m42, init.f, 0.0);
+ let is_2d = {
+ let is_2d_can_be_true = *init.m13 == 0.0
+ && *init.m14 == 0.0
+ && *init.m23 == 0.0
+ && *init.m24 == 0.0
+ && *init.m31 == 0.0
+ && *init.m32 == 0.0
+ && *init.m33 == 1.0
+ && *init.m34 == 0.0
+ && *init.m43 == 0.0
+ && *init.m44 == 1.0;
+ if let Some(is_2d) = init.is_2d {
+ if is_2d && !is_2d_can_be_true {
+ return Err(GeometryError::Inconsistent2DMatrix);
+ } else {
+ is_2d
+ }
+ } else {
+ is_2d_can_be_true
+ }
+ };
+
+ if is_2d {
+ Ok(DOMMatrixInner {
+ #[rustfmt::skip]
+ inner: RefCell::new(Matrix4::new(
+ *m11, *m21, 0.0, *m41,
+ *m12, *m22, 0.0, *m42,
+ 0.0, 0.0, 1.0, 0.0,
+ 0.0, 0.0, 0.0, 1.0,
+ )),
+ is_2d: Cell::new(true),
+ })
+ } else {
+ let DOMMatrixInit {
+ m13,
+ m14,
+ m23,
+ m24,
+ m31,
+ m32,
+ m33,
+ m34,
+ m43,
+ m44,
+ ..
+ } = init;
+ Ok(DOMMatrixInner {
+ #[rustfmt::skip]
+ inner: RefCell::new(Matrix4::new(
+ *m11, *m21, *m31, *m41,
+ *m12, *m22, *m32, *m42,
+ *m13, *m23, *m33, *m43,
+ *m14, *m24, *m34, *m44,
+ )),
+ is_2d: Cell::new(false),
+ })
+ }
+ }
+
+ #[static_method]
+ #[cppgc]
+ pub fn identity() -> DOMMatrixInner {
+ DOMMatrixInner {
+ inner: RefCell::new(Matrix4::identity()),
+ is_2d: Cell::new(true),
+ }
+ }
+
+ #[cppgc]
+ pub fn clone(&self) -> DOMMatrixInner {
+ self.clone()
+ }
+
+ #[fast]
+ #[getter]
+ pub fn a(&self) -> f64 {
+ // SAFETY: in-range access
+ unsafe { *self.inner.borrow().get_unchecked(INDEX_A) }
+ }
+
+ #[setter]
+ pub fn a(&self, #[webidl] value: webidl::UnrestrictedDouble) {
+ // SAFETY: in-range access
+ unsafe {
+ *self.inner.borrow_mut().get_unchecked_mut(INDEX_A) = *value;
+ }
+ }
+
+ #[fast]
+ #[getter]
+ pub fn b(&self) -> f64 {
+ // SAFETY: in-range access
+ unsafe { *self.inner.borrow().get_unchecked(INDEX_B) }
+ }
+
+ #[setter]
+ pub fn b(&self, #[webidl] value: webidl::UnrestrictedDouble) {
+ // SAFETY: in-range access
+ unsafe {
+ *self.inner.borrow_mut().get_unchecked_mut(INDEX_B) = *value;
+ }
+ }
+
+ #[fast]
+ #[getter]
+ pub fn c(&self) -> f64 {
+ // SAFETY: in-range access
+ unsafe { *self.inner.borrow().get_unchecked(INDEX_C) }
+ }
+
+ #[setter]
+ pub fn c(&self, #[webidl] value: webidl::UnrestrictedDouble) {
+ // SAFETY: in-range access
+ unsafe {
+ *self.inner.borrow_mut().get_unchecked_mut(INDEX_C) = *value;
+ }
+ }
+
+ #[fast]
+ #[getter]
+ pub fn d(&self) -> f64 {
+ // SAFETY: in-range access
+ unsafe { *self.inner.borrow().get_unchecked(INDEX_D) }
+ }
+
+ #[setter]
+ pub fn d(&self, #[webidl] value: webidl::UnrestrictedDouble) {
+ // SAFETY: in-range access
+ unsafe {
+ *self.inner.borrow_mut().get_unchecked_mut(INDEX_D) = *value;
+ }
+ }
+
+ #[fast]
+ #[getter]
+ pub fn e(&self) -> f64 {
+ // SAFETY: in-range access
+ unsafe { *self.inner.borrow().get_unchecked(INDEX_E) }
+ }
+
+ #[setter]
+ pub fn e(&self, #[webidl] value: webidl::UnrestrictedDouble) {
+ // SAFETY: in-range access
+ unsafe {
+ *self.inner.borrow_mut().get_unchecked_mut(INDEX_E) = *value;
+ }
+ }
+
+ #[fast]
+ #[getter]
+ pub fn f(&self) -> f64 {
+ // SAFETY: in-range access
+ unsafe { *self.inner.borrow().get_unchecked(INDEX_F) }
+ }
+
+ #[setter]
+ pub fn f(&self, #[webidl] value: webidl::UnrestrictedDouble) {
+ // SAFETY: in-range access
+ unsafe {
+ *self.inner.borrow_mut().get_unchecked_mut(INDEX_F) = *value;
+ }
+ }
+
+ #[fast]
+ #[getter]
+ pub fn m11(&self) -> f64 {
+ // SAFETY: in-range access
+ unsafe { *self.inner.borrow().get_unchecked(INDEX_M11) }
+ }
+
+ #[setter]
+ pub fn m11(&self, #[webidl] value: webidl::UnrestrictedDouble) {
+ // SAFETY: in-range access
+ unsafe {
+ *self.inner.borrow_mut().get_unchecked_mut(INDEX_M11) = *value;
+ }
+ }
+
+ #[fast]
+ #[getter]
+ pub fn m12(&self) -> f64 {
+ // SAFETY: in-range access
+ unsafe { *self.inner.borrow().get_unchecked(INDEX_M12) }
+ }
+
+ #[setter]
+ pub fn m12(&self, #[webidl] value: webidl::UnrestrictedDouble) {
+ // SAFETY: in-range access
+ unsafe {
+ *self.inner.borrow_mut().get_unchecked_mut(INDEX_M12) = *value;
+ }
+ }
+
+ #[fast]
+ #[getter]
+ pub fn m13(&self) -> f64 {
+ // SAFETY: in-range access
+ unsafe { *self.inner.borrow().get_unchecked(INDEX_M13) }
+ }
+
+ #[setter]
+ pub fn m13(&self, #[webidl] value: webidl::UnrestrictedDouble) {
+ // SAFETY: in-range access
+ unsafe {
+ *self.inner.borrow_mut().get_unchecked_mut(INDEX_M13) = *value;
+ }
+ if *value != 0.0 {
+ self.is_2d.set(false);
+ }
+ }
+
+ #[fast]
+ #[getter]
+ pub fn m14(&self) -> f64 {
+ // SAFETY: in-range access
+ unsafe { *self.inner.borrow().get_unchecked(INDEX_M14) }
+ }
+
+ #[setter]
+ pub fn m14(&self, #[webidl] value: webidl::UnrestrictedDouble) {
+ // SAFETY: in-range access
+ unsafe {
+ *self.inner.borrow_mut().get_unchecked_mut(INDEX_M14) = *value;
+ }
+ if *value != 0.0 {
+ self.is_2d.set(false);
+ }
+ }
+
+ #[fast]
+ #[getter]
+ pub fn m21(&self) -> f64 {
+ // SAFETY: in-range access
+ unsafe { *self.inner.borrow().get_unchecked(INDEX_M21) }
+ }
+
+ #[setter]
+ pub fn m21(&self, #[webidl] value: webidl::UnrestrictedDouble) {
+ // SAFETY: in-range access
+ unsafe {
+ *self.inner.borrow_mut().get_unchecked_mut(INDEX_M21) = *value;
+ }
+ }
+
+ #[fast]
+ #[getter]
+ pub fn m22(&self) -> f64 {
+ // SAFETY: in-range access
+ unsafe { *self.inner.borrow().get_unchecked(INDEX_M22) }
+ }
+
+ #[setter]
+ pub fn m22(&self, #[webidl] value: webidl::UnrestrictedDouble) {
+ // SAFETY: in-range access
+ unsafe {
+ *self.inner.borrow_mut().get_unchecked_mut(INDEX_M22) = *value;
+ }
+ }
+
+ #[fast]
+ #[getter]
+ pub fn m23(&self) -> f64 {
+ // SAFETY: in-range access
+ unsafe { *self.inner.borrow().get_unchecked(INDEX_M23) }
+ }
+
+ #[setter]
+ pub fn m23(&self, #[webidl] value: webidl::UnrestrictedDouble) {
+ // SAFETY: in-range access
+ unsafe {
+ *self.inner.borrow_mut().get_unchecked_mut(INDEX_M23) = *value;
+ }
+ if *value != 0.0 {
+ self.is_2d.set(false);
+ }
+ }
+
+ #[fast]
+ #[getter]
+ pub fn m24(&self) -> f64 {
+ // SAFETY: in-range access
+ unsafe { *self.inner.borrow().get_unchecked(INDEX_M24) }
+ }
+
+ #[setter]
+ pub fn m24(&self, #[webidl] value: webidl::UnrestrictedDouble) {
+ // SAFETY: in-range access
+ unsafe {
+ *self.inner.borrow_mut().get_unchecked_mut(INDEX_M24) = *value;
+ }
+ if *value != 0.0 {
+ self.is_2d.set(false);
+ }
+ }
+
+ #[fast]
+ #[getter]
+ pub fn m31(&self) -> f64 {
+ // SAFETY: in-range access
+ unsafe { *self.inner.borrow().get_unchecked(INDEX_M31) }
+ }
+
+ #[setter]
+ pub fn m31(&self, #[webidl] value: webidl::UnrestrictedDouble) {
+ // SAFETY: in-range access
+ unsafe {
+ *self.inner.borrow_mut().get_unchecked_mut(INDEX_M31) = *value;
+ }
+ if *value != 0.0 {
+ self.is_2d.set(false);
+ }
+ }
+
+ #[fast]
+ #[getter]
+ pub fn m32(&self) -> f64 {
+ // SAFETY: in-range access
+ unsafe { *self.inner.borrow().get_unchecked(INDEX_M32) }
+ }
+
+ #[setter]
+ pub fn m32(&self, #[webidl] value: webidl::UnrestrictedDouble) {
+ // SAFETY: in-range access
+ unsafe {
+ *self.inner.borrow_mut().get_unchecked_mut(INDEX_M32) = *value;
+ }
+ if *value != 0.0 {
+ self.is_2d.set(false);
+ }
+ }
+
+ #[fast]
+ #[getter]
+ pub fn m33(&self) -> f64 {
+ // SAFETY: in-range access
+ unsafe { *self.inner.borrow().get_unchecked(INDEX_M33) }
+ }
+
+ #[setter]
+ pub fn m33(&self, #[webidl] value: webidl::UnrestrictedDouble) {
+ // SAFETY: in-range access
+ unsafe {
+ *self.inner.borrow_mut().get_unchecked_mut(INDEX_M33) = *value;
+ }
+ if *value != 1.0 {
+ self.is_2d.set(false);
+ }
+ }
+
+ #[fast]
+ #[getter]
+ pub fn m34(&self) -> f64 {
+ // SAFETY: in-range access
+ unsafe { *self.inner.borrow().get_unchecked(INDEX_M34) }
+ }
+
+ #[setter]
+ pub fn m34(&self, #[webidl] value: webidl::UnrestrictedDouble) {
+ // SAFETY: in-range access
+ unsafe {
+ *self.inner.borrow_mut().get_unchecked_mut(INDEX_M34) = *value;
+ }
+ if *value != 0.0 {
+ self.is_2d.set(false);
+ }
+ }
+
+ #[fast]
+ #[getter]
+ pub fn m41(&self) -> f64 {
+ // SAFETY: in-range access
+ unsafe { *self.inner.borrow().get_unchecked(INDEX_M41) }
+ }
+
+ #[setter]
+ pub fn m41(&self, #[webidl] value: webidl::UnrestrictedDouble) {
+ // SAFETY: in-range access
+ unsafe {
+ *self.inner.borrow_mut().get_unchecked_mut(INDEX_M41) = *value;
+ }
+ }
+
+ #[fast]
+ #[getter]
+ pub fn m42(&self) -> f64 {
+ // SAFETY: in-range access
+ unsafe { *self.inner.borrow().get_unchecked(INDEX_M42) }
+ }
+
+ #[setter]
+ pub fn m42(&self, #[webidl] value: webidl::UnrestrictedDouble) {
+ // SAFETY: in-range access
+ unsafe {
+ *self.inner.borrow_mut().get_unchecked_mut(INDEX_M42) = *value;
+ }
+ }
+
+ #[fast]
+ #[getter]
+ pub fn m43(&self) -> f64 {
+ // SAFETY: in-range access
+ unsafe { *self.inner.borrow().get_unchecked(INDEX_M43) }
+ }
+
+ #[setter]
+ pub fn m43(&self, #[webidl] value: webidl::UnrestrictedDouble) {
+ // SAFETY: in-range access
+ unsafe {
+ *self.inner.borrow_mut().get_unchecked_mut(INDEX_M43) = *value;
+ }
+ if *value != 0.0 {
+ self.is_2d.set(false);
+ }
+ }
+
+ #[fast]
+ #[getter]
+ pub fn m44(&self) -> f64 {
+ // SAFETY: in-range access
+ unsafe { *self.inner.borrow().get_unchecked(INDEX_M44) }
+ }
+
+ #[setter]
+ pub fn m44(&self, #[webidl] value: webidl::UnrestrictedDouble) {
+ // SAFETY: in-range access
+ unsafe {
+ *self.inner.borrow_mut().get_unchecked_mut(INDEX_M44) = *value;
+ }
+ if *value != 1.0 {
+ self.is_2d.set(false);
+ }
+ }
+
+ #[fast]
+ #[getter]
+ pub fn is_2d(&self) -> bool {
+ self.is_2d.get()
+ }
+
+ #[fast]
+ #[getter]
+ pub fn is_identity(&self) -> bool {
+ let inner = self.inner.borrow();
+ // SAFETY: in-range access
+ unsafe {
+ *inner.get_unchecked(INDEX_M11) == 1.0
+ && *inner.get_unchecked(INDEX_M12) == 0.0
+ && *inner.get_unchecked(INDEX_M13) == 0.0
+ && *inner.get_unchecked(INDEX_M14) == 0.0
+ && *inner.get_unchecked(INDEX_M21) == 0.0
+ && *inner.get_unchecked(INDEX_M22) == 1.0
+ && *inner.get_unchecked(INDEX_M23) == 0.0
+ && *inner.get_unchecked(INDEX_M24) == 0.0
+ && *inner.get_unchecked(INDEX_M31) == 0.0
+ && *inner.get_unchecked(INDEX_M32) == 0.0
+ && *inner.get_unchecked(INDEX_M33) == 1.0
+ && *inner.get_unchecked(INDEX_M34) == 0.0
+ && *inner.get_unchecked(INDEX_M41) == 0.0
+ && *inner.get_unchecked(INDEX_M42) == 0.0
+ && *inner.get_unchecked(INDEX_M43) == 0.0
+ && *inner.get_unchecked(INDEX_M44) == 1.0
+ }
+ }
+
+ #[fast]
+ #[getter]
+ pub fn is_finite(&self) -> bool {
+ self
+ .inner
+ .borrow()
+ .into_iter()
+ .all(|&item| item.is_finite())
+ }
+
+ #[arraybuffer]
+ pub fn to_buffer(&self) -> Vec {
+ // SAFETY: in-range access
+ unsafe {
+ slice::from_raw_parts(
+ self.inner.borrow().as_slice().as_ptr() as *mut u8,
+ mem::size_of::() * 16,
+ )
+ }
+ .to_vec()
+ }
+
+ #[cppgc]
+ pub fn translate(
+ &self,
+ #[webidl] tx: webidl::UnrestrictedDouble,
+ #[webidl] ty: webidl::UnrestrictedDouble,
+ #[webidl] tz: webidl::UnrestrictedDouble,
+ ) -> DOMMatrixInner {
+ let out = self.clone();
+ matrix_translate(&out, *tx, *ty, *tz);
+ out
+ }
+
+ pub fn translate_self(
+ &self,
+ #[webidl] tx: webidl::UnrestrictedDouble,
+ #[webidl] ty: webidl::UnrestrictedDouble,
+ #[webidl] tz: webidl::UnrestrictedDouble,
+ ) {
+ matrix_translate(self, *tx, *ty, *tz);
+ }
+
+ #[cppgc]
+ pub fn scale_without_origin(
+ &self,
+ #[webidl] sx: webidl::UnrestrictedDouble,
+ #[webidl] sy: webidl::UnrestrictedDouble,
+ #[webidl] sz: webidl::UnrestrictedDouble,
+ ) -> DOMMatrixInner {
+ let out = self.clone();
+ matrix_scale_without_origin(&out, *sx, *sy, *sz);
+ out
+ }
+
+ pub fn scale_without_origin_self(
+ &self,
+ #[webidl] sx: webidl::UnrestrictedDouble,
+ #[webidl] sy: webidl::UnrestrictedDouble,
+ #[webidl] sz: webidl::UnrestrictedDouble,
+ ) {
+ matrix_scale_without_origin(self, *sx, *sy, *sz);
+ }
+
+ #[cppgc]
+ pub fn scale_with_origin(
+ &self,
+ #[webidl] sx: webidl::UnrestrictedDouble,
+ #[webidl] sy: webidl::UnrestrictedDouble,
+ #[webidl] sz: webidl::UnrestrictedDouble,
+ #[webidl] origin_x: webidl::UnrestrictedDouble,
+ #[webidl] origin_y: webidl::UnrestrictedDouble,
+ #[webidl] origin_z: webidl::UnrestrictedDouble,
+ ) -> DOMMatrixInner {
+ let out = self.clone();
+ matrix_scale_with_origin(
+ &out, *sx, *sy, *sz, *origin_x, *origin_y, *origin_z,
+ );
+ out
+ }
+
+ pub fn scale_with_origin_self(
+ &self,
+ #[webidl] sx: webidl::UnrestrictedDouble,
+ #[webidl] sy: webidl::UnrestrictedDouble,
+ #[webidl] sz: webidl::UnrestrictedDouble,
+ #[webidl] origin_x: webidl::UnrestrictedDouble,
+ #[webidl] origin_y: webidl::UnrestrictedDouble,
+ #[webidl] origin_z: webidl::UnrestrictedDouble,
+ ) {
+ matrix_scale_with_origin(
+ self, *sx, *sy, *sz, *origin_x, *origin_y, *origin_z,
+ );
+ }
+
+ #[cppgc]
+ pub fn rotate(
+ &self,
+ #[webidl] roll_deg: webidl::UnrestrictedDouble,
+ #[webidl] pitch_deg: webidl::UnrestrictedDouble,
+ #[webidl] yaw_deg: webidl::UnrestrictedDouble,
+ ) -> DOMMatrixInner {
+ let out = self.clone();
+ matrix_rotate(&out, *roll_deg, *pitch_deg, *yaw_deg);
+ out
+ }
+
+ pub fn rotate_self(
+ &self,
+ #[webidl] roll_deg: webidl::UnrestrictedDouble,
+ #[webidl] pitch_deg: webidl::UnrestrictedDouble,
+ #[webidl] yaw_deg: webidl::UnrestrictedDouble,
+ ) {
+ matrix_rotate(self, *roll_deg, *pitch_deg, *yaw_deg);
+ }
+
+ #[cppgc]
+ pub fn rotate_from_vector(
+ &self,
+ #[webidl] x: webidl::UnrestrictedDouble,
+ #[webidl] y: webidl::UnrestrictedDouble,
+ ) -> DOMMatrixInner {
+ let out = self.clone();
+ matrix_rotate_from_vector(&out, *x, *y);
+ out
+ }
+
+ pub fn rotate_from_vector_self(
+ &self,
+ #[webidl] x: webidl::UnrestrictedDouble,
+ #[webidl] y: webidl::UnrestrictedDouble,
+ ) {
+ matrix_rotate_from_vector(self, *x, *y);
+ }
+
+ #[cppgc]
+ pub fn rotate_axis_angle(
+ &self,
+ #[webidl] x: webidl::UnrestrictedDouble,
+ #[webidl] y: webidl::UnrestrictedDouble,
+ #[webidl] z: webidl::UnrestrictedDouble,
+ #[webidl] angle_deg: webidl::UnrestrictedDouble,
+ ) -> DOMMatrixInner {
+ let out = self.clone();
+ matrix_rotate_axis_angle(&out, *x, *y, *z, *angle_deg);
+ out
+ }
+
+ pub fn rotate_axis_angle_self(
+ &self,
+ #[webidl] x: webidl::UnrestrictedDouble,
+ #[webidl] y: webidl::UnrestrictedDouble,
+ #[webidl] z: webidl::UnrestrictedDouble,
+ #[webidl] angle_deg: webidl::UnrestrictedDouble,
+ ) {
+ matrix_rotate_axis_angle(self, *x, *y, *z, *angle_deg);
+ }
+
+ #[cppgc]
+ pub fn skew_x(
+ &self,
+ #[webidl] x_deg: webidl::UnrestrictedDouble,
+ ) -> DOMMatrixInner {
+ let out = self.clone();
+ matrix_skew_x(&out, *x_deg);
+ out
+ }
+
+ pub fn skew_x_self(&self, #[webidl] x_deg: webidl::UnrestrictedDouble) {
+ matrix_skew_x(self, *x_deg);
+ }
+
+ #[cppgc]
+ pub fn skew_y(
+ &self,
+ #[webidl] y_deg: webidl::UnrestrictedDouble,
+ ) -> DOMMatrixInner {
+ let out = self.clone();
+ matrix_skew_y(&out, *y_deg);
+ out
+ }
+
+ pub fn skew_y_self(&self, #[webidl] y_deg: webidl::UnrestrictedDouble) {
+ matrix_skew_y(self, *y_deg);
+ }
+
+ #[cppgc]
+ pub fn multiply(&self, #[cppgc] other: &DOMMatrixInner) -> DOMMatrixInner {
+ let out = DOMMatrixInner {
+ inner: RefCell::new(Matrix4::zeros()),
+ is_2d: Cell::new(true),
+ };
+ matrix_multiply(&out, self, other);
+ out
+ }
+
+ #[fast]
+ pub fn multiply_self(&self, #[cppgc] other: &DOMMatrixInner) {
+ let result = DOMMatrixInner {
+ inner: RefCell::new(Matrix4::zeros()),
+ is_2d: Cell::new(true),
+ };
+ matrix_multiply(&result, self, other);
+ self.inner.borrow_mut().copy_from(&result.inner.borrow());
+ self.is_2d.set(result.is_2d.get());
+ }
+
+ #[fast]
+ pub fn pre_multiply_self(&self, #[cppgc] other: &DOMMatrixInner) {
+ let result = DOMMatrixInner {
+ inner: RefCell::new(Matrix4::zeros()),
+ is_2d: Cell::new(true),
+ };
+ matrix_multiply(&result, other, self);
+ self.inner.borrow_mut().copy_from(&result.inner.borrow());
+ self.is_2d.set(result.is_2d.get());
+ }
+
+ #[cppgc]
+ pub fn flip_x(&self) -> DOMMatrixInner {
+ let out = self.clone();
+ matrix_flip_x(&out);
+ out
+ }
+
+ #[cppgc]
+ pub fn flip_y(&self) -> DOMMatrixInner {
+ let out = self.clone();
+ matrix_flip_y(&out);
+ out
+ }
+
+ #[cppgc]
+ pub fn inverse(&self) -> DOMMatrixInner {
+ let out = self.clone();
+ matrix_inverse(&out);
+ out
+ }
+
+ #[fast]
+ pub fn invert_self(&self) {
+ matrix_inverse(self);
+ }
+
+ #[cppgc]
+ pub fn transform_point(
+ &self,
+ #[cppgc] point: &DOMPointInner,
+ ) -> DOMPointInner {
+ let out = DOMPointInner {
+ inner: RefCell::new(Vector4::zeros()),
+ };
+ matrix_transform_point(self, point, &out);
+ out
+ }
+}
+
+// TODO(petamoriken) Use f64::maximum instead https://github.com/rust-lang/rust/issues/91079
+#[inline]
+fn maximum(a: f64, b: f64) -> f64 {
+ if a > b {
+ a
+ } else if b > a {
+ b
+ } else if a == b {
+ if a.is_sign_positive() && b.is_sign_negative() {
+ a
+ } else {
+ b
+ }
+ } else {
+ // At least one input is NaN. Use `+` to perform NaN propagation and quieting.
+ a + b
+ }
+}
+
+// TODO(petamoriken) Use f64::minimum instead https://github.com/rust-lang/rust/issues/91079
+#[inline]
+fn minimum(a: f64, b: f64) -> f64 {
+ if a < b {
+ a
+ } else if b < a {
+ b
+ } else if a == b {
+ if a.is_sign_negative() && b.is_sign_positive() {
+ a
+ } else {
+ b
+ }
+ } else {
+ // At least one input is NaN. Use `+` to perform NaN propagation and quieting.
+ a + b
+ }
+}
+
+#[inline]
+fn matrix_translate(matrix: &DOMMatrixInner, tx: f64, ty: f64, tz: f64) {
+ let mut inner = matrix.inner.borrow_mut();
+ let is_2d = matrix.is_2d.get();
+ let shift = Vector3::new(tx, ty, tz);
+ inner.prepend_translation_mut(&shift);
+ matrix.is_2d.set(is_2d && tz == 0.0);
+}
+
+#[inline]
+fn matrix_scale_without_origin(
+ matrix: &DOMMatrixInner,
+ sx: f64,
+ sy: f64,
+ sz: f64,
+) {
+ let mut inner = matrix.inner.borrow_mut();
+ let is_2d = matrix.is_2d.get();
+ let scaling = Vector3::new(sx, sy, sz);
+ inner.prepend_nonuniform_scaling_mut(&scaling);
+ matrix.is_2d.set(is_2d && sz == 1.0);
+}
+
+#[inline]
+fn matrix_scale_with_origin(
+ matrix: &DOMMatrixInner,
+ sx: f64,
+ sy: f64,
+ sz: f64,
+ origin_x: f64,
+ origin_y: f64,
+ origin_z: f64,
+) {
+ let mut inner = matrix.inner.borrow_mut();
+ let is_2d = matrix.is_2d.get();
+ let scaling = Vector3::new(sx, sy, sz);
+ let mut shift = Vector3::new(origin_x, origin_y, origin_z);
+ inner.prepend_translation_mut(&shift);
+ inner.prepend_nonuniform_scaling_mut(&scaling);
+ shift.neg_mut();
+ inner.prepend_translation_mut(&shift);
+ matrix.is_2d.set(is_2d && sz == 1.0 && origin_z == 0.0);
+}
+
+#[inline]
+fn matrix_rotate(
+ matrix: &DOMMatrixInner,
+ roll_deg: f64,
+ pitch_deg: f64,
+ yaw_deg: f64,
+) {
+ let mut inner = matrix.inner.borrow_mut();
+ let is_2d = matrix.is_2d.get();
+ let rotation = Rotation3::from_euler_angles(
+ roll_deg.to_radians(),
+ pitch_deg.to_radians(),
+ yaw_deg.to_radians(),
+ )
+ .to_homogeneous();
+ let mut result = Matrix4x3::zeros();
+ inner.mul_to(&rotation.fixed_view::<4, 3>(0, 0), &mut result);
+ inner.set_column(0, &result.column(0));
+ inner.set_column(1, &result.column(1));
+ inner.set_column(2, &result.column(2));
+ matrix
+ .is_2d
+ .set(is_2d && roll_deg == 0.0 && pitch_deg == 0.0);
+}
+
+#[inline]
+fn matrix_rotate_from_vector(matrix: &DOMMatrixInner, x: f64, y: f64) {
+ let mut inner = matrix.inner.borrow_mut();
+ let rotation =
+ Rotation3::from_axis_angle(&Vector3::z_axis(), y.atan2(x)).to_homogeneous();
+ let mut result = Matrix4x3::zeros();
+ inner.mul_to(&rotation.fixed_view::<4, 3>(0, 0), &mut result);
+ inner.set_column(0, &result.column(0));
+ inner.set_column(1, &result.column(1));
+ inner.set_column(2, &result.column(2));
+}
+
+#[inline]
+fn matrix_rotate_axis_angle(
+ matrix: &DOMMatrixInner,
+ x: f64,
+ y: f64,
+ z: f64,
+ angle_deg: f64,
+) {
+ if x == 0.0 && y == 0.0 && z == 0.0 {
+ return;
+ }
+ let mut inner = matrix.inner.borrow_mut();
+ let is_2d = matrix.is_2d.get();
+ let rotation = Rotation3::from_axis_angle(
+ &UnitVector3::new_normalize(Vector3::new(x, y, z)),
+ angle_deg.to_radians(),
+ )
+ .to_homogeneous();
+ let mut result = Matrix4x3::zeros();
+ inner.mul_to(&rotation.fixed_view::<4, 3>(0, 0), &mut result);
+ inner.set_column(0, &result.column(0));
+ inner.set_column(1, &result.column(1));
+ inner.set_column(2, &result.column(2));
+ matrix.is_2d.set(is_2d && x == 0.0 && y == 0.0);
+}
+
+#[inline]
+fn matrix_skew_x(matrix: &DOMMatrixInner, x_deg: f64) {
+ let mut inner = matrix.inner.borrow_mut();
+ let skew =
+ Matrix4x2::new(1.0, x_deg.to_radians().tan(), 0.0, 1.0, 0.0, 0.0, 0.0, 0.0);
+ let mut result = Matrix4x2::zeros();
+ inner.mul_to(&skew, &mut result);
+ inner.set_column(0, &result.column(0));
+ inner.set_column(1, &result.column(1));
+}
+
+#[inline]
+fn matrix_skew_y(matrix: &DOMMatrixInner, y_deg: f64) {
+ let mut inner = matrix.inner.borrow_mut();
+ let skew =
+ Matrix4x2::new(1.0, 0.0, y_deg.to_radians().tan(), 1.0, 0.0, 0.0, 0.0, 0.0);
+ let mut result = Matrix4x2::zeros();
+ inner.mul_to(&skew, &mut result);
+ inner.set_column(0, &result.column(0));
+ inner.set_column(1, &result.column(1));
+}
+
+#[inline]
+fn matrix_multiply(
+ out: &DOMMatrixInner,
+ lhs: &DOMMatrixInner,
+ rhs: &DOMMatrixInner,
+) {
+ let lhs_inner = lhs.inner.borrow();
+ let lhs_is_2d = lhs.is_2d.get();
+ let rhs_inner = rhs.inner.borrow();
+ let rhs_is_2d = rhs.is_2d.get();
+ let mut out_inner = out.inner.borrow_mut();
+ lhs_inner.mul_to(&rhs_inner, &mut out_inner);
+ out.is_2d.set(lhs_is_2d && rhs_is_2d);
+}
+
+#[inline]
+fn matrix_flip_x(matrix: &DOMMatrixInner) {
+ let mut inner = matrix.inner.borrow_mut();
+ inner.column_mut(0).neg_mut();
+}
+
+#[inline]
+fn matrix_flip_y(matrix: &DOMMatrixInner) {
+ let mut inner = matrix.inner.borrow_mut();
+ inner.column_mut(1).neg_mut();
+}
+
+#[inline]
+fn matrix_inverse(matrix: &DOMMatrixInner) {
+ let mut inner = matrix.inner.borrow_mut();
+ let is_2d = matrix.is_2d.get();
+ if inner.iter().any(|&x| x.is_infinite()) {
+ inner.fill(f64::NAN);
+ matrix.is_2d.set(false);
+ return;
+ }
+ if is_2d {
+ // SAFETY: in-range access
+ let mut matrix3 = unsafe {
+ Matrix3::new(
+ *inner.get_unchecked(INDEX_A),
+ *inner.get_unchecked(INDEX_C),
+ *inner.get_unchecked(INDEX_E),
+ *inner.get_unchecked(INDEX_B),
+ *inner.get_unchecked(INDEX_D),
+ *inner.get_unchecked(INDEX_F),
+ 0.0,
+ 0.0,
+ 1.0,
+ )
+ };
+ if !matrix3.try_inverse_mut() {
+ inner.fill(f64::NAN);
+ matrix.is_2d.set(false);
+ return;
+ }
+ // SAFETY: in-range access
+ unsafe {
+ *inner.get_unchecked_mut(INDEX_A) = *matrix3.get_unchecked(0);
+ *inner.get_unchecked_mut(INDEX_B) = *matrix3.get_unchecked(1);
+ *inner.get_unchecked_mut(INDEX_C) = *matrix3.get_unchecked(3);
+ *inner.get_unchecked_mut(INDEX_D) = *matrix3.get_unchecked(4);
+ *inner.get_unchecked_mut(INDEX_E) = *matrix3.get_unchecked(6);
+ *inner.get_unchecked_mut(INDEX_F) = *matrix3.get_unchecked(7);
+ }
+ } else if !inner.try_inverse_mut() {
+ inner.fill(f64::NAN);
+ }
+}
+
+fn matrix_transform_point(
+ matrix: &DOMMatrixInner,
+ point: &DOMPointInner,
+ out: &DOMPointInner,
+) {
+ let inner = matrix.inner.borrow();
+ let point = point.inner.borrow();
+ let mut result = out.inner.borrow_mut();
+ inner.mul_to(&point, &mut result);
+}
diff --git a/ext/webidl/00_webidl.js b/ext/webidl/00_webidl.js
index b3c3b299f03cbd..e7252cb0ece4d9 100644
--- a/ext/webidl/00_webidl.js
+++ b/ext/webidl/00_webidl.js
@@ -679,6 +679,9 @@ converters["UVString?"] = createNullableConverter(
converters["sequence"] = createSequenceConverter(
converters.double,
);
+converters["sequence"] = createSequenceConverter(
+ converters["unrestricted double"],
+);
converters["sequence