Bug 1943761 - Add class alignment to the mozsearch analysis file. r=asuth
[gecko.git] / js / xpconnect / wrappers / XrayWrapper.h
blob3df88ef812fc9093b3964ab941f760a2627f7089
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
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef XrayWrapper_h
8 #define XrayWrapper_h
10 #include "mozilla/Maybe.h"
12 #include "WrapperFactory.h"
14 #include "jsapi.h"
15 #include "jsfriendapi.h"
16 #include "js/friend/XrayJitInfo.h" // JS::XrayJitInfo
17 #include "js/Object.h" // JS::GetReservedSlot
18 #include "js/Proxy.h"
19 #include "js/Wrapper.h"
20 #include "mozilla/dom/ScriptSettings.h"
22 // Slot where Xray functions for Web IDL methods store a pointer to
23 // the Xray wrapper they're associated with.
24 #define XRAY_DOM_FUNCTION_PARENT_WRAPPER_SLOT 0
25 // Slot where in debug builds Xray functions for Web IDL methods store
26 // a pointer to their themselves, just so we can assert that they're the
27 // sort of functions we expect.
28 #define XRAY_DOM_FUNCTION_NATIVE_SLOT_FOR_SELF 1
30 // Xray wrappers re-resolve the original native properties on the native
31 // object and always directly access to those properties.
32 // Because they work so differently from the rest of the wrapper hierarchy,
33 // we pull them out of the Wrapper inheritance hierarchy and create a
34 // little world around them.
36 class nsIPrincipal;
38 namespace xpc {
40 enum XrayType {
41 XrayForDOMObject,
42 XrayForJSObject,
43 XrayForOpaqueObject,
44 NotXray
47 class XrayTraits {
48 public:
49 constexpr XrayTraits() = default;
51 static JSObject* getTargetObject(JSObject* wrapper) {
52 JSObject* target =
53 js::UncheckedUnwrap(wrapper, /* stopAtWindowProxy = */ false);
54 if (target) {
55 JS::ExposeObjectToActiveJS(target);
57 return target;
60 // NB: resolveOwnProperty may decide whether or not to cache what it finds
61 // on the holder. If the result is not cached, the lookup will happen afresh
62 // for each access, which is the right thing for things like dynamic NodeList
63 // properties.
64 virtual bool resolveOwnProperty(
65 JSContext* cx, JS::HandleObject wrapper, JS::HandleObject target,
66 JS::HandleObject holder, JS::HandleId id,
67 JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> desc);
69 bool delete_(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id,
70 JS::ObjectOpResult& result) {
71 return result.succeed();
74 static bool getBuiltinClass(JSContext* cx, JS::HandleObject wrapper,
75 const js::Wrapper& baseInstance,
76 js::ESClass* cls) {
77 return baseInstance.getBuiltinClass(cx, wrapper, cls);
80 static const char* className(JSContext* cx, JS::HandleObject wrapper,
81 const js::Wrapper& baseInstance) {
82 return baseInstance.className(cx, wrapper);
85 virtual void preserveWrapper(JSObject* target) = 0;
87 bool getExpandoObject(JSContext* cx, JS::HandleObject target,
88 JS::HandleObject consumer,
89 JS::MutableHandleObject expandObject);
90 JSObject* ensureExpandoObject(JSContext* cx, JS::HandleObject wrapper,
91 JS::HandleObject target);
93 // Slots for holder objects.
94 enum {
95 HOLDER_SLOT_CACHED_PROTO = 0,
96 HOLDER_SLOT_EXPANDO = 1,
97 HOLDER_SHARED_SLOT_COUNT
100 static JSObject* getHolder(JSObject* wrapper);
101 JSObject* ensureHolder(JSContext* cx, JS::HandleObject wrapper);
102 virtual JSObject* createHolder(JSContext* cx, JSObject* wrapper) = 0;
104 JSObject* getExpandoChain(JS::HandleObject obj);
105 JSObject* detachExpandoChain(JS::HandleObject obj);
106 bool setExpandoChain(JSContext* cx, JS::HandleObject obj,
107 JS::HandleObject chain);
108 bool cloneExpandoChain(JSContext* cx, JS::HandleObject dst,
109 JS::HandleObject srcChain);
111 protected:
112 static const JSClass HolderClass;
114 // Get the JSClass we should use for our expando object.
115 virtual const JSClass* getExpandoClass(JSContext* cx,
116 JS::HandleObject target) const;
118 private:
119 bool expandoObjectMatchesConsumer(JSContext* cx,
120 JS::HandleObject expandoObject,
121 nsIPrincipal* consumerOrigin);
123 // |expandoChain| is the expando chain in the wrapped object's compartment.
124 // |exclusiveWrapper| is any xray that has exclusive use of the expando.
125 // |cx| may be in any compartment.
126 bool getExpandoObjectInternal(JSContext* cx, JSObject* expandoChain,
127 JS::HandleObject exclusiveWrapper,
128 nsIPrincipal* origin,
129 JS::MutableHandleObject expandoObject);
131 // |cx| is in the target's compartment, and |exclusiveWrapper| is any xray
132 // that has exclusive use of the expando. |exclusiveWrapperGlobal| is the
133 // caller's global and must be same-compartment with |exclusiveWrapper|.
134 JSObject* attachExpandoObject(JSContext* cx, JS::HandleObject target,
135 JS::HandleObject exclusiveWrapper,
136 JS::HandleObject exclusiveWrapperGlobal,
137 nsIPrincipal* origin);
139 XrayTraits(XrayTraits&) = delete;
140 const XrayTraits& operator=(XrayTraits&) = delete;
143 void ExpandoObjectFinalize(JS::GCContext* gcx, JSObject* obj);
145 class DOMXrayTraits : public XrayTraits {
146 public:
147 constexpr DOMXrayTraits() = default;
149 static const XrayType Type = XrayForDOMObject;
151 virtual bool resolveOwnProperty(
152 JSContext* cx, JS::HandleObject wrapper, JS::HandleObject target,
153 JS::HandleObject holder, JS::HandleId id,
154 JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> desc) override;
156 bool delete_(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id,
157 JS::ObjectOpResult& result);
159 bool defineProperty(
160 JSContext* cx, JS::HandleObject wrapper, JS::HandleId id,
161 JS::Handle<JS::PropertyDescriptor> desc,
162 JS::Handle<mozilla::Maybe<JS::PropertyDescriptor>> existingDesc,
163 JS::Handle<JSObject*> existingHolder, JS::ObjectOpResult& result,
164 bool* done);
165 virtual bool enumerateNames(JSContext* cx, JS::HandleObject wrapper,
166 unsigned flags, JS::MutableHandleIdVector props);
167 static bool call(JSContext* cx, JS::HandleObject wrapper,
168 const JS::CallArgs& args, const js::Wrapper& baseInstance);
169 static bool construct(JSContext* cx, JS::HandleObject wrapper,
170 const JS::CallArgs& args,
171 const js::Wrapper& baseInstance);
173 static bool getPrototype(JSContext* cx, JS::HandleObject wrapper,
174 JS::HandleObject target,
175 JS::MutableHandleObject protop);
177 virtual void preserveWrapper(JSObject* target) override;
179 virtual JSObject* createHolder(JSContext* cx, JSObject* wrapper) override;
181 static DOMXrayTraits singleton;
183 protected:
184 virtual const JSClass* getExpandoClass(
185 JSContext* cx, JS::HandleObject target) const override;
188 class JSXrayTraits : public XrayTraits {
189 public:
190 static const XrayType Type = XrayForJSObject;
192 virtual bool resolveOwnProperty(
193 JSContext* cx, JS::HandleObject wrapper, JS::HandleObject target,
194 JS::HandleObject holder, JS::HandleId id,
195 JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> desc) override;
197 bool delete_(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id,
198 JS::ObjectOpResult& result);
200 bool defineProperty(
201 JSContext* cx, JS::HandleObject wrapper, JS::HandleId id,
202 JS::Handle<JS::PropertyDescriptor> desc,
203 JS::Handle<mozilla::Maybe<JS::PropertyDescriptor>> existingDesc,
204 JS::Handle<JSObject*> existingHolder, JS::ObjectOpResult& result,
205 bool* defined);
207 virtual bool enumerateNames(JSContext* cx, JS::HandleObject wrapper,
208 unsigned flags, JS::MutableHandleIdVector props);
210 static bool call(JSContext* cx, JS::HandleObject wrapper,
211 const JS::CallArgs& args, const js::Wrapper& baseInstance) {
212 JSXrayTraits& self = JSXrayTraits::singleton;
213 JS::RootedObject holder(cx, self.ensureHolder(cx, wrapper));
214 if (!holder) {
215 return false;
217 JSProtoKey key = xpc::JSXrayTraits::getProtoKey(holder);
218 if (key == JSProto_Function || key == JSProto_BoundFunction) {
219 return baseInstance.call(cx, wrapper, args);
222 JS::RootedValue v(cx, JS::ObjectValue(*wrapper));
223 js::ReportIsNotFunction(cx, v);
224 return false;
227 static bool construct(JSContext* cx, JS::HandleObject wrapper,
228 const JS::CallArgs& args,
229 const js::Wrapper& baseInstance);
231 bool getPrototype(JSContext* cx, JS::HandleObject wrapper,
232 JS::HandleObject target, JS::MutableHandleObject protop) {
233 JS::RootedObject holder(cx, ensureHolder(cx, wrapper));
234 if (!holder) {
235 return false;
237 JSProtoKey key = getProtoKey(holder);
238 if (isPrototype(holder)) {
239 JSProtoKey protoKey = js::InheritanceProtoKeyForStandardClass(key);
240 if (protoKey == JSProto_Null) {
241 protop.set(nullptr);
242 return true;
244 key = protoKey;
248 JSAutoRealm ar(cx, target);
249 if (!JS_GetClassPrototype(cx, key, protop)) {
250 return false;
253 return JS_WrapObject(cx, protop);
256 virtual void preserveWrapper(JSObject* target) override {
257 // In the case of pure JS objects, there is no underlying object, and
258 // the target is the canonical representation of state. If it gets
259 // collected, then expandos and such should be collected too. So there's
260 // nothing to do here.
263 enum {
264 SLOT_PROTOKEY = HOLDER_SHARED_SLOT_COUNT,
265 SLOT_ISPROTOTYPE,
266 SLOT_CONSTRUCTOR_FOR,
267 SLOT_COUNT
269 virtual JSObject* createHolder(JSContext* cx, JSObject* wrapper) override;
271 static JSProtoKey getProtoKey(JSObject* holder) {
272 int32_t key = JS::GetReservedSlot(holder, SLOT_PROTOKEY).toInt32();
273 return static_cast<JSProtoKey>(key);
276 static bool isPrototype(JSObject* holder) {
277 return JS::GetReservedSlot(holder, SLOT_ISPROTOTYPE).toBoolean();
280 static JSProtoKey constructorFor(JSObject* holder) {
281 int32_t key = JS::GetReservedSlot(holder, SLOT_CONSTRUCTOR_FOR).toInt32();
282 return static_cast<JSProtoKey>(key);
285 // Operates in the wrapper compartment.
286 static bool getOwnPropertyFromWrapperIfSafe(
287 JSContext* cx, JS::HandleObject wrapper, JS::HandleId id,
288 JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> desc);
290 // Like the above, but operates in the target compartment. wrapperGlobal is
291 // the caller's global (must be in the wrapper compartment).
292 static bool getOwnPropertyFromTargetIfSafe(
293 JSContext* cx, JS::HandleObject target, JS::HandleObject wrapper,
294 JS::HandleObject wrapperGlobal, JS::HandleId id,
295 JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> desc);
297 static const JSClass HolderClass;
298 static JSXrayTraits singleton;
301 // These traits are used when the target is not Xrayable and we therefore want
302 // to make it opaque modulo the usual Xray machinery (like expandos and
303 // .wrappedJSObject).
304 class OpaqueXrayTraits : public XrayTraits {
305 public:
306 static const XrayType Type = XrayForOpaqueObject;
308 virtual bool resolveOwnProperty(
309 JSContext* cx, JS::HandleObject wrapper, JS::HandleObject target,
310 JS::HandleObject holder, JS::HandleId id,
311 JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> desc) override;
313 bool defineProperty(
314 JSContext* cx, JS::HandleObject wrapper, JS::HandleId id,
315 JS::Handle<JS::PropertyDescriptor> desc,
316 JS::Handle<mozilla::Maybe<JS::PropertyDescriptor>> existingDesc,
317 JS::Handle<JSObject*> existingHolder, JS::ObjectOpResult& result,
318 bool* defined) {
319 *defined = false;
320 return true;
323 virtual bool enumerateNames(JSContext* cx, JS::HandleObject wrapper,
324 unsigned flags, JS::MutableHandleIdVector props) {
325 return true;
328 static bool call(JSContext* cx, JS::HandleObject wrapper,
329 const JS::CallArgs& args, const js::Wrapper& baseInstance) {
330 JS::RootedValue v(cx, JS::ObjectValue(*wrapper));
331 js::ReportIsNotFunction(cx, v);
332 return false;
335 static bool construct(JSContext* cx, JS::HandleObject wrapper,
336 const JS::CallArgs& args,
337 const js::Wrapper& baseInstance) {
338 JS::RootedValue v(cx, JS::ObjectValue(*wrapper));
339 js::ReportIsNotFunction(cx, v);
340 return false;
343 bool getPrototype(JSContext* cx, JS::HandleObject wrapper,
344 JS::HandleObject target, JS::MutableHandleObject protop) {
345 // Opaque wrappers just get targetGlobal.Object.prototype as their
346 // prototype. This is preferable to using a null prototype because it
347 // lets things like |toString| and |__proto__| work.
349 JSAutoRealm ar(cx, target);
350 if (!JS_GetClassPrototype(cx, JSProto_Object, protop)) {
351 return false;
354 return JS_WrapObject(cx, protop);
357 static bool getBuiltinClass(JSContext* cx, JS::HandleObject wrapper,
358 const js::Wrapper& baseInstance,
359 js::ESClass* cls) {
360 *cls = js::ESClass::Other;
361 return true;
364 static const char* className(JSContext* cx, JS::HandleObject wrapper,
365 const js::Wrapper& baseInstance) {
366 return "Opaque";
369 virtual void preserveWrapper(JSObject* target) override {}
371 virtual JSObject* createHolder(JSContext* cx, JSObject* wrapper) override {
372 return JS_NewObjectWithGivenProto(cx, &HolderClass, nullptr);
375 static OpaqueXrayTraits singleton;
378 XrayType GetXrayType(JSObject* obj);
379 XrayTraits* GetXrayTraits(JSObject* obj);
381 template <typename Base, typename Traits>
382 class XrayWrapper : public Base {
383 static_assert(std::is_base_of_v<js::BaseProxyHandler, Base>,
384 "Base *must* derive from js::BaseProxyHandler");
386 public:
387 constexpr explicit XrayWrapper(unsigned flags)
388 : Base(flags | WrapperFactory::IS_XRAY_WRAPPER_FLAG,
389 /* aHasPrototype = */ true) {};
391 /* Standard internal methods. */
392 virtual bool getOwnPropertyDescriptor(
393 JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<jsid> id,
394 JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> desc)
395 const override;
396 virtual bool defineProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
397 JS::Handle<jsid> id,
398 JS::Handle<JS::PropertyDescriptor> desc,
399 JS::ObjectOpResult& result) const override;
400 virtual bool ownPropertyKeys(JSContext* cx, JS::Handle<JSObject*> wrapper,
401 JS::MutableHandleIdVector props) const override;
402 virtual bool delete_(JSContext* cx, JS::Handle<JSObject*> wrapper,
403 JS::Handle<jsid> id,
404 JS::ObjectOpResult& result) const override;
405 virtual bool enumerate(JSContext* cx, JS::Handle<JSObject*> wrapper,
406 JS::MutableHandleIdVector props) const override;
407 virtual bool getPrototype(JSContext* cx, JS::HandleObject wrapper,
408 JS::MutableHandleObject protop) const override;
409 virtual bool setPrototype(JSContext* cx, JS::HandleObject wrapper,
410 JS::HandleObject proto,
411 JS::ObjectOpResult& result) const override;
412 virtual bool getPrototypeIfOrdinary(
413 JSContext* cx, JS::HandleObject wrapper, bool* isOrdinary,
414 JS::MutableHandleObject protop) const override;
415 virtual bool setImmutablePrototype(JSContext* cx, JS::HandleObject wrapper,
416 bool* succeeded) const override;
417 virtual bool preventExtensions(JSContext* cx, JS::Handle<JSObject*> wrapper,
418 JS::ObjectOpResult& result) const override;
419 virtual bool isExtensible(JSContext* cx, JS::Handle<JSObject*> wrapper,
420 bool* extensible) const override;
421 virtual bool has(JSContext* cx, JS::Handle<JSObject*> wrapper,
422 JS::Handle<jsid> id, bool* bp) const override;
423 virtual bool get(JSContext* cx, JS::Handle<JSObject*> wrapper,
424 JS::HandleValue receiver, JS::Handle<jsid> id,
425 JS::MutableHandle<JS::Value> vp) const override;
426 virtual bool set(JSContext* cx, JS::Handle<JSObject*> wrapper,
427 JS::Handle<jsid> id, JS::Handle<JS::Value> v,
428 JS::Handle<JS::Value> receiver,
429 JS::ObjectOpResult& result) const override;
430 virtual bool call(JSContext* cx, JS::Handle<JSObject*> wrapper,
431 const JS::CallArgs& args) const override;
432 virtual bool construct(JSContext* cx, JS::Handle<JSObject*> wrapper,
433 const JS::CallArgs& args) const override;
435 /* SpiderMonkey extensions. */
436 virtual bool hasOwn(JSContext* cx, JS::Handle<JSObject*> wrapper,
437 JS::Handle<jsid> id, bool* bp) const override;
438 virtual bool getOwnEnumerablePropertyKeys(
439 JSContext* cx, JS::Handle<JSObject*> wrapper,
440 JS::MutableHandleIdVector props) const override;
442 virtual bool getBuiltinClass(JSContext* cx, JS::HandleObject wapper,
443 js::ESClass* cls) const override;
444 virtual const char* className(JSContext* cx,
445 JS::HandleObject proxy) const override;
447 static const XrayWrapper singleton;
449 protected:
450 bool getPropertyKeys(JSContext* cx, JS::Handle<JSObject*> wrapper,
451 unsigned flags, JS::MutableHandleIdVector props) const;
454 #define PermissiveXrayDOM \
455 xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::DOMXrayTraits>
456 #define PermissiveXrayJS \
457 xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::JSXrayTraits>
458 #define PermissiveXrayOpaque \
459 xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::OpaqueXrayTraits>
461 extern template class PermissiveXrayDOM;
462 extern template class PermissiveXrayJS;
463 extern template class PermissiveXrayOpaque;
466 * Slots for Xray expando objects. See comments in XrayWrapper.cpp for details
467 * of how these get used; we mostly want the value of JSSLOT_EXPANDO_COUNT here.
469 enum ExpandoSlots {
470 JSSLOT_EXPANDO_NEXT = 0,
471 JSSLOT_EXPANDO_ORIGIN,
472 JSSLOT_EXPANDO_EXCLUSIVE_WRAPPER_HOLDER,
473 JSSLOT_EXPANDO_PROTOTYPE,
474 JSSLOT_EXPANDO_COUNT
477 extern const JSClassOps XrayExpandoObjectClassOps;
480 * Call aFunc on all Xray expandos for the given object. aFunc will be passed a
481 * JSObject pointer to an Xray expando object.
483 * No-op when called on non-main threads (where Xrays don't exist).
485 template <typename F>
486 void ForEachXrayExpandoObject(JS::RootingContext* aCx, JSObject* aTarget,
487 F&& aFunc) {
488 if (!NS_IsMainThread()) {
489 // No Xrays
490 return;
493 MOZ_ASSERT(GetXrayTraits(aTarget) == &DOMXrayTraits::singleton);
494 JS::RootedObject rootedTarget(aCx, aTarget);
495 JS::RootedObject head(aCx,
496 DOMXrayTraits::singleton.getExpandoChain(rootedTarget));
497 while (head) {
498 aFunc(head);
499 head = JS::GetReservedSlot(head, JSSLOT_EXPANDO_NEXT).toObjectOrNull();
504 * Ensure the given wrapper has an expando object and return it. This can
505 * return null on failure. Will only be called when "wrapper" is an Xray for a
506 * DOM object.
508 JSObject* EnsureXrayExpandoObject(JSContext* cx, JS::HandleObject wrapper);
510 // Information about xrays for use by the JITs.
511 extern JS::XrayJitInfo gXrayJitInfo;
513 } // namespace xpc
515 #endif