From ec2fb72568a6bf778410080c3f0c5c91912d1b79 Mon Sep 17 00:00:00 2001 From: Job Bautista Date: Wed, 10 May 2023 19:10:41 +0800 Subject: [PATCH 01/16] Issue #2241 - Part 1: Move {js::,JS_}{{Strictly,Loosely}Equal,SameValue} into their own header and implementation files Backported from Mozilla bug 1516742. The .from* methods are going to depend on SameValueZero, which needs to be visible to /dom. This patch provides the foundation for that. --- js/public/Equality.h | 55 ++++++ js/src/builtin/MapObject.cpp | 1 + js/src/builtin/Object.cpp | 1 + js/src/jit/VMFunctions.cpp | 1 + js/src/jsapi-tests/testLooselyEqual.cpp | 10 +- js/src/jsapi-tests/testSameValue.cpp | 5 +- js/src/jsapi-tests/tests.h | 7 +- js/src/jsapi.cpp | 30 ---- js/src/jsapi.h | 9 - js/src/moz.build | 2 + js/src/proxy/ScriptedProxyHandler.cpp | 2 + js/src/shell/js.cpp | 3 +- js/src/vm/EqualityOperations.cpp | 215 ++++++++++++++++++++++++ js/src/vm/EqualityOperations.h | 44 +++++ js/src/vm/Interpreter.cpp | 165 +----------------- js/src/vm/Interpreter.h | 10 -- js/src/vm/NativeObject.cpp | 1 + 17 files changed, 338 insertions(+), 223 deletions(-) create mode 100644 js/public/Equality.h create mode 100644 js/src/vm/EqualityOperations.cpp create mode 100644 js/src/vm/EqualityOperations.h diff --git a/js/public/Equality.h b/js/public/Equality.h new file mode 100644 index 0000000000..03181074de --- /dev/null +++ b/js/public/Equality.h @@ -0,0 +1,55 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Equality operations. */ + +#ifndef js_Equality_h +#define js_Equality_h + +#include "jstypes.h" // JS_PUBLIC_API + +#include "js/RootingAPI.h" // JS::Handle +#include "js/Value.h" // JS::Value + +struct JSContext; + +namespace JS { + +/** + * Store |v1 === v2| to |*equal| -- strict equality, which performs no + * conversions on |v1| or |v2| before comparing. + * + * This operation can fail only if an internal error occurs (e.g. OOM while + * linearizing a string value). + */ +extern JS_PUBLIC_API(bool) +StrictlyEqual(JSContext* cx, JS::Handle v1, JS::Handle v2, bool* equal); + +/** + * Store |v1 == v2| to |*equal| -- loose equality, which may perform + * user-modifiable conversions on |v1| or |v2|. + * + * This operation can fail if a user-modifiable conversion fails *or* if an + * internal error occurs. (e.g. OOM while linearizing a string value). + */ +extern JS_PUBLIC_API(bool) +LooselyEqual(JSContext* cx, JS::Handle v1, JS::Handle v2, bool* equal); + +/** + * Stores |SameValue(v1, v2)| to |*equal| -- using the SameValue operation + * defined in ECMAScript, initially exposed to script as |Object.is|. SameValue + * behaves identically to strict equality, except that it equates two NaN values + * and does not equate differently-signed zeroes. It performs no conversions on + * |v1| or |v2| before comparing. + * + * This operation can fail only if an internal error occurs (e.g. OOM while + * linearizing a string value). + */ +extern JS_PUBLIC_API(bool) +SameValue(JSContext* cx, JS::Handle v1, JS::Handle v2, bool* same); + +} // namespace JS + +#endif /* js_Equality_h */ diff --git a/js/src/builtin/MapObject.cpp b/js/src/builtin/MapObject.cpp index 9d17acbf8a..893e0448a4 100644 --- a/js/src/builtin/MapObject.cpp +++ b/js/src/builtin/MapObject.cpp @@ -12,6 +12,7 @@ #include "ds/OrderedHashTable.h" #include "gc/Marking.h" #include "js/Utility.h" +#include "vm/EqualityOperations.h" // js::SameValue #include "vm/GlobalObject.h" #include "vm/Interpreter.h" #include "vm/SelfHosting.h" diff --git a/js/src/builtin/Object.cpp b/js/src/builtin/Object.cpp index d661a222e5..70a21079c0 100644 --- a/js/src/builtin/Object.cpp +++ b/js/src/builtin/Object.cpp @@ -15,6 +15,7 @@ #include "jit/InlinableNatives.h" #include "js/UniquePtr.h" #include "vm/AsyncFunction.h" +#include "vm/EqualityOperations.h" // js::SameValue #include "vm/StringBuffer.h" #include "jsobjinlines.h" diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp index f191ce7d6d..6e5676f153 100644 --- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -17,6 +17,7 @@ #include "jit/mips64/Simulator-mips64.h" #include "vm/ArrayObject.h" #include "vm/Debugger.h" +#include "vm/EqualityOperations.h" // js::StrictlyEqual #include "vm/Interpreter.h" #include "vm/TraceLogging.h" diff --git a/js/src/jsapi-tests/testLooselyEqual.cpp b/js/src/jsapi-tests/testLooselyEqual.cpp index 70f5cf8964..5c017497c9 100644 --- a/js/src/jsapi-tests/testLooselyEqual.cpp +++ b/js/src/jsapi-tests/testLooselyEqual.cpp @@ -5,6 +5,8 @@ #include #include +#include "js/Equality.h" // JS::LooselyEqual + #include "jsapi-tests/tests.h" using namespace std; @@ -15,15 +17,15 @@ struct LooseEqualityFixture : public JSAPITest bool leq(JS::HandleValue x, JS::HandleValue y) { bool equal; - CHECK(JS_LooselyEqual(cx, x, y, &equal) && equal); - CHECK(JS_LooselyEqual(cx, y, x, &equal) && equal); + CHECK(JS::LooselyEqual(cx, x, y, &equal) && equal); + CHECK(JS::LooselyEqual(cx, y, x, &equal) && equal); return true; } bool nleq(JS::HandleValue x, JS::HandleValue y) { bool equal; - CHECK(JS_LooselyEqual(cx, x, y, &equal) && !equal); - CHECK(JS_LooselyEqual(cx, y, x, &equal) && !equal); + CHECK(JS::LooselyEqual(cx, x, y, &equal) && !equal); + CHECK(JS::LooselyEqual(cx, y, x, &equal) && !equal); return true; } }; diff --git a/js/src/jsapi-tests/testSameValue.cpp b/js/src/jsapi-tests/testSameValue.cpp index 666da40360..50d2208d86 100644 --- a/js/src/jsapi-tests/testSameValue.cpp +++ b/js/src/jsapi-tests/testSameValue.cpp @@ -4,6 +4,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "js/Equality.h" // JS::SameValue #include "jsapi-tests/tests.h" BEGIN_TEST(testSameValue) @@ -11,7 +12,7 @@ BEGIN_TEST(testSameValue) /* * NB: passing a double that fits in an integer jsval is API misuse. As a - * matter of defense in depth, however, JS_SameValue should return the + * matter of defense in depth, however, JS::SameValue should return the * correct result comparing a positive-zero double to a negative-zero * double, and this is believed to be the only way to make such a * comparison possible. @@ -19,7 +20,7 @@ BEGIN_TEST(testSameValue) JS::RootedValue v1(cx, JS::DoubleValue(0.0)); JS::RootedValue v2(cx, JS::DoubleValue(-0.0)); bool same; - CHECK(JS_SameValue(cx, v1, v2, &same)); + CHECK(JS::SameValue(cx, v1, v2, &same)); CHECK(!same); return true; } diff --git a/js/src/jsapi-tests/tests.h b/js/src/jsapi-tests/tests.h index 4d30ba8c85..dfb9f49887 100644 --- a/js/src/jsapi-tests/tests.h +++ b/js/src/jsapi-tests/tests.h @@ -18,6 +18,7 @@ #include "jscntxt.h" #include "jsgc.h" +#include "js/Equality.h" // JS::SameValue #include "js/Vector.h" /* Note: Aborts on OOM. */ @@ -201,9 +202,9 @@ class JSAPITest const char* filename, int lineno) { bool same; JS::RootedValue actual(cx, actualArg), expected(cx, expectedArg); - return (JS_SameValue(cx, actual, expected, &same) && same) || - fail(JSAPITestString("CHECK_SAME failed: expected JS_SameValue(cx, ") + - actualExpr + ", " + expectedExpr + "), got !JS_SameValue(cx, " + + return (JS::SameValue(cx, actual, expected, &same) && same) || + fail(JSAPITestString("CHECK_SAME failed: expected JS::SameValue(cx, ") + + actualExpr + ", " + expectedExpr + "), got !JS::SameValue(cx, " + jsvalToSource(actual) + ", " + jsvalToSource(expected) + ")", filename, lineno); } diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 50e3442ae8..9e5853b454 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -391,36 +391,6 @@ JS_TypeOfValue(JSContext* cx, HandleValue value) return TypeOfValue(value); } -JS_PUBLIC_API(bool) -JS_StrictlyEqual(JSContext* cx, HandleValue value1, HandleValue value2, bool* equal) -{ - AssertHeapIsIdle(cx); - CHECK_REQUEST(cx); - assertSameCompartment(cx, value1, value2); - MOZ_ASSERT(equal); - return StrictlyEqual(cx, value1, value2, equal); -} - -JS_PUBLIC_API(bool) -JS_LooselyEqual(JSContext* cx, HandleValue value1, HandleValue value2, bool* equal) -{ - AssertHeapIsIdle(cx); - CHECK_REQUEST(cx); - assertSameCompartment(cx, value1, value2); - MOZ_ASSERT(equal); - return LooselyEqual(cx, value1, value2, equal); -} - -JS_PUBLIC_API(bool) -JS_SameValue(JSContext* cx, HandleValue value1, HandleValue value2, bool* same) -{ - AssertHeapIsIdle(cx); - CHECK_REQUEST(cx); - assertSameCompartment(cx, value1, value2); - MOZ_ASSERT(same); - return SameValue(cx, value1, value2, same); -} - JS_PUBLIC_API(bool) JS_IsBuiltinEvalFunction(JSFunction* fun) { diff --git a/js/src/jsapi.h b/js/src/jsapi.h index a6a5429cf5..923aa2bb05 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -959,15 +959,6 @@ InformalValueTypeName(const JS::Value& v); } /* namespace JS */ -extern JS_PUBLIC_API(bool) -JS_StrictlyEqual(JSContext* cx, JS::Handle v1, JS::Handle v2, bool* equal); - -extern JS_PUBLIC_API(bool) -JS_LooselyEqual(JSContext* cx, JS::Handle v1, JS::Handle v2, bool* equal); - -extern JS_PUBLIC_API(bool) -JS_SameValue(JSContext* cx, JS::Handle v1, JS::Handle v2, bool* same); - /** True iff fun is the global eval function. */ extern JS_PUBLIC_API(bool) JS_IsBuiltinEvalFunction(JSFunction* fun); diff --git a/js/src/moz.build b/js/src/moz.build index b12d0a90cc..0f1302bb93 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -70,6 +70,7 @@ EXPORTS.js += [ '../public/Conversions.h', '../public/Date.h', '../public/Debug.h', + '../public/Equality.h', '../public/GCAnnotations.h', '../public/GCAPI.h', '../public/GCHashTable.h', @@ -293,6 +294,7 @@ main_deunified_sources = [ 'vm/Debugger.cpp', 'vm/DebuggerMemory.cpp', 'vm/EnvironmentObject.cpp', + 'vm/EqualityOperations.cpp', 'vm/ErrorObject.cpp', 'vm/ForOfIterator.cpp', 'vm/GeneratorObject.cpp', diff --git a/js/src/proxy/ScriptedProxyHandler.cpp b/js/src/proxy/ScriptedProxyHandler.cpp index adb98edbdd..d396b7805c 100644 --- a/js/src/proxy/ScriptedProxyHandler.cpp +++ b/js/src/proxy/ScriptedProxyHandler.cpp @@ -7,6 +7,8 @@ #include "jsapi.h" +#include "vm/EqualityOperations.h" // js::SameValue + #include "jsobjinlines.h" #include "vm/NativeObject-inl.h" diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index bab5cbb0b5..eaac23b537 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -71,6 +71,7 @@ #include "jit/JitcodeMap.h" #include "jit/OptimizationTracking.h" #include "js/Debug.h" +#include "js/Equality.h" // JS::SameValue #include "js/GCAPI.h" #include "js/Initialization.h" #include "js/StructuredClone.h" @@ -2286,7 +2287,7 @@ AssertEq(JSContext* cx, unsigned argc, Value* vp) } bool same; - if (!JS_SameValue(cx, args[0], args[1], &same)) + if (!JS::SameValue(cx, args[0], args[1], &same)) return false; if (!same) { JSAutoByteString bytes0, bytes1; diff --git a/js/src/vm/EqualityOperations.cpp b/js/src/vm/EqualityOperations.cpp new file mode 100644 index 0000000000..6f90450b49 --- /dev/null +++ b/js/src/vm/EqualityOperations.cpp @@ -0,0 +1,215 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "vm/EqualityOperations.h" // js::LooselyEqual, js::StrictlyEqual, js::SameValue + +#include "mozilla/Assertions.h" // MOZ_ASSERT, MOZ_ASSERT_IF + +#include "jsapi.h" // js::AssertHeapIsIdle, CHECK_REQUEST +#include "jsnum.h" // js::StringToNumber +#include "jsobj.h" // js::ToPrimitive +#include "jsstr.h" // js::EqualStrings +#include "jstypes.h" // JS_PUBLIC_API + +#include "js/Equality.h" // JS::LooselyEqual, JS::StrictlyEqual, JS::SameValue +#include "js/RootingAPI.h" // JS::Rooted, JS::Handle +#include "js/Value.h" // JS::Int32Value, JS::SameType, JS::Value + +#include "jsboolinlines.h" // js::EmulatesUndefined +#include "jscntxtinlines.h" // js::assertSameCompartment + +static inline bool +EqualGivenSameType(JSContext* cx, JS::HandleValue lval, JS::HandleValue rval, bool* equal) +{ + MOZ_ASSERT(SameType(lval, rval)); + + if (lval.isString()) + return EqualStrings(cx, lval.toString(), rval.toString(), equal); + if (lval.isDouble()) { + *equal = (lval.toDouble() == rval.toDouble()); + return true; + } + if (lval.isGCThing()) { // objects or symbols + *equal = (lval.toGCThing() == rval.toGCThing()); + return true; + } + *equal = lval.get().payloadAsRawUint32() == rval.get().payloadAsRawUint32(); + MOZ_ASSERT_IF(lval.isUndefined() || lval.isNull(), *equal); + return true; +} + +static inline bool +LooselyEqualBooleanAndOther(JSContext* cx, JS::HandleValue lval, JS::HandleValue rval, bool* result) +{ + MOZ_ASSERT(!rval.isBoolean()); + JS::RootedValue lvalue(cx, JS::Int32Value(lval.toBoolean() ? 1 : 0)); + + // The tail-call would end up in Step 3. + if (rval.isNumber()) { + *result = (lvalue.toNumber() == rval.toNumber()); + return true; + } + // The tail-call would end up in Step 6. + if (rval.isString()) { + double num; + if (!StringToNumber(cx, rval.toString(), &num)) + return false; + *result = (lvalue.toNumber() == num); + return true; + } + + return js::LooselyEqual(cx, lvalue, rval, result); +} + +// ES6 draft rev32 7.2.12 Abstract Equality Comparison +bool +js::LooselyEqual(JSContext* cx, JS::HandleValue lval, JS::HandleValue rval, bool* result) +{ + // Step 3. + if (SameType(lval, rval)) + return EqualGivenSameType(cx, lval, rval, result); + + // Handle int32 x double. + if (lval.isNumber() && rval.isNumber()) { + *result = (lval.toNumber() == rval.toNumber()); + return true; + } + + // Step 4. This a bit more complex, because of the undefined emulating object. + if (lval.isNullOrUndefined()) { + // We can return early here, because null | undefined is only equal to the same set. + *result = rval.isNullOrUndefined() || + (rval.isObject() && EmulatesUndefined(&rval.toObject())); + return true; + } + + // Step 5. + if (rval.isNullOrUndefined()) { + MOZ_ASSERT(!lval.isNullOrUndefined()); + *result = lval.isObject() && EmulatesUndefined(&lval.toObject()); + return true; + } + + // Step 6. + if (lval.isNumber() && rval.isString()) { + double num; + if (!StringToNumber(cx, rval.toString(), &num)) + return false; + *result = (lval.toNumber() == num); + return true; + } + + // Step 7. + if (lval.isString() && rval.isNumber()) { + double num; + if (!StringToNumber(cx, lval.toString(), &num)) + return false; + *result = (num == rval.toNumber()); + return true; + } + + // Step 8. + if (lval.isBoolean()) + return LooselyEqualBooleanAndOther(cx, lval, rval, result); + + // Step 9. + if (rval.isBoolean()) + return LooselyEqualBooleanAndOther(cx, rval, lval, result); + + // Step 10. + if ((lval.isString() || lval.isNumber() || lval.isSymbol()) && rval.isObject()) { + JS::RootedValue rvalue(cx, rval); + if (!ToPrimitive(cx, &rvalue)) + return false; + return js::LooselyEqual(cx, lval, rvalue, result); + } + + // Step 11. + if (lval.isObject() && (rval.isString() || rval.isNumber() || rval.isSymbol())) { + JS::RootedValue lvalue(cx, lval); + if (!ToPrimitive(cx, &lvalue)) + return false; + return js::LooselyEqual(cx, lvalue, rval, result); + } + + // Step 12. + *result = false; + return true; +} + +JS_PUBLIC_API(bool) +JS::LooselyEqual(JSContext* cx, Handle value1, Handle value2, bool* equal) +{ + js::AssertHeapIsIdle(cx); + CHECK_REQUEST(cx); + js::assertSameCompartment(cx, value1, value2); + MOZ_ASSERT(equal); + return js::LooselyEqual(cx, value1, value2, equal); +} + +bool +js::StrictlyEqual(JSContext* cx, JS::HandleValue lval, JS::HandleValue rval, bool* equal) +{ + if (SameType(lval, rval)) + return EqualGivenSameType(cx, lval, rval, equal); + + if (lval.isNumber() && rval.isNumber()) { + *equal = (lval.toNumber() == rval.toNumber()); + return true; + } + + *equal = false; + return true; +} + +JS_PUBLIC_API(bool) +JS::StrictlyEqual(JSContext* cx, Handle value1, Handle value2, bool* equal) +{ + js::AssertHeapIsIdle(cx); + CHECK_REQUEST(cx); + js::assertSameCompartment(cx, value1, value2); + MOZ_ASSERT(equal); + return js::StrictlyEqual(cx, value1, value2, equal); +} + +static inline bool +IsNegativeZero(const JS::Value& v) +{ + return v.isDouble() && mozilla::IsNegativeZero(v.toDouble()); +} + +static inline bool +IsNaN(const JS::Value& v) +{ + return v.isDouble() && mozilla::IsNaN(v.toDouble()); +} + +bool +js::SameValue(JSContext* cx, JS::HandleValue v1, JS::HandleValue v2, bool* same) +{ + if (IsNegativeZero(v1)) { + *same = IsNegativeZero(v2); + return true; + } + if (IsNegativeZero(v2)) { + *same = false; + return true; + } + if (IsNaN(v1) && IsNaN(v2)) { + *same = true; + return true; + } + return js::StrictlyEqual(cx, v1, v2, same); +} + +JS_PUBLIC_API(bool) +JS::SameValue(JSContext* cx, Handle value1, Handle value2, bool* same) +{ + js::AssertHeapIsIdle(cx); + CHECK_REQUEST(cx); + js::assertSameCompartment(cx, value1, value2); + MOZ_ASSERT(same); + return js::SameValue(cx, value1, value2, same); +} diff --git a/js/src/vm/EqualityOperations.h b/js/src/vm/EqualityOperations.h new file mode 100644 index 0000000000..13cae70b16 --- /dev/null +++ b/js/src/vm/EqualityOperations.h @@ -0,0 +1,44 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * The equality comparisons of js/Equality.h, but with extra efficiency for + * SpiderMonkey-internal callers. + * + * These functions, assuming they're passed C++-valid arguments, are identical + * to the same-named JS::-namespaced functions -- just with hidden linkage (so + * they're more efficient to call), and without various external-caller-focused + * JSAPI-usage assertions performed that SpiderMonkey users never come close to + * failing. + */ + +#ifndef vm_EqualityOperations_h +#define vm_EqualityOperations_h + +#include "js/RootingAPI.h" // JS::Handle +#include "js/Value.h" // JS::Value + +struct JSContext; + +namespace js { + +/** Computes |lval === rval|. */ +extern bool +StrictlyEqual(JSContext* cx, JS::HandleValue lval, JS::HandleValue rval, bool* equal); + +/** Computes |lval == rval|. */ +extern bool +LooselyEqual(JSContext* cx, JS::HandleValue lval, JS::HandleValue rval, bool* result); + +/** + * Computes |SameValue(v1, v2)| -- strict equality except that NaNs are + * considered equal and opposite-signed zeroes are considered unequal. + */ +extern bool +SameValue(JSContext* cx, JS::HandleValue v1, JS::HandleValue v2, bool* same); + +} // namespace js + +#endif // vm_EqualityOperations_h diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index b17c762eae..d7c1b8e84a 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -41,6 +41,7 @@ #include "vm/AsyncFunction.h" #include "vm/AsyncIteration.h" #include "vm/Debugger.h" +#include "vm/EqualityOperations.h" // js::StrictlyEqual #include "vm/GeneratorObject.h" #include "vm/Opcodes.h" #include "vm/Scope.h" @@ -786,170 +787,6 @@ js::HasInstance(JSContext* cx, HandleObject obj, HandleValue v, bool* bp) return JS::InstanceofOperator(cx, obj, local, bp); } -static inline bool -EqualGivenSameType(JSContext* cx, HandleValue lval, HandleValue rval, bool* equal) -{ - MOZ_ASSERT(SameType(lval, rval)); - - if (lval.isString()) - return EqualStrings(cx, lval.toString(), rval.toString(), equal); - if (lval.isDouble()) { - *equal = (lval.toDouble() == rval.toDouble()); - return true; - } - if (lval.isGCThing()) { // objects or symbols - *equal = (lval.toGCThing() == rval.toGCThing()); - return true; - } - *equal = lval.get().payloadAsRawUint32() == rval.get().payloadAsRawUint32(); - MOZ_ASSERT_IF(lval.isUndefined() || lval.isNull(), *equal); - return true; -} - -static inline bool -LooselyEqualBooleanAndOther(JSContext* cx, HandleValue lval, HandleValue rval, bool* result) -{ - MOZ_ASSERT(!rval.isBoolean()); - RootedValue lvalue(cx, Int32Value(lval.toBoolean() ? 1 : 0)); - - // The tail-call would end up in Step 3. - if (rval.isNumber()) { - *result = (lvalue.toNumber() == rval.toNumber()); - return true; - } - // The tail-call would end up in Step 6. - if (rval.isString()) { - double num; - if (!StringToNumber(cx, rval.toString(), &num)) - return false; - *result = (lvalue.toNumber() == num); - return true; - } - - return LooselyEqual(cx, lvalue, rval, result); -} - -// ES6 draft rev32 7.2.12 Abstract Equality Comparison -bool -js::LooselyEqual(JSContext* cx, HandleValue lval, HandleValue rval, bool* result) -{ - // Step 3. - if (SameType(lval, rval)) - return EqualGivenSameType(cx, lval, rval, result); - - // Handle int32 x double. - if (lval.isNumber() && rval.isNumber()) { - *result = (lval.toNumber() == rval.toNumber()); - return true; - } - - // Step 4. This a bit more complex, because of the undefined emulating object. - if (lval.isNullOrUndefined()) { - // We can return early here, because null | undefined is only equal to the same set. - *result = rval.isNullOrUndefined() || - (rval.isObject() && EmulatesUndefined(&rval.toObject())); - return true; - } - - // Step 5. - if (rval.isNullOrUndefined()) { - MOZ_ASSERT(!lval.isNullOrUndefined()); - *result = lval.isObject() && EmulatesUndefined(&lval.toObject()); - return true; - } - - // Step 6. - if (lval.isNumber() && rval.isString()) { - double num; - if (!StringToNumber(cx, rval.toString(), &num)) - return false; - *result = (lval.toNumber() == num); - return true; - } - - // Step 7. - if (lval.isString() && rval.isNumber()) { - double num; - if (!StringToNumber(cx, lval.toString(), &num)) - return false; - *result = (num == rval.toNumber()); - return true; - } - - // Step 8. - if (lval.isBoolean()) - return LooselyEqualBooleanAndOther(cx, lval, rval, result); - - // Step 9. - if (rval.isBoolean()) - return LooselyEqualBooleanAndOther(cx, rval, lval, result); - - // Step 10. - if ((lval.isString() || lval.isNumber() || lval.isSymbol()) && rval.isObject()) { - RootedValue rvalue(cx, rval); - if (!ToPrimitive(cx, &rvalue)) - return false; - return LooselyEqual(cx, lval, rvalue, result); - } - - // Step 11. - if (lval.isObject() && (rval.isString() || rval.isNumber() || rval.isSymbol())) { - RootedValue lvalue(cx, lval); - if (!ToPrimitive(cx, &lvalue)) - return false; - return LooselyEqual(cx, lvalue, rval, result); - } - - // Step 12. - *result = false; - return true; -} - -bool -js::StrictlyEqual(JSContext* cx, HandleValue lval, HandleValue rval, bool* equal) -{ - if (SameType(lval, rval)) - return EqualGivenSameType(cx, lval, rval, equal); - - if (lval.isNumber() && rval.isNumber()) { - *equal = (lval.toNumber() == rval.toNumber()); - return true; - } - - *equal = false; - return true; -} - -static inline bool -IsNegativeZero(const Value& v) -{ - return v.isDouble() && mozilla::IsNegativeZero(v.toDouble()); -} - -static inline bool -IsNaN(const Value& v) -{ - return v.isDouble() && mozilla::IsNaN(v.toDouble()); -} - -bool -js::SameValue(JSContext* cx, HandleValue v1, HandleValue v2, bool* same) -{ - if (IsNegativeZero(v1)) { - *same = IsNegativeZero(v2); - return true; - } - if (IsNegativeZero(v2)) { - *same = false; - return true; - } - if (IsNaN(v1) && IsNaN(v2)) { - *same = true; - return true; - } - return StrictlyEqual(cx, v1, v2, same); -} - JSType js::TypeOfObject(JSObject* obj) { diff --git a/js/src/vm/Interpreter.h b/js/src/vm/Interpreter.h index df9368e71c..1927e8cc7f 100644 --- a/js/src/vm/Interpreter.h +++ b/js/src/vm/Interpreter.h @@ -305,16 +305,6 @@ class InvokeState final : public RunState extern bool RunScript(JSContext* cx, RunState& state); -extern bool -StrictlyEqual(JSContext* cx, HandleValue lval, HandleValue rval, bool* equal); - -extern bool -LooselyEqual(JSContext* cx, HandleValue lval, HandleValue rval, bool* equal); - -/* === except that NaN is the same as NaN and -0 is not the same as +0. */ -extern bool -SameValue(JSContext* cx, HandleValue v1, HandleValue v2, bool* same); - extern JSType TypeOfObject(JSObject* obj); diff --git a/js/src/vm/NativeObject.cpp b/js/src/vm/NativeObject.cpp index 91b7cacb4d..a6bb9826ee 100644 --- a/js/src/vm/NativeObject.cpp +++ b/js/src/vm/NativeObject.cpp @@ -11,6 +11,7 @@ #include "gc/Marking.h" #include "js/Value.h" #include "vm/Debugger.h" +#include "vm/EqualityOperations.h" // js::SameValue #include "vm/TypedArrayCommon.h" #include "jsobjinlines.h" From 621868a340e0f17e926c572ee4a19fabd0dbf50a Mon Sep 17 00:00:00 2001 From: Job Bautista Date: Wed, 10 May 2023 19:22:39 +0800 Subject: [PATCH 02/16] Issue #2241 - Part 2: Add SameValueZero implementation to mfbt/FloatingPoint.h Backported from Mozilla bug 1560658. This is to prevent duplication of code while implementing DOMMatrix operations. --- js/public/Equality.h | 13 +++++++++++++ mfbt/FloatingPoint.h | 14 ++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/js/public/Equality.h b/js/public/Equality.h index 03181074de..6d2db50fa9 100644 --- a/js/public/Equality.h +++ b/js/public/Equality.h @@ -8,6 +8,8 @@ #ifndef js_Equality_h #define js_Equality_h +#include "mozilla/FloatingPoint.h" + #include "jstypes.h" // JS_PUBLIC_API #include "js/RootingAPI.h" // JS::Handle @@ -50,6 +52,17 @@ LooselyEqual(JSContext* cx, JS::Handle v1, JS::Handle v2, extern JS_PUBLIC_API(bool) SameValue(JSContext* cx, JS::Handle v1, JS::Handle v2, bool* same); +/** + * Implements |SameValueZero(v1, v2)| for Number values |v1| and |v2|. + * SameValueZero equates NaNs, equal nonzero values, and zeroes without respect + * to their signs. + */ +static inline bool +SameValueZero(double v1, double v2) +{ + return mozilla::EqualOrBothNaN(v1, v2); +} + } // namespace JS #endif /* js_Equality_h */ diff --git a/mfbt/FloatingPoint.h b/mfbt/FloatingPoint.h index 6a0e454ae7..7d73d7e848 100644 --- a/mfbt/FloatingPoint.h +++ b/mfbt/FloatingPoint.h @@ -396,6 +396,20 @@ NumbersAreIdentical(T aValue1, T aValue2) return BitwiseCast(aValue1) == BitwiseCast(aValue2); } +/** + * Return true if |aValue| and |aValue2| are equal (ignoring sign if both are + * zero) or both NaN. + */ +template +static inline bool +EqualOrBothNaN(T aValue1, T aValue2) +{ + if (IsNaN(aValue1)) { + return IsNaN(aValue2); + } + return aValue1 == aValue2; +} + namespace detail { template From d2c3ea08ebf8f94ba711dbb7bb222d3ba26cca12 Mon Sep 17 00:00:00 2001 From: Job Bautista Date: Thu, 11 May 2023 22:03:17 +0800 Subject: [PATCH 03/16] Issue #2241 - Part 3: Extend DOMMatrixReadOnly to allow instantiation with a Matrix4x4. Backported from Mozilla bug 1355675. --- dom/base/DOMMatrix.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/dom/base/DOMMatrix.h b/dom/base/DOMMatrix.h index a9c52fa8c3..9b83548efa 100644 --- a/dom/base/DOMMatrix.h +++ b/dom/base/DOMMatrix.h @@ -42,6 +42,12 @@ public: } } + DOMMatrixReadOnly(nsISupports* aParent, const gfx::Matrix4x4& aMatrix) + : mParent(aParent) + { + mMatrix3D = new gfx::Matrix4x4(aMatrix); + } + NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(DOMMatrixReadOnly) NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(DOMMatrixReadOnly) @@ -154,6 +160,10 @@ public: : DOMMatrixReadOnly(aParent, other) {} + DOMMatrix(nsISupports* aParent, const gfx::Matrix4x4& aMatrix) + : DOMMatrixReadOnly(aParent, aMatrix) + {} + static already_AddRefed Constructor(const GlobalObject& aGlobal, ErrorResult& aRv); static already_AddRefed From 60c88dd11bb98b09d627c55c6bc453615e3c25f0 Mon Sep 17 00:00:00 2001 From: Job Bautista Date: Fri, 12 May 2023 11:35:18 +0800 Subject: [PATCH 04/16] Issue #2241 - Part 4.1: Get DOMPoint, DOMQuad, DOMRect, DOMMatrix a bit closer to spec. Backported from Mozilla bug 1186265's part 1. --- dom/base/DOMMatrix.cpp | 59 +++++++++++++++-- dom/base/DOMMatrix.h | 102 +++++++++++++++------------- dom/base/DOMPoint.cpp | 27 +++++++- dom/base/DOMPoint.h | 13 +++- dom/base/DOMQuad.cpp | 129 ++++++++++++------------------------ dom/base/DOMQuad.h | 8 +-- dom/base/DOMRect.cpp | 19 +++--- dom/base/DOMRect.h | 66 +++++++++--------- dom/bindings/Bindings.conf | 2 - dom/bindings/Errors.msg | 1 + dom/webidl/DOMMatrix.webidl | 4 +- dom/webidl/DOMPoint.webidl | 15 +++-- dom/webidl/DOMQuad.webidl | 6 +- dom/webidl/DOMRect.webidl | 9 ++- 14 files changed, 257 insertions(+), 203 deletions(-) diff --git a/dom/base/DOMMatrix.cpp b/dom/base/DOMMatrix.cpp index 72c8d9b76b..1631f2cdcf 100644 --- a/dom/base/DOMMatrix.cpp +++ b/dom/base/DOMMatrix.cpp @@ -20,6 +20,10 @@ namespace mozilla { namespace dom { +template +static void +SetDataInMatrix(DOMMatrixReadOnly* aMatrix, const T* aData, int aLength, ErrorResult& aRv); + static const double radPerDegree = 2.0 * M_PI / 360.0; NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DOMMatrixReadOnly, mParent) @@ -27,6 +31,39 @@ NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DOMMatrixReadOnly, mParent) NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(DOMMatrixReadOnly, AddRef) NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(DOMMatrixReadOnly, Release) +JSObject* +DOMMatrixReadOnly::WrapObject(JSContext* aCx, JS::Handle aGivenProto) +{ + return DOMMatrixReadOnlyBinding::Wrap(aCx, this, aGivenProto); +} + +already_AddRefed +DOMMatrixReadOnly::Constructor( + const GlobalObject& aGlobal, + const Optional& aArg, + ErrorResult& aRv) +{ + RefPtr rval = new DOMMatrixReadOnly(aGlobal.GetAsSupports()); + if (!aArg.WasPassed()) { + return rval.forget(); + } + + const auto& arg = aArg.Value(); + if (arg.IsString()) { + nsCOMPtr win = do_QueryInterface(aGlobal.GetAsSupports()); + if (!win) { + aRv.ThrowTypeError(); + return nullptr; + } + rval->SetMatrixValue(arg.GetAsString(), aRv); + } else { + const auto& sequence = arg.GetAsUnrestrictedDoubleSequence(); + SetDataInMatrix(rval, sequence.Elements(), sequence.Length(), aRv); + } + + return rval.forget(); +} + already_AddRefed DOMMatrixReadOnly::Translate(double aTx, double aTy, @@ -330,7 +367,9 @@ DOMMatrix::Constructor(const GlobalObject& aGlobal, const DOMMatrixReadOnly& aOt return obj.forget(); } -template void SetDataInMatrix(DOMMatrix* aMatrix, const T* aData, int aLength, ErrorResult& aRv) +template +static void +SetDataInMatrix(DOMMatrixReadOnly* aMatrix, const T* aData, int aLength, ErrorResult& aRv) { if (aLength == 16) { aMatrix->SetM11(aData[0]); @@ -357,7 +396,9 @@ template void SetDataInMatrix(DOMMatrix* aMatrix, const T* aData, i aMatrix->SetE(aData[4]); aMatrix->SetF(aData[5]); } else { - aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); + nsAutoString lengthStr; + lengthStr.AppendInt(aLength); + aRv.ThrowTypeError(lengthStr); } } @@ -390,7 +431,8 @@ DOMMatrix::Constructor(const GlobalObject& aGlobal, const Sequence& aNum return obj.forget(); } -void DOMMatrix::Ensure3DMatrix() +void +DOMMatrixReadOnly::Ensure3DMatrix() { if (!mMatrix3D) { mMatrix3D = new gfx::Matrix4x4(gfx::Matrix4x4::From2D(*mMatrix2D)); @@ -617,8 +659,8 @@ DOMMatrix::InvertSelf() return this; } -DOMMatrix* -DOMMatrix::SetMatrixValue(const nsAString& aTransformList, ErrorResult& aRv) +DOMMatrixReadOnly* +DOMMatrixReadOnly::SetMatrixValue(const nsAString& aTransformList, ErrorResult& aRv) { SVGTransformListParser parser(aTransformList); if (!parser.Parse()) { @@ -644,6 +686,13 @@ DOMMatrix::SetMatrixValue(const nsAString& aTransformList, ErrorResult& aRv) return this; } +DOMMatrix* +DOMMatrix::SetMatrixValue(const nsAString& aTransformList, ErrorResult& aRv) +{ + DOMMatrixReadOnly::SetMatrixValue(aTransformList, aRv); + return this; +} + JSObject* DOMMatrix::WrapObject(JSContext* aCx, JS::Handle aGivenProto) { diff --git a/dom/base/DOMMatrix.h b/dom/base/DOMMatrix.h index 9b83548efa..e956878c20 100644 --- a/dom/base/DOMMatrix.h +++ b/dom/base/DOMMatrix.h @@ -22,6 +22,7 @@ namespace dom { class GlobalObject; class DOMMatrix; class DOMPoint; +class StringOrUnrestrictedDoubleSequence; struct DOMPointInit; class DOMMatrixReadOnly : public nsWrapperCache @@ -51,6 +52,12 @@ public: NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(DOMMatrixReadOnly) NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(DOMMatrixReadOnly) + nsISupports* GetParentObject() const { return mParent; } + virtual JSObject* WrapObject(JSContext* cx, JS::Handle aGivenProto) override; + + static already_AddRefed + Constructor(const GlobalObject& aGlobal, const Optional& aArg, ErrorResult& aRv); + #define GetMatrixMember(entry2D, entry3D, default) \ { \ if (mMatrix3D) { \ @@ -94,6 +101,51 @@ public: #undef GetMatrixMember #undef Get3DMatrixMember + // Defined here so we can construct DOMMatrixReadOnly objects. +#define Set2DMatrixMember(entry2D, entry3D) \ +{ \ + if (mMatrix3D) { \ + mMatrix3D->entry3D = v; \ + } else { \ + mMatrix2D->entry2D = v; \ + } \ +} + +#define Set3DMatrixMember(entry3D, default) \ +{ \ + if (mMatrix3D || (v != default)) { \ + Ensure3DMatrix(); \ + mMatrix3D->entry3D = v; \ + } \ +} + + void SetA(double v) Set2DMatrixMember(_11, _11) + void SetB(double v) Set2DMatrixMember(_12, _12) + void SetC(double v) Set2DMatrixMember(_21, _21) + void SetD(double v) Set2DMatrixMember(_22, _22) + void SetE(double v) Set2DMatrixMember(_31, _41) + void SetF(double v) Set2DMatrixMember(_32, _42) + + void SetM11(double v) Set2DMatrixMember(_11, _11) + void SetM12(double v) Set2DMatrixMember(_12, _12) + void SetM13(double v) Set3DMatrixMember(_13, 0) + void SetM14(double v) Set3DMatrixMember(_14, 0) + void SetM21(double v) Set2DMatrixMember(_21, _21) + void SetM22(double v) Set2DMatrixMember(_22, _22) + void SetM23(double v) Set3DMatrixMember(_23, 0) + void SetM24(double v) Set3DMatrixMember(_24, 0) + void SetM31(double v) Set3DMatrixMember(_31, 0) + void SetM32(double v) Set3DMatrixMember(_32, 0) + void SetM33(double v) Set3DMatrixMember(_33, 1.0) + void SetM34(double v) Set3DMatrixMember(_34, 0) + void SetM41(double v) Set2DMatrixMember(_31, _41) + void SetM42(double v) Set2DMatrixMember(_32, _42) + void SetM43(double v) Set3DMatrixMember(_43, 0) + void SetM44(double v) Set3DMatrixMember(_44, 1.0) + +#undef Set2DMatrixMember +#undef Set3DMatrixMember + already_AddRefed Translate(double aTx, double aTy, double aTz = 0) const; @@ -143,6 +195,9 @@ protected: virtual ~DOMMatrixReadOnly() {} + DOMMatrixReadOnly* SetMatrixValue(const nsAString& aTransformList, ErrorResult& aRv); + void Ensure3DMatrix(); + private: DOMMatrixReadOnly() = delete; DOMMatrixReadOnly(const DOMMatrixReadOnly&) = delete; @@ -177,53 +232,8 @@ public: static already_AddRefed Constructor(const GlobalObject& aGlobal, const Sequence& aNumberSequence, ErrorResult& aRv); - nsISupports* GetParentObject() const { return mParent; } virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; -#define Set2DMatrixMember(entry2D, entry3D) \ -{ \ - if (mMatrix3D) { \ - mMatrix3D->entry3D = v; \ - } else { \ - mMatrix2D->entry2D = v; \ - } \ -} - -#define Set3DMatrixMember(entry3D, default) \ -{ \ - if (mMatrix3D || (v != default)) { \ - Ensure3DMatrix(); \ - mMatrix3D->entry3D = v; \ - } \ -} - - void SetA(double v) Set2DMatrixMember(_11, _11) - void SetB(double v) Set2DMatrixMember(_12, _12) - void SetC(double v) Set2DMatrixMember(_21, _21) - void SetD(double v) Set2DMatrixMember(_22, _22) - void SetE(double v) Set2DMatrixMember(_31, _41) - void SetF(double v) Set2DMatrixMember(_32, _42) - - void SetM11(double v) Set2DMatrixMember(_11, _11) - void SetM12(double v) Set2DMatrixMember(_12, _12) - void SetM13(double v) Set3DMatrixMember(_13, 0) - void SetM14(double v) Set3DMatrixMember(_14, 0) - void SetM21(double v) Set2DMatrixMember(_21, _21) - void SetM22(double v) Set2DMatrixMember(_22, _22) - void SetM23(double v) Set3DMatrixMember(_23, 0) - void SetM24(double v) Set3DMatrixMember(_24, 0) - void SetM31(double v) Set3DMatrixMember(_31, 0) - void SetM32(double v) Set3DMatrixMember(_32, 0) - void SetM33(double v) Set3DMatrixMember(_33, 1.0) - void SetM34(double v) Set3DMatrixMember(_34, 0) - void SetM41(double v) Set2DMatrixMember(_31, _41) - void SetM42(double v) Set2DMatrixMember(_32, _42) - void SetM43(double v) Set3DMatrixMember(_43, 0) - void SetM44(double v) Set3DMatrixMember(_44, 1.0) - -#undef Set2DMatrixMember -#undef Set3DMatrixMember - DOMMatrix* MultiplySelf(const DOMMatrix& aOther); DOMMatrix* PreMultiplySelf(const DOMMatrix& aOther); DOMMatrix* TranslateSelf(double aTx, @@ -255,8 +265,6 @@ public: DOMMatrix* SkewYSelf(double aSy); DOMMatrix* InvertSelf(); DOMMatrix* SetMatrixValue(const nsAString& aTransformList, ErrorResult& aRv); -protected: - void Ensure3DMatrix(); virtual ~DOMMatrix() {} }; diff --git a/dom/base/DOMPoint.cpp b/dom/base/DOMPoint.cpp index 97eec9e766..508bfab1e2 100644 --- a/dom/base/DOMPoint.cpp +++ b/dom/base/DOMPoint.cpp @@ -16,9 +16,32 @@ NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DOMPointReadOnly, mParent) NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(DOMPointReadOnly, AddRef) NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(DOMPointReadOnly, Release) +already_AddRefed +DOMPointReadOnly::FromPoint(const GlobalObject& aGlobal, const DOMPointInit& aParams) +{ + RefPtr obj = + new DOMPointReadOnly(aGlobal.GetAsSupports(), aParams.mX, aParams.mY, + aParams.mZ, aParams.mW); + return obj.forget(); +} + +already_AddRefed +DOMPointReadOnly::Constructor(const GlobalObject& aGlobal, double aX, double aY, + double aZ, double aW, ErrorResult& aRV) +{ + RefPtr obj = + new DOMPointReadOnly(aGlobal.GetAsSupports(), aX, aY, aZ, aW); + return obj.forget(); +} + +JSObject* +DOMPointReadOnly::WrapObject(JSContext* aCx, JS::Handle aGivenProto) +{ + return DOMPointReadOnlyBinding::Wrap(aCx, this, aGivenProto); +} + already_AddRefed -DOMPoint::Constructor(const GlobalObject& aGlobal, const DOMPointInit& aParams, - ErrorResult& aRV) +DOMPoint::FromPoint(const GlobalObject& aGlobal, const DOMPointInit& aParams) { RefPtr obj = new DOMPoint(aGlobal.GetAsSupports(), aParams.mX, aParams.mY, diff --git a/dom/base/DOMPoint.h b/dom/base/DOMPoint.h index 1a85982cc7..79937f83a3 100644 --- a/dom/base/DOMPoint.h +++ b/dom/base/DOMPoint.h @@ -33,6 +33,12 @@ public: { } + static already_AddRefed + FromPoint(const GlobalObject& aGlobal, const DOMPointInit& aParams); + static already_AddRefed + Constructor(const GlobalObject& aGlobal, double aX, double aY, + double aZ, double aW, ErrorResult& aRV); + NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(DOMPointReadOnly) NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(DOMPointReadOnly) @@ -41,6 +47,9 @@ public: double Z() const { return mZ; } double W() const { return mW; } + nsISupports* GetParentObject() const { return mParent; } + virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; + protected: virtual ~DOMPointReadOnly() {} @@ -57,13 +66,11 @@ public: {} static already_AddRefed - Constructor(const GlobalObject& aGlobal, const DOMPointInit& aParams, - ErrorResult& aRV); + FromPoint(const GlobalObject& aGlobal, const DOMPointInit& aParams); static already_AddRefed Constructor(const GlobalObject& aGlobal, double aX, double aY, double aZ, double aW, ErrorResult& aRV); - nsISupports* GetParentObject() const { return mParent; } virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; void SetX(double aX) { mX = aX; } diff --git a/dom/base/DOMQuad.cpp b/dom/base/DOMQuad.cpp index 9da70c043d..2cf55e1b8b 100644 --- a/dom/base/DOMQuad.cpp +++ b/dom/base/DOMQuad.cpp @@ -14,7 +14,7 @@ using namespace mozilla; using namespace mozilla::dom; using namespace mozilla::gfx; -NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DOMQuad, mParent, mBounds, mPoints[0], +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DOMQuad, mParent, mPoints[0], mPoints[1], mPoints[2], mPoints[3]) NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(DOMQuad, AddRef) @@ -52,10 +52,10 @@ DOMQuad::Constructor(const GlobalObject& aGlobal, ErrorResult& aRV) { RefPtr obj = new DOMQuad(aGlobal.GetAsSupports()); - obj->mPoints[0] = DOMPoint::Constructor(aGlobal, aP1, aRV); - obj->mPoints[1] = DOMPoint::Constructor(aGlobal, aP2, aRV); - obj->mPoints[2] = DOMPoint::Constructor(aGlobal, aP3, aRV); - obj->mPoints[3] = DOMPoint::Constructor(aGlobal, aP4, aRV); + obj->mPoints[0] = DOMPoint::FromPoint(aGlobal, aP1); + obj->mPoints[1] = DOMPoint::FromPoint(aGlobal, aP2); + obj->mPoints[2] = DOMPoint::FromPoint(aGlobal, aP3); + obj->mPoints[3] = DOMPoint::FromPoint(aGlobal, aP4); return obj.forget(); } @@ -73,87 +73,44 @@ DOMQuad::Constructor(const GlobalObject& aGlobal, const DOMRectReadOnly& aRect, return obj.forget(); } -class DOMQuad::QuadBounds final : public DOMRectReadOnly +void +DOMQuad::GetHorizontalMinMax(double* aX1, double* aX2) const { -public: - explicit QuadBounds(DOMQuad* aQuad) - : DOMRectReadOnly(aQuad->GetParentObject()) - , mQuad(aQuad) - {} - - NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(QuadBounds, DOMRectReadOnly) - NS_DECL_ISUPPORTS_INHERITED - - virtual double X() const override - { - double x1, x2; - GetHorizontalMinMax(&x1, &x2); - return x1; + double x1, x2; + x1 = x2 = Point(0)->X(); + for (uint32_t i = 1; i < 4; ++i) { + double x = Point(i)->X(); + x1 = std::min(x1, x); + x2 = std::max(x2, x); } - virtual double Y() const override - { - double y1, y2; - GetVerticalMinMax(&y1, &y2); - return y1; - } - virtual double Width() const override - { - double x1, x2; - GetHorizontalMinMax(&x1, &x2); - return x2 - x1; - } - virtual double Height() const override - { - double y1, y2; - GetVerticalMinMax(&y1, &y2); - return y2 - y1; - } - - void GetHorizontalMinMax(double* aX1, double* aX2) const - { - double x1, x2; - x1 = x2 = mQuad->Point(0)->X(); - for (uint32_t i = 1; i < 4; ++i) { - double x = mQuad->Point(i)->X(); - x1 = std::min(x1, x); - x2 = std::max(x2, x); - } - *aX1 = x1; - *aX2 = x2; - } - - void GetVerticalMinMax(double* aY1, double* aY2) const - { - double y1, y2; - y1 = y2 = mQuad->Point(0)->Y(); - for (uint32_t i = 1; i < 4; ++i) { - double y = mQuad->Point(i)->Y(); - y1 = std::min(y1, y); - y2 = std::max(y2, y); - } - *aY1 = y1; - *aY2 = y2; - } - -protected: - virtual ~QuadBounds() {} - - RefPtr mQuad; -}; - -NS_IMPL_CYCLE_COLLECTION_INHERITED(DOMQuad::QuadBounds, DOMRectReadOnly, mQuad) - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DOMQuad::QuadBounds) -NS_INTERFACE_MAP_END_INHERITING(DOMRectReadOnly) - -NS_IMPL_ADDREF_INHERITED(DOMQuad::QuadBounds, DOMRectReadOnly) -NS_IMPL_RELEASE_INHERITED(DOMQuad::QuadBounds, DOMRectReadOnly) - -DOMRectReadOnly* -DOMQuad::Bounds() const -{ - if (!mBounds) { - mBounds = new QuadBounds(const_cast(this)); - } - return mBounds; + *aX1 = x1; + *aX2 = x2; +} + +void +DOMQuad::GetVerticalMinMax(double* aY1, double* aY2) const +{ + double y1, y2; + y1 = y2 = Point(0)->Y(); + for (uint32_t i = 1; i < 4; ++i) { + double y = Point(i)->Y(); + y1 = std::min(y1, y); + y2 = std::max(y2, y); + } + *aY1 = y1; + *aY2 = y2; +} + +already_AddRefed +DOMQuad::GetBounds() const +{ + double x1, x2; + double y1, y2; + + GetHorizontalMinMax(&x1, &x2); + GetVerticalMinMax(&y1, &y2); + + RefPtr rval = new DOMRectReadOnly(GetParentObject(), + x1, y1, x2 - x1, y2 - y1); + return rval.forget(); } diff --git a/dom/base/DOMQuad.h b/dom/base/DOMQuad.h index 89d258a106..25cf7dbd06 100644 --- a/dom/base/DOMQuad.h +++ b/dom/base/DOMQuad.h @@ -47,20 +47,20 @@ public: Constructor(const GlobalObject& aGlobal, const DOMRectReadOnly& aRect, ErrorResult& aRV); - DOMRectReadOnly* Bounds() const; + already_AddRefed GetBounds() const; DOMPoint* P1() const { return mPoints[0]; } DOMPoint* P2() const { return mPoints[1]; } DOMPoint* P3() const { return mPoints[2]; } DOMPoint* P4() const { return mPoints[3]; } - DOMPoint* Point(uint32_t aIndex) { return mPoints[aIndex]; } + DOMPoint* Point(uint32_t aIndex) const { return mPoints[aIndex]; } protected: - class QuadBounds; + void GetHorizontalMinMax(double* aX1, double* aX2) const; + void GetVerticalMinMax(double* aY1, double* aY2) const; nsCOMPtr mParent; RefPtr mPoints[4]; - mutable RefPtr mBounds; // allocated lazily }; } // namespace dom diff --git a/dom/base/DOMRect.cpp b/dom/base/DOMRect.cpp index 3728ea7a7c..ecd56f10a7 100644 --- a/dom/base/DOMRect.cpp +++ b/dom/base/DOMRect.cpp @@ -27,6 +27,15 @@ DOMRectReadOnly::WrapObject(JSContext* aCx, JS::Handle aGivenProto) return DOMRectReadOnlyBinding::Wrap(aCx, this, aGivenProto); } +already_AddRefed +DOMRectReadOnly::Constructor(const GlobalObject& aGlobal, double aX, double aY, + double aWidth, double aHeight, ErrorResult& aRv) +{ + RefPtr obj = + new DOMRectReadOnly(aGlobal.GetAsSupports(), aX, aY, aWidth, aHeight); + return obj.forget(); +} + // ----------------------------------------------------------------------------- NS_IMPL_ISUPPORTS_INHERITED(DOMRect, DOMRectReadOnly, nsIDOMClientRect) @@ -53,17 +62,9 @@ DOMRect::WrapObject(JSContext* aCx, JS::Handle aGivenProto) return DOMRectBinding::Wrap(aCx, this, aGivenProto); } -already_AddRefed -DOMRect::Constructor(const GlobalObject& aGlobal, ErrorResult& aRV) -{ - RefPtr obj = - new DOMRect(aGlobal.GetAsSupports(), 0.0, 0.0, 0.0, 0.0); - return obj.forget(); -} - already_AddRefed DOMRect::Constructor(const GlobalObject& aGlobal, double aX, double aY, - double aWidth, double aHeight, ErrorResult& aRV) + double aWidth, double aHeight, ErrorResult& aRv) { RefPtr obj = new DOMRect(aGlobal.GetAsSupports(), aX, aY, aWidth, aHeight); diff --git a/dom/base/DOMRect.h b/dom/base/DOMRect.h index da3162be0c..baf3268b20 100644 --- a/dom/base/DOMRect.h +++ b/dom/base/DOMRect.h @@ -32,8 +32,13 @@ public: NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DOMRectReadOnly) - explicit DOMRectReadOnly(nsISupports* aParent) + explicit DOMRectReadOnly(nsISupports* aParent, double aX = 0, double aY = 0, + double aWidth = 0, double aHeight = 0) : mParent(aParent) + , mX(aX) + , mY(aY) + , mWidth(aWidth) + , mHeight(aHeight) { } @@ -44,10 +49,26 @@ public: } virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; - virtual double X() const = 0; - virtual double Y() const = 0; - virtual double Width() const = 0; - virtual double Height() const = 0; + static already_AddRefed + Constructor(const GlobalObject& aGlobal, double aX, double aY, + double aWidth, double aHeight, ErrorResult& aRv); + + double X() const + { + return mX; + } + double Y() const + { + return mY; + } + double Width() const + { + return mWidth; + } + double Height() const + { + return mHeight; + } double Left() const { @@ -72,6 +93,7 @@ public: protected: nsCOMPtr mParent; + double mX, mY, mWidth, mHeight; }; class DOMRect final : public DOMRectReadOnly @@ -80,22 +102,16 @@ class DOMRect final : public DOMRectReadOnly public: explicit DOMRect(nsISupports* aParent, double aX = 0, double aY = 0, double aWidth = 0, double aHeight = 0) - : DOMRectReadOnly(aParent) - , mX(aX) - , mY(aY) - , mWidth(aWidth) - , mHeight(aHeight) + : DOMRectReadOnly(aParent, aX, aY, aWidth, aHeight) { } - + NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSIDOMCLIENTRECT - static already_AddRefed - Constructor(const GlobalObject& aGlobal, ErrorResult& aRV); static already_AddRefed Constructor(const GlobalObject& aGlobal, double aX, double aY, - double aWidth, double aHeight, ErrorResult& aRV); + double aWidth, double aHeight, ErrorResult& aRv); virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; @@ -104,23 +120,6 @@ public: } void SetLayoutRect(const nsRect& aLayoutRect); - virtual double X() const override - { - return mX; - } - virtual double Y() const override - { - return mY; - } - virtual double Width() const override - { - return mWidth; - } - virtual double Height() const override - { - return mHeight; - } - void SetX(double aX) { mX = aX; @@ -138,11 +137,8 @@ public: mHeight = aHeight; } -protected: - double mX, mY, mWidth, mHeight; - private: - ~DOMRect() {}; + ~DOMRect() {} }; class DOMRectList final : public nsIDOMClientRectList, diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index 5f6a9083dd..e1560bfa3f 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -253,12 +253,10 @@ DOMInterfaces = { 'DOMMatrixReadOnly': { 'headerFile': 'mozilla/dom/DOMMatrix.h', - 'concrete': False, }, 'DOMPointReadOnly': { 'headerFile': 'mozilla/dom/DOMPoint.h', - 'concrete': False, }, 'DOMRectList': { diff --git a/dom/bindings/Errors.msg b/dom/bindings/Errors.msg index c894c6c7b4..b40dc239d5 100644 --- a/dom/bindings/Errors.msg +++ b/dom/bindings/Errors.msg @@ -100,6 +100,7 @@ MSG_DEF(MSG_TIME_VALUE_OUT_OF_RANGE, 1, JSEXN_TYPEERR, "{0} is outside the suppo MSG_DEF(MSG_ONLY_IF_CACHED_WITHOUT_SAME_ORIGIN, 1, JSEXN_TYPEERR, "Request mode '{0}' was used, but request cache mode 'only-if-cached' can only be used with request mode 'same-origin'.") MSG_DEF(MSG_THRESHOLD_RANGE_ERROR, 0, JSEXN_RANGEERR, "Threshold values must all be in the range [0, 1].") MSG_DEF(MSG_CACHE_OPEN_FAILED, 0, JSEXN_TYPEERR, "CacheStorage.open() failed to access the storage system.") +MSG_DEF(MSG_MATRIX_INIT_LENGTH_WRONG, 1, JSEXN_TYPEERR, "Matrix init sequence must have a length of 6 or 16 (actual value: {0})") MSG_DEF(MSG_NO_NEGATIVE_ATTR, 1, JSEXN_TYPEERR, "Given attribute {0} cannot be negative.") MSG_DEF(MSG_PMO_NO_SEPARATE_ENDMARK, 0, JSEXN_TYPEERR, "Cannot provide separate endMark argument if PerformanceMeasureOptions argument is given.") MSG_DEF(MSG_PMO_MISSING_STARTENDMARK, 0, JSEXN_TYPEERR, "PerformanceMeasureOptions must have start and/or end member.") diff --git a/dom/webidl/DOMMatrix.webidl b/dom/webidl/DOMMatrix.webidl index 6b236ae666..97dc16616c 100644 --- a/dom/webidl/DOMMatrix.webidl +++ b/dom/webidl/DOMMatrix.webidl @@ -10,7 +10,8 @@ * liability, trademark and document use rules apply. */ -[Pref="layout.css.DOMMatrix.enabled"] +[Pref="layout.css.DOMMatrix.enabled", + Constructor(optional (DOMString or sequence) init)] interface DOMMatrixReadOnly { // These attributes are simple aliases for certain elements of the 4x4 matrix readonly attribute unrestricted double a; @@ -77,6 +78,7 @@ interface DOMMatrixReadOnly { [Throws] Float32Array toFloat32Array(); [Throws] Float64Array toFloat64Array(); stringifier; + [Default] object toJSON(); }; [Pref="layout.css.DOMMatrix.enabled", diff --git a/dom/webidl/DOMPoint.webidl b/dom/webidl/DOMPoint.webidl index d092d900f5..2ebba958e4 100644 --- a/dom/webidl/DOMPoint.webidl +++ b/dom/webidl/DOMPoint.webidl @@ -10,19 +10,26 @@ * liability, trademark and document use rules apply. */ -[Pref="layout.css.DOMPoint.enabled"] +[Pref="layout.css.DOMPoint.enabled", + Constructor(optional unrestricted double x = 0, optional unrestricted double y = 0, + optional unrestricted double z = 0, optional unrestricted double w = 1)] interface DOMPointReadOnly { + [NewObject] static DOMPointReadOnly fromPoint(optional DOMPointInit other); + readonly attribute unrestricted double x; readonly attribute unrestricted double y; readonly attribute unrestricted double z; readonly attribute unrestricted double w; + + [Default] object toJSON(); }; [Pref="layout.css.DOMPoint.enabled", - Constructor(optional DOMPointInit point), - Constructor(unrestricted double x, unrestricted double y, + Constructor(optional unrestricted double x = 0, optional unrestricted double y = 0, optional unrestricted double z = 0, optional unrestricted double w = 1)] interface DOMPoint : DOMPointReadOnly { + [NewObject] static DOMPoint fromPoint(optional DOMPointInit other); + inherit attribute unrestricted double x; inherit attribute unrestricted double y; inherit attribute unrestricted double z; @@ -34,4 +41,4 @@ dictionary DOMPointInit { unrestricted double y = 0; unrestricted double z = 0; unrestricted double w = 1; -}; \ No newline at end of file +}; diff --git a/dom/webidl/DOMQuad.webidl b/dom/webidl/DOMQuad.webidl index b933987d59..f3674e9ad7 100644 --- a/dom/webidl/DOMQuad.webidl +++ b/dom/webidl/DOMQuad.webidl @@ -19,5 +19,7 @@ interface DOMQuad { [SameObject] readonly attribute DOMPoint p2; [SameObject] readonly attribute DOMPoint p3; [SameObject] readonly attribute DOMPoint p4; - [SameObject] readonly attribute DOMRectReadOnly bounds; -}; \ No newline at end of file + [NewObject] DOMRectReadOnly getBounds(); + + [Default] object toJSON(); +}; diff --git a/dom/webidl/DOMRect.webidl b/dom/webidl/DOMRect.webidl index 24a07900c5..4a4f3e4eda 100644 --- a/dom/webidl/DOMRect.webidl +++ b/dom/webidl/DOMRect.webidl @@ -10,9 +10,8 @@ * liability, trademark and document use rules apply. */ -[Constructor, - Constructor(unrestricted double x, unrestricted double y, - unrestricted double width, unrestricted double height)] +[Constructor(optional unrestricted double x = 0, optional unrestricted double y = 0, + optional unrestricted double width = 0, optional unrestricted double height = 0)] interface DOMRect : DOMRectReadOnly { inherit attribute unrestricted double x; inherit attribute unrestricted double y; @@ -20,6 +19,8 @@ interface DOMRect : DOMRectReadOnly { inherit attribute unrestricted double height; }; +[Constructor(optional unrestricted double x = 0, optional unrestricted double y = 0, + optional unrestricted double width = 0, optional unrestricted double height = 0)] interface DOMRectReadOnly { readonly attribute unrestricted double x; readonly attribute unrestricted double y; @@ -29,6 +30,8 @@ interface DOMRectReadOnly { readonly attribute unrestricted double right; readonly attribute unrestricted double bottom; readonly attribute unrestricted double left; + + [Default] object toJSON(); }; dictionary DOMRectInit { From 840658ab398c6ce5af0bc1f65e220756aa852679 Mon Sep 17 00:00:00 2001 From: Job Bautista Date: Fri, 12 May 2023 11:42:59 +0800 Subject: [PATCH 05/16] Issue #2241 - Part 4.2: Resurrect DOMQuad.bounds, but deprecated. This also forces DOMQuad.toJSON() to only return the points. Backported from Mozilla bug 1186265. --- dom/base/DOMQuad.cpp | 20 +++++++++++++++++++- dom/base/DOMQuad.h | 5 +++++ dom/base/nsDeprecatedOperationList.h | 1 + dom/locales/en-US/chrome/dom/dom.properties | 1 + dom/webidl/DOMQuad.webidl | 18 +++++++++++++++++- 5 files changed, 43 insertions(+), 2 deletions(-) diff --git a/dom/base/DOMQuad.cpp b/dom/base/DOMQuad.cpp index 2cf55e1b8b..a64c883982 100644 --- a/dom/base/DOMQuad.cpp +++ b/dom/base/DOMQuad.cpp @@ -14,7 +14,7 @@ using namespace mozilla; using namespace mozilla::dom; using namespace mozilla::gfx; -NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DOMQuad, mParent, mPoints[0], +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DOMQuad, mParent, mBounds, mPoints[0], mPoints[1], mPoints[2], mPoints[3]) NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(DOMQuad, AddRef) @@ -101,6 +101,15 @@ DOMQuad::GetVerticalMinMax(double* aY1, double* aY2) const *aY2 = y2; } +DOMRectReadOnly* +DOMQuad::Bounds() +{ + if (!mBounds) { + mBounds = GetBounds(); + } + return mBounds; +} + already_AddRefed DOMQuad::GetBounds() const { @@ -114,3 +123,12 @@ DOMQuad::GetBounds() const x1, y1, x2 - x1, y2 - y1); return rval.forget(); } + +void +DOMQuad::ToJSON(DOMQuadJSON& aInit) +{ + aInit.mP1.Construct(RefPtr(P1()).forget()); + aInit.mP2.Construct(RefPtr(P2()).forget()); + aInit.mP3.Construct(RefPtr(P3()).forget()); + aInit.mP4.Construct(RefPtr(P4()).forget()); +} diff --git a/dom/base/DOMQuad.h b/dom/base/DOMQuad.h index 25cf7dbd06..db38e5d236 100644 --- a/dom/base/DOMQuad.h +++ b/dom/base/DOMQuad.h @@ -20,6 +20,7 @@ namespace dom { class DOMRectReadOnly; class DOMPoint; +struct DOMQuadJSON; struct DOMPointInit; class DOMQuad final : public nsWrapperCache @@ -47,6 +48,7 @@ public: Constructor(const GlobalObject& aGlobal, const DOMRectReadOnly& aRect, ErrorResult& aRV); + DOMRectReadOnly* Bounds(); already_AddRefed GetBounds() const; DOMPoint* P1() const { return mPoints[0]; } DOMPoint* P2() const { return mPoints[1]; } @@ -55,12 +57,15 @@ public: DOMPoint* Point(uint32_t aIndex) const { return mPoints[aIndex]; } + void ToJSON(DOMQuadJSON& aInit); + protected: void GetHorizontalMinMax(double* aX1, double* aX2) const; void GetVerticalMinMax(double* aY1, double* aY2) const; nsCOMPtr mParent; RefPtr mPoints[4]; + RefPtr mBounds; }; } // namespace dom diff --git a/dom/base/nsDeprecatedOperationList.h b/dom/base/nsDeprecatedOperationList.h index 0bae2d6211..bb9d8fd3b1 100644 --- a/dom/base/nsDeprecatedOperationList.h +++ b/dom/base/nsDeprecatedOperationList.h @@ -47,3 +47,4 @@ DEPRECATED_OPERATION(PrefixedFullscreenAPI) DEPRECATED_OPERATION(LenientSetter) DEPRECATED_OPERATION(FileLastModifiedDate) DEPRECATED_OPERATION(ImageBitmapRenderingContext_TransferImageBitmap) +DEPRECATED_OPERATION(DOMQuadBoundsAttr) diff --git a/dom/locales/en-US/chrome/dom/dom.properties b/dom/locales/en-US/chrome/dom/dom.properties index 60104a63ad..27b4eebf12 100644 --- a/dom/locales/en-US/chrome/dom/dom.properties +++ b/dom/locales/en-US/chrome/dom/dom.properties @@ -320,3 +320,4 @@ LargeAllocationNonE10S=A Large-Allocation header was ignored due to the document PushStateFloodingPrevented=Call to pushState or replaceState ignored due to excessive calls within a short timeframe. # LOCALIZATION NOTE: Do not translate "Reload" ReloadFloodingPrevented=Call to Reload ignored due to excessive calls within a short timeframe. +DOMQuadBoundsAttrWarning=DOMQuad.bounds is deprecated in favor of DOMQuad.getBounds() diff --git a/dom/webidl/DOMQuad.webidl b/dom/webidl/DOMQuad.webidl index f3674e9ad7..7370330b29 100644 --- a/dom/webidl/DOMQuad.webidl +++ b/dom/webidl/DOMQuad.webidl @@ -21,5 +21,21 @@ interface DOMQuad { [SameObject] readonly attribute DOMPoint p4; [NewObject] DOMRectReadOnly getBounds(); - [Default] object toJSON(); + [SameObject, Deprecated=DOMQuadBoundsAttr] readonly attribute DOMRectReadOnly bounds; + + DOMQuadJSON toJSON(); +}; + +dictionary DOMQuadJSON { + DOMPoint p1; + DOMPoint p2; + DOMPoint p3; + DOMPoint p4; +}; + +dictionary DOMQuadInit { + DOMPointInit p1; + DOMPointInit p2; + DOMPointInit p3; + DOMPointInit p4; }; From 69e45fd84e2d9ad91f8d3cb471a16012d2f2048b Mon Sep 17 00:00:00 2001 From: Job Bautista Date: Fri, 12 May 2023 11:54:35 +0800 Subject: [PATCH 06/16] Issue #2241 - Part 4.3: Move devtools over to getBounds(). This leaves getAdjustedQuads alone because it lives in its own world and its result gets sent over IPC. That leaves things in a bit of an intermediate state, but that should be OK for now. Backported from Mozilla bug 1186265. --- ...rules_completion-new-property_multiline.js | 6 ++--- devtools/client/shared/autocomplete-popup.js | 2 +- .../test/browser_html_tooltip_arrow-01.js | 6 ++--- .../test/browser_html_tooltip_arrow-02.js | 6 ++--- ...wser_inplace-editor_autocomplete_offset.js | 2 +- .../shared/widgets/tooltip/HTMLTooltip.js | 6 ++--- devtools/server/actors/inspector.js | 5 ++++- devtools/shared/layout/utils.js | 22 ++++++++++++------- 8 files changed, 32 insertions(+), 23 deletions(-) diff --git a/devtools/client/inspector/rules/test/browser_rules_completion-new-property_multiline.js b/devtools/client/inspector/rules/test/browser_rules_completion-new-property_multiline.js index f29de54a44..a4fafa2c1b 100644 --- a/devtools/client/inspector/rules/test/browser_rules_completion-new-property_multiline.js +++ b/devtools/client/inspector/rules/test/browser_rules_completion-new-property_multiline.js @@ -39,14 +39,14 @@ add_task(function* () { // Calculate offsets to click in the middle of the first box quad. let rect = prop.editor.valueSpan.getBoundingClientRect(); - let firstQuad = prop.editor.valueSpan.getBoxQuads()[0]; + let firstQuadBounds = prop.editor.valueSpan.getBoxQuads()[0].getBounds(); // For a multiline value, the first quad left edge is not aligned with the // bounding rect left edge. The offsets expected by focusEditableField are // relative to the bouding rectangle, so we need to translate the x-offset. - let x = firstQuad.bounds.left - rect.left + firstQuad.bounds.width / 2; + let x = firstQuadBounds.left - rect.left + firstQuadBounds.width / 2; // The first quad top edge is aligned with the bounding top edge, no // translation needed here. - let y = firstQuad.bounds.height / 2; + let y = firstQuadBounds.height / 2; info("Focusing the css property editable value"); let editor = yield focusEditableField(view, prop.editor.valueSpan, x, y); diff --git a/devtools/client/shared/autocomplete-popup.js b/devtools/client/shared/autocomplete-popup.js index b22de4d992..e848f6c1fd 100644 --- a/devtools/client/shared/autocomplete-popup.js +++ b/devtools/client/shared/autocomplete-popup.js @@ -283,7 +283,7 @@ AutocompletePopup.prototype = { return; } - let {top, height} = quads[0].bounds; + let {top, height} = quads[0].getBounds(); let containerHeight = this._tooltip.panel.getBoundingClientRect().height; if (top < 0) { // Element is above container. diff --git a/devtools/client/shared/test/browser_html_tooltip_arrow-01.js b/devtools/client/shared/test/browser_html_tooltip_arrow-01.js index a20c67529a..4c3b332d26 100644 --- a/devtools/client/shared/test/browser_html_tooltip_arrow-01.js +++ b/devtools/client/shared/test/browser_html_tooltip_arrow-01.js @@ -85,9 +85,9 @@ function* runTests(doc) { ok(arrow, "Tooltip has an arrow"); // Get the geometry of the anchor, the tooltip panel & arrow. - let arrowBounds = arrow.getBoxQuads({relativeTo: doc})[0].bounds; - let panelBounds = tooltip.panel.getBoxQuads({relativeTo: doc})[0].bounds; - let anchorBounds = el.getBoxQuads({relativeTo: doc})[0].bounds; + let arrowBounds = arrow.getBoxQuads({relativeTo: doc})[0].getBounds(); + let panelBounds = tooltip.panel.getBoxQuads({relativeTo: doc})[0].getBounds(); + let anchorBounds = el.getBoxQuads({relativeTo: doc})[0].getBounds(); let intersects = arrowBounds.left <= anchorBounds.right && arrowBounds.right >= anchorBounds.left; diff --git a/devtools/client/shared/test/browser_html_tooltip_arrow-02.js b/devtools/client/shared/test/browser_html_tooltip_arrow-02.js index 098f1ac7bf..5bf13e911e 100644 --- a/devtools/client/shared/test/browser_html_tooltip_arrow-02.js +++ b/devtools/client/shared/test/browser_html_tooltip_arrow-02.js @@ -78,9 +78,9 @@ function* runTests(doc) { ok(arrow, "Tooltip has an arrow"); // Get the geometry of the anchor, the tooltip panel & arrow. - let arrowBounds = arrow.getBoxQuads({relativeTo: doc})[0].bounds; - let panelBounds = tooltip.panel.getBoxQuads({relativeTo: doc})[0].bounds; - let anchorBounds = el.getBoxQuads({relativeTo: doc})[0].bounds; + let arrowBounds = arrow.getBoxQuads({relativeTo: doc})[0].getBounds(); + let panelBounds = tooltip.panel.getBoxQuads({relativeTo: doc})[0].getBounds(); + let anchorBounds = el.getBoxQuads({relativeTo: doc})[0].getBounds(); let intersects = arrowBounds.left <= anchorBounds.right && arrowBounds.right >= anchorBounds.left; diff --git a/devtools/client/shared/test/browser_inplace-editor_autocomplete_offset.js b/devtools/client/shared/test/browser_inplace-editor_autocomplete_offset.js index 2734125bd1..e5ce45b280 100644 --- a/devtools/client/shared/test/browser_inplace-editor_autocomplete_offset.js +++ b/devtools/client/shared/test/browser_inplace-editor_autocomplete_offset.js @@ -114,5 +114,5 @@ let runAutocompletionTest = Task.async(function* (editor) { */ function getPopupOffset({popup, input}) { let popupQuads = popup._panel.getBoxQuads({relativeTo: input}); - return popupQuads[0].bounds.left; + return popupQuads[0].getBounds().left; } diff --git a/devtools/client/shared/widgets/tooltip/HTMLTooltip.js b/devtools/client/shared/widgets/tooltip/HTMLTooltip.js index 9eb7910303..6cc12a53a4 100644 --- a/devtools/client/shared/widgets/tooltip/HTMLTooltip.js +++ b/devtools/client/shared/widgets/tooltip/HTMLTooltip.js @@ -176,9 +176,9 @@ const getRelativeRect = function (node, relativeTo) { // Width and Height can be taken from the rect. let {width, height} = node.getBoundingClientRect(); - let quads = node.getBoxQuads({relativeTo}); - let top = quads[0].bounds.top; - let left = quads[0].bounds.left; + let quadBounds = node.getBoxQuads({relativeTo})[0].getBounds(); + let top = quadBounds.top; + let left = quadBounds.left; // Compute right and bottom coordinates using the rest of the data. let right = left + width; diff --git a/devtools/server/actors/inspector.js b/devtools/server/actors/inspector.js index 883809b6cf..ba9393de58 100644 --- a/devtools/server/actors/inspector.js +++ b/devtools/server/actors/inspector.js @@ -3071,7 +3071,10 @@ function nodeHasSize(node) { } let quads = node.getBoxQuads(); - return quads.length && quads.some(quad => quad.bounds.width && quad.bounds.height); + return quads.some(quad => { + let bounds = quad.getBounds(); + return bounds.width && bounds.height; + }); } /** diff --git a/devtools/shared/layout/utils.js b/devtools/shared/layout/utils.js index 1e6ab5075a..bbe6df4c85 100644 --- a/devtools/shared/layout/utils.js +++ b/devtools/shared/layout/utils.js @@ -175,6 +175,11 @@ exports.getFrameOffsets = getFrameOffsets; /** * Get box quads adjusted for iframes and zoom level. * + * Warning: this function returns things that look like DOMQuad objects but + * aren't (they resemble an old version of the spec). Unlike the return value + * of node.getBoxQuads, they have a .bounds property and not a .getBounds() + * method. + * * @param {DOMWindow} boundaryWindow * The window where to stop to iterate. If `null` is given, the top * window is used. @@ -206,6 +211,7 @@ function getAdjustedQuads(boundaryWindow, node, region) { let adjustedQuads = []; for (let quad of quads) { + let bounds = quad.getBounds(); adjustedQuads.push({ p1: { w: quad.p1.w * scale, @@ -232,14 +238,14 @@ function getAdjustedQuads(boundaryWindow, node, region) { z: quad.p4.z * scale }, bounds: { - bottom: quad.bounds.bottom * scale + yOffset, - height: quad.bounds.height * scale, - left: quad.bounds.left * scale + xOffset, - right: quad.bounds.right * scale + xOffset, - top: quad.bounds.top * scale + yOffset, - width: quad.bounds.width * scale, - x: quad.bounds.x * scale + xOffset, - y: quad.bounds.y * scale + yOffset + bottom: bounds.bottom * scale + yOffset, + height: bounds.height * scale, + left: bounds.left * scale + xOffset, + right: bounds.right * scale + xOffset, + top: bounds.top * scale + yOffset, + width: bounds.width * scale, + x: bounds.x * scale + xOffset, + y: bounds.y * scale + yOffset } }); } From c3042a2f41884f75d6e1ee040864a0734d6cf7bd Mon Sep 17 00:00:00 2001 From: Job Bautista Date: Fri, 12 May 2023 13:45:39 +0800 Subject: [PATCH 07/16] Issue #2241 - Part 5: Expose Geometry interfaces to web workers. Exposes DOMMatrix, DOMPoint, DOMQuad, and DOMRect to workers. Backported from Mozilla bug 1420580. --- dom/base/DOMMatrix.cpp | 105 ++++++++++++++++++++++ dom/base/DOMMatrix.h | 24 ++++++ dom/base/DOMPoint.cpp | 36 ++++++++ dom/base/DOMPoint.h | 9 +- dom/base/DOMQuad.cpp | 24 ++++++ dom/base/DOMQuad.h | 5 ++ dom/base/DOMRect.cpp | 36 ++++++++ dom/base/DOMRect.h | 5 ++ dom/base/StructuredCloneHolder.cpp | 134 ++++++++++++++++++++++++++++- dom/base/StructuredCloneTags.h | 9 ++ dom/webidl/DOMMatrix.webidl | 12 +-- dom/webidl/DOMPoint.webidl | 8 +- dom/webidl/DOMQuad.webidl | 5 +- dom/webidl/DOMRect.webidl | 8 +- 14 files changed, 404 insertions(+), 16 deletions(-) diff --git a/dom/base/DOMMatrix.cpp b/dom/base/DOMMatrix.cpp index 1631f2cdcf..0045e17d84 100644 --- a/dom/base/DOMMatrix.cpp +++ b/dom/base/DOMMatrix.cpp @@ -64,6 +64,24 @@ DOMMatrixReadOnly::Constructor( return rval.forget(); } +already_AddRefed +DOMMatrixReadOnly::ReadStructuredClone(nsISupports* aParent, JSStructuredCloneReader* aReader) +{ + uint8_t is2D; + + if (!JS_ReadBytes(aReader, &is2D, 1)) { + return nullptr; + } + + RefPtr rval = new DOMMatrixReadOnly(aParent, is2D); + + if (!ReadStructuredCloneElements(aReader, rval)) { + return nullptr; + }; + + return rval.forget(); +} + already_AddRefed DOMMatrixReadOnly::Translate(double aTx, double aTy, @@ -344,6 +362,70 @@ DOMMatrixReadOnly::Stringify(nsAString& aResult) aResult = matrixStr; } +// https://drafts.fxtf.org/geometry/#structured-serialization +bool +DOMMatrixReadOnly::WriteStructuredClone(JSStructuredCloneWriter* aWriter) const +{ +#define WriteFloatPair(f1, f2) \ + JS_WriteUint32Pair(aWriter, BitwiseCast(f1), \ + BitwiseCast(f2)) + + const uint8_t is2D = Is2D(); + + if (!JS_WriteBytes(aWriter, &is2D, 1)) { + return false; + } + + if (is2D == 1) { + return WriteFloatPair(mMatrix2D->_11, mMatrix2D->_12) && + WriteFloatPair(mMatrix2D->_21, mMatrix2D->_22) && + WriteFloatPair(mMatrix2D->_31, mMatrix2D->_32); + } + + return WriteFloatPair(mMatrix3D->_11, mMatrix3D->_12) && + WriteFloatPair(mMatrix3D->_13, mMatrix3D->_14) && + WriteFloatPair(mMatrix3D->_21, mMatrix3D->_22) && + WriteFloatPair(mMatrix3D->_23, mMatrix3D->_24) && + WriteFloatPair(mMatrix3D->_31, mMatrix3D->_32) && + WriteFloatPair(mMatrix3D->_33, mMatrix3D->_34) && + WriteFloatPair(mMatrix3D->_41, mMatrix3D->_42) && + WriteFloatPair(mMatrix3D->_43, mMatrix3D->_44); +#undef WriteFloatPair +} + +bool +DOMMatrixReadOnly::ReadStructuredCloneElements(JSStructuredCloneReader* aReader, DOMMatrixReadOnly* matrix) +{ + uint32_t high; + uint32_t low; + +#define ReadFloatPair(f1, f2) \ + if (!JS_ReadUint32Pair(aReader, &high, &low)) { \ + return false; \ + } \ + (*(f1) = BitwiseCast(high)); \ + (*(f2) = BitwiseCast(low)); + + if (matrix->Is2D() == 1) { + ReadFloatPair(&(matrix->mMatrix2D->_11), &(matrix->mMatrix2D->_12)); + ReadFloatPair(&(matrix->mMatrix2D->_21), &(matrix->mMatrix2D->_22)); + ReadFloatPair(&(matrix->mMatrix2D->_31), &(matrix->mMatrix2D->_32)); + } else { + ReadFloatPair(&(matrix->mMatrix3D->_11), &(matrix->mMatrix3D->_12)); + ReadFloatPair(&(matrix->mMatrix3D->_13), &(matrix->mMatrix3D->_14)); + ReadFloatPair(&(matrix->mMatrix3D->_21), &(matrix->mMatrix3D->_22)); + ReadFloatPair(&(matrix->mMatrix3D->_23), &(matrix->mMatrix3D->_24)); + ReadFloatPair(&(matrix->mMatrix3D->_31), &(matrix->mMatrix3D->_32)); + ReadFloatPair(&(matrix->mMatrix3D->_33), &(matrix->mMatrix3D->_34)); + ReadFloatPair(&(matrix->mMatrix3D->_41), &(matrix->mMatrix3D->_42)); + ReadFloatPair(&(matrix->mMatrix3D->_43), &(matrix->mMatrix3D->_44)); + } + + return true; + +#undef ReadFloatPair +} + already_AddRefed DOMMatrix::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv) { @@ -354,6 +436,11 @@ DOMMatrix::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv) already_AddRefed DOMMatrix::Constructor(const GlobalObject& aGlobal, const nsAString& aTransformList, ErrorResult& aRv) { + nsCOMPtr win = do_QueryInterface(aGlobal.GetAsSupports()); + if (!win) { + aRv.ThrowTypeError(); + return nullptr; + } RefPtr obj = new DOMMatrix(aGlobal.GetAsSupports()); obj = obj->SetMatrixValue(aTransformList, aRv); @@ -431,6 +518,24 @@ DOMMatrix::Constructor(const GlobalObject& aGlobal, const Sequence& aNum return obj.forget(); } +already_AddRefed +DOMMatrix::ReadStructuredClone(nsISupports* aParent, JSStructuredCloneReader* aReader) +{ + uint8_t is2D; + + if (!JS_ReadBytes(aReader, &is2D, 1)) { + return nullptr; + } + + RefPtr rval = new DOMMatrix(aParent, is2D); + + if (!ReadStructuredCloneElements(aReader, rval)) { + return nullptr; + }; + + return rval.forget(); +} + void DOMMatrixReadOnly::Ensure3DMatrix() { diff --git a/dom/base/DOMMatrix.h b/dom/base/DOMMatrix.h index e956878c20..fe1325c594 100644 --- a/dom/base/DOMMatrix.h +++ b/dom/base/DOMMatrix.h @@ -6,6 +6,7 @@ #ifndef MOZILLA_DOM_DOMMATRIX_H_ #define MOZILLA_DOM_DOMMATRIX_H_ +#include "js/StructuredClone.h" #include "nsWrapperCache.h" #include "nsISupports.h" #include "nsCycleCollectionParticipant.h" @@ -58,6 +59,12 @@ public: static already_AddRefed Constructor(const GlobalObject& aGlobal, const Optional& aArg, ErrorResult& aRv); + static already_AddRefed + ReadStructuredClone(nsISupports* aParent, JSStructuredCloneReader* aReader); + + static bool + ReadStructuredCloneElements(JSStructuredCloneReader* aReader, DOMMatrixReadOnly* matrix); + #define GetMatrixMember(entry2D, entry3D, default) \ { \ if (mMatrix3D) { \ @@ -188,6 +195,8 @@ public: JS::MutableHandle aResult, ErrorResult& aRv) const; void Stringify(nsAString& aResult); + bool WriteStructuredClone(JSStructuredCloneWriter* aWriter) const; + protected: nsCOMPtr mParent; nsAutoPtr mMatrix2D; @@ -198,6 +207,14 @@ protected: DOMMatrixReadOnly* SetMatrixValue(const nsAString& aTransformList, ErrorResult& aRv); void Ensure3DMatrix(); + DOMMatrixReadOnly(nsISupports* aParent, bool is2D) : mParent(aParent) { + if (is2D) { + mMatrix2D = new gfx::Matrix(); + } else { + mMatrix3D = new gfx::Matrix4x4(); + } + } + private: DOMMatrixReadOnly() = delete; DOMMatrixReadOnly(const DOMMatrixReadOnly&) = delete; @@ -232,6 +249,9 @@ public: static already_AddRefed Constructor(const GlobalObject& aGlobal, const Sequence& aNumberSequence, ErrorResult& aRv); + static already_AddRefed + ReadStructuredClone(nsISupports* aParent, JSStructuredCloneReader* aReader); + virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; DOMMatrix* MultiplySelf(const DOMMatrix& aOther); @@ -267,6 +287,10 @@ public: DOMMatrix* SetMatrixValue(const nsAString& aTransformList, ErrorResult& aRv); virtual ~DOMMatrix() {} + + private: + DOMMatrix(nsISupports* aParent, bool is2D) + : DOMMatrixReadOnly(aParent, is2D) {} }; } // namespace dom diff --git a/dom/base/DOMPoint.cpp b/dom/base/DOMPoint.cpp index 508bfab1e2..7174a0cb1b 100644 --- a/dom/base/DOMPoint.cpp +++ b/dom/base/DOMPoint.cpp @@ -40,6 +40,42 @@ DOMPointReadOnly::WrapObject(JSContext* aCx, JS::Handle aGivenProto) return DOMPointReadOnlyBinding::Wrap(aCx, this, aGivenProto); } +// https://drafts.fxtf.org/geometry/#structured-serialization +bool +DOMPointReadOnly::WriteStructuredClone(JSStructuredCloneWriter* aWriter) const +{ +#define WriteDouble(d) \ + JS_WriteUint32Pair(aWriter, (BitwiseCast(d) >> 32) & 0xffffffff, \ + BitwiseCast(d) & 0xffffffff) + + return WriteDouble(mX) && WriteDouble(mY) && WriteDouble(mZ) && + WriteDouble(mW); + +#undef WriteDouble +} + +bool +DOMPointReadOnly::ReadStructuredClone(JSStructuredCloneReader* aReader) +{ + uint32_t high; + uint32_t low; + +#define ReadDouble(d) \ + if (!JS_ReadUint32Pair(aReader, &high, &low)) { \ + return false; \ + } \ + (*(d) = BitwiseCast(static_cast(high) << 32 | low)) + + ReadDouble(&mX); + ReadDouble(&mY); + ReadDouble(&mZ); + ReadDouble(&mW); + + return true; + +#undef ReadDouble +} + already_AddRefed DOMPoint::FromPoint(const GlobalObject& aGlobal, const DOMPointInit& aParams) { diff --git a/dom/base/DOMPoint.h b/dom/base/DOMPoint.h index 79937f83a3..f460ea725c 100644 --- a/dom/base/DOMPoint.h +++ b/dom/base/DOMPoint.h @@ -6,6 +6,7 @@ #ifndef MOZILLA_DOMPOINT_H_ #define MOZILLA_DOMPOINT_H_ +#include "js/StructuredClone.h" #include "nsWrapperCache.h" #include "nsISupports.h" #include "nsCycleCollectionParticipant.h" @@ -23,8 +24,8 @@ struct DOMPointInit; class DOMPointReadOnly : public nsWrapperCache { public: - DOMPointReadOnly(nsISupports* aParent, double aX, double aY, - double aZ, double aW) + explicit DOMPointReadOnly(nsISupports* aParent, double aX = 0.0, + double aY = 0.0, double aZ = 0.0, double aW = 1.0) : mParent(aParent) , mX(aX) , mY(aY) @@ -50,6 +51,10 @@ public: nsISupports* GetParentObject() const { return mParent; } virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; + bool WriteStructuredClone(JSStructuredCloneWriter* aWriter) const; + + bool ReadStructuredClone(JSStructuredCloneReader* aReader); + protected: virtual ~DOMPointReadOnly() {} diff --git a/dom/base/DOMQuad.cpp b/dom/base/DOMQuad.cpp index a64c883982..8457d9ddaa 100644 --- a/dom/base/DOMQuad.cpp +++ b/dom/base/DOMQuad.cpp @@ -132,3 +132,27 @@ DOMQuad::ToJSON(DOMQuadJSON& aInit) aInit.mP3.Construct(RefPtr(P3()).forget()); aInit.mP4.Construct(RefPtr(P4()).forget()); } + +// https://drafts.fxtf.org/geometry/#structured-serialization +bool +DOMQuad::WriteStructuredClone(JSStructuredCloneWriter* aWriter) const +{ + for (const auto& point : mPoints) { + if (!point->WriteStructuredClone(aWriter)) { + return false; + } + } + return true; +} + +bool +DOMQuad::ReadStructuredClone(JSStructuredCloneReader* aReader) +{ + for (auto& point : mPoints) { + point = new DOMPoint(mParent); + if (!point->ReadStructuredClone(aReader)) { + return false; + } + } + return true; +} diff --git a/dom/base/DOMQuad.h b/dom/base/DOMQuad.h index db38e5d236..9d740e0b5e 100644 --- a/dom/base/DOMQuad.h +++ b/dom/base/DOMQuad.h @@ -6,6 +6,7 @@ #ifndef MOZILLA_DOMQUAD_H_ #define MOZILLA_DOMQUAD_H_ +#include "js/StructuredClone.h" #include "nsWrapperCache.h" #include "nsISupports.h" #include "nsCycleCollectionParticipant.h" @@ -59,6 +60,10 @@ public: void ToJSON(DOMQuadJSON& aInit); + bool WriteStructuredClone(JSStructuredCloneWriter* aWriter) const; + + bool ReadStructuredClone(JSStructuredCloneReader* aReader); + protected: void GetHorizontalMinMax(double* aX1, double* aX2) const; void GetVerticalMinMax(double* aY1, double* aY2) const; diff --git a/dom/base/DOMRect.cpp b/dom/base/DOMRect.cpp index ecd56f10a7..46f395a083 100644 --- a/dom/base/DOMRect.cpp +++ b/dom/base/DOMRect.cpp @@ -36,6 +36,42 @@ DOMRectReadOnly::Constructor(const GlobalObject& aGlobal, double aX, double aY, return obj.forget(); } +// https://drafts.fxtf.org/geometry/#structured-serialization +bool +DOMRectReadOnly::WriteStructuredClone(JSStructuredCloneWriter* aWriter) const +{ +#define WriteDouble(d) \ + JS_WriteUint32Pair(aWriter, (BitwiseCast(d) >> 32) & 0xffffffff, \ + BitwiseCast(d) & 0xffffffff) + + return WriteDouble(mX) && WriteDouble(mY) && WriteDouble(mWidth) && + WriteDouble(mHeight); + +#undef WriteDouble +} + +bool +DOMRectReadOnly::ReadStructuredClone(JSStructuredCloneReader* aReader) +{ + uint32_t high; + uint32_t low; + +#define ReadDouble(d) \ + if (!JS_ReadUint32Pair(aReader, &high, &low)) { \ + return false; \ + } \ + (*(d) = BitwiseCast(static_cast(high) << 32 | low)) + + ReadDouble(&mX); + ReadDouble(&mY); + ReadDouble(&mWidth); + ReadDouble(&mHeight); + + return true; + +#undef ReadDouble +} + // ----------------------------------------------------------------------------- NS_IMPL_ISUPPORTS_INHERITED(DOMRect, DOMRectReadOnly, nsIDOMClientRect) diff --git a/dom/base/DOMRect.h b/dom/base/DOMRect.h index baf3268b20..56478f284c 100644 --- a/dom/base/DOMRect.h +++ b/dom/base/DOMRect.h @@ -6,6 +6,7 @@ #ifndef MOZILLA_DOMRECT_H_ #define MOZILLA_DOMRECT_H_ +#include "js/StructuredClone.h" #include "nsIDOMClientRect.h" #include "nsIDOMClientRectList.h" #include "nsTArray.h" @@ -91,6 +92,10 @@ public: return std::max(y, y + h); } + bool WriteStructuredClone(JSStructuredCloneWriter* aWriter) const; + + bool ReadStructuredClone(JSStructuredCloneReader* aReader); + protected: nsCOMPtr mParent; double mX, mY, mWidth, mHeight; diff --git a/dom/base/StructuredCloneHolder.cpp b/dom/base/StructuredCloneHolder.cpp index 5ad8ebb688..71eab63138 100644 --- a/dom/base/StructuredCloneHolder.cpp +++ b/dom/base/StructuredCloneHolder.cpp @@ -11,6 +11,14 @@ #include "mozilla/dom/CryptoKey.h" #include "mozilla/dom/Directory.h" #include "mozilla/dom/DirectoryBinding.h" +#include "mozilla/dom/DOMMatrix.h" +#include "mozilla/dom/DOMMatrixBinding.h" +#include "mozilla/dom/DOMPoint.h" +#include "mozilla/dom/DOMPointBinding.h" +#include "mozilla/dom/DOMQuad.h" +#include "mozilla/dom/DOMQuadBinding.h" +#include "mozilla/dom/DOMRect.h" +#include "mozilla/dom/DOMRectBinding.h" #include "mozilla/dom/File.h" #include "mozilla/dom/FileList.h" #include "mozilla/dom/FileListBinding.h" @@ -355,7 +363,11 @@ StructuredCloneHolder::ReadFullySerializableObjects(JSContext* aCx, return ReadStructuredCloneImageData(aCx, aReader); } - if (aTag == SCTAG_DOM_WEBCRYPTO_KEY || aTag == SCTAG_DOM_URLSEARCHPARAMS) { + if (aTag == SCTAG_DOM_WEBCRYPTO_KEY || aTag == SCTAG_DOM_URLSEARCHPARAMS || + aTag == SCTAG_DOM_DOMPOINT || aTag == SCTAG_DOM_DOMPOINT_READONLY || + aTag == SCTAG_DOM_DOMRECT || aTag == SCTAG_DOM_DOMRECT_READONLY || + aTag == SCTAG_DOM_DOMQUAD || aTag == SCTAG_DOM_DOMMATRIX || + aTag == SCTAG_DOM_DOMMATRIX_READONLY) { nsIGlobalObject *global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx)); if (!global) { return nullptr; @@ -378,6 +390,57 @@ StructuredCloneHolder::ReadFullySerializableObjects(JSContext* aCx, } else { result = usp->WrapObject(aCx, nullptr); } + } else if (aTag == SCTAG_DOM_DOMPOINT) { + RefPtr domPoint = new DOMPoint(global); + if (!domPoint->ReadStructuredClone(aReader)) { + result = nullptr; + } else { + result = domPoint->WrapObject(aCx, nullptr); + } + } else if (aTag == SCTAG_DOM_DOMPOINT_READONLY) { + RefPtr domPoint = new DOMPointReadOnly(global); + if (!domPoint->ReadStructuredClone(aReader)) { + result = nullptr; + } else { + result = domPoint->WrapObject(aCx, nullptr); + } + } else if (aTag == SCTAG_DOM_DOMRECT) { + RefPtr domRect = new DOMRect(global); + if (!domRect->ReadStructuredClone(aReader)) { + result = nullptr; + } else { + result = domRect->WrapObject(aCx, nullptr); + } + } else if (aTag == SCTAG_DOM_DOMRECT_READONLY) { + RefPtr domRect = new DOMRectReadOnly(global); + if (!domRect->ReadStructuredClone(aReader)) { + result = nullptr; + } else { + result = domRect->WrapObject(aCx, nullptr); + } + } else if (aTag == SCTAG_DOM_DOMQUAD) { + RefPtr domQuad = new DOMQuad(global); + if (!domQuad->ReadStructuredClone(aReader)) { + result = nullptr; + } else { + result = domQuad->WrapObject(aCx, nullptr); + } + } else if (aTag == SCTAG_DOM_DOMMATRIX) { + RefPtr domMatrix = + DOMMatrix::ReadStructuredClone(global, aReader); + if (!domMatrix) { + result = nullptr; + } else { + result = domMatrix->WrapObject(aCx, nullptr); + } + } else if (aTag == SCTAG_DOM_DOMMATRIX_READONLY) { + RefPtr domMatrix = + DOMMatrixReadOnly::ReadStructuredClone(global, aReader); + if (!domMatrix) { + result = nullptr; + } else { + result = domMatrix->WrapObject(aCx, nullptr); + } } } return result; @@ -483,6 +546,75 @@ StructuredCloneHolder::WriteFullySerializableObjects(JSContext* aCx, } #endif + // Handle DOMPoint cloning + // Should be done before DOMPointeReadOnly check + // because every DOMPoint is also a DOMPointReadOnly + { + DOMPoint* domPoint = nullptr; + if (NS_SUCCEEDED(UNWRAP_OBJECT(DOMPoint, &obj, domPoint))) { + return JS_WriteUint32Pair(aWriter, SCTAG_DOM_DOMPOINT, 0) && + domPoint->WriteStructuredClone(aWriter); + } + } + + // Handle DOMPointReadOnly cloning + { + DOMPointReadOnly* domPoint = nullptr; + if (NS_SUCCEEDED(UNWRAP_OBJECT(DOMPointReadOnly, &obj, domPoint))) { + return JS_WriteUint32Pair(aWriter, SCTAG_DOM_DOMPOINT_READONLY, 0) && + domPoint->WriteStructuredClone(aWriter); + } + } + + // Handle DOMRect cloning + // Should be done before DOMRecteReadOnly check + // because every DOMRect is also a DOMRectReadOnly + { + DOMRect* domRect = nullptr; + if (NS_SUCCEEDED(UNWRAP_OBJECT(DOMRect, &obj, domRect))) { + return JS_WriteUint32Pair(aWriter, SCTAG_DOM_DOMRECT, 0) && + domRect->WriteStructuredClone(aWriter); + } + } + + // Handle DOMRectReadOnly cloning + { + DOMRectReadOnly* domRect = nullptr; + if (NS_SUCCEEDED(UNWRAP_OBJECT(DOMRectReadOnly, &obj, domRect))) { + return JS_WriteUint32Pair(aWriter, SCTAG_DOM_DOMRECT_READONLY, 0) && + domRect->WriteStructuredClone(aWriter); + } + } + + // Handle DOMQuad cloning + { + DOMQuad* domQuad = nullptr; + if (NS_SUCCEEDED(UNWRAP_OBJECT(DOMQuad, &obj, domQuad))) { + return JS_WriteUint32Pair(aWriter, SCTAG_DOM_DOMQUAD, 0) && + domQuad->WriteStructuredClone(aWriter); + } + } + + // Handle DOMMatrix cloning + // Should be done before DOMMatrixeReadOnly check + // because every DOMMatrix is also a DOMMatrixReadOnly + { + DOMMatrix* domMatrix = nullptr; + if (NS_SUCCEEDED(UNWRAP_OBJECT(DOMMatrix, &obj, domMatrix))) { + return JS_WriteUint32Pair(aWriter, SCTAG_DOM_DOMMATRIX, 0) && + domMatrix->WriteStructuredClone(aWriter); + } + } + + // Handle DOMMatrixReadOnly cloning + { + DOMMatrixReadOnly* domMatrix = nullptr; + if (NS_SUCCEEDED(UNWRAP_OBJECT(DOMMatrixReadOnly, &obj, domMatrix))) { + return JS_WriteUint32Pair(aWriter, SCTAG_DOM_DOMMATRIX_READONLY, 0) && + domMatrix->WriteStructuredClone(aWriter); + } + } + if (NS_IsMainThread() && xpc::IsReflector(obj)) { nsCOMPtr base = xpc::UnwrapReflectorToISupports(obj); nsCOMPtr principal = do_QueryInterface(base); diff --git a/dom/base/StructuredCloneTags.h b/dom/base/StructuredCloneTags.h index 8766d8e4ad..09b91f7afb 100644 --- a/dom/base/StructuredCloneTags.h +++ b/dom/base/StructuredCloneTags.h @@ -30,6 +30,15 @@ enum StructuredCloneTags { // New IDB tags go here! + // Tags for Geometry interfaces. + SCTAG_DOM_DOMPOINT, + SCTAG_DOM_DOMPOINT_READONLY, + SCTAG_DOM_DOMQUAD, + SCTAG_DOM_DOMRECT, + SCTAG_DOM_DOMRECT_READONLY, + SCTAG_DOM_DOMMATRIX, + SCTAG_DOM_DOMMATRIX_READONLY, + // These tags are used for both main thread and workers. SCTAG_DOM_IMAGEDATA, SCTAG_DOM_MAP_MESSAGEPORT, diff --git a/dom/webidl/DOMMatrix.webidl b/dom/webidl/DOMMatrix.webidl index 97dc16616c..e54d16c21b 100644 --- a/dom/webidl/DOMMatrix.webidl +++ b/dom/webidl/DOMMatrix.webidl @@ -4,14 +4,15 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * The origin of this IDL file is - * http://dev.w3.org/fxtf/geometry/ + * https://drafts.fxtf.org/geometry/ * * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C * liability, trademark and document use rules apply. */ [Pref="layout.css.DOMMatrix.enabled", - Constructor(optional (DOMString or sequence) init)] + Constructor(optional (DOMString or sequence) init), + Exposed=(Window,Worker)] interface DOMMatrixReadOnly { // These attributes are simple aliases for certain elements of the 4x4 matrix readonly attribute unrestricted double a; @@ -77,7 +78,7 @@ interface DOMMatrixReadOnly { DOMPoint transformPoint(optional DOMPointInit point); [Throws] Float32Array toFloat32Array(); [Throws] Float64Array toFloat64Array(); - stringifier; + [Exposed=Window] stringifier; [Default] object toJSON(); }; @@ -87,7 +88,8 @@ interface DOMMatrixReadOnly { Constructor(DOMMatrixReadOnly other), Constructor(Float32Array array32), Constructor(Float64Array array64), - Constructor(sequence numberSequence)] + Constructor(sequence numberSequence), + Exposed=(Window,Worker)] interface DOMMatrix : DOMMatrixReadOnly { // These attributes are simple aliases for certain elements of the 4x4 matrix inherit attribute unrestricted double a; @@ -145,6 +147,6 @@ interface DOMMatrix : DOMMatrixReadOnly { DOMMatrix skewXSelf(unrestricted double sx); DOMMatrix skewYSelf(unrestricted double sy); DOMMatrix invertSelf(); - [Throws] DOMMatrix setMatrixValue(DOMString transformList); + [Exposed=Window, Throws] DOMMatrix setMatrixValue(DOMString transformList); }; diff --git a/dom/webidl/DOMPoint.webidl b/dom/webidl/DOMPoint.webidl index 2ebba958e4..1603253a69 100644 --- a/dom/webidl/DOMPoint.webidl +++ b/dom/webidl/DOMPoint.webidl @@ -4,7 +4,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * The origin of this IDL file is - * http://dev.w3.org/fxtf/geometry/ + * https://drafts.fxtf.org/geometry/ * * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C * liability, trademark and document use rules apply. @@ -12,7 +12,8 @@ [Pref="layout.css.DOMPoint.enabled", Constructor(optional unrestricted double x = 0, optional unrestricted double y = 0, - optional unrestricted double z = 0, optional unrestricted double w = 1)] + optional unrestricted double z = 0, optional unrestricted double w = 1), + Exposed=(Window,Worker)] interface DOMPointReadOnly { [NewObject] static DOMPointReadOnly fromPoint(optional DOMPointInit other); @@ -26,7 +27,8 @@ interface DOMPointReadOnly { [Pref="layout.css.DOMPoint.enabled", Constructor(optional unrestricted double x = 0, optional unrestricted double y = 0, - optional unrestricted double z = 0, optional unrestricted double w = 1)] + optional unrestricted double z = 0, optional unrestricted double w = 1), + Exposed=(Window,Worker)] interface DOMPoint : DOMPointReadOnly { [NewObject] static DOMPoint fromPoint(optional DOMPointInit other); diff --git a/dom/webidl/DOMQuad.webidl b/dom/webidl/DOMQuad.webidl index 7370330b29..ac89ac4450 100644 --- a/dom/webidl/DOMQuad.webidl +++ b/dom/webidl/DOMQuad.webidl @@ -4,7 +4,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * The origin of this IDL file is - * http://dev.w3.org/fxtf/geometry/ + * https://drafts.fxtf.org/geometry/ * * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C * liability, trademark and document use rules apply. @@ -13,7 +13,8 @@ [Pref="layout.css.DOMQuad.enabled", Constructor(optional DOMPointInit p1, optional DOMPointInit p2, optional DOMPointInit p3, optional DOMPointInit p4), - Constructor(DOMRectReadOnly rect)] + Constructor(DOMRectReadOnly rect), + Exposed=(Window,Worker)] interface DOMQuad { [SameObject] readonly attribute DOMPoint p1; [SameObject] readonly attribute DOMPoint p2; diff --git a/dom/webidl/DOMRect.webidl b/dom/webidl/DOMRect.webidl index 4a4f3e4eda..92c0cadc67 100644 --- a/dom/webidl/DOMRect.webidl +++ b/dom/webidl/DOMRect.webidl @@ -4,14 +4,15 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * The origin of this IDL file is - * http://dev.w3.org/fxtf/geometry/ + * https://drafts.fxtf.org/geometry/ * * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C * liability, trademark and document use rules apply. */ [Constructor(optional unrestricted double x = 0, optional unrestricted double y = 0, - optional unrestricted double width = 0, optional unrestricted double height = 0)] + optional unrestricted double width = 0, optional unrestricted double height = 0), + Exposed=(Window,Worker)] interface DOMRect : DOMRectReadOnly { inherit attribute unrestricted double x; inherit attribute unrestricted double y; @@ -20,7 +21,8 @@ interface DOMRect : DOMRectReadOnly { }; [Constructor(optional unrestricted double x = 0, optional unrestricted double y = 0, - optional unrestricted double width = 0, optional unrestricted double height = 0)] + optional unrestricted double width = 0, optional unrestricted double height = 0), + Exposed=(Window,Worker)] interface DOMRectReadOnly { readonly attribute unrestricted double x; readonly attribute unrestricted double y; From 51f812bb807094aa64c026845e991459f4fd6a5d Mon Sep 17 00:00:00 2001 From: Job Bautista Date: Fri, 12 May 2023 15:37:58 +0800 Subject: [PATCH 08/16] Issue #2241 - Part 4&5 Follow-up: Fix WebIDL errors Remove [Pref] attribute from Geometry interfaces as they're now exposed to web workers too via Part 5. Remove nonexistent toJSON objects erroneously added in Part 4.1. --- dom/webidl/DOMMatrix.webidl | 7 ++----- dom/webidl/DOMPoint.webidl | 8 ++------ dom/webidl/DOMQuad.webidl | 3 +-- dom/webidl/DOMRect.webidl | 2 -- 4 files changed, 5 insertions(+), 15 deletions(-) diff --git a/dom/webidl/DOMMatrix.webidl b/dom/webidl/DOMMatrix.webidl index e54d16c21b..68c70507b7 100644 --- a/dom/webidl/DOMMatrix.webidl +++ b/dom/webidl/DOMMatrix.webidl @@ -10,8 +10,7 @@ * liability, trademark and document use rules apply. */ -[Pref="layout.css.DOMMatrix.enabled", - Constructor(optional (DOMString or sequence) init), +[Constructor(optional (DOMString or sequence) init), Exposed=(Window,Worker)] interface DOMMatrixReadOnly { // These attributes are simple aliases for certain elements of the 4x4 matrix @@ -79,11 +78,9 @@ interface DOMMatrixReadOnly { [Throws] Float32Array toFloat32Array(); [Throws] Float64Array toFloat64Array(); [Exposed=Window] stringifier; - [Default] object toJSON(); }; -[Pref="layout.css.DOMMatrix.enabled", - Constructor, +[Constructor, Constructor(DOMString transformList), Constructor(DOMMatrixReadOnly other), Constructor(Float32Array array32), diff --git a/dom/webidl/DOMPoint.webidl b/dom/webidl/DOMPoint.webidl index 1603253a69..313a51bc53 100644 --- a/dom/webidl/DOMPoint.webidl +++ b/dom/webidl/DOMPoint.webidl @@ -10,8 +10,7 @@ * liability, trademark and document use rules apply. */ -[Pref="layout.css.DOMPoint.enabled", - Constructor(optional unrestricted double x = 0, optional unrestricted double y = 0, +[Constructor(optional unrestricted double x = 0, optional unrestricted double y = 0, optional unrestricted double z = 0, optional unrestricted double w = 1), Exposed=(Window,Worker)] interface DOMPointReadOnly { @@ -21,12 +20,9 @@ interface DOMPointReadOnly { readonly attribute unrestricted double y; readonly attribute unrestricted double z; readonly attribute unrestricted double w; - - [Default] object toJSON(); }; -[Pref="layout.css.DOMPoint.enabled", - Constructor(optional unrestricted double x = 0, optional unrestricted double y = 0, +[Constructor(optional unrestricted double x = 0, optional unrestricted double y = 0, optional unrestricted double z = 0, optional unrestricted double w = 1), Exposed=(Window,Worker)] interface DOMPoint : DOMPointReadOnly { diff --git a/dom/webidl/DOMQuad.webidl b/dom/webidl/DOMQuad.webidl index ac89ac4450..5b130d271f 100644 --- a/dom/webidl/DOMQuad.webidl +++ b/dom/webidl/DOMQuad.webidl @@ -10,8 +10,7 @@ * liability, trademark and document use rules apply. */ -[Pref="layout.css.DOMQuad.enabled", - Constructor(optional DOMPointInit p1, optional DOMPointInit p2, +[Constructor(optional DOMPointInit p1, optional DOMPointInit p2, optional DOMPointInit p3, optional DOMPointInit p4), Constructor(DOMRectReadOnly rect), Exposed=(Window,Worker)] diff --git a/dom/webidl/DOMRect.webidl b/dom/webidl/DOMRect.webidl index 92c0cadc67..c3bedc7ef5 100644 --- a/dom/webidl/DOMRect.webidl +++ b/dom/webidl/DOMRect.webidl @@ -32,8 +32,6 @@ interface DOMRectReadOnly { readonly attribute unrestricted double right; readonly attribute unrestricted double bottom; readonly attribute unrestricted double left; - - [Default] object toJSON(); }; dictionary DOMRectInit { From 10bdcb7e5f45f3f5503a5264d25265b47520cafe Mon Sep 17 00:00:00 2001 From: Job Bautista Date: Fri, 12 May 2023 15:42:46 +0800 Subject: [PATCH 09/16] Issue #2241 - Part 6: Implement DOMMatrix.fromMatrix. Also fixes .multiply() to use DOMMatrixInit. Backported from Mozilla bug 1560462. --- dom/base/DOMMatrix.cpp | 177 +++++++++++++++++++++++++++--- dom/base/DOMMatrix.h | 23 +++- dom/base/WebKitCSSMatrix.cpp | 4 +- dom/base/WebKitCSSMatrix.h | 3 +- dom/bindings/Errors.msg | 2 + dom/webidl/DOMMatrix.webidl | 38 ++++++- dom/webidl/WebKitCSSMatrix.webidl | 3 +- 7 files changed, 226 insertions(+), 24 deletions(-) diff --git a/dom/base/DOMMatrix.cpp b/dom/base/DOMMatrix.cpp index 0045e17d84..09c186e77e 100644 --- a/dom/base/DOMMatrix.cpp +++ b/dom/base/DOMMatrix.cpp @@ -17,6 +17,8 @@ #include +#include "js/Equality.h" // JS::SameValueZero + namespace mozilla { namespace dom { @@ -37,6 +39,130 @@ DOMMatrixReadOnly::WrapObject(JSContext* aCx, JS::Handle aGivenProto) return DOMMatrixReadOnlyBinding::Wrap(aCx, this, aGivenProto); } +// https://drafts.fxtf.org/geometry/#matrix-validate-and-fixup-2d +static bool +ValidateAndFixupMatrix2DInit(DOMMatrix2DInit& aMatrixInit, ErrorResult& aRv) +{ +#define ValidateAliases(field, alias, fieldName, aliasName) \ + if ((field).WasPassed() && (alias).WasPassed() && \ + !JS::SameValueZero((field).Value(), (alias).Value())) { \ + aRv.ThrowTypeError((fieldName), \ + (aliasName)); \ + return false; \ + } +#define SetFromAliasOrDefault(field, alias, defaultValue) \ + if (!(field).WasPassed()) { \ + if ((alias).WasPassed()) { \ + (field).Construct((alias).Value()); \ + } else { \ + (field).Construct(defaultValue); \ + } \ + } +#define ValidateAndSet(field, alias, fieldName, aliasName, defaultValue) \ + ValidateAliases((field), (alias), NS_LITERAL_STRING(fieldName), \ + NS_LITERAL_STRING(aliasName)); \ + SetFromAliasOrDefault((field), (alias), (defaultValue)); + + ValidateAndSet(aMatrixInit.mM11, aMatrixInit.mA, "m11", "a", 1); + ValidateAndSet(aMatrixInit.mM12, aMatrixInit.mB, "m12", "b", 0); + ValidateAndSet(aMatrixInit.mM21, aMatrixInit.mC, "m21", "c", 0); + ValidateAndSet(aMatrixInit.mM22, aMatrixInit.mD, "m22", "d", 1); + ValidateAndSet(aMatrixInit.mM41, aMatrixInit.mE, "m41", "e", 0); + ValidateAndSet(aMatrixInit.mM42, aMatrixInit.mF, "m42", "f", 0); + + return true; + +#undef ValidateAliases +#undef SetFromAliasOrDefault +#undef ValidateAndSet +} + +// https://drafts.fxtf.org/geometry/#matrix-validate-and-fixup +static bool +ValidateAndFixupMatrixInit(DOMMatrixInit& aMatrixInit, ErrorResult& aRv) +{ +#define Check3DField(field, fieldName, defaultValue) \ + if ((field) != (defaultValue)) { \ + if (!aMatrixInit.mIs2D.WasPassed()) { \ + aMatrixInit.mIs2D.Construct(false); \ + return true; \ + } \ + if (aMatrixInit.mIs2D.Value()) { \ + aRv.ThrowTypeError( \ + NS_LITERAL_STRING(fieldName)); \ + return false; \ + } \ + } + + if (!ValidateAndFixupMatrix2DInit(aMatrixInit, aRv)) { + return false; + } + + Check3DField(aMatrixInit.mM13, "m13", 0); + Check3DField(aMatrixInit.mM14, "m14", 0); + Check3DField(aMatrixInit.mM23, "m23", 0); + Check3DField(aMatrixInit.mM24, "m24", 0); + Check3DField(aMatrixInit.mM31, "m31", 0); + Check3DField(aMatrixInit.mM32, "m32", 0); + Check3DField(aMatrixInit.mM34, "m34", 0); + Check3DField(aMatrixInit.mM43, "m43", 0); + Check3DField(aMatrixInit.mM33, "m33", 1); + Check3DField(aMatrixInit.mM44, "m44", 1); + + if (!aMatrixInit.mIs2D.WasPassed()) { + aMatrixInit.mIs2D.Construct(true); + } + return true; + +#undef Check3DField +} + +void +DOMMatrixReadOnly::SetDataFromMatrixInit(DOMMatrixInit& aMatrixInit) +{ + const bool is2D = aMatrixInit.mIs2D.Value(); + MOZ_ASSERT(is2D == Is2D()); + if (is2D) { + mMatrix2D->_11 = aMatrixInit.mM11.Value(); + mMatrix2D->_12 = aMatrixInit.mM12.Value(); + mMatrix2D->_21 = aMatrixInit.mM21.Value(); + mMatrix2D->_22 = aMatrixInit.mM22.Value(); + mMatrix2D->_31 = aMatrixInit.mM41.Value(); + mMatrix2D->_32 = aMatrixInit.mM42.Value(); + } else { + mMatrix3D->_11 = aMatrixInit.mM11.Value(); + mMatrix3D->_12 = aMatrixInit.mM12.Value(); + mMatrix3D->_13 = aMatrixInit.mM13; + mMatrix3D->_14 = aMatrixInit.mM14; + mMatrix3D->_21 = aMatrixInit.mM21.Value(); + mMatrix3D->_22 = aMatrixInit.mM22.Value(); + mMatrix3D->_23 = aMatrixInit.mM23; + mMatrix3D->_24 = aMatrixInit.mM24; + mMatrix3D->_31 = aMatrixInit.mM31; + mMatrix3D->_32 = aMatrixInit.mM32; + mMatrix3D->_33 = aMatrixInit.mM33; + mMatrix3D->_34 = aMatrixInit.mM34; + mMatrix3D->_41 = aMatrixInit.mM41.Value(); + mMatrix3D->_42 = aMatrixInit.mM42.Value(); + mMatrix3D->_43 = aMatrixInit.mM43; + mMatrix3D->_44 = aMatrixInit.mM44; + } +} + +already_AddRefed +DOMMatrixReadOnly::FromMatrix(const GlobalObject& aGlobal, const DOMMatrixInit& aMatrixInit, ErrorResult& aRv) +{ + DOMMatrixInit matrixInit(aMatrixInit); + if (!ValidateAndFixupMatrixInit(matrixInit, aRv)) { + return nullptr; + }; + + RefPtr rval = + new DOMMatrixReadOnly(aGlobal.GetAsSupports(), matrixInit.mIs2D.Value()); + rval->SetDataFromMatrixInit(matrixInit); + return rval.forget(); +} + already_AddRefed DOMMatrixReadOnly::Constructor( const GlobalObject& aGlobal, @@ -182,10 +308,10 @@ DOMMatrixReadOnly::SkewY(double aSy) const } already_AddRefed -DOMMatrixReadOnly::Multiply(const DOMMatrix& other) const +DOMMatrixReadOnly::Multiply(const DOMMatrixInit& other, ErrorResult& aRv) const { RefPtr retval = new DOMMatrix(mParent, *this); - retval->MultiplySelf(other); + retval->MultiplySelf(other, aRv); return retval.forget(); } @@ -426,6 +552,27 @@ DOMMatrixReadOnly::ReadStructuredCloneElements(JSStructuredCloneReader* aReader, #undef ReadFloatPair } +already_AddRefed +DOMMatrix::FromMatrix(nsISupports* aParent, const DOMMatrixInit& aMatrixInit, ErrorResult& aRv) +{ + DOMMatrixInit matrixInit(aMatrixInit); + if (!ValidateAndFixupMatrixInit(matrixInit, aRv)) { + return nullptr; + }; + + RefPtr matrix = new DOMMatrix(aParent, matrixInit.mIs2D.Value()); + matrix->SetDataFromMatrixInit(matrixInit); + return matrix.forget(); +} + +already_AddRefed +DOMMatrix::FromMatrix(const GlobalObject& aGlobal, const DOMMatrixInit& aMatrixInit, ErrorResult& aRv) +{ + RefPtr matrix = + FromMatrix(aGlobal.GetAsSupports(), aMatrixInit, aRv); + return matrix.forget(); +} + already_AddRefed DOMMatrix::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv) { @@ -546,42 +693,44 @@ DOMMatrixReadOnly::Ensure3DMatrix() } DOMMatrix* -DOMMatrix::MultiplySelf(const DOMMatrix& aOther) +DOMMatrix::MultiplySelf(const DOMMatrixInit& aOtherInit, ErrorResult& aRv) { - if (aOther.Identity()) { + RefPtr other = FromMatrix(mParent, aOtherInit, aRv); + if (other->Identity()) { return this; } - if (aOther.Is2D()) { + if (other->Is2D()) { if (mMatrix3D) { - *mMatrix3D = gfx::Matrix4x4::From2D(*aOther.mMatrix2D) * *mMatrix3D; + *mMatrix3D = gfx::Matrix4x4::From2D(*other->mMatrix2D) * *mMatrix3D; } else { - *mMatrix2D = *aOther.mMatrix2D * *mMatrix2D; + *mMatrix2D = *other->mMatrix2D * *mMatrix2D; } } else { Ensure3DMatrix(); - *mMatrix3D = *aOther.mMatrix3D * *mMatrix3D; + *mMatrix3D = *other->mMatrix3D * *mMatrix3D; } return this; } DOMMatrix* -DOMMatrix::PreMultiplySelf(const DOMMatrix& aOther) +DOMMatrix::PreMultiplySelf(const DOMMatrixInit& aOtherInit, ErrorResult& aRv) { - if (aOther.Identity()) { + RefPtr other = FromMatrix(mParent, aOtherInit, aRv); + if (other->Identity()) { return this; } - if (aOther.Is2D()) { + if (other->Is2D()) { if (mMatrix3D) { - *mMatrix3D = *mMatrix3D * gfx::Matrix4x4::From2D(*aOther.mMatrix2D); + *mMatrix3D = *mMatrix3D * gfx::Matrix4x4::From2D(*other->mMatrix2D); } else { - *mMatrix2D = *mMatrix2D * *aOther.mMatrix2D; + *mMatrix2D = *mMatrix2D * *other->mMatrix2D; } } else { Ensure3DMatrix(); - *mMatrix3D = *mMatrix3D * *aOther.mMatrix3D; + *mMatrix3D = *mMatrix3D * *other->mMatrix3D; } return this; diff --git a/dom/base/DOMMatrix.h b/dom/base/DOMMatrix.h index fe1325c594..9bbdef688f 100644 --- a/dom/base/DOMMatrix.h +++ b/dom/base/DOMMatrix.h @@ -25,6 +25,7 @@ class DOMMatrix; class DOMPoint; class StringOrUnrestrictedDoubleSequence; struct DOMPointInit; +struct DOMMatrixInit; class DOMMatrixReadOnly : public nsWrapperCache { @@ -56,6 +57,9 @@ public: nsISupports* GetParentObject() const { return mParent; } virtual JSObject* WrapObject(JSContext* cx, JS::Handle aGivenProto) override; + static already_AddRefed + FromMatrix(const GlobalObject& aGlobal, const DOMMatrixInit& aMatrixInit, ErrorResult& aRv); + static already_AddRefed Constructor(const GlobalObject& aGlobal, const Optional& aArg, ErrorResult& aRv); @@ -180,7 +184,8 @@ public: double aAngle) const; already_AddRefed SkewX(double aSx) const; already_AddRefed SkewY(double aSy) const; - already_AddRefed Multiply(const DOMMatrix& aOther) const; + already_AddRefed Multiply(const DOMMatrixInit& aOther, + ErrorResult& aRv) const; already_AddRefed FlipX() const; already_AddRefed FlipY() const; already_AddRefed Inverse() const; @@ -204,6 +209,13 @@ protected: virtual ~DOMMatrixReadOnly() {} + /** + * Sets data from a fully validated and fixed-up matrix init, + * where all of its members are properly defined. + * The init dictionary's dimension must match the matrix one. + */ + void SetDataFromMatrixInit(DOMMatrixInit& aMatrixInit); + DOMMatrixReadOnly* SetMatrixValue(const nsAString& aTransformList, ErrorResult& aRv); void Ensure3DMatrix(); @@ -236,6 +248,11 @@ public: : DOMMatrixReadOnly(aParent, aMatrix) {} + static already_AddRefed + FromMatrix(nsISupports* aParent, const DOMMatrixInit& aMatrixInit, ErrorResult& aRv); + static already_AddRefed + FromMatrix(const GlobalObject& aGlobal, const DOMMatrixInit& aMatrixInit, ErrorResult& aRv); + static already_AddRefed Constructor(const GlobalObject& aGlobal, ErrorResult& aRv); static already_AddRefed @@ -254,8 +271,8 @@ public: virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; - DOMMatrix* MultiplySelf(const DOMMatrix& aOther); - DOMMatrix* PreMultiplySelf(const DOMMatrix& aOther); + DOMMatrix* MultiplySelf(const DOMMatrixInit& aOther, ErrorResult& aRv); + DOMMatrix* PreMultiplySelf(const DOMMatrixInit& aOther, ErrorResult& aRv); DOMMatrix* TranslateSelf(double aTx, double aTy, double aTz = 0); diff --git a/dom/base/WebKitCSSMatrix.cpp b/dom/base/WebKitCSSMatrix.cpp index fe26b74554..003fdfea30 100644 --- a/dom/base/WebKitCSSMatrix.cpp +++ b/dom/base/WebKitCSSMatrix.cpp @@ -115,10 +115,10 @@ WebKitCSSMatrix::SetMatrixValue(const nsAString& aTransformList, } already_AddRefed -WebKitCSSMatrix::Multiply(const WebKitCSSMatrix& other) const +WebKitCSSMatrix::Multiply(const DOMMatrixInit& aOtherInit, ErrorResult& aRv) const { RefPtr retval = new WebKitCSSMatrix(mParent, *this); - retval->MultiplySelf(other); + retval->MultiplySelf(aOtherInit, aRv); return retval.forget(); } diff --git a/dom/base/WebKitCSSMatrix.h b/dom/base/WebKitCSSMatrix.h index e50c617260..590548b767 100644 --- a/dom/base/WebKitCSSMatrix.h +++ b/dom/base/WebKitCSSMatrix.h @@ -40,7 +40,8 @@ public: WebKitCSSMatrix* SetMatrixValue(const nsAString& aTransformList, ErrorResult& aRv); - already_AddRefed Multiply(const WebKitCSSMatrix& aOther) const; + already_AddRefed Multiply(const DOMMatrixInit& aOtherInit, + ErrorResult& aRv) const; already_AddRefed Inverse(ErrorResult& aRv) const; already_AddRefed Translate(double aTx, double aTy, diff --git a/dom/bindings/Errors.msg b/dom/bindings/Errors.msg index b40dc239d5..d22ef2c27f 100644 --- a/dom/bindings/Errors.msg +++ b/dom/bindings/Errors.msg @@ -100,6 +100,8 @@ MSG_DEF(MSG_TIME_VALUE_OUT_OF_RANGE, 1, JSEXN_TYPEERR, "{0} is outside the suppo MSG_DEF(MSG_ONLY_IF_CACHED_WITHOUT_SAME_ORIGIN, 1, JSEXN_TYPEERR, "Request mode '{0}' was used, but request cache mode 'only-if-cached' can only be used with request mode 'same-origin'.") MSG_DEF(MSG_THRESHOLD_RANGE_ERROR, 0, JSEXN_RANGEERR, "Threshold values must all be in the range [0, 1].") MSG_DEF(MSG_CACHE_OPEN_FAILED, 0, JSEXN_TYPEERR, "CacheStorage.open() failed to access the storage system.") +MSG_DEF(MSG_MATRIX_INIT_CONFLICTING_VALUE, 2, JSEXN_TYPEERR, "Matrix init unexpectedly got different values for '{0}' and '{1}'.") +MSG_DEF(MSG_MATRIX_INIT_EXCEEDS_2D, 1, JSEXN_TYPEERR, "Matrix init has an unexpected 3D element '{0}' which cannot coexist with 'is2D: true'.") MSG_DEF(MSG_MATRIX_INIT_LENGTH_WRONG, 1, JSEXN_TYPEERR, "Matrix init sequence must have a length of 6 or 16 (actual value: {0})") MSG_DEF(MSG_NO_NEGATIVE_ATTR, 1, JSEXN_TYPEERR, "Given attribute {0} cannot be negative.") MSG_DEF(MSG_PMO_NO_SEPARATE_ENDMARK, 0, JSEXN_TYPEERR, "Cannot provide separate endMark argument if PerformanceMeasureOptions argument is given.") diff --git a/dom/webidl/DOMMatrix.webidl b/dom/webidl/DOMMatrix.webidl index 68c70507b7..bf65da35a9 100644 --- a/dom/webidl/DOMMatrix.webidl +++ b/dom/webidl/DOMMatrix.webidl @@ -13,6 +13,8 @@ [Constructor(optional (DOMString or sequence) init), Exposed=(Window,Worker)] interface DOMMatrixReadOnly { + [NewObject, Throws] static DOMMatrixReadOnly fromMatrix(optional DOMMatrixInit other); + // These attributes are simple aliases for certain elements of the 4x4 matrix readonly attribute unrestricted double a; readonly attribute unrestricted double b; @@ -66,7 +68,7 @@ interface DOMMatrixReadOnly { unrestricted double angle); DOMMatrix skewX(unrestricted double sx); DOMMatrix skewY(unrestricted double sy); - DOMMatrix multiply(DOMMatrix other); + [NewObject, Throws] DOMMatrix multiply(optional DOMMatrixInit other); DOMMatrix flipX(); DOMMatrix flipY(); DOMMatrix inverse(); @@ -88,6 +90,8 @@ interface DOMMatrixReadOnly { Constructor(sequence numberSequence), Exposed=(Window,Worker)] interface DOMMatrix : DOMMatrixReadOnly { + [NewObject, Throws] static DOMMatrix fromMatrix(optional DOMMatrixInit other); + // These attributes are simple aliases for certain elements of the 4x4 matrix inherit attribute unrestricted double a; inherit attribute unrestricted double b; @@ -114,8 +118,8 @@ interface DOMMatrix : DOMMatrixReadOnly { inherit attribute unrestricted double m44; // Mutable transform methods - DOMMatrix multiplySelf(DOMMatrix other); - DOMMatrix preMultiplySelf(DOMMatrix other); + [Throws] DOMMatrix multiplySelf(optional DOMMatrixInit other); + [Throws] DOMMatrix preMultiplySelf(optional DOMMatrixInit other); DOMMatrix translateSelf(unrestricted double tx, unrestricted double ty, optional unrestricted double tz = 0); @@ -147,3 +151,31 @@ interface DOMMatrix : DOMMatrixReadOnly { [Exposed=Window, Throws] DOMMatrix setMatrixValue(DOMString transformList); }; +dictionary DOMMatrix2DInit { + unrestricted double a; + unrestricted double b; + unrestricted double c; + unrestricted double d; + unrestricted double e; + unrestricted double f; + unrestricted double m11; + unrestricted double m12; + unrestricted double m21; + unrestricted double m22; + unrestricted double m41; + unrestricted double m42; +}; + +dictionary DOMMatrixInit : DOMMatrix2DInit { + unrestricted double m13 = 0; + unrestricted double m14 = 0; + unrestricted double m23 = 0; + unrestricted double m24 = 0; + unrestricted double m31 = 0; + unrestricted double m32 = 0; + unrestricted double m33 = 1; + unrestricted double m34 = 0; + unrestricted double m43 = 0; + unrestricted double m44 = 1; + boolean is2D; +}; diff --git a/dom/webidl/WebKitCSSMatrix.webidl b/dom/webidl/WebKitCSSMatrix.webidl index 8115711a33..e939d37acf 100644 --- a/dom/webidl/WebKitCSSMatrix.webidl +++ b/dom/webidl/WebKitCSSMatrix.webidl @@ -18,7 +18,8 @@ interface WebKitCSSMatrix : DOMMatrix { WebKitCSSMatrix setMatrixValue(DOMString transformList); // Immutable transform methods - WebKitCSSMatrix multiply(WebKitCSSMatrix other); + [Throws] + WebKitCSSMatrix multiply(optional DOMMatrixInit other); [Throws] WebKitCSSMatrix inverse(); WebKitCSSMatrix translate(optional unrestricted double tx = 0, From 0e2f687f4dd0bf0c7539de932d983fe66691bbc9 Mon Sep 17 00:00:00 2001 From: Job Bautista Date: Fri, 12 May 2023 15:59:55 +0800 Subject: [PATCH 10/16] Issue #2241 - Part 7.1: Implement .fromFloat{32/64}Array. Backported from Mozilla bug 1558101. --- dom/base/DOMMatrix.cpp | 67 +++++++++++++++++++++++++++++++------ dom/base/DOMMatrix.h | 12 +++++++ dom/webidl/DOMMatrix.webidl | 4 +++ 3 files changed, 73 insertions(+), 10 deletions(-) diff --git a/dom/base/DOMMatrix.cpp b/dom/base/DOMMatrix.cpp index 09c186e77e..f0358125f8 100644 --- a/dom/base/DOMMatrix.cpp +++ b/dom/base/DOMMatrix.cpp @@ -163,6 +163,35 @@ DOMMatrixReadOnly::FromMatrix(const GlobalObject& aGlobal, const DOMMatrixInit& return rval.forget(); } + +already_AddRefed +DOMMatrixReadOnly::FromFloat32Array(const GlobalObject& aGlobal, const Float32Array& aArray32, ErrorResult& aRv) +{ + aArray32.ComputeLengthAndData(); + + const int length = aArray32.Length(); + const bool is2D = length == 6; + RefPtr obj = + new DOMMatrixReadOnly(aGlobal.GetAsSupports(), is2D); + SetDataInMatrix(obj, aArray32.Data(), length, aRv); + + return obj.forget(); +} + +already_AddRefed +DOMMatrixReadOnly::FromFloat64Array(const GlobalObject& aGlobal, const Float64Array& aArray64, ErrorResult& aRv) +{ + aArray64.ComputeLengthAndData(); + + const int length = aArray64.Length(); + const bool is2D = length == 6; + RefPtr obj = + new DOMMatrixReadOnly(aGlobal.GetAsSupports(), is2D); + SetDataInMatrix(obj, aArray64.Data(), length, aRv); + + return obj.forget(); +} + already_AddRefed DOMMatrixReadOnly::Constructor( const GlobalObject& aGlobal, @@ -573,6 +602,32 @@ DOMMatrix::FromMatrix(const GlobalObject& aGlobal, const DOMMatrixInit& aMatrixI return matrix.forget(); } +already_AddRefed +DOMMatrix::FromFloat32Array(const GlobalObject& aGlobal, const Float32Array& aArray32, ErrorResult& aRv) +{ + aArray32.ComputeLengthAndData(); + + const int length = aArray32.Length(); + const bool is2D = length == 6; + RefPtr obj = new DOMMatrix(aGlobal.GetAsSupports(), is2D); + SetDataInMatrix(obj, aArray32.Data(), length, aRv); + + return obj.forget(); +} + +already_AddRefed +DOMMatrix::FromFloat64Array(const GlobalObject& aGlobal, const Float64Array& aArray64, ErrorResult& aRv) +{ + aArray64.ComputeLengthAndData(); + + const int length = aArray64.Length(); + const bool is2D = length == 6; + RefPtr obj = new DOMMatrix(aGlobal.GetAsSupports(), is2D); + SetDataInMatrix(obj, aArray64.Data(), length, aRv); + + return obj.forget(); +} + already_AddRefed DOMMatrix::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv) { @@ -639,21 +694,13 @@ SetDataInMatrix(DOMMatrixReadOnly* aMatrix, const T* aData, int aLength, ErrorRe already_AddRefed DOMMatrix::Constructor(const GlobalObject& aGlobal, const Float32Array& aArray32, ErrorResult& aRv) { - RefPtr obj = new DOMMatrix(aGlobal.GetAsSupports()); - aArray32.ComputeLengthAndData(); - SetDataInMatrix(obj, aArray32.Data(), aArray32.Length(), aRv); - - return obj.forget(); + return FromFloat32Array(aGlobal, aArray32, aRv); } already_AddRefed DOMMatrix::Constructor(const GlobalObject& aGlobal, const Float64Array& aArray64, ErrorResult& aRv) { - RefPtr obj = new DOMMatrix(aGlobal.GetAsSupports()); - aArray64.ComputeLengthAndData(); - SetDataInMatrix(obj, aArray64.Data(), aArray64.Length(), aRv); - - return obj.forget(); + return FromFloat64Array(aGlobal, aArray64, aRv); } already_AddRefed diff --git a/dom/base/DOMMatrix.h b/dom/base/DOMMatrix.h index 9bbdef688f..99e7714add 100644 --- a/dom/base/DOMMatrix.h +++ b/dom/base/DOMMatrix.h @@ -60,6 +60,12 @@ public: static already_AddRefed FromMatrix(const GlobalObject& aGlobal, const DOMMatrixInit& aMatrixInit, ErrorResult& aRv); + static already_AddRefed + FromFloat32Array(const GlobalObject& aGlobal, const Float32Array& aArray32, ErrorResult& aRv); + + static already_AddRefed + FromFloat64Array(const GlobalObject& aGlobal, const Float64Array& aArray64, ErrorResult& aRv); + static already_AddRefed Constructor(const GlobalObject& aGlobal, const Optional& aArg, ErrorResult& aRv); @@ -253,6 +259,12 @@ public: static already_AddRefed FromMatrix(const GlobalObject& aGlobal, const DOMMatrixInit& aMatrixInit, ErrorResult& aRv); + static already_AddRefed + FromFloat32Array(const GlobalObject& aGlobal, const Float32Array& aArray32, ErrorResult& aRv); + + static already_AddRefed + FromFloat64Array(const GlobalObject& aGlobal, const Float64Array& aArray64, ErrorResult& aRv); + static already_AddRefed Constructor(const GlobalObject& aGlobal, ErrorResult& aRv); static already_AddRefed diff --git a/dom/webidl/DOMMatrix.webidl b/dom/webidl/DOMMatrix.webidl index bf65da35a9..f5c9f99406 100644 --- a/dom/webidl/DOMMatrix.webidl +++ b/dom/webidl/DOMMatrix.webidl @@ -14,6 +14,8 @@ Exposed=(Window,Worker)] interface DOMMatrixReadOnly { [NewObject, Throws] static DOMMatrixReadOnly fromMatrix(optional DOMMatrixInit other); + [NewObject, Throws] static DOMMatrixReadOnly fromFloat32Array(Float32Array array32); + [NewObject, Throws] static DOMMatrixReadOnly fromFloat64Array(Float64Array array64); // These attributes are simple aliases for certain elements of the 4x4 matrix readonly attribute unrestricted double a; @@ -91,6 +93,8 @@ interface DOMMatrixReadOnly { Exposed=(Window,Worker)] interface DOMMatrix : DOMMatrixReadOnly { [NewObject, Throws] static DOMMatrix fromMatrix(optional DOMMatrixInit other); + [NewObject, Throws] static DOMMatrix fromFloat32Array(Float32Array array32); + [NewObject, Throws] static DOMMatrix fromFloat64Array(Float64Array array64); // These attributes are simple aliases for certain elements of the 4x4 matrix inherit attribute unrestricted double a; From 4ec35fdcf28d4a67830feac5b77d3339e7cffa72 Mon Sep 17 00:00:00 2001 From: Job Bautista Date: Fri, 12 May 2023 16:07:16 +0800 Subject: [PATCH 11/16] Issue #2241 - Part 7.2: Implement .fromRect and .fromQuad. Backported from Mozilla bug 1558101. --- dom/base/DOMQuad.cpp | 26 ++++++++++++++++++++++++++ dom/base/DOMQuad.h | 7 +++++++ dom/base/DOMRect.cpp | 16 ++++++++++++++++ dom/base/DOMRect.h | 8 ++++++++ dom/webidl/DOMQuad.webidl | 11 +++++++---- dom/webidl/DOMRect.webidl | 4 ++++ 6 files changed, 68 insertions(+), 4 deletions(-) diff --git a/dom/base/DOMQuad.cpp b/dom/base/DOMQuad.cpp index 8457d9ddaa..258bfc1bfd 100644 --- a/dom/base/DOMQuad.cpp +++ b/dom/base/DOMQuad.cpp @@ -43,6 +43,32 @@ DOMQuad::WrapObject(JSContext* aCx, JS::Handle aGivenProto) return DOMQuadBinding::Wrap(aCx, this, aGivenProto); } +already_AddRefed +DOMQuad::FromRect(const GlobalObject& aGlobal, const DOMRectInit& aInit) +{ + nsISupports* parent = aGlobal.GetAsSupports(); + RefPtr obj = new DOMQuad(parent); + obj->mPoints[0] = new DOMPoint(parent, aInit.mX, aInit.mY, 0, 1); + obj->mPoints[1] = + new DOMPoint(parent, aInit.mX + aInit.mWidth, aInit.mY, 0, 1); + obj->mPoints[2] = new DOMPoint(parent, aInit.mX + aInit.mWidth, + aInit.mY + aInit.mHeight, 0, 1); + obj->mPoints[3] = + new DOMPoint(parent, aInit.mX, aInit.mY + aInit.mHeight, 0, 1); + return obj.forget(); +} + +already_AddRefed +DOMQuad::FromQuad(const GlobalObject& aGlobal, const DOMQuadInit& aInit) +{ + RefPtr obj = new DOMQuad(aGlobal.GetAsSupports()); + obj->mPoints[0] = DOMPoint::FromPoint(aGlobal, aInit.mP1); + obj->mPoints[1] = DOMPoint::FromPoint(aGlobal, aInit.mP2); + obj->mPoints[2] = DOMPoint::FromPoint(aGlobal, aInit.mP3); + obj->mPoints[3] = DOMPoint::FromPoint(aGlobal, aInit.mP4); + return obj.forget(); +} + already_AddRefed DOMQuad::Constructor(const GlobalObject& aGlobal, const DOMPointInit& aP1, diff --git a/dom/base/DOMQuad.h b/dom/base/DOMQuad.h index 9d740e0b5e..9508d9f431 100644 --- a/dom/base/DOMQuad.h +++ b/dom/base/DOMQuad.h @@ -23,6 +23,7 @@ class DOMRectReadOnly; class DOMPoint; struct DOMQuadJSON; struct DOMPointInit; +struct DOMQuadInit; class DOMQuad final : public nsWrapperCache { @@ -38,6 +39,12 @@ public: nsISupports* GetParentObject() const { return mParent; } virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; + static already_AddRefed + FromRect(const GlobalObject& aGlobal, const DOMRectInit& aInit); + + static already_AddRefed + FromQuad(const GlobalObject& aGlobal, const DOMQuadInit& aInit); + static already_AddRefed Constructor(const GlobalObject& aGlobal, const DOMPointInit& aP1, diff --git a/dom/base/DOMRect.cpp b/dom/base/DOMRect.cpp index 46f395a083..8dd634b547 100644 --- a/dom/base/DOMRect.cpp +++ b/dom/base/DOMRect.cpp @@ -27,6 +27,14 @@ DOMRectReadOnly::WrapObject(JSContext* aCx, JS::Handle aGivenProto) return DOMRectReadOnlyBinding::Wrap(aCx, this, aGivenProto); } +already_AddRefed +DOMRectReadOnly::FromRect(const GlobalObject& aGlobal, const DOMRectInit& aInit) +{ + RefPtr obj = new DOMRectReadOnly( + aGlobal.GetAsSupports(), aInit.mX, aInit.mY, aInit.mWidth, aInit.mHeight); + return obj.forget(); +} + already_AddRefed DOMRectReadOnly::Constructor(const GlobalObject& aGlobal, double aX, double aY, double aWidth, double aHeight, ErrorResult& aRv) @@ -98,6 +106,14 @@ DOMRect::WrapObject(JSContext* aCx, JS::Handle aGivenProto) return DOMRectBinding::Wrap(aCx, this, aGivenProto); } +already_AddRefed +DOMRect::FromRect(const GlobalObject& aGlobal, const DOMRectInit& aInit) +{ + RefPtr obj = new DOMRect(aGlobal.GetAsSupports(), aInit.mX, aInit.mY, + aInit.mWidth, aInit.mHeight); + return obj.forget(); +} + already_AddRefed DOMRect::Constructor(const GlobalObject& aGlobal, double aX, double aY, double aWidth, double aHeight, ErrorResult& aRv) diff --git a/dom/base/DOMRect.h b/dom/base/DOMRect.h index 56478f284c..541ff02539 100644 --- a/dom/base/DOMRect.h +++ b/dom/base/DOMRect.h @@ -23,6 +23,8 @@ struct nsRect; namespace mozilla { namespace dom { +struct DOMRectInit; + class DOMRectReadOnly : public nsISupports , public nsWrapperCache { @@ -50,6 +52,9 @@ public: } virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; + static already_AddRefed + FromRect(const GlobalObject& aGlobal, const DOMRectInit& aInit); + static already_AddRefed Constructor(const GlobalObject& aGlobal, double aX, double aY, double aWidth, double aHeight, ErrorResult& aRv); @@ -114,6 +119,9 @@ public: NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSIDOMCLIENTRECT + static already_AddRefed + FromRect(const GlobalObject& aGlobal, const DOMRectInit& aInit); + static already_AddRefed Constructor(const GlobalObject& aGlobal, double aX, double aY, double aWidth, double aHeight, ErrorResult& aRv); diff --git a/dom/webidl/DOMQuad.webidl b/dom/webidl/DOMQuad.webidl index 5b130d271f..05b611a5e3 100644 --- a/dom/webidl/DOMQuad.webidl +++ b/dom/webidl/DOMQuad.webidl @@ -15,6 +15,9 @@ Constructor(DOMRectReadOnly rect), Exposed=(Window,Worker)] interface DOMQuad { + [NewObject] static DOMQuad fromRect(optional DOMRectInit other); + [NewObject] static DOMQuad fromQuad(optional DOMQuadInit other); + [SameObject] readonly attribute DOMPoint p1; [SameObject] readonly attribute DOMPoint p2; [SameObject] readonly attribute DOMPoint p3; @@ -34,8 +37,8 @@ dictionary DOMQuadJSON { }; dictionary DOMQuadInit { - DOMPointInit p1; - DOMPointInit p2; - DOMPointInit p3; - DOMPointInit p4; + DOMPointInit p1 = null; + DOMPointInit p2 = null; + DOMPointInit p3 = null; + DOMPointInit p4 = null; }; diff --git a/dom/webidl/DOMRect.webidl b/dom/webidl/DOMRect.webidl index c3bedc7ef5..baf7ce2456 100644 --- a/dom/webidl/DOMRect.webidl +++ b/dom/webidl/DOMRect.webidl @@ -14,6 +14,8 @@ optional unrestricted double width = 0, optional unrestricted double height = 0), Exposed=(Window,Worker)] interface DOMRect : DOMRectReadOnly { + [NewObject] static DOMRect fromRect(optional DOMRectInit other); + inherit attribute unrestricted double x; inherit attribute unrestricted double y; inherit attribute unrestricted double width; @@ -24,6 +26,8 @@ interface DOMRect : DOMRectReadOnly { optional unrestricted double width = 0, optional unrestricted double height = 0), Exposed=(Window,Worker)] interface DOMRectReadOnly { + [NewObject] static DOMRectReadOnly fromRect(optional DOMRectInit other); + readonly attribute unrestricted double x; readonly attribute unrestricted double y; readonly attribute unrestricted double width; From e072ef0dfc6e11a33a8587339c68c12083d2f00b Mon Sep 17 00:00:00 2001 From: Job Bautista Date: Fri, 12 May 2023 16:28:57 +0800 Subject: [PATCH 12/16] Issue #2241 - Part 8: Remove non-working layout.css.DOM*.enabled prefs. A follow-up to fdfe7a8245eb33db252f2a9a3474ac931f15a7d8. --- dom/base/WebKitCSSMatrix.cpp | 3 +-- modules/libpref/init/all.js | 9 --------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/dom/base/WebKitCSSMatrix.cpp b/dom/base/WebKitCSSMatrix.cpp index 003fdfea30..15ca6ba776 100644 --- a/dom/base/WebKitCSSMatrix.cpp +++ b/dom/base/WebKitCSSMatrix.cpp @@ -20,8 +20,7 @@ static const double sRadPerDegree = 2.0 * M_PI / 360.0; bool WebKitCSSMatrix::FeatureEnabled(JSContext* aCx, JSObject* aObj) { - return Preferences::GetBool("layout.css.DOMMatrix.enabled", false) && - Preferences::GetBool("layout.css.prefixes.webkit", false); + return Preferences::GetBool("layout.css.prefixes.webkit", false); } already_AddRefed diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 85d5359f9a..270a58ef01 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -2501,15 +2501,6 @@ pref("layout.css.scroll-snap.prediction-sensitivity", "0.750"); // Is support for basic shapes in clip-path enabled? pref("layout.css.clip-path-shapes.enabled", true); -// Is support for DOMPoint enabled? -pref("layout.css.DOMPoint.enabled", true); - -// Is support for DOMQuad enabled? -pref("layout.css.DOMQuad.enabled", true); - -// Is support for DOMMatrix enabled? -pref("layout.css.DOMMatrix.enabled", true); - // Is support for GeometryUtils.getBoxQuads enabled? pref("layout.css.getBoxQuads.enabled", true); From ae27fff4bbcf7d2283857abcecdbd1b413db97d1 Mon Sep 17 00:00:00 2001 From: Job Bautista Date: Fri, 12 May 2023 19:04:16 +0800 Subject: [PATCH 13/16] Issue #2241 - Part 7.2 Follow-up: Fix build bustage due to unified building in Basilisk. Tag #80 --- dom/base/DOMQuad.h | 1 + 1 file changed, 1 insertion(+) diff --git a/dom/base/DOMQuad.h b/dom/base/DOMQuad.h index 9508d9f431..e32aea26f8 100644 --- a/dom/base/DOMQuad.h +++ b/dom/base/DOMQuad.h @@ -24,6 +24,7 @@ class DOMPoint; struct DOMQuadJSON; struct DOMPointInit; struct DOMQuadInit; +struct DOMRectInit; class DOMQuad final : public nsWrapperCache { From 73a6dc3122301a50482fcc92807238528b389464 Mon Sep 17 00:00:00 2001 From: FranklinDM Date: Sat, 13 May 2023 14:50:12 +0800 Subject: [PATCH 14/16] Issue #1765 - Part 1: Move ReduceNumberCalcOps struct up higher, rename IsCSSTokenCalcFunction to CSSParserImpl::IsCalcFunctionToken --- layout/style/nsCSSParser.cpp | 52 +++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp index 92cc84a7c1..376df8aab4 100644 --- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -137,6 +137,22 @@ struct CSSParserInputState { bool mHavePushBack; }; +struct ReduceNumberCalcOps : public mozilla::css::BasicFloatCalcOps, + public mozilla::css::CSSValueInputCalcOps +{ + result_type ComputeLeafValue(const nsCSSValue& aValue) + { + // FIXME: Restore this assertion once ParseColor no longer uses this class. + //MOZ_ASSERT(aValue.GetUnit() == eCSSUnit_Number, "unexpected unit"); + return aValue.GetFloatValue(); + } + + float ComputeNumber(const nsCSSValue& aValue) + { + return mozilla::css::ComputeCalc(aValue, *this); + } +}; + static_assert(css::eAuthorSheetFeatures == 0 && css::eUserSheetFeatures == 1 && css::eAgentSheetFeatures == 2, @@ -896,6 +912,7 @@ protected: }; bool IsFunctionTokenValidForImageLayerImage(const nsCSSToken& aToken) const; + bool IsCalcFunctionToken(const nsCSSToken& aToken) const; bool ParseImageLayersItem(ImageLayersShorthandParseState& aState, const nsCSSPropertyID aTable[]); @@ -7874,14 +7891,6 @@ CSSParserImpl::ParseOneOrLargerVariant(nsCSSValue& aValue, return result; } -static bool -IsCSSTokenCalcFunction(const nsCSSToken& aToken) -{ - return aToken.mType == eCSSToken_Function && - (aToken.mIdent.LowerCaseEqualsLiteral("calc") || - aToken.mIdent.LowerCaseEqualsLiteral("-moz-calc")); -} - // Assigns to aValue iff it returns CSSParseResult::Ok. CSSParseResult CSSParserImpl::ParseVariant(nsCSSValue& aValue, @@ -8181,7 +8190,7 @@ CSSParserImpl::ParseVariant(nsCSSValue& aValue, } } if ((aVariantMask & VARIANT_CALC) && - IsCSSTokenCalcFunction(*tk)) { + IsCalcFunctionToken(*tk)) { // calc() currently allows only lengths and percents and number inside it. // And note that in current implementation, number cannot be mixed with // length and percent. @@ -12430,6 +12439,14 @@ CSSParserImpl::IsFunctionTokenValidForImageLayerImage( funcName.LowerCaseEqualsLiteral("-webkit-repeating-radial-gradient"))); } +bool +CSSParserImpl::IsCalcFunctionToken(const nsCSSToken& aToken) const +{ + return aToken.mType == eCSSToken_Function && + (aToken.mIdent.LowerCaseEqualsLiteral("calc") || + aToken.mIdent.LowerCaseEqualsLiteral("-moz-calc")); +} + // Parse one item of the background shorthand property. bool CSSParserImpl::ParseImageLayersItem( @@ -13822,21 +13839,6 @@ CSSParserImpl::ParseCalcAdditiveExpression(nsCSSValue& aValue, } } -struct ReduceNumberCalcOps : public mozilla::css::BasicFloatCalcOps, - public mozilla::css::CSSValueInputCalcOps -{ - result_type ComputeLeafValue(const nsCSSValue& aValue) - { - MOZ_ASSERT(aValue.GetUnit() == eCSSUnit_Number, "unexpected unit"); - return aValue.GetFloatValue(); - } - - float ComputeNumber(const nsCSSValue& aValue) - { - return mozilla::css::ComputeCalc(aValue, *this); - } -}; - // * If aVariantMask is VARIANT_NUMBER, this function parses the // production. // * If aVariantMask does not contain VARIANT_NUMBER, this function @@ -13957,7 +13959,7 @@ CSSParserImpl::ParseCalcTerm(nsCSSValue& aValue, uint32_t& aVariantMask) // Either an additive expression in parentheses... if (mToken.IsSymbol('(') || // Treat nested calc() as plain parenthesis. - IsCSSTokenCalcFunction(mToken)) { + IsCalcFunctionToken(mToken)) { if (!ParseCalcAdditiveExpression(aValue, aVariantMask) || !ExpectSymbol(')', true)) { SkipUntil(')'); From d678cc2c4533f6dd3608d863c1b3bf03e6047a69 Mon Sep 17 00:00:00 2001 From: FranklinDM Date: Sat, 13 May 2023 18:15:13 +0800 Subject: [PATCH 15/16] Issue #1765 - Part 2: Implement calc() parsing inside rgb/a() and the non-hue component of hsl/a() This also adds a new helper method for checking the type of tokens that will be parsed after the current token, which is useful for determining the unit of values inside calc() functions. Partially based on https://github.com/roytam1/UXP/commit/8a0897d23f5b51526f8a2c6ef63cb26968e6b985 --- layout/style/nsCSSParser.cpp | 70 ++++++++++++++++++++++++++++++++---- 1 file changed, 63 insertions(+), 7 deletions(-) diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp index 376df8aab4..247d351464 100644 --- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -675,6 +675,11 @@ protected: bool SkipAtRule(bool aInsideBlock); bool SkipDeclaration(bool aCheckForBraces); + // Returns true when the target token type is found, and false for the + // end of declaration, start of !important flag, end of declaration + // block, or EOF. + bool LookForTokenType(nsCSSTokenType aType); + void PushGroup(css::GroupRule* aRule); void PopGroup(); @@ -5413,6 +5418,33 @@ CSSParserImpl::SkipDeclaration(bool aCheckForBraces) return true; } +bool +CSSParserImpl::LookForTokenType(nsCSSTokenType aType) { + bool rv = false; + CSSParserInputState stateBeforeValue; + SaveInputState(stateBeforeValue); + + const char16_t stopChars[] = { ';', '!', '}', 0 }; + nsDependentString stopSymbolChars(stopChars); + while (GetToken(true)) { + // The current function has percentage values. + if (mToken.mType == eCSSToken_Percentage) { + rv = true; + break; + } + // Stop looking if we're at the end of the declaration, encountered an + // !important flag, or at the end of the declaration block. + if (mToken.mType == eCSSToken_Symbol && + stopSymbolChars.FindChar(mToken.mSymbol) != -1) { + rv = false; + break; + } + } + + RestoreSavedInputState(stateBeforeValue); + return rv; +} + void CSSParserImpl::SkipRuleSet(bool aInsideBraces) { @@ -7005,7 +7037,15 @@ CSSParserImpl::ParseColor(nsCSSValue& aValue) if (GetToken(true)) { UngetToken(); } - if (mToken.mType == eCSSToken_Number) { // + + bool isNumber = mToken.mType == eCSSToken_Number; + + // Check first if we have percentage values inside the function. + if (mToken.mType == eCSSToken_Function) { + isNumber = !LookForTokenType(eCSSToken_Percentage); + } + + if (isNumber) { // uint8_t r, g, b, a; if (ParseRGBColor(r, g, b, a)) { @@ -7112,14 +7152,22 @@ CSSParserImpl::ParseColorComponent(uint8_t& aComponent, Maybe aSeparator) return false; } - if (mToken.mType != eCSSToken_Number) { + float value; + if (mToken.mType == eCSSToken_Number) { + value = mToken.mNumber; + } else if (IsCalcFunctionToken(mToken)) { + nsCSSValue aValue; + if (!ParseCalc(aValue, VARIANT_LPN | VARIANT_CALC)) { + return false; + } + ReduceNumberCalcOps ops; + value = mozilla::css::ComputeCalc(aValue, ops); + } else { REPORT_UNEXPECTED_TOKEN(PEExpectedNumber); UngetToken(); return false; } - float value = mToken.mNumber; - if (aSeparator && !ExpectSymbol(*aSeparator, true)) { REPORT_UNEXPECTED_TOKEN_CHAR(PEColorComponentBadTerm, *aSeparator); return false; @@ -7140,14 +7188,22 @@ CSSParserImpl::ParseColorComponent(float& aComponent, Maybe aSeparator) return false; } - if (mToken.mType != eCSSToken_Percentage) { + float value; + if (mToken.mType == eCSSToken_Percentage) { + value = mToken.mNumber; + } else if (IsCalcFunctionToken(mToken)) { + nsCSSValue aValue; + if (!ParseCalc(aValue, VARIANT_LPN | VARIANT_CALC)) { + return false; + } + ReduceNumberCalcOps ops; + value = mozilla::css::ComputeCalc(aValue, ops); + } else { REPORT_UNEXPECTED_TOKEN(PEExpectedPercent); UngetToken(); return false; } - float value = mToken.mNumber; - if (aSeparator && !ExpectSymbol(*aSeparator, true)) { REPORT_UNEXPECTED_TOKEN_CHAR(PEColorComponentBadTerm, *aSeparator); return false; From 6ae0a69ac7d9cf49f06d24ccd8162e786042fe52 Mon Sep 17 00:00:00 2001 From: FranklinDM Date: Sun, 14 May 2023 18:31:21 +0800 Subject: [PATCH 16/16] Issue #1765 - Part 3: Provided token type should be used in LookForTokenType I didn't realize this immediately after moving the code into a method. Oops. --- layout/style/nsCSSParser.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp index 247d351464..494d8f6aa6 100644 --- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -5427,8 +5427,7 @@ CSSParserImpl::LookForTokenType(nsCSSTokenType aType) { const char16_t stopChars[] = { ';', '!', '}', 0 }; nsDependentString stopSymbolChars(stopChars); while (GetToken(true)) { - // The current function has percentage values. - if (mToken.mType == eCSSToken_Percentage) { + if (mToken.mType == aType) { rv = true; break; }