1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=2 sw=2 et tw=99 ft=cpp: */
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/. */
7 #include "mozilla/dom/DOMJSProxyHandler.h"
9 #include "xpcprivate.h"
10 #include "XPCWrapper.h"
11 #include "WrapperFactory.h"
12 #include "nsDOMClassInfo.h"
13 #include "nsWrapperCacheInlines.h"
14 #include "mozilla/dom/BindingUtils.h"
23 jsid s_length_id
= JSID_VOID
;
26 DefineStaticJSVals(JSContext
* cx
)
28 return InternJSString(cx
, s_length_id
, "length");
31 const char DOMProxyHandler::family
= 0;
33 js::DOMProxyShadowsResult
34 DOMProxyShadows(JSContext
* cx
, JS::Handle
<JSObject
*> proxy
, JS::Handle
<jsid
> id
)
36 JS::Value v
= js::GetProxyExtra(proxy
, JSPROXYSLOT_EXPANDO
);
39 Rooted
<JSObject
*> object(cx
, &v
.toObject());
40 if (!JS_AlreadyHasOwnPropertyById(cx
, object
, id
, &hasOwn
))
41 return js::ShadowCheckFailed
;
43 return hasOwn
? js::Shadows
: js::DoesntShadow
;
46 if (v
.isUndefined()) {
47 return js::DoesntShadow
;
51 if (!GetProxyHandler(proxy
)->hasOwn(cx
, proxy
, id
, &hasOwn
))
52 return js::ShadowCheckFailed
;
54 return hasOwn
? js::Shadows
: js::DoesntShadowUnique
;
57 // Store the information for the specialized ICs.
58 struct SetDOMProxyInformation
60 SetDOMProxyInformation() {
61 js::SetDOMProxyInformation((const void*) &DOMProxyHandler::family
,
62 JSPROXYSLOT_EXPANDO
, DOMProxyShadows
);
66 SetDOMProxyInformation gSetDOMProxyInformation
;
70 DOMProxyHandler::GetAndClearExpandoObject(JSObject
* obj
)
72 MOZ_ASSERT(IsDOMProxy(obj
), "expected a DOM proxy object");
73 JS::Value v
= js::GetProxyExtra(obj
, JSPROXYSLOT_EXPANDO
);
74 if (v
.isUndefined()) {
79 js::SetProxyExtra(obj
, JSPROXYSLOT_EXPANDO
, UndefinedValue());
80 xpc::ObjectScope(obj
)->RemoveDOMExpandoObject(obj
);
82 js::ExpandoAndGeneration
* expandoAndGeneration
=
83 static_cast<js::ExpandoAndGeneration
*>(v
.toPrivate());
84 v
= expandoAndGeneration
->expando
;
85 if (v
.isUndefined()) {
88 expandoAndGeneration
->expando
= UndefinedValue();
97 DOMProxyHandler::EnsureExpandoObject(JSContext
* cx
, JS::Handle
<JSObject
*> obj
)
99 NS_ASSERTION(IsDOMProxy(obj
), "expected a DOM proxy object");
100 JS::Value v
= js::GetProxyExtra(obj
, JSPROXYSLOT_EXPANDO
);
102 return &v
.toObject();
105 js::ExpandoAndGeneration
* expandoAndGeneration
;
106 if (!v
.isUndefined()) {
107 expandoAndGeneration
= static_cast<js::ExpandoAndGeneration
*>(v
.toPrivate());
108 if (expandoAndGeneration
->expando
.isObject()) {
109 return &expandoAndGeneration
->expando
.toObject();
112 expandoAndGeneration
= nullptr;
115 JS::Rooted
<JSObject
*> parent(cx
, js::GetObjectParent(obj
));
116 JS::Rooted
<JSObject
*> expando(cx
,
117 JS_NewObjectWithGivenProto(cx
, nullptr, JS::NullPtr(), parent
));
122 nsISupports
* native
= UnwrapDOMObject
<nsISupports
>(obj
);
123 nsWrapperCache
* cache
;
124 CallQueryInterface(native
, &cache
);
125 if (expandoAndGeneration
) {
126 cache
->PreserveWrapper(native
);
127 expandoAndGeneration
->expando
.setObject(*expando
);
132 if (!xpc::ObjectScope(obj
)->RegisterDOMExpandoObject(obj
)) {
136 cache
->SetPreservingWrapper(true);
137 js::SetProxyExtra(obj
, JSPROXYSLOT_EXPANDO
, ObjectValue(*expando
));
143 DOMProxyHandler::preventExtensions(JSContext
*cx
, JS::Handle
<JSObject
*> proxy
,
144 bool *succeeded
) const
146 // always extensible per WebIDL
152 DOMProxyHandler::isExtensible(JSContext
*cx
, JS::Handle
<JSObject
*> proxy
, bool *extensible
) const
159 BaseDOMProxyHandler::getPropertyDescriptor(JSContext
* cx
,
160 JS::Handle
<JSObject
*> proxy
,
162 MutableHandle
<JSPropertyDescriptor
> desc
) const
164 if (!getOwnPropertyDescriptor(cx
, proxy
, id
, desc
)) {
171 JS::Rooted
<JSObject
*> proto(cx
);
172 if (!js::GetObjectProto(cx
, proxy
, &proto
)) {
176 desc
.object().set(nullptr);
180 return JS_GetPropertyDescriptorById(cx
, proto
, id
, desc
);
184 BaseDOMProxyHandler::getOwnPropertyDescriptor(JSContext
* cx
,
185 JS::Handle
<JSObject
*> proxy
,
187 MutableHandle
<JSPropertyDescriptor
> desc
) const
189 return getOwnPropDescriptor(cx
, proxy
, id
, /* ignoreNamedProps = */ false,
194 DOMProxyHandler::defineProperty(JSContext
* cx
, JS::Handle
<JSObject
*> proxy
, JS::Handle
<jsid
> id
,
195 MutableHandle
<JSPropertyDescriptor
> desc
, bool* defined
) const
197 if (desc
.hasGetterObject() && desc
.setter() == JS_StrictPropertyStub
) {
198 return JS_ReportErrorFlagsAndNumber(cx
,
199 JSREPORT_WARNING
| JSREPORT_STRICT
|
200 JSREPORT_STRICT_MODE_ERROR
,
201 js_GetErrorMessage
, nullptr,
205 if (xpc::WrapperFactory::IsXrayWrapper(proxy
)) {
209 JSObject
* expando
= EnsureExpandoObject(cx
, proxy
);
215 return js_DefineOwnProperty(cx
, expando
, id
, desc
, &dummy
);
219 DOMProxyHandler::set(JSContext
*cx
, Handle
<JSObject
*> proxy
, Handle
<JSObject
*> receiver
,
220 Handle
<jsid
> id
, bool strict
, MutableHandle
<JS::Value
> vp
) const
222 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy
),
223 "Should not have a XrayWrapper here");
225 if (!setCustom(cx
, proxy
, id
, vp
, &done
)) {
232 // Make sure to ignore our named properties when checking for own
233 // property descriptors for a set.
234 JS::Rooted
<JSPropertyDescriptor
> desc(cx
);
235 if (!getOwnPropDescriptor(cx
, proxy
, id
, /* ignoreNamedProps = */ true,
239 bool descIsOwn
= desc
.object() != nullptr;
240 if (!desc
.object()) {
241 // Don't just use getPropertyDescriptor, unlike BaseProxyHandler::set,
242 // because that would call getOwnPropertyDescriptor on ourselves. Instead,
243 // directly delegate to the proto, if any.
244 JS::Rooted
<JSObject
*> proto(cx
);
245 if (!js::GetObjectProto(cx
, proxy
, &proto
)) {
248 if (proto
&& !JS_GetPropertyDescriptorById(cx
, proto
, id
, &desc
)) {
253 return js::SetPropertyIgnoringNamedGetter(cx
, this, proxy
, receiver
, id
,
254 &desc
, descIsOwn
, strict
, vp
);
258 DOMProxyHandler::delete_(JSContext
* cx
, JS::Handle
<JSObject
*> proxy
,
259 JS::Handle
<jsid
> id
, bool* bp
) const
261 JS::Rooted
<JSObject
*> expando(cx
);
262 if (!xpc::WrapperFactory::IsXrayWrapper(proxy
) && (expando
= GetExpandoObject(proxy
))) {
263 return JS_DeletePropertyById2(cx
, expando
, id
, bp
);
271 BaseDOMProxyHandler::watch(JSContext
* cx
, JS::Handle
<JSObject
*> proxy
, JS::Handle
<jsid
> id
,
272 JS::Handle
<JSObject
*> callable
) const
274 return js::WatchGuts(cx
, proxy
, id
, callable
);
278 BaseDOMProxyHandler::unwatch(JSContext
* cx
, JS::Handle
<JSObject
*> proxy
, JS::Handle
<jsid
> id
) const
280 return js::UnwatchGuts(cx
, proxy
, id
);
284 BaseDOMProxyHandler::ownPropertyKeys(JSContext
* cx
,
285 JS::Handle
<JSObject
*> proxy
,
286 JS::AutoIdVector
& props
) const
288 return ownPropNames(cx
, proxy
, JSITER_OWNONLY
| JSITER_HIDDEN
| JSITER_SYMBOLS
, props
);
292 BaseDOMProxyHandler::getOwnEnumerablePropertyKeys(JSContext
* cx
,
293 JS::Handle
<JSObject
*> proxy
,
294 JS::AutoIdVector
& props
) const
296 return ownPropNames(cx
, proxy
, JSITER_OWNONLY
, props
);
300 BaseDOMProxyHandler::enumerate(JSContext
*cx
, JS::Handle
<JSObject
*> proxy
,
301 JS::MutableHandle
<JSObject
*> objp
) const
303 return BaseProxyHandler::enumerate(cx
, proxy
, objp
);
307 DOMProxyHandler::has(JSContext
* cx
, JS::Handle
<JSObject
*> proxy
, JS::Handle
<jsid
> id
, bool* bp
) const
309 if (!hasOwn(cx
, proxy
, id
, bp
)) {
314 // We have the property ourselves; no need to worry about our prototype
319 // OK, now we have to look at the proto
320 JS::Rooted
<JSObject
*> proto(cx
);
321 if (!js::GetObjectProto(cx
, proxy
, &proto
)) {
328 bool ok
= JS_HasPropertyById(cx
, proto
, id
, &protoHasProp
);
336 IdToInt32(JSContext
* cx
, JS::Handle
<jsid
> id
)
338 JS::Rooted
<JS::Value
> idval(cx
);
341 if (JSID_IS_SYMBOL(id
) ||
342 !::JS_IdToValue(cx
, id
, &idval
) ||
343 !JS::ToNumber(cx
, idval
, &array_index
) ||
344 !::JS_DoubleIsInt32(array_index
, &i
)) {
352 DOMProxyHandler::setCustom(JSContext
* cx
, JS::Handle
<JSObject
*> proxy
, JS::Handle
<jsid
> id
,
353 JS::MutableHandle
<JS::Value
> vp
, bool *done
) const
361 DOMProxyHandler::GetExpandoObject(JSObject
*obj
)
363 MOZ_ASSERT(IsDOMProxy(obj
), "expected a DOM proxy object");
364 JS::Value v
= js::GetProxyExtra(obj
, JSPROXYSLOT_EXPANDO
);
366 return &v
.toObject();
369 if (v
.isUndefined()) {
373 js::ExpandoAndGeneration
* expandoAndGeneration
=
374 static_cast<js::ExpandoAndGeneration
*>(v
.toPrivate());
375 v
= expandoAndGeneration
->expando
;
376 return v
.isUndefined() ? nullptr : &v
.toObject();
380 } // namespace mozilla