Bug 1857998 [wpt PR 42432] - [css-nesting-ident] Enable relaxed syntax, a=testonly
[gecko.git] / toolkit / modules / ObjectUtils.sys.mjs
blobe0fbeead1248c0217b39c500f7b4c1cd1d91839b
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 // Portions of this file are originally from narwhal.js (http://narwhaljs.org)
6 // Copyright (c) 2009 Thomas Robinson <280north.com>
7 // MIT license: http://opensource.org/licenses/MIT
9 // Used only to cause test failures.
11 var pSlice = Array.prototype.slice;
13 export var ObjectUtils = {
14   /**
15    * This tests objects & values for deep equality.
16    *
17    * We check using the most exact approximation of equality between two objects
18    * to keep the chance of false positives to a minimum.
19    * `JSON.stringify` is not designed to be used for this purpose; objects may
20    * have ambiguous `toJSON()` implementations that would influence the test.
21    *
22    * @param a (mixed) Object or value to be compared.
23    * @param b (mixed) Object or value to be compared.
24    * @return Boolean Whether the objects are deep equal.
25    */
26   deepEqual(a, b) {
27     return _deepEqual(a, b);
28   },
30   /**
31    * A thin wrapper on an object, designed to prevent client code from
32    * accessing non-existent properties because of typos.
33    *
34    * // Without `strict`
35    * let foo = { myProperty: 1 };
36    * foo.MyProperty; // undefined
37    *
38    * // With `strict`
39    * let strictFoo = ObjectUtils.strict(foo);
40    * strictFoo.myProperty; // 1
41    * strictFoo.MyProperty; // TypeError: No such property "MyProperty"
42    *
43    * Note that `strict` has no effect in non-DEBUG mode.
44    */
45   strict(obj) {
46     return _strict(obj);
47   },
49   /**
50    * Returns `true` if `obj` is an array without elements, an object without
51    * enumerable properties, or a falsy primitive; `false` otherwise.
52    */
53   isEmpty(obj) {
54     if (!obj) {
55       return true;
56     }
57     if (typeof obj != "object") {
58       return false;
59     }
60     if (Array.isArray(obj)) {
61       return !obj.length;
62     }
63     for (let key in obj) {
64       return false;
65     }
66     return true;
67   },
70 // ... Start of previously MIT-licensed code.
71 // This deepEqual implementation is originally from narwhal.js (http://narwhaljs.org)
72 // Copyright (c) 2009 Thomas Robinson <280north.com>
73 // MIT license: http://opensource.org/licenses/MIT
75 function _deepEqual(a, b) {
76   // The numbering below refers to sections in the CommonJS spec.
78   // 7.1 All identical values are equivalent, as determined by ===.
79   if (a === b) {
80     return true;
81     // 7.2 If the b value is a Date object, the a value is
82     // equivalent if it is also a Date object that refers to the same time.
83   }
84   let aIsDate = instanceOf(a, "Date");
85   let bIsDate = instanceOf(b, "Date");
86   if (aIsDate || bIsDate) {
87     if (!aIsDate || !bIsDate) {
88       return false;
89     }
90     if (isNaN(a.getTime()) && isNaN(b.getTime())) {
91       return true;
92     }
93     return a.getTime() === b.getTime();
94     // 7.3 If the b value is a RegExp object, the a value is
95     // equivalent if it is also a RegExp object with the same source and
96     // properties (`global`, `multiline`, `lastIndex`, `ignoreCase`).
97   }
98   let aIsRegExp = instanceOf(a, "RegExp");
99   let bIsRegExp = instanceOf(b, "RegExp");
100   if (aIsRegExp || bIsRegExp) {
101     return (
102       aIsRegExp &&
103       bIsRegExp &&
104       a.source === b.source &&
105       a.global === b.global &&
106       a.multiline === b.multiline &&
107       a.lastIndex === b.lastIndex &&
108       a.ignoreCase === b.ignoreCase
109     );
110     // 7.4 Other pairs that do not both pass typeof value == "object",
111     // equivalence is determined by ==.
112   }
113   if (typeof a != "object" || typeof b != "object") {
114     return a == b;
115   }
116   // 7.5 For all other Object pairs, including Array objects, equivalence is
117   // determined by having the same number of owned properties (as verified
118   // with Object.prototype.hasOwnProperty.call), the same set of keys
119   // (although not necessarily the same order), equivalent values for every
120   // corresponding key, and an identical 'prototype' property. Note: this
121   // accounts for both named and indexed properties on Arrays.
122   return objEquiv(a, b);
125 function instanceOf(object, type) {
126   return Object.prototype.toString.call(object) == "[object " + type + "]";
129 function isUndefinedOrNull(value) {
130   return value === null || value === undefined;
133 function isArguments(object) {
134   return instanceOf(object, "Arguments");
137 function objEquiv(a, b) {
138   if (isUndefinedOrNull(a) || isUndefinedOrNull(b)) {
139     return false;
140   }
141   // An identical 'prototype' property.
142   if ((a.prototype || undefined) != (b.prototype || undefined)) {
143     return false;
144   }
145   // Object.keys may be broken through screwy arguments passing. Converting to
146   // an array solves the problem.
147   if (isArguments(a)) {
148     if (!isArguments(b)) {
149       return false;
150     }
151     a = pSlice.call(a);
152     b = pSlice.call(b);
153     return _deepEqual(a, b);
154   }
155   let ka, kb;
156   try {
157     ka = Object.keys(a);
158     kb = Object.keys(b);
159   } catch (e) {
160     // Happens when one is a string literal and the other isn't
161     return false;
162   }
163   // Having the same number of owned properties (keys incorporates
164   // hasOwnProperty)
165   if (ka.length != kb.length) {
166     return false;
167   }
168   // The same set of keys (although not necessarily the same order),
169   ka.sort();
170   kb.sort();
171   // Equivalent values for every corresponding key, and possibly expensive deep
172   // test
173   for (let key of ka) {
174     if (!_deepEqual(a[key], b[key])) {
175       return false;
176     }
177   }
178   return true;
181 // ... End of previously MIT-licensed code.
183 function _strict(obj) {
184   if (typeof obj != "object") {
185     throw new TypeError("Expected an object");
186   }
188   return new Proxy(obj, {
189     get(target, name) {
190       if (name in obj) {
191         return obj[name];
192       }
194       let error = new TypeError(`No such property: "${name}"`);
195       Promise.reject(error); // Cause an xpcshell/mochitest failure.
196       throw error;
197     },
198   });