Bumping manifests a=b2g-bump
[gecko.git] / dom / bindings / DOMJSProxyHandler.cpp
blob0b9444e0f2a3b6ed997228cc2dbc72e5d4cb2b1d
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"
8 #include "xpcpublic.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"
16 #include "jsapi.h"
18 using namespace JS;
20 namespace mozilla {
21 namespace dom {
23 jsid s_length_id = JSID_VOID;
25 bool
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);
37 if (v.isObject()) {
38 bool hasOwn;
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;
50 bool hasOwn;
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;
68 // static
69 JSObject*
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()) {
75 return nullptr;
78 if (v.isObject()) {
79 js::SetProxyExtra(obj, JSPROXYSLOT_EXPANDO, UndefinedValue());
80 xpc::ObjectScope(obj)->RemoveDOMExpandoObject(obj);
81 } else {
82 js::ExpandoAndGeneration* expandoAndGeneration =
83 static_cast<js::ExpandoAndGeneration*>(v.toPrivate());
84 v = expandoAndGeneration->expando;
85 if (v.isUndefined()) {
86 return nullptr;
88 expandoAndGeneration->expando = UndefinedValue();
92 return &v.toObject();
95 // static
96 JSObject*
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);
101 if (v.isObject()) {
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();
111 } else {
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));
118 if (!expando) {
119 return nullptr;
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);
129 return expando;
132 if (!xpc::ObjectScope(obj)->RegisterDOMExpandoObject(obj)) {
133 return nullptr;
136 cache->SetPreservingWrapper(true);
137 js::SetProxyExtra(obj, JSPROXYSLOT_EXPANDO, ObjectValue(*expando));
139 return expando;
142 bool
143 DOMProxyHandler::preventExtensions(JSContext *cx, JS::Handle<JSObject*> proxy,
144 bool *succeeded) const
146 // always extensible per WebIDL
147 *succeeded = false;
148 return true;
151 bool
152 DOMProxyHandler::isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy, bool *extensible) const
154 *extensible = true;
155 return true;
158 bool
159 BaseDOMProxyHandler::getPropertyDescriptor(JSContext* cx,
160 JS::Handle<JSObject*> proxy,
161 JS::Handle<jsid> id,
162 MutableHandle<JSPropertyDescriptor> desc) const
164 if (!getOwnPropertyDescriptor(cx, proxy, id, desc)) {
165 return false;
167 if (desc.object()) {
168 return true;
171 JS::Rooted<JSObject*> proto(cx);
172 if (!js::GetObjectProto(cx, proxy, &proto)) {
173 return false;
175 if (!proto) {
176 desc.object().set(nullptr);
177 return true;
180 return JS_GetPropertyDescriptorById(cx, proto, id, desc);
183 bool
184 BaseDOMProxyHandler::getOwnPropertyDescriptor(JSContext* cx,
185 JS::Handle<JSObject*> proxy,
186 JS::Handle<jsid> id,
187 MutableHandle<JSPropertyDescriptor> desc) const
189 return getOwnPropDescriptor(cx, proxy, id, /* ignoreNamedProps = */ false,
190 desc);
193 bool
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,
202 JSMSG_GETTER_ONLY);
205 if (xpc::WrapperFactory::IsXrayWrapper(proxy)) {
206 return true;
209 JSObject* expando = EnsureExpandoObject(cx, proxy);
210 if (!expando) {
211 return false;
214 bool dummy;
215 return js_DefineOwnProperty(cx, expando, id, desc, &dummy);
218 bool
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");
224 bool done;
225 if (!setCustom(cx, proxy, id, vp, &done)) {
226 return false;
228 if (done) {
229 return true;
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,
236 &desc)) {
237 return false;
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)) {
246 return false;
248 if (proto && !JS_GetPropertyDescriptorById(cx, proto, id, &desc)) {
249 return false;
253 return js::SetPropertyIgnoringNamedGetter(cx, this, proxy, receiver, id,
254 &desc, descIsOwn, strict, vp);
257 bool
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);
266 *bp = true;
267 return true;
270 bool
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);
277 bool
278 BaseDOMProxyHandler::unwatch(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id) const
280 return js::UnwatchGuts(cx, proxy, id);
283 bool
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);
291 bool
292 BaseDOMProxyHandler::getOwnEnumerablePropertyKeys(JSContext* cx,
293 JS::Handle<JSObject*> proxy,
294 JS::AutoIdVector& props) const
296 return ownPropNames(cx, proxy, JSITER_OWNONLY, props);
299 bool
300 BaseDOMProxyHandler::enumerate(JSContext *cx, JS::Handle<JSObject*> proxy,
301 JS::MutableHandle<JSObject*> objp) const
303 return BaseProxyHandler::enumerate(cx, proxy, objp);
306 bool
307 DOMProxyHandler::has(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id, bool* bp) const
309 if (!hasOwn(cx, proxy, id, bp)) {
310 return false;
313 if (*bp) {
314 // We have the property ourselves; no need to worry about our prototype
315 // chain.
316 return true;
319 // OK, now we have to look at the proto
320 JS::Rooted<JSObject*> proto(cx);
321 if (!js::GetObjectProto(cx, proxy, &proto)) {
322 return false;
324 if (!proto) {
325 return true;
327 bool protoHasProp;
328 bool ok = JS_HasPropertyById(cx, proto, id, &protoHasProp);
329 if (ok) {
330 *bp = protoHasProp;
332 return ok;
335 int32_t
336 IdToInt32(JSContext* cx, JS::Handle<jsid> id)
338 JS::Rooted<JS::Value> idval(cx);
339 double array_index;
340 int32_t i;
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)) {
345 return -1;
348 return i;
351 bool
352 DOMProxyHandler::setCustom(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
353 JS::MutableHandle<JS::Value> vp, bool *done) const
355 *done = false;
356 return true;
359 //static
360 JSObject *
361 DOMProxyHandler::GetExpandoObject(JSObject *obj)
363 MOZ_ASSERT(IsDOMProxy(obj), "expected a DOM proxy object");
364 JS::Value v = js::GetProxyExtra(obj, JSPROXYSLOT_EXPANDO);
365 if (v.isObject()) {
366 return &v.toObject();
369 if (v.isUndefined()) {
370 return nullptr;
373 js::ExpandoAndGeneration* expandoAndGeneration =
374 static_cast<js::ExpandoAndGeneration*>(v.toPrivate());
375 v = expandoAndGeneration->expando;
376 return v.isUndefined() ? nullptr : &v.toObject();
379 } // namespace dom
380 } // namespace mozilla