/work/obj-fuzz/dist/include/mozilla/dom/DOMJSProxyHandler.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
5 | | * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #ifndef mozilla_dom_DOMJSProxyHandler_h |
8 | | #define mozilla_dom_DOMJSProxyHandler_h |
9 | | |
10 | | #include "mozilla/Attributes.h" |
11 | | #include "mozilla/Likely.h" |
12 | | |
13 | | #include "jsapi.h" |
14 | | #include "js/Proxy.h" |
15 | | #include "nsString.h" |
16 | | |
17 | | namespace mozilla { |
18 | | namespace dom { |
19 | | |
20 | | /** |
21 | | * DOM proxies store the expando object in the private slot. |
22 | | * |
23 | | * The expando object is a plain JSObject whose properties correspond to |
24 | | * "expandos" (custom properties set by the script author). |
25 | | * |
26 | | * The exact value stored in the proxy's private slot depends on whether the |
27 | | * interface is annotated with the [OverrideBuiltins] extended attribute. |
28 | | * |
29 | | * If it is, the proxy is initialized with a PrivateValue, which contains a |
30 | | * pointer to a js::ExpandoAndGeneration object; this contains a pointer to |
31 | | * the actual expando object as well as the "generation" of the object. The |
32 | | * proxy handler will trace the expando object stored in the |
33 | | * js::ExpandoAndGeneration while the proxy itself is alive. |
34 | | * |
35 | | * If it is not, the proxy is initialized with an UndefinedValue. In |
36 | | * EnsureExpandoObject, it is set to an ObjectValue that points to the |
37 | | * expando object directly. (It is set back to an UndefinedValue only when |
38 | | * the object is about to die.) |
39 | | */ |
40 | | |
41 | | template<typename T> struct Prefable; |
42 | | |
43 | | class BaseDOMProxyHandler : public js::BaseProxyHandler |
44 | | { |
45 | | public: |
46 | | explicit constexpr BaseDOMProxyHandler(const void* aProxyFamily, bool aHasPrototype = false) |
47 | | : js::BaseProxyHandler(aProxyFamily, aHasPrototype) |
48 | 0 | {} |
49 | | |
50 | | // Implementations of methods that can be implemented in terms of |
51 | | // other lower-level methods. |
52 | | bool getOwnPropertyDescriptor(JSContext* cx, JS::Handle<JSObject*> proxy, |
53 | | JS::Handle<jsid> id, |
54 | | JS::MutableHandle<JS::PropertyDescriptor> desc) const override; |
55 | | virtual bool ownPropertyKeys(JSContext* cx, JS::Handle<JSObject*> proxy, |
56 | | JS::AutoIdVector &props) const override; |
57 | | |
58 | | virtual bool getPrototypeIfOrdinary(JSContext* cx, JS::Handle<JSObject*> proxy, |
59 | | bool* isOrdinary, |
60 | | JS::MutableHandle<JSObject*> proto) const override; |
61 | | |
62 | | // We override getOwnEnumerablePropertyKeys() and implement it directly |
63 | | // instead of using the default implementation, which would call |
64 | | // ownPropertyKeys and then filter out the non-enumerable ones. This avoids |
65 | | // unnecessary work during enumeration. |
66 | | virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, JS::Handle<JSObject*> proxy, |
67 | | JS::AutoIdVector &props) const override; |
68 | | |
69 | | protected: |
70 | | // Hook for subclasses to implement shared ownPropertyKeys()/keys() |
71 | | // functionality. The "flags" argument is either JSITER_OWNONLY (for keys()) |
72 | | // or JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS (for |
73 | | // ownPropertyKeys()). |
74 | | virtual bool ownPropNames(JSContext* cx, JS::Handle<JSObject*> proxy, |
75 | | unsigned flags, |
76 | | JS::AutoIdVector& props) const = 0; |
77 | | |
78 | | // Hook for subclasses to allow set() to ignore named props while other things |
79 | | // that look at property descriptors see them. This is intentionally not |
80 | | // named getOwnPropertyDescriptor to avoid subclasses that override it hiding |
81 | | // our public getOwnPropertyDescriptor. |
82 | | virtual bool getOwnPropDescriptor(JSContext* cx, |
83 | | JS::Handle<JSObject*> proxy, |
84 | | JS::Handle<jsid> id, |
85 | | bool ignoreNamedProps, |
86 | | JS::MutableHandle<JS::PropertyDescriptor> desc) const = 0; |
87 | | }; |
88 | | |
89 | | class DOMProxyHandler : public BaseDOMProxyHandler |
90 | | { |
91 | | public: |
92 | | constexpr DOMProxyHandler() |
93 | | : BaseDOMProxyHandler(&family) |
94 | 0 | {} |
95 | | |
96 | | bool defineProperty(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id, |
97 | | JS::Handle<JS::PropertyDescriptor> desc, |
98 | | JS::ObjectOpResult &result) const override |
99 | 0 | { |
100 | 0 | bool unused; |
101 | 0 | return defineProperty(cx, proxy, id, desc, result, &unused); |
102 | 0 | } |
103 | | virtual bool defineProperty(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id, |
104 | | JS::Handle<JS::PropertyDescriptor> desc, |
105 | | JS::ObjectOpResult &result, bool *defined) const; |
106 | | bool delete_(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id, |
107 | | JS::ObjectOpResult &result) const override; |
108 | | bool preventExtensions(JSContext* cx, JS::Handle<JSObject*> proxy, |
109 | | JS::ObjectOpResult& result) const override; |
110 | | bool isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy, bool *extensible) |
111 | | const override; |
112 | | bool set(JSContext *cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id, |
113 | | JS::Handle<JS::Value> v, JS::Handle<JS::Value> receiver, JS::ObjectOpResult &result) |
114 | | const override; |
115 | | |
116 | | /* |
117 | | * If assigning to proxy[id] hits a named setter with OverrideBuiltins or |
118 | | * an indexed setter, call it and set *done to true on success. Otherwise, set |
119 | | * *done to false. |
120 | | */ |
121 | | virtual bool setCustom(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id, |
122 | | JS::Handle<JS::Value> v, bool *done) const; |
123 | | |
124 | | /* |
125 | | * Get the expando object for the given DOM proxy. |
126 | | */ |
127 | | static JSObject* GetExpandoObject(JSObject* obj); |
128 | | |
129 | | /* |
130 | | * Clear the expando object for the given DOM proxy and return it. This |
131 | | * function will ensure that the returned object is exposed to active JS if |
132 | | * the given object is exposed. |
133 | | * |
134 | | * GetAndClearExpandoObject does not DROP or clear the preserving wrapper |
135 | | * flag. |
136 | | */ |
137 | | static JSObject* GetAndClearExpandoObject(JSObject* obj); |
138 | | |
139 | | /* |
140 | | * Ensure that the given proxy (obj) has an expando object, and return it. |
141 | | * Returns null on failure. |
142 | | */ |
143 | | static JSObject* EnsureExpandoObject(JSContext* cx, |
144 | | JS::Handle<JSObject*> obj); |
145 | | |
146 | | static const char family; |
147 | | }; |
148 | | |
149 | | // Class used by shadowing handlers (the ones that have [OverrideBuiltins]. |
150 | | // This handles tracing the expando in ExpandoAndGeneration. |
151 | | class ShadowingDOMProxyHandler : public DOMProxyHandler |
152 | | { |
153 | | virtual void trace(JSTracer* trc, JSObject* proxy) const override; |
154 | | }; |
155 | | |
156 | | inline bool IsDOMProxy(JSObject *obj) |
157 | 0 | { |
158 | 0 | const js::Class* clasp = js::GetObjectClass(obj); |
159 | 0 | return clasp->isProxy() && |
160 | 0 | js::GetProxyHandler(obj)->family() == &DOMProxyHandler::family; |
161 | 0 | } |
162 | | |
163 | | inline const DOMProxyHandler* |
164 | | GetDOMProxyHandler(JSObject* obj) |
165 | 0 | { |
166 | 0 | MOZ_ASSERT(IsDOMProxy(obj)); |
167 | 0 | return static_cast<const DOMProxyHandler*>(js::GetProxyHandler(obj)); |
168 | 0 | } |
169 | | |
170 | | extern jsid s_length_id; |
171 | | |
172 | | // A return value of UINT32_MAX indicates "not an array index". Note, in |
173 | | // particular, that UINT32_MAX itself is not a valid array index in general. |
174 | | inline uint32_t |
175 | | GetArrayIndexFromId(JSContext* cx, JS::Handle<jsid> id) |
176 | 0 | { |
177 | 0 | // Much like js::IdIsIndex, except with a fast path for "length" and another |
178 | 0 | // fast path for starting with a lowercase ascii char. Is that second one |
179 | 0 | // really needed? I guess it is because StringIsArrayIndex is out of line... |
180 | 0 | if (MOZ_LIKELY(JSID_IS_INT(id))) { |
181 | 0 | return JSID_TO_INT(id); |
182 | 0 | } |
183 | 0 | if (MOZ_LIKELY(id == s_length_id)) { |
184 | 0 | return UINT32_MAX; |
185 | 0 | } |
186 | 0 | if (MOZ_UNLIKELY(!JSID_IS_ATOM(id))) { |
187 | 0 | return UINT32_MAX; |
188 | 0 | } |
189 | 0 | |
190 | 0 | JSLinearString* str = js::AtomToLinearString(JSID_TO_ATOM(id)); |
191 | 0 | char16_t s; |
192 | 0 | { |
193 | 0 | JS::AutoCheckCannotGC nogc; |
194 | 0 | if (js::LinearStringHasLatin1Chars(str)) { |
195 | 0 | s = *js::GetLatin1LinearStringChars(nogc, str); |
196 | 0 | } else { |
197 | 0 | s = *js::GetTwoByteLinearStringChars(nogc, str); |
198 | 0 | } |
199 | 0 | } |
200 | 0 | if (MOZ_LIKELY((unsigned)s >= 'a' && (unsigned)s <= 'z')) |
201 | 0 | return UINT32_MAX; |
202 | 0 | |
203 | 0 | uint32_t i; |
204 | 0 | return js::StringIsArrayIndex(str, &i) ? i : UINT32_MAX; |
205 | 0 | } |
206 | | |
207 | | inline bool |
208 | | IsArrayIndex(uint32_t index) |
209 | 0 | { |
210 | 0 | return index < UINT32_MAX; |
211 | 0 | } |
212 | | |
213 | | inline void |
214 | | FillPropertyDescriptor(JS::MutableHandle<JS::PropertyDescriptor> desc, |
215 | | JSObject* obj, bool readonly, bool enumerable = true) |
216 | 0 | { |
217 | 0 | desc.object().set(obj); |
218 | 0 | desc.setAttributes((readonly ? JSPROP_READONLY : 0) | |
219 | 0 | (enumerable ? JSPROP_ENUMERATE : 0)); |
220 | 0 | desc.setGetter(nullptr); |
221 | 0 | desc.setSetter(nullptr); |
222 | 0 | } |
223 | | |
224 | | inline void |
225 | | FillPropertyDescriptor(JS::MutableHandle<JS::PropertyDescriptor> desc, |
226 | | JSObject* obj, const JS::Value& v, |
227 | | bool readonly, bool enumerable = true) |
228 | 0 | { |
229 | 0 | desc.value().set(v); |
230 | 0 | FillPropertyDescriptor(desc, obj, readonly, enumerable); |
231 | 0 | } |
232 | | |
233 | | inline void |
234 | | FillPropertyDescriptor(JS::MutableHandle<JS::PropertyDescriptor> desc, |
235 | | JSObject* obj, unsigned attributes, const JS::Value& v) |
236 | 0 | { |
237 | 0 | desc.object().set(obj); |
238 | 0 | desc.value().set(v); |
239 | 0 | desc.setAttributes(attributes); |
240 | 0 | desc.setGetter(nullptr); |
241 | 0 | desc.setSetter(nullptr); |
242 | 0 | } |
243 | | |
244 | | } // namespace dom |
245 | | } // namespace mozilla |
246 | | |
247 | | #endif /* mozilla_dom_DOMProxyHandler_h */ |