Merge m-c to fx-team.
[gecko.git] / dom / bindings / DOMJSProxyHandler.cpp
blobca4189f069d59c76e0611c7a263c72153af5f89c
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"
19 #include "jsapi.h"
21 using namespace JS;
23 namespace mozilla {
24 namespace dom {
26 jsid s_length_id = JSID_VOID;
28 bool
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);
41 if (v.isObject()) {
42 bool hasOwn;
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;
53 bool hasOwn;
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;
71 // static
72 JSObject*
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()) {
78 return nullptr;
81 if (v.isObject()) {
82 js::SetProxyExtra(obj, JSPROXYSLOT_EXPANDO, UndefinedValue());
83 XPCWrappedNativeScope* scope = xpc::MaybeGetObjectScope(obj);
84 if (scope) {
85 scope->RemoveDOMExpandoObject(obj);
87 } else {
88 js::ExpandoAndGeneration* expandoAndGeneration =
89 static_cast<js::ExpandoAndGeneration*>(v.toPrivate());
90 v = expandoAndGeneration->expando;
91 if (v.isUndefined()) {
92 return nullptr;
94 expandoAndGeneration->expando = UndefinedValue();
98 return &v.toObject();
101 // static
102 JSObject*
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);
107 if (v.isObject()) {
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();
117 } else {
118 expandoAndGeneration = nullptr;
121 JS::Rooted<JSObject*> expando(cx,
122 JS_NewObjectWithGivenProto(cx, nullptr, nullptr, js::GetObjectParent(obj)));
123 if (!expando) {
124 return nullptr;
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);
134 return expando;
137 XPCWrappedNativeScope* scope = xpc::GetObjectScope(obj);
138 if (!scope->RegisterDOMExpandoObject(obj)) {
139 return nullptr;
142 cache->SetPreservingWrapper(true);
143 js::SetProxyExtra(obj, JSPROXYSLOT_EXPANDO, ObjectValue(*expando));
145 return expando;
148 bool
149 DOMProxyHandler::isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy, bool *extensible)
151 // always extensible per WebIDL
152 *extensible = true;
153 return true;
156 bool
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);
162 return false;
165 bool
166 BaseDOMProxyHandler::getPropertyDescriptor(JSContext* cx,
167 JS::Handle<JSObject*> proxy,
168 JS::Handle<jsid> id,
169 MutableHandle<JSPropertyDescriptor> desc,
170 unsigned flags)
172 if (!getOwnPropertyDescriptor(cx, proxy, id, desc, flags)) {
173 return false;
175 if (desc.object()) {
176 return true;
179 JS::Rooted<JSObject*> proto(cx);
180 if (!js::GetObjectProto(cx, proxy, &proto)) {
181 return false;
183 if (!proto) {
184 desc.object().set(nullptr);
185 return true;
188 return JS_GetPropertyDescriptorById(cx, proto, id, 0, desc);
191 bool
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,
200 JSMSG_GETTER_ONLY);
203 if (xpc::WrapperFactory::IsXrayWrapper(proxy)) {
204 return true;
207 JSObject* expando = EnsureExpandoObject(cx, proxy);
208 if (!expando) {
209 return false;
212 bool dummy;
213 return js_DefineOwnProperty(cx, expando, id, desc, &dummy);
216 bool
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);
225 *bp = true;
226 return true;
229 bool
230 BaseDOMProxyHandler::enumerate(JSContext* cx, JS::Handle<JSObject*> proxy,
231 AutoIdVector& props)
233 JS::Rooted<JSObject*> proto(cx);
234 if (!JS_GetPrototype(cx, proxy, &proto)) {
235 return false;
237 return getOwnPropertyNames(cx, proxy, props) &&
238 (!proto || js::GetPropertyNames(cx, proto, 0, &props));
241 bool
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);
248 bool
249 BaseDOMProxyHandler::unwatch(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id)
251 return js::UnwatchGuts(cx, proxy, id);
254 bool
255 DOMProxyHandler::has(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id, bool* bp)
257 if (!hasOwn(cx, proxy, id, bp)) {
258 return false;
261 if (*bp) {
262 // We have the property ourselves; no need to worry about our prototype
263 // chain.
264 return true;
267 // OK, now we have to look at the proto
268 JS::Rooted<JSObject*> proto(cx);
269 if (!js::GetObjectProto(cx, proxy, &proto)) {
270 return false;
272 if (!proto) {
273 return true;
275 bool protoHasProp;
276 bool ok = JS_HasPropertyById(cx, proto, id, &protoHasProp);
277 if (ok) {
278 *bp = protoHasProp;
280 return ok;
283 int32_t
284 IdToInt32(JSContext* cx, JS::Handle<jsid> id)
286 JS::Rooted<JS::Value> idval(cx);
287 double array_index;
288 int32_t i;
289 if (!::JS_IdToValue(cx, id, idval.address()) ||
290 !JS::ToNumber(cx, idval, &array_index) ||
291 !::JS_DoubleIsInt32(array_index, &i)) {
292 return -1;
295 return i;
298 } // namespace dom
299 } // namespace mozilla