dom/bindings/DOMJSProxyHandler.cpp

branch
TOR_BUG_9701
changeset 8
97036ab72558
equal deleted inserted replaced
-1:000000000000 0:e49be3c79b88
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/. */
6
7 #include "mozilla/dom/DOMJSProxyHandler.h"
8 #include "xpcpublic.h"
9 #include "xpcprivate.h"
10 #include "XPCQuickStubs.h"
11 #include "XPCWrapper.h"
12 #include "WrapperFactory.h"
13 #include "nsDOMClassInfo.h"
14 #include "nsWrapperCacheInlines.h"
15 #include "mozilla/dom/BindingUtils.h"
16
17 #include "jsapi.h"
18
19 using namespace JS;
20
21 namespace mozilla {
22 namespace dom {
23
24 jsid s_length_id = JSID_VOID;
25
26 bool
27 DefineStaticJSVals(JSContext* cx)
28 {
29 return InternJSString(cx, s_length_id, "length");
30 }
31
32
33 const char HandlerFamily = 0;
34
35 js::DOMProxyShadowsResult
36 DOMProxyShadows(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id)
37 {
38 JS::Value v = js::GetProxyExtra(proxy, JSPROXYSLOT_EXPANDO);
39 if (v.isObject()) {
40 bool hasOwn;
41 Rooted<JSObject*> object(cx, &v.toObject());
42 if (!JS_AlreadyHasOwnPropertyById(cx, object, id, &hasOwn))
43 return js::ShadowCheckFailed;
44
45 return hasOwn ? js::Shadows : js::DoesntShadow;
46 }
47
48 if (v.isUndefined()) {
49 return js::DoesntShadow;
50 }
51
52 bool hasOwn;
53 if (!GetProxyHandler(proxy)->hasOwn(cx, proxy, id, &hasOwn))
54 return js::ShadowCheckFailed;
55
56 return hasOwn ? js::Shadows : js::DoesntShadowUnique;
57 }
58
59 // Store the information for the specialized ICs.
60 struct SetDOMProxyInformation
61 {
62 SetDOMProxyInformation() {
63 js::SetDOMProxyInformation((const void*) &HandlerFamily,
64 js::PROXY_EXTRA_SLOT + JSPROXYSLOT_EXPANDO, DOMProxyShadows);
65 }
66 };
67
68 SetDOMProxyInformation gSetDOMProxyInformation;
69
70 // static
71 JSObject*
72 DOMProxyHandler::GetAndClearExpandoObject(JSObject* obj)
73 {
74 MOZ_ASSERT(IsDOMProxy(obj), "expected a DOM proxy object");
75 JS::Value v = js::GetProxyExtra(obj, JSPROXYSLOT_EXPANDO);
76 if (v.isUndefined()) {
77 return nullptr;
78 }
79
80 if (v.isObject()) {
81 js::SetProxyExtra(obj, JSPROXYSLOT_EXPANDO, UndefinedValue());
82 XPCWrappedNativeScope* scope = xpc::MaybeGetObjectScope(obj);
83 if (scope) {
84 scope->RemoveDOMExpandoObject(obj);
85 }
86 } else {
87 js::ExpandoAndGeneration* expandoAndGeneration =
88 static_cast<js::ExpandoAndGeneration*>(v.toPrivate());
89 v = expandoAndGeneration->expando;
90 if (v.isUndefined()) {
91 return nullptr;
92 }
93 expandoAndGeneration->expando = UndefinedValue();
94 }
95
96
97 return &v.toObject();
98 }
99
100 // static
101 JSObject*
102 DOMProxyHandler::EnsureExpandoObject(JSContext* cx, JS::Handle<JSObject*> obj)
103 {
104 NS_ASSERTION(IsDOMProxy(obj), "expected a DOM proxy object");
105 JS::Value v = js::GetProxyExtra(obj, JSPROXYSLOT_EXPANDO);
106 if (v.isObject()) {
107 return &v.toObject();
108 }
109
110 js::ExpandoAndGeneration* expandoAndGeneration;
111 if (!v.isUndefined()) {
112 expandoAndGeneration = static_cast<js::ExpandoAndGeneration*>(v.toPrivate());
113 if (expandoAndGeneration->expando.isObject()) {
114 return &expandoAndGeneration->expando.toObject();
115 }
116 } else {
117 expandoAndGeneration = nullptr;
118 }
119
120 JS::Rooted<JSObject*> parent(cx, js::GetObjectParent(obj));
121 JS::Rooted<JSObject*> expando(cx,
122 JS_NewObjectWithGivenProto(cx, nullptr, JS::NullPtr(), parent));
123 if (!expando) {
124 return nullptr;
125 }
126
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);
133
134 return expando;
135 }
136
137 XPCWrappedNativeScope* scope = xpc::GetObjectScope(obj);
138 if (!scope->RegisterDOMExpandoObject(obj)) {
139 return nullptr;
140 }
141
142 cache->SetPreservingWrapper(true);
143 js::SetProxyExtra(obj, JSPROXYSLOT_EXPANDO, ObjectValue(*expando));
144
145 return expando;
146 }
147
148 bool
149 DOMProxyHandler::isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy, bool *extensible)
150 {
151 // always extensible per WebIDL
152 *extensible = true;
153 return true;
154 }
155
156 bool
157 DOMProxyHandler::preventExtensions(JSContext *cx, JS::Handle<JSObject*> proxy)
158 {
159 // Throw a TypeError, per WebIDL.
160 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
161 JSMSG_CANT_CHANGE_EXTENSIBILITY);
162 return false;
163 }
164
165 bool
166 BaseDOMProxyHandler::getPropertyDescriptor(JSContext* cx,
167 JS::Handle<JSObject*> proxy,
168 JS::Handle<jsid> id,
169 MutableHandle<JSPropertyDescriptor> desc)
170 {
171 if (!getOwnPropertyDescriptor(cx, proxy, id, desc)) {
172 return false;
173 }
174 if (desc.object()) {
175 return true;
176 }
177
178 JS::Rooted<JSObject*> proto(cx);
179 if (!js::GetObjectProto(cx, proxy, &proto)) {
180 return false;
181 }
182 if (!proto) {
183 desc.object().set(nullptr);
184 return true;
185 }
186
187 return JS_GetPropertyDescriptorById(cx, proto, id, desc);
188 }
189
190 bool
191 BaseDOMProxyHandler::getOwnPropertyDescriptor(JSContext* cx,
192 JS::Handle<JSObject*> proxy,
193 JS::Handle<jsid> id,
194 MutableHandle<JSPropertyDescriptor> desc)
195 {
196 return getOwnPropDescriptor(cx, proxy, id, /* ignoreNamedProps = */ false,
197 desc);
198 }
199
200 bool
201 DOMProxyHandler::defineProperty(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
202 MutableHandle<JSPropertyDescriptor> desc, bool* defined)
203 {
204 if (desc.hasGetterObject() && desc.setter() == JS_StrictPropertyStub) {
205 return JS_ReportErrorFlagsAndNumber(cx,
206 JSREPORT_WARNING | JSREPORT_STRICT |
207 JSREPORT_STRICT_MODE_ERROR,
208 js_GetErrorMessage, nullptr,
209 JSMSG_GETTER_ONLY);
210 }
211
212 if (xpc::WrapperFactory::IsXrayWrapper(proxy)) {
213 return true;
214 }
215
216 JSObject* expando = EnsureExpandoObject(cx, proxy);
217 if (!expando) {
218 return false;
219 }
220
221 bool dummy;
222 return js_DefineOwnProperty(cx, expando, id, desc, &dummy);
223 }
224
225 bool
226 DOMProxyHandler::set(JSContext *cx, Handle<JSObject*> proxy, Handle<JSObject*> receiver,
227 Handle<jsid> id, bool strict, MutableHandle<JS::Value> vp)
228 {
229 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
230 "Should not have a XrayWrapper here");
231 bool done;
232 if (!setCustom(cx, proxy, id, vp, &done)) {
233 return false;
234 }
235 if (done) {
236 return true;
237 }
238
239 // Make sure to ignore our named properties when checking for own
240 // property descriptors for a set.
241 JS::Rooted<JSPropertyDescriptor> desc(cx);
242 if (!getOwnPropDescriptor(cx, proxy, id, /* ignoreNamedProps = */ true,
243 &desc)) {
244 return false;
245 }
246 bool descIsOwn = desc.object() != nullptr;
247 if (!desc.object()) {
248 // Don't just use getPropertyDescriptor, unlike BaseProxyHandler::set,
249 // because that would call getOwnPropertyDescriptor on ourselves. Instead,
250 // directly delegate to the proto, if any.
251 JS::Rooted<JSObject*> proto(cx);
252 if (!js::GetObjectProto(cx, proxy, &proto)) {
253 return false;
254 }
255 if (proto && !JS_GetPropertyDescriptorById(cx, proto, id, &desc)) {
256 return false;
257 }
258 }
259
260 return js::SetPropertyIgnoringNamedGetter(cx, this, proxy, receiver, id,
261 &desc, descIsOwn, strict, vp);
262 }
263
264 bool
265 DOMProxyHandler::delete_(JSContext* cx, JS::Handle<JSObject*> proxy,
266 JS::Handle<jsid> id, bool* bp)
267 {
268 JS::Rooted<JSObject*> expando(cx);
269 if (!xpc::WrapperFactory::IsXrayWrapper(proxy) && (expando = GetExpandoObject(proxy))) {
270 return JS_DeletePropertyById2(cx, expando, id, bp);
271 }
272
273 *bp = true;
274 return true;
275 }
276
277 bool
278 BaseDOMProxyHandler::enumerate(JSContext* cx, JS::Handle<JSObject*> proxy,
279 AutoIdVector& props)
280 {
281 JS::Rooted<JSObject*> proto(cx);
282 if (!JS_GetPrototype(cx, proxy, &proto)) {
283 return false;
284 }
285 return keys(cx, proxy, props) &&
286 (!proto || js::GetPropertyNames(cx, proto, 0, &props));
287 }
288
289 bool
290 BaseDOMProxyHandler::watch(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
291 JS::Handle<JSObject*> callable)
292 {
293 return js::WatchGuts(cx, proxy, id, callable);
294 }
295
296 bool
297 BaseDOMProxyHandler::unwatch(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id)
298 {
299 return js::UnwatchGuts(cx, proxy, id);
300 }
301
302 bool
303 BaseDOMProxyHandler::getOwnPropertyNames(JSContext* cx,
304 JS::Handle<JSObject*> proxy,
305 JS::AutoIdVector& props)
306 {
307 return ownPropNames(cx, proxy, JSITER_OWNONLY | JSITER_HIDDEN, props);
308 }
309
310 bool
311 BaseDOMProxyHandler::keys(JSContext* cx,
312 JS::Handle<JSObject*> proxy,
313 JS::AutoIdVector& props)
314 {
315 return ownPropNames(cx, proxy, JSITER_OWNONLY, props);
316 }
317
318 bool
319 DOMProxyHandler::has(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id, bool* bp)
320 {
321 if (!hasOwn(cx, proxy, id, bp)) {
322 return false;
323 }
324
325 if (*bp) {
326 // We have the property ourselves; no need to worry about our prototype
327 // chain.
328 return true;
329 }
330
331 // OK, now we have to look at the proto
332 JS::Rooted<JSObject*> proto(cx);
333 if (!js::GetObjectProto(cx, proxy, &proto)) {
334 return false;
335 }
336 if (!proto) {
337 return true;
338 }
339 bool protoHasProp;
340 bool ok = JS_HasPropertyById(cx, proto, id, &protoHasProp);
341 if (ok) {
342 *bp = protoHasProp;
343 }
344 return ok;
345 }
346
347 int32_t
348 IdToInt32(JSContext* cx, JS::Handle<jsid> id)
349 {
350 JS::Rooted<JS::Value> idval(cx);
351 double array_index;
352 int32_t i;
353 if (!::JS_IdToValue(cx, id, &idval) ||
354 !JS::ToNumber(cx, idval, &array_index) ||
355 !::JS_DoubleIsInt32(array_index, &i)) {
356 return -1;
357 }
358
359 return i;
360 }
361
362 bool
363 DOMProxyHandler::setCustom(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
364 JS::MutableHandle<JS::Value> vp, bool *done)
365 {
366 *done = false;
367 return true;
368 }
369
370 } // namespace dom
371 } // namespace mozilla

mercurial