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/Util.h"
9 #include "mozilla/dom/DOMJSProxyHandler.h"
10 #include "xpcpublic.h"
11 #include "xpcprivate.h"
12 #include "XPCQuickStubs.h"
13 #include "XPCWrapper.h"
14 #include "WrapperFactory.h"
15 #include "nsDOMClassInfo.h"
16 #include "nsWrapperCacheInlines.h"
17 #include "mozilla/dom/BindingUtils.h"
26 jsid s_length_id
= JSID_VOID
;
29 DefineStaticJSVals(JSContext
* cx
)
31 return InternJSString(cx
, s_length_id
, "length");
35 const char HandlerFamily
= 0;
37 js::DOMProxyShadowsResult
38 DOMProxyShadows(JSContext
* cx
, JS::Handle
<JSObject
*> proxy
, JS::Handle
<jsid
> id
)
40 JS::Value v
= js::GetProxyExtra(proxy
, JSPROXYSLOT_EXPANDO
);
43 if (!JS_AlreadyHasOwnPropertyById(cx
, &v
.toObject(), id
, &hasOwn
))
44 return js::ShadowCheckFailed
;
46 return hasOwn
? js::Shadows
: js::DoesntShadow
;
49 if (v
.isUndefined()) {
50 return js::DoesntShadow
;
54 if (!GetProxyHandler(proxy
)->hasOwn(cx
, proxy
, id
, &hasOwn
))
55 return js::ShadowCheckFailed
;
57 return hasOwn
? js::Shadows
: js::DoesntShadowUnique
;
60 // Store the information for the specialized ICs.
61 struct SetDOMProxyInformation
63 SetDOMProxyInformation() {
64 js::SetDOMProxyInformation((const void*) &HandlerFamily
,
65 js::PROXY_EXTRA_SLOT
+ JSPROXYSLOT_EXPANDO
, DOMProxyShadows
);
69 SetDOMProxyInformation gSetDOMProxyInformation
;
73 DOMProxyHandler::GetAndClearExpandoObject(JSObject
* obj
)
75 MOZ_ASSERT(IsDOMProxy(obj
), "expected a DOM proxy object");
76 JS::Value v
= js::GetProxyExtra(obj
, JSPROXYSLOT_EXPANDO
);
77 if (v
.isUndefined()) {
82 js::SetProxyExtra(obj
, JSPROXYSLOT_EXPANDO
, UndefinedValue());
83 XPCWrappedNativeScope
* scope
= xpc::MaybeGetObjectScope(obj
);
85 scope
->RemoveDOMExpandoObject(obj
);
88 js::ExpandoAndGeneration
* expandoAndGeneration
=
89 static_cast<js::ExpandoAndGeneration
*>(v
.toPrivate());
90 v
= expandoAndGeneration
->expando
;
91 if (v
.isUndefined()) {
94 expandoAndGeneration
->expando
= UndefinedValue();
103 DOMProxyHandler::EnsureExpandoObject(JSContext
* cx
, JS::Handle
<JSObject
*> obj
)
105 NS_ASSERTION(IsDOMProxy(obj
), "expected a DOM proxy object");
106 JS::Value v
= js::GetProxyExtra(obj
, JSPROXYSLOT_EXPANDO
);
108 return &v
.toObject();
111 js::ExpandoAndGeneration
* expandoAndGeneration
;
112 if (!v
.isUndefined()) {
113 expandoAndGeneration
= static_cast<js::ExpandoAndGeneration
*>(v
.toPrivate());
114 if (expandoAndGeneration
->expando
.isObject()) {
115 return &expandoAndGeneration
->expando
.toObject();
118 expandoAndGeneration
= nullptr;
121 JS::Rooted
<JSObject
*> expando(cx
,
122 JS_NewObjectWithGivenProto(cx
, nullptr, nullptr, js::GetObjectParent(obj
)));
127 nsISupports
* native
= UnwrapDOMObject
<nsISupports
>(obj
);
128 nsWrapperCache
* cache
;
129 CallQueryInterface(native
, &cache
);
130 if (expandoAndGeneration
) {
131 cache
->PreserveWrapper(native
);
132 expandoAndGeneration
->expando
.setObject(*expando
);
137 XPCWrappedNativeScope
* scope
= xpc::GetObjectScope(obj
);
138 if (!scope
->RegisterDOMExpandoObject(obj
)) {
142 cache
->SetPreservingWrapper(true);
143 js::SetProxyExtra(obj
, JSPROXYSLOT_EXPANDO
, ObjectValue(*expando
));
149 DOMProxyHandler::isExtensible(JSContext
*cx
, JS::Handle
<JSObject
*> proxy
, bool *extensible
)
151 // always extensible per WebIDL
157 DOMProxyHandler::preventExtensions(JSContext
*cx
, JS::Handle
<JSObject
*> proxy
)
159 // Throw a TypeError, per WebIDL.
160 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, nullptr,
161 JSMSG_CANT_CHANGE_EXTENSIBILITY
);
166 BaseDOMProxyHandler::getPropertyDescriptor(JSContext
* cx
,
167 JS::Handle
<JSObject
*> proxy
,
169 MutableHandle
<JSPropertyDescriptor
> desc
,
172 if (!getOwnPropertyDescriptor(cx
, proxy
, id
, desc
, flags
)) {
179 JS::Rooted
<JSObject
*> proto(cx
);
180 if (!js::GetObjectProto(cx
, proxy
, &proto
)) {
184 desc
.object().set(nullptr);
188 return JS_GetPropertyDescriptorById(cx
, proto
, id
, 0, desc
);
192 DOMProxyHandler::defineProperty(JSContext
* cx
, JS::Handle
<JSObject
*> proxy
, JS::Handle
<jsid
> id
,
193 MutableHandle
<JSPropertyDescriptor
> desc
, bool* defined
)
195 if (desc
.hasGetterObject() && desc
.setter() == JS_StrictPropertyStub
) {
196 return JS_ReportErrorFlagsAndNumber(cx
,
197 JSREPORT_WARNING
| JSREPORT_STRICT
|
198 JSREPORT_STRICT_MODE_ERROR
,
199 js_GetErrorMessage
, nullptr,
203 if (xpc::WrapperFactory::IsXrayWrapper(proxy
)) {
207 JSObject
* expando
= EnsureExpandoObject(cx
, proxy
);
213 return js_DefineOwnProperty(cx
, expando
, id
, desc
, &dummy
);
217 DOMProxyHandler::delete_(JSContext
* cx
, JS::Handle
<JSObject
*> proxy
,
218 JS::Handle
<jsid
> id
, bool* bp
)
220 JS::Rooted
<JSObject
*> expando(cx
);
221 if (!xpc::WrapperFactory::IsXrayWrapper(proxy
) && (expando
= GetExpandoObject(proxy
))) {
222 return JS_DeletePropertyById2(cx
, expando
, id
, bp
);
230 BaseDOMProxyHandler::enumerate(JSContext
* cx
, JS::Handle
<JSObject
*> proxy
,
233 JS::Rooted
<JSObject
*> proto(cx
);
234 if (!JS_GetPrototype(cx
, proxy
, &proto
)) {
237 return getOwnPropertyNames(cx
, proxy
, props
) &&
238 (!proto
|| js::GetPropertyNames(cx
, proto
, 0, &props
));
242 BaseDOMProxyHandler::watch(JSContext
* cx
, JS::Handle
<JSObject
*> proxy
, JS::Handle
<jsid
> id
,
243 JS::Handle
<JSObject
*> callable
)
245 return js::WatchGuts(cx
, proxy
, id
, callable
);
249 BaseDOMProxyHandler::unwatch(JSContext
* cx
, JS::Handle
<JSObject
*> proxy
, JS::Handle
<jsid
> id
)
251 return js::UnwatchGuts(cx
, proxy
, id
);
255 DOMProxyHandler::has(JSContext
* cx
, JS::Handle
<JSObject
*> proxy
, JS::Handle
<jsid
> id
, bool* bp
)
257 if (!hasOwn(cx
, proxy
, id
, bp
)) {
262 // We have the property ourselves; no need to worry about our prototype
267 // OK, now we have to look at the proto
268 JS::Rooted
<JSObject
*> proto(cx
);
269 if (!js::GetObjectProto(cx
, proxy
, &proto
)) {
276 bool ok
= JS_HasPropertyById(cx
, proto
, id
, &protoHasProp
);
284 IdToInt32(JSContext
* cx
, JS::Handle
<jsid
> id
)
286 JS::Rooted
<JS::Value
> idval(cx
);
289 if (!::JS_IdToValue(cx
, id
, idval
.address()) ||
290 !JS::ToNumber(cx
, idval
, &array_index
) ||
291 !::JS_DoubleIsInt32(array_index
, &i
)) {
299 } // namespace mozilla