DOMMatrixReadOnly.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620
  1. /*
  2. * Copyright (c) 2023, Luke Wilde <lukew@serenityos.org>
  3. * Copyright (c) 2023, Bastiaan van der Plaat <bastiaan.v.d.plaat@gmail.com>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include <LibWeb/Bindings/Intrinsics.h>
  8. #include <LibWeb/Geometry/DOMMatrix.h>
  9. #include <LibWeb/Geometry/DOMMatrixReadOnly.h>
  10. #include <LibWeb/Geometry/DOMPoint.h>
  11. #include <LibWeb/WebIDL/ExceptionOr.h>
  12. namespace Web::Geometry {
  13. WebIDL::ExceptionOr<JS::NonnullGCPtr<DOMMatrixReadOnly>> DOMMatrixReadOnly::construct_impl(JS::Realm& realm, Optional<Variant<String, Vector<double>>> const& init)
  14. {
  15. auto& vm = realm.vm();
  16. // https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-dommatrixreadonly
  17. if (init.has_value()) {
  18. // -> Otherwise
  19. // Throw a TypeError exception.
  20. // The only condition where this can be met is with a sequence type which doesn't have exactly 6 or 16 elements.
  21. if (auto* double_sequence = init.value().get_pointer<Vector<double>>(); double_sequence && (double_sequence->size() != 6 && double_sequence->size() != 16))
  22. return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, TRY_OR_THROW_OOM(vm, String::formatted("Sequence must contain exactly 6 or 16 elements, got {} element(s)", double_sequence->size())) };
  23. }
  24. return realm.heap().allocate<DOMMatrixReadOnly>(realm, realm, init);
  25. }
  26. // https://drafts.fxtf.org/geometry/#create-a-dommatrixreadonly-from-the-2d-dictionary
  27. WebIDL::ExceptionOr<JS::NonnullGCPtr<DOMMatrixReadOnly>> DOMMatrixReadOnly::create_from_dom_matrix_2d_init(JS::Realm& realm, DOMMatrix2DInit& init)
  28. {
  29. // 1. Validate and fixup (2D) other.
  30. TRY(validate_and_fixup_dom_matrix_2d_init(init));
  31. // These should all have values after calling `validate_and_fixup_dom_matrix_2d_init`
  32. VERIFY(init.m11.has_value());
  33. VERIFY(init.m12.has_value());
  34. VERIFY(init.m21.has_value());
  35. VERIFY(init.m22.has_value());
  36. VERIFY(init.m41.has_value());
  37. VERIFY(init.m42.has_value());
  38. // 2. Return the result of invoking create a 2d matrix of type DOMMatrixReadOnly or DOMMatrix as appropriate, with a sequence of numbers,
  39. // the values being the 6 elements m11, m12, m21, m22, m41 and m42 of other in the given order.
  40. return realm.heap().allocate<DOMMatrixReadOnly>(realm, realm, init.m11.value(), init.m12.value(), init.m21.value(), init.m22.value(), init.m41.value(), init.m42.value());
  41. }
  42. DOMMatrixReadOnly::DOMMatrixReadOnly(JS::Realm& realm, double m11, double m12, double m21, double m22, double m41, double m42)
  43. : Bindings::PlatformObject(realm)
  44. {
  45. initialize_from_create_2d_matrix(m11, m12, m21, m22, m41, m42);
  46. }
  47. DOMMatrixReadOnly::DOMMatrixReadOnly(JS::Realm& realm, double m11, double m12, double m13, double m14, double m21, double m22, double m23, double m24, double m31, double m32, double m33, double m34, double m41, double m42, double m43, double m44)
  48. : Bindings::PlatformObject(realm)
  49. {
  50. initialize_from_create_3d_matrix(m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44);
  51. }
  52. DOMMatrixReadOnly::DOMMatrixReadOnly(JS::Realm& realm, Optional<Variant<String, Vector<double>>> const& init)
  53. : Bindings::PlatformObject(realm)
  54. {
  55. // https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-dommatrixreadonly
  56. // -> If init is omitted
  57. if (!init.has_value()) {
  58. // Return the result of invoking create a 2d matrix of type DOMMatrixReadOnly or DOMMatrix as appropriate, with the sequence [1, 0, 0, 1, 0, 0].
  59. initialize_from_create_2d_matrix(1, 0, 0, 1, 0, 0);
  60. return;
  61. }
  62. auto const& init_value = init.value();
  63. // -> If init is a DOMString
  64. if (init_value.has<String>()) {
  65. dbgln("FIXME: Implement initializing DOMMatrix(ReadOnly) from DOMString: '{}'", init_value.get<String>());
  66. // NOTE: This will result in an identity matrix for now.
  67. return;
  68. }
  69. auto const& double_sequence = init_value.get<Vector<double>>();
  70. // -> If init is a sequence with 6 elements
  71. if (double_sequence.size() == 6) {
  72. // Return the result of invoking create a 2d matrix of type DOMMatrixReadOnly or DOMMatrix as appropriate, with the sequence init.
  73. initialize_from_create_2d_matrix(double_sequence[0], double_sequence[1], double_sequence[2], double_sequence[3], double_sequence[4], double_sequence[5]);
  74. return;
  75. }
  76. // -> If init is a sequence with 16 elements
  77. // NOTE: The "otherwise" case should be handled in construct_impl, leaving the only other possible condition here to be 16 elements.
  78. VERIFY(double_sequence.size() == 16);
  79. // Return the result of invoking create a 3d matrix of type DOMMatrixReadOnly or DOMMatrix as appropriate, with the sequence init.
  80. initialize_from_create_3d_matrix(
  81. double_sequence[0], double_sequence[1], double_sequence[2], double_sequence[3],
  82. double_sequence[4], double_sequence[5], double_sequence[6], double_sequence[7],
  83. double_sequence[8], double_sequence[9], double_sequence[10], double_sequence[11],
  84. double_sequence[12], double_sequence[13], double_sequence[14], double_sequence[15]);
  85. }
  86. DOMMatrixReadOnly::DOMMatrixReadOnly(JS::Realm& realm, DOMMatrixReadOnly const& other)
  87. : Bindings::PlatformObject(realm)
  88. , m_matrix(other.m_matrix)
  89. , m_is_2d(other.m_is_2d)
  90. {
  91. }
  92. DOMMatrixReadOnly::~DOMMatrixReadOnly() = default;
  93. void DOMMatrixReadOnly::initialize(JS::Realm& realm)
  94. {
  95. Base::initialize(realm);
  96. set_prototype(&Bindings::ensure_web_prototype<Bindings::DOMMatrixReadOnlyPrototype>(realm, "DOMMatrixReadOnly"));
  97. }
  98. // https://drafts.fxtf.org/geometry/#create-a-2d-matrix
  99. void DOMMatrixReadOnly::initialize_from_create_2d_matrix(double m11, double m12, double m21, double m22, double m41, double m42)
  100. {
  101. // NOTE: The matrix used in the spec is column-major (https://drafts.fxtf.org/geometry/#4x4-abstract-matrix) but Gfx::Matrix4x4 is row-major so we need to transpose the values.
  102. // 1. Let matrix be a new instance of type.
  103. // 2. Set m11 element, m12 element, m21 element, m22 element, m41 element and m42 element to the values of init in order starting with the first value.
  104. auto* elements = m_matrix.elements();
  105. elements[0][0] = m11;
  106. elements[1][0] = m12;
  107. elements[0][1] = m21;
  108. elements[1][1] = m22;
  109. elements[0][3] = m41;
  110. elements[1][3] = m42;
  111. // 3. Set m13 element, m14 element, m23 element, m24 element, m31 element, m32 element, m34 element, and m43 element to 0.
  112. elements[2][0] = 0.0;
  113. elements[3][0] = 0.0;
  114. elements[2][1] = 0.0;
  115. elements[3][1] = 0.0;
  116. elements[0][2] = 0.0;
  117. elements[1][2] = 0.0;
  118. elements[3][2] = 0.0;
  119. elements[2][3] = 0.0;
  120. // 4. Set m33 element and m44 element to 1.
  121. elements[2][2] = 1.0;
  122. elements[3][3] = 1.0;
  123. // 5. Set is 2D to true.
  124. m_is_2d = true;
  125. // 6. Return matrix
  126. }
  127. // https://drafts.fxtf.org/geometry/#create-a-3d-matrix
  128. void DOMMatrixReadOnly::initialize_from_create_3d_matrix(double m11, double m12, double m13, double m14, double m21, double m22, double m23, double m24, double m31, double m32, double m33, double m34, double m41, double m42, double m43, double m44)
  129. {
  130. // NOTE: The matrix used in the spec is column-major (https://drafts.fxtf.org/geometry/#4x4-abstract-matrix) but Gfx::Matrix4x4 is row-major so we need to transpose the values.
  131. // 1. Let matrix be a new instance of type.
  132. // 2. Set m11 element to m44 element to the values of init in column-major order.
  133. auto* elements = m_matrix.elements();
  134. elements[0][0] = m11;
  135. elements[1][0] = m12;
  136. elements[2][0] = m13;
  137. elements[3][0] = m14;
  138. elements[0][1] = m21;
  139. elements[1][1] = m22;
  140. elements[2][1] = m23;
  141. elements[3][1] = m24;
  142. elements[0][2] = m31;
  143. elements[1][2] = m32;
  144. elements[2][2] = m33;
  145. elements[3][2] = m34;
  146. elements[0][3] = m41;
  147. elements[1][3] = m42;
  148. elements[2][3] = m43;
  149. elements[3][3] = m44;
  150. // 3. Set is 2D to false.
  151. m_is_2d = false;
  152. // 4. Return matrix
  153. }
  154. // https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-frommatrix
  155. WebIDL::ExceptionOr<JS::NonnullGCPtr<DOMMatrixReadOnly>> DOMMatrixReadOnly::from_matrix(JS::VM& vm, DOMMatrixInit& other)
  156. {
  157. return create_from_dom_matrix_2d_init(*vm.current_realm(), other);
  158. }
  159. // https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-isidentity
  160. bool DOMMatrixReadOnly::is_identity() const
  161. {
  162. // The isIdentity attribute must return true if
  163. // m12 element, m13 element, m14 element,
  164. // m21 element, m23 element, m24 element,
  165. // m31 element, m32 element, m34 element
  166. // m41 element, m42 element, m43 element
  167. // are 0 or -0 and
  168. // m11 element, m22 element, m33 element, m44 element are 1.
  169. // Otherwise it must return false.
  170. if (m12() != 0.0 && m12() != -0.0)
  171. return false;
  172. if (m13() != 0.0 && m13() != -0.0)
  173. return false;
  174. if (m14() != 0.0 && m14() != -0.0)
  175. return false;
  176. if (m21() != 0.0 && m21() != -0.0)
  177. return false;
  178. if (m23() != 0.0 && m24() != -0.0)
  179. return false;
  180. if (m31() != 0.0 && m32() != -0.0)
  181. return false;
  182. if (m34() != 0.0 && m34() != -0.0)
  183. return false;
  184. if (m41() != 0.0 && m42() != -0.0)
  185. return false;
  186. if (m43() != 0.0 && m43() != -0.0)
  187. return false;
  188. if (m11() != 1.0)
  189. return false;
  190. if (m22() != 1.0)
  191. return false;
  192. if (m33() != 1.0)
  193. return false;
  194. if (m44() != 1.0)
  195. return false;
  196. return true;
  197. }
  198. // https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-translate
  199. JS::NonnullGCPtr<DOMMatrix> DOMMatrixReadOnly::translate(Optional<double> const& tx, Optional<double> const& ty, Optional<double> const& tz) const
  200. {
  201. // 1. Let result be the resulting matrix initialized to the values of the current matrix.
  202. auto result = DOMMatrix::create_from_dom_matrix_read_only(realm(), *this);
  203. // 2. Perform a translateSelf() transformation on result with the arguments tx, ty, tz.
  204. // 3. Return result.
  205. return result->translate_self(tx, ty, tz);
  206. }
  207. // https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-skewx
  208. JS::NonnullGCPtr<DOMMatrix> DOMMatrixReadOnly::skew_x(double sx) const
  209. {
  210. // 1. Let result be the resulting matrix initialized to the values of the current matrix.
  211. auto result = DOMMatrix::create_from_dom_matrix_read_only(realm(), *this);
  212. // 2. Perform a skewXSelf() transformation on result with the argument sx.
  213. // 3. Return result.
  214. return result->skew_x_self(sx);
  215. }
  216. // https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-skewy
  217. JS::NonnullGCPtr<DOMMatrix> DOMMatrixReadOnly::skew_y(double sy) const
  218. {
  219. // 1. Let result be the resulting matrix initialized to the values of the current matrix.
  220. auto result = DOMMatrix::create_from_dom_matrix_read_only(realm(), *this);
  221. // 2. Perform a skewYSelf() transformation on result with the argument sy.
  222. // 3. Return result.
  223. return result->skew_y_self(sy);
  224. }
  225. // https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-multiply
  226. WebIDL::ExceptionOr<JS::NonnullGCPtr<DOMMatrix>> DOMMatrixReadOnly::multiply(DOMMatrixInit other)
  227. {
  228. // 1. Let result be the resulting matrix initialized to the values of the current matrix.
  229. auto result = DOMMatrix::create_from_dom_matrix_read_only(realm(), *this);
  230. // 2. Perform a multiplySelf() transformation on result with the argument other.
  231. // 3. Return result.
  232. return result->multiply_self(other);
  233. }
  234. // https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-flipx
  235. JS::NonnullGCPtr<DOMMatrix> DOMMatrixReadOnly::flip_x()
  236. {
  237. // 1. Let result be the resulting matrix initialized to the values of the current matrix.
  238. auto result = DOMMatrix::create_from_dom_matrix_read_only(realm(), *this);
  239. // 2. Post-multiply result with new DOMMatrix([-1, 0, 0, 1, 0, 0]).
  240. // clang-format off
  241. Gfx::DoubleMatrix4x4 flip_matrix = { -1, 0, 0, 0,
  242. 0, 1, 0, 0,
  243. 0, 0, 1, 0,
  244. 0, 0, 0, 1 };
  245. // clang-format on
  246. result->m_matrix = result->m_matrix * flip_matrix;
  247. // 3. Return result.
  248. return result;
  249. }
  250. // https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-flipy
  251. JS::NonnullGCPtr<DOMMatrix> DOMMatrixReadOnly::flip_y()
  252. {
  253. // 1. Let result be the resulting matrix initialized to the values of the current matrix.
  254. auto result = DOMMatrix::create_from_dom_matrix_read_only(realm(), *this);
  255. // 2. Post-multiply result with new DOMMatrix([1, 0, 0, -1, 0, 0]).
  256. // clang-format off
  257. Gfx::DoubleMatrix4x4 flip_matrix = { 1, 0, 0, 0,
  258. 0, -1, 0, 0,
  259. 0, 0, 1, 0,
  260. 0, 0, 0, 1 };
  261. // clang-format on
  262. result->m_matrix = result->m_matrix * flip_matrix;
  263. // 3. Return result.
  264. return result;
  265. }
  266. // https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-inverse
  267. JS::NonnullGCPtr<DOMMatrix> DOMMatrixReadOnly::inverse() const
  268. {
  269. // 1. Let result be the resulting matrix initialized to the values of the current matrix.
  270. auto result = DOMMatrix::create_from_dom_matrix_read_only(realm(), *this);
  271. // 2. Perform a invertSelf() transformation on result.
  272. // 3. Return result.
  273. // The current matrix is not modified.
  274. return result->invert_self();
  275. }
  276. // https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-transformpoint
  277. JS::NonnullGCPtr<DOMPoint> DOMMatrixReadOnly::transform_point(DOMPointInit const& point) const
  278. {
  279. // Let pointObject be the result of invoking create a DOMPoint from the dictionary point.
  280. auto point_object = DOMPoint::from_point(realm().vm(), point);
  281. // Return the result of invoking transform a point with a matrix, given pointObject and the current matrix. The passed argument does not get modified.
  282. return transform_point(point_object);
  283. }
  284. // https://drafts.fxtf.org/geometry/#transform-a-point-with-a-matrix
  285. JS::NonnullGCPtr<DOMPoint> DOMMatrixReadOnly::transform_point(DOMPointReadOnly const& point) const
  286. {
  287. // 1. Let x be point’s x coordinate.
  288. // 2. Let y be point’s y coordinate.
  289. // 3. Let z be point’s z coordinate.
  290. // 4. Let w be point’s w perspective.
  291. // 5. Let pointVector be a new column vector with the elements being x, y, z, and w, respectively.
  292. Vector4<double> point_vector { point.x(), point.y(), point.z(), point.w() };
  293. // 6. Set pointVector to pointVector pre-multiplied by matrix.
  294. // This is really a post multiply because of the transposed m_matrix.
  295. point_vector = m_matrix * point_vector;
  296. // 7. Let transformedPoint be a new DOMPoint object.
  297. // 8. Set transformedPoint’s x coordinate to pointVector’s first element.
  298. // 9. Set transformedPoint’s y coordinate to pointVector’s second element.
  299. // 10. Set transformedPoint’s z coordinate to pointVector’s third element.
  300. // 11. Set transformedPoint’s w perspective to pointVector’s fourth element.
  301. // 12. Return transformedPoint.
  302. return DOMPoint::construct_impl(realm(), point_vector.x(), point_vector.y(), point_vector.z(), point_vector.w());
  303. }
  304. // https://drafts.fxtf.org/geometry/#dommatrixreadonly-stringification-behavior
  305. WebIDL::ExceptionOr<String> DOMMatrixReadOnly::to_string() const
  306. {
  307. auto& vm = this->vm();
  308. // 1. If one or more of m11 element through m44 element are a non-finite value, then throw an "InvalidStateError" DOMException.
  309. // Spec Note: The CSS syntax cannot represent NaN or Infinity values.
  310. if (!isfinite(m11()) || !isfinite(m12()) || !isfinite(m13()) || !isfinite(m14())
  311. || !isfinite(m21()) || !isfinite(m22()) || !isfinite(m23()) || !isfinite(m24())
  312. || !isfinite(m31()) || !isfinite(m32()) || !isfinite(m33()) || !isfinite(m34())
  313. || !isfinite(m41()) || !isfinite(m42()) || !isfinite(m43()) || !isfinite(m44())) {
  314. return WebIDL::InvalidStateError::create(realm(), "Cannot stringify non-finite matrix values"_fly_string);
  315. }
  316. // 2. Let string be the empty string.
  317. StringBuilder builder;
  318. // 3. If is 2D is true, then:
  319. if (m_is_2d) {
  320. // 1. Append "matrix(" to string.
  321. TRY_OR_THROW_OOM(vm, builder.try_append("matrix("sv));
  322. // 2. Append ! ToString(m11 element) to string.
  323. TRY_OR_THROW_OOM(vm, builder.try_append(JS::Value(m11()).to_string_without_side_effects()));
  324. // 3. Append ", " to string.
  325. TRY_OR_THROW_OOM(vm, builder.try_append(", "sv));
  326. // 4. Append ! ToString(m12 element) to string.
  327. TRY_OR_THROW_OOM(vm, builder.try_append(JS::Value(m12()).to_string_without_side_effects()));
  328. // 5. Append ", " to string.
  329. TRY_OR_THROW_OOM(vm, builder.try_append(", "sv));
  330. // 6. Append ! ToString(m21 element) to string.
  331. TRY_OR_THROW_OOM(vm, builder.try_append(JS::Value(m21()).to_string_without_side_effects()));
  332. // 7. Append ", " to string.
  333. TRY_OR_THROW_OOM(vm, builder.try_append(", "sv));
  334. // 8. Append ! ToString(m22 element) to string.
  335. TRY_OR_THROW_OOM(vm, builder.try_append(JS::Value(m22()).to_string_without_side_effects()));
  336. // 9. Append ", " to string.
  337. TRY_OR_THROW_OOM(vm, builder.try_append(", "sv));
  338. // 10. Append ! ToString(m41 element) to string.
  339. TRY_OR_THROW_OOM(vm, builder.try_append(JS::Value(m41()).to_string_without_side_effects()));
  340. // 11. Append ", " to string.
  341. TRY_OR_THROW_OOM(vm, builder.try_append(", "sv));
  342. // 12. Append ! ToString(m42 element) to string.
  343. TRY_OR_THROW_OOM(vm, builder.try_append(JS::Value(m42()).to_string_without_side_effects()));
  344. // 13. Append ")" to string.
  345. TRY_OR_THROW_OOM(vm, builder.try_append(")"sv));
  346. } else {
  347. // 1. Append "matrix3d(" to string.
  348. TRY_OR_THROW_OOM(vm, builder.try_append("matrix3d("sv));
  349. // 2. Append ! ToString(m11 element) to string.
  350. TRY_OR_THROW_OOM(vm, builder.try_append(JS::number_to_string(m11())));
  351. // 3. Append ", " to string.
  352. TRY_OR_THROW_OOM(vm, builder.try_append(", "sv));
  353. // 4. Append ! ToString(m12 element) to string.
  354. TRY_OR_THROW_OOM(vm, builder.try_append(JS::number_to_string(m12())));
  355. // 5. Append ", " to string.
  356. TRY_OR_THROW_OOM(vm, builder.try_append(", "sv));
  357. // 6. Append ! ToString(m13 element) to string.
  358. TRY_OR_THROW_OOM(vm, builder.try_append(JS::number_to_string(m13())));
  359. // 7. Append ", " to string.
  360. TRY_OR_THROW_OOM(vm, builder.try_append(", "sv));
  361. // 8. Append ! ToString(m14 element) to string.
  362. TRY_OR_THROW_OOM(vm, builder.try_append(JS::number_to_string(m14())));
  363. // 9. Append ", " to string.
  364. TRY_OR_THROW_OOM(vm, builder.try_append(", "sv));
  365. // 10. Append ! ToString(m21 element) to string.
  366. TRY_OR_THROW_OOM(vm, builder.try_append(JS::number_to_string(m21())));
  367. // 11. Append ", " to string.
  368. TRY_OR_THROW_OOM(vm, builder.try_append(", "sv));
  369. // 12. Append ! ToString(m22 element) to string.
  370. TRY_OR_THROW_OOM(vm, builder.try_append(JS::number_to_string(m22())));
  371. // 13. Append ", " to string.
  372. TRY_OR_THROW_OOM(vm, builder.try_append(", "sv));
  373. // 14. Append ! ToString(m23 element) to string.
  374. TRY_OR_THROW_OOM(vm, builder.try_append(JS::number_to_string(m23())));
  375. // 15. Append ", " to string.
  376. TRY_OR_THROW_OOM(vm, builder.try_append(", "sv));
  377. // 16. Append ! ToString(m24 element) to string.
  378. TRY_OR_THROW_OOM(vm, builder.try_append(JS::number_to_string(m24())));
  379. // 17. Append ", " to string.
  380. TRY_OR_THROW_OOM(vm, builder.try_append(", "sv));
  381. // NOTE: The spec doesn't include the steps to append m31 to m34, but they are required as matrix3d requires 16 elements.
  382. TRY_OR_THROW_OOM(vm, builder.try_append(JS::number_to_string(m31())));
  383. TRY_OR_THROW_OOM(vm, builder.try_append(", "sv));
  384. TRY_OR_THROW_OOM(vm, builder.try_append(JS::number_to_string(m32())));
  385. TRY_OR_THROW_OOM(vm, builder.try_append(", "sv));
  386. TRY_OR_THROW_OOM(vm, builder.try_append(JS::number_to_string(m33())));
  387. TRY_OR_THROW_OOM(vm, builder.try_append(", "sv));
  388. TRY_OR_THROW_OOM(vm, builder.try_append(JS::number_to_string(m34())));
  389. TRY_OR_THROW_OOM(vm, builder.try_append(", "sv));
  390. // 18. Append ! ToString(m41 element) to string.
  391. TRY_OR_THROW_OOM(vm, builder.try_append(JS::number_to_string(m41())));
  392. // 19. Append ", " to string.
  393. TRY_OR_THROW_OOM(vm, builder.try_append(", "sv));
  394. // 20. Append ! ToString(m42 element) to string.
  395. TRY_OR_THROW_OOM(vm, builder.try_append(JS::number_to_string(m42())));
  396. // 21. Append ", " to string.
  397. TRY_OR_THROW_OOM(vm, builder.try_append(", "sv));
  398. // 22. Append ! ToString(m43 element) to string.
  399. TRY_OR_THROW_OOM(vm, builder.try_append(JS::number_to_string(m43())));
  400. // 23. Append ", " to string.
  401. TRY_OR_THROW_OOM(vm, builder.try_append(", "sv));
  402. // 24. Append ! ToString(m44 element) to string.
  403. TRY_OR_THROW_OOM(vm, builder.try_append(JS::number_to_string(m44())));
  404. // 25. Append ")" to string.
  405. TRY_OR_THROW_OOM(vm, builder.try_append(")"sv));
  406. }
  407. // 5. Return string.
  408. return TRY_OR_THROW_OOM(vm, builder.to_string());
  409. }
  410. // https://drafts.fxtf.org/geometry/#matrix-validate-and-fixup-2d
  411. WebIDL::ExceptionOr<void> validate_and_fixup_dom_matrix_2d_init(DOMMatrix2DInit& init)
  412. {
  413. // 1. If at least one of the following conditions are true for dict, then throw a TypeError exception and abort these steps.
  414. // - a and m11 are both present and SameValueZero(a, m11) is false.
  415. if (init.a.has_value() && init.m11.has_value() && !JS::same_value_zero(JS::Value(init.a.value()), JS::Value(init.m11.value())))
  416. return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "DOMMatrix2DInit.a and DOMMatrix2DInit.m11 must have the same value if they are both present"sv };
  417. // - b and m12 are both present and SameValueZero(b, m12) is false.
  418. if (init.b.has_value() && init.m12.has_value() && !JS::same_value_zero(JS::Value(init.b.value()), JS::Value(init.m12.value())))
  419. return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "DOMMatrix2DInit.b and DOMMatrix2DInit.m12 must have the same value if they are both present"sv };
  420. // - c and m21 are both present and SameValueZero(c, m21) is false.
  421. if (init.c.has_value() && init.m21.has_value() && !JS::same_value_zero(JS::Value(init.c.value()), JS::Value(init.m21.value())))
  422. return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "DOMMatrix2DInit.c and DOMMatrix2DInit.m21 must have the same value if they are both present"sv };
  423. // - d and m22 are both present and SameValueZero(d, m22) is false.
  424. if (init.d.has_value() && init.m22.has_value() && !JS::same_value_zero(JS::Value(init.d.value()), JS::Value(init.m22.value())))
  425. return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "DOMMatrix2DInit.d and DOMMatrix2DInit.m22 must have the same value if they are both present"sv };
  426. // - e and m41 are both present and SameValueZero(e, m41) is false.
  427. if (init.e.has_value() && init.m41.has_value() && !JS::same_value_zero(JS::Value(init.e.value()), JS::Value(init.m41.value())))
  428. return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "DOMMatrix2DInit.e and DOMMatrix2DInit.m41 must have the same value if they are both present"sv };
  429. // - f and m42 are both present and SameValueZero(f, m42) is false.
  430. if (init.f.has_value() && init.m42.has_value() && !JS::same_value_zero(JS::Value(init.f.value()), JS::Value(init.m42.value())))
  431. return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "DOMMatrix2DInit.f and DOMMatrix2DInit.m42 must have the same value if they are both present"sv };
  432. // 2. If m11 is not present then set it to the value of member a, or value 1 if a is also not present.
  433. if (!init.m11.has_value())
  434. init.m11 = init.a.value_or(1.0);
  435. // 3. If m12 is not present then set it to the value of member b, or value 0 if b is also not present.
  436. if (!init.m12.has_value())
  437. init.m12 = init.b.value_or(0.0);
  438. // 4. If m21 is not present then set it to the value of member c, or value 0 if c is also not present.
  439. if (!init.m21.has_value())
  440. init.m21 = init.c.value_or(0.0);
  441. // 5. If m22 is not present then set it to the value of member d, or value 1 if d is also not present.
  442. if (!init.m22.has_value())
  443. init.m22 = init.d.value_or(1.0);
  444. // 6. If m41 is not present then set it to the value of member e, or value 0 if e is also not present.
  445. if (!init.m41.has_value())
  446. init.m41 = init.e.value_or(0.0);
  447. // 7. If m42 is not present then set it to the value of member f, or value 0 if f is also not present.
  448. if (!init.m42.has_value())
  449. init.m42 = init.f.value_or(0.0);
  450. return {};
  451. }
  452. // https://drafts.fxtf.org/geometry/#matrix-validate-and-fixup
  453. WebIDL::ExceptionOr<void> validate_and_fixup_dom_matrix_init(DOMMatrixInit& init)
  454. {
  455. // 1. Validate and fixup (2D) dict.
  456. TRY(validate_and_fixup_dom_matrix_2d_init(init));
  457. // 2. If is2D is true and: at least one of m13, m14, m23, m24, m31, m32, m34, m43 are present with a value other than 0 or -0,
  458. // or at least one of m33, m44 are present with a value other than 1, then throw a TypeError exception and abort these steps.
  459. if (init.is2d.has_value() && init.is2d.value()) {
  460. if ((init.m13 != 0.0 && init.m13 != -0.0)
  461. || (init.m14 != 0.0 && init.m14 != -0.0)
  462. || (init.m23 != 0.0 && init.m23 != -0.0)
  463. || (init.m24 != 0.0 && init.m24 != -0.0)
  464. || (init.m31 != 0.0 && init.m31 != -0.0)
  465. || (init.m32 != 0.0 && init.m32 != -0.0)
  466. || (init.m34 != 0.0 && init.m34 != -0.0)
  467. || (init.m43 != 0.0 && init.m43 != -0.0)
  468. || init.m33 != 1.0
  469. || init.m44 != 1.0) {
  470. return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "DOMMatrixInit.is2D is true, but the given matrix is not a 2D matrix"sv };
  471. }
  472. }
  473. // If is2D is not present and at least one of m13, m14, m23, m24, m31, m32, m34, m43 are present with a value other than 0 or -0,
  474. // or at least one of m33, m44 are present with a value other than 1, set is2D to false.
  475. if (!init.is2d.has_value()) {
  476. if ((init.m13 != 0.0 && init.m13 != -0.0)
  477. || (init.m14 != 0.0 && init.m14 != -0.0)
  478. || (init.m23 != 0.0 && init.m23 != -0.0)
  479. || (init.m24 != 0.0 && init.m24 != -0.0)
  480. || (init.m31 != 0.0 && init.m31 != -0.0)
  481. || (init.m32 != 0.0 && init.m32 != -0.0)
  482. || (init.m34 != 0.0 && init.m34 != -0.0)
  483. || (init.m43 != 0.0 && init.m43 != -0.0)
  484. || init.m33 != 1.0
  485. || init.m44 != 1.0) {
  486. init.is2d = false;
  487. }
  488. }
  489. // 4. If is2D is still not present, set it to true.
  490. if (!init.is2d.has_value())
  491. init.is2d = true;
  492. return {};
  493. }
  494. }