1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=4 sw=4 et tw=99 ft=cpp:
3 : *
4 : * ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
7 : * The contents of this file are subject to the Mozilla Public License Version
8 : * 1.1 (the "License"); you may not use this file except in compliance with
9 : * the License. You may obtain a copy of the License at
10 : * http://www.mozilla.org/MPL/
11 : *
12 : * Software distributed under the License is distributed on an "AS IS" basis,
13 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 : * for the specific language governing rights and limitations under the
15 : * License.
16 : *
17 : * The Original Code is mozilla.org code, released
18 : * June 24, 2010.
19 : *
20 : * The Initial Developer of the Original Code is
21 : * The Mozilla Foundation
22 : *
23 : * Contributor(s):
24 : * Andreas Gal <gal@mozilla.com>
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either of the GNU General Public License Version 2 or later (the "GPL"),
28 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #include "mozilla/Util.h"
41 :
42 : #include "dombindings.h"
43 : #include "xpcpublic.h"
44 : #include "xpcprivate.h"
45 : #include "XPCQuickStubs.h"
46 : #include "XPCWrapper.h"
47 : #include "WrapperFactory.h"
48 : #include "nsDOMClassInfo.h"
49 : #include "nsGlobalWindow.h"
50 : #include "nsWrapperCacheInlines.h"
51 :
52 : #include "jsapi.h"
53 : #include "jsatom.h"
54 :
55 : using namespace JS;
56 :
57 : namespace mozilla {
58 : namespace dom {
59 : namespace binding {
60 :
61 :
62 1464 : static jsid s_prototype_id = JSID_VOID;
63 :
64 1464 : static jsid s_length_id = JSID_VOID;
65 :
66 1464 : static jsid s_VOID_id = JSID_VOID;
67 :
68 : bool
69 32292 : DefineStaticJSVal(JSContext *cx, jsid &id, const char *string)
70 : {
71 32292 : if (JSString *str = ::JS_InternString(cx, string)) {
72 32292 : id = INTERNED_STRING_TO_JSID(cx, str);
73 32292 : return true;
74 : }
75 0 : return false;
76 : }
77 :
78 : #define SET_JSID_TO_STRING(_cx, _string) \
79 : DefineStaticJSVal(_cx, s_##_string##_id, #_string)
80 :
81 : bool
82 1404 : DefineStaticJSVals(JSContext *cx)
83 : {
84 2808 : JSAutoRequest ar(cx);
85 :
86 1404 : return SET_JSID_TO_STRING(cx, prototype) &&
87 1404 : SET_JSID_TO_STRING(cx, length) &&
88 2808 : DefinePropertyStaticJSVals(cx);
89 : }
90 :
91 :
92 : int HandlerFamily;
93 :
94 :
95 : JSBool
96 0 : Throw(JSContext *cx, nsresult rv)
97 : {
98 0 : XPCThrower::Throw(rv, cx);
99 0 : return false;
100 : }
101 :
102 :
103 : // Only set allowNativeWrapper to false if you really know you need it, if in
104 : // doubt use true. Setting it to false disables security wrappers.
105 : static bool
106 11532 : XPCOMObjectToJsval(JSContext *cx, JSObject *scope, xpcObjectHelper &helper,
107 : bool allowNativeWrapper, jsval *rval)
108 : {
109 23064 : XPCLazyCallContext lccx(JS_CALLER, cx, scope);
110 :
111 : nsresult rv;
112 11532 : if (!XPCConvert::NativeInterface2JSObject(lccx, rval, NULL, helper, NULL, NULL,
113 11532 : allowNativeWrapper, &rv)) {
114 : // I can't tell if NativeInterface2JSObject throws JS exceptions
115 : // or not. This is a sloppy stab at the right semantics; the
116 : // method really ought to be fixed to behave consistently.
117 0 : if (!JS_IsExceptionPending(cx))
118 0 : Throw(cx, NS_FAILED(rv) ? rv : NS_ERROR_UNEXPECTED);
119 0 : return false;
120 : }
121 :
122 : #ifdef DEBUG
123 11532 : JSObject* jsobj = JSVAL_TO_OBJECT(*rval);
124 11532 : if (jsobj && !js::GetObjectParent(jsobj))
125 0 : NS_ASSERTION(js::GetObjectClass(jsobj)->flags & JSCLASS_IS_GLOBAL,
126 : "Why did we recreate this wrapper?");
127 : #endif
128 :
129 11532 : return true;
130 : }
131 :
132 : template<class T>
133 : static inline JSObject*
134 4321 : WrapNativeParent(JSContext *cx, JSObject *scope, T *p)
135 : {
136 4321 : if (!p)
137 0 : return NULL;
138 :
139 4321 : nsWrapperCache *cache = GetWrapperCache(p);
140 : JSObject* obj;
141 4321 : if (cache && (obj = cache->GetWrapper())) {
142 : #ifdef DEBUG
143 8642 : qsObjectHelper helper(p, cache);
144 : jsval debugVal;
145 :
146 4321 : bool ok = XPCOMObjectToJsval(cx, scope, helper, false, &debugVal);
147 4321 : NS_ASSERTION(ok && JSVAL_TO_OBJECT(debugVal) == obj,
148 : "Unexpected object in nsWrapperCache");
149 : #endif
150 4321 : return obj;
151 : }
152 :
153 0 : qsObjectHelper helper(p, cache);
154 : jsval v;
155 0 : return XPCOMObjectToJsval(cx, scope, helper, false, &v) ? JSVAL_TO_OBJECT(v) : NULL;
156 : }
157 :
158 : template<class T>
159 : static bool
160 7638 : Wrap(JSContext *cx, JSObject *scope, T *p, nsWrapperCache *cache, jsval *vp)
161 : {
162 7638 : if (xpc_FastGetCachedWrapper(cache, scope, vp))
163 427 : return true;
164 14422 : qsObjectHelper helper(p, cache);
165 7211 : return XPCOMObjectToJsval(cx, scope, helper, true, vp);
166 : }
167 :
168 : template<class T>
169 : static inline bool
170 7638 : Wrap(JSContext *cx, JSObject *scope, T *p, jsval *vp)
171 : {
172 7638 : return Wrap(cx, scope, p, GetWrapperCache(p), vp);
173 : }
174 :
175 : template<>
176 : inline bool
177 : Wrap(JSContext *cx, JSObject *scope, NoType *p, jsval *vp)
178 : {
179 : NS_RUNTIMEABORT("We try to wrap the result from calling a noop?");
180 : return false;
181 : }
182 :
183 : template<class T>
184 : inline bool
185 0 : Wrap(JSContext *cx, JSObject *scope, nsCOMPtr<T> &p, jsval *vp)
186 : {
187 0 : return Wrap(cx, scope, p.get(), vp);
188 : }
189 :
190 : static inline bool
191 0 : Wrap(JSContext *cx, JSObject *scope, nsISupportsResult &result, jsval *vp)
192 : {
193 0 : return Wrap(cx, scope, result.mResult, result.mCache, vp);
194 : }
195 :
196 : static inline bool
197 0 : Wrap(JSContext *cx, JSObject *scope, nsString &result, jsval *vp)
198 : {
199 0 : return xpc::StringToJsval(cx, result, vp);
200 : }
201 :
202 : template<class T>
203 : bool
204 0 : Unwrap(JSContext *cx, jsval v, T **ppArg, nsISupports **ppArgRef, jsval *vp)
205 : {
206 0 : nsresult rv = xpc_qsUnwrapArg(cx, v, ppArg, ppArgRef, vp);
207 0 : if (NS_FAILED(rv))
208 0 : return Throw(cx, rv);
209 0 : return true;
210 : }
211 :
212 : template<>
213 : bool
214 0 : Unwrap(JSContext *cx, jsval v, NoType **ppArg, nsISupports **ppArgRef, jsval *vp)
215 : {
216 0 : NS_RUNTIMEABORT("We try to unwrap an argument for a noop?");
217 0 : return false;
218 : }
219 :
220 :
221 : // Because we use proxies for wrapping DOM list objects we don't get the benefits of the property
222 : // cache. To improve performance when using a property that lives on the prototype chain we
223 : // implemented a cheap caching mechanism. Every DOM list proxy object stores a pointer to a shape
224 : // in an extra slot. The first time we access a property on the object that lives on the prototype
225 : // we check if all the DOM properties on the prototype chain are the real DOM properties and in
226 : // that case we store a pointer to the shape of the object's prototype in the extra slot. From
227 : // then on, every time we access a DOM property that lives on the prototype we check that the
228 : // shape of the prototype is still identical to the cached shape and we do a fast lookup of the
229 : // property. If the shape has changed, we recheck all the DOM properties on the prototype chain
230 : // and we update the shape pointer if they are still the real DOM properties. This mechanism
231 : // covers addition/removal of properties, changes in getters/setters, changes in the prototype
232 : // chain, ... It does not cover changes in the values of the properties. For those we store an
233 : // enum value in a reserved slot in every DOM prototype object. The value starts off as USE_CACHE.
234 : // If a property of a DOM prototype object is set to a different value, we set the value to
235 : // CHECK_CACHE. The next time we try to access the value of a property on that DOM prototype
236 : // object we check if all the DOM properties on that DOM prototype object still match the real DOM
237 : // properties. If they do we set the value to USE_CACHE again, if they're not we set the value to
238 : // DONT_USE_CACHE. If the value is USE_CACHE we do the fast lookup.
239 :
240 : template<class LC>
241 : typename ListBase<LC>::Properties ListBase<LC>::sProtoProperties[] = {
242 : { s_VOID_id, NULL, NULL }
243 : };
244 : template<class LC>
245 : size_t ListBase<LC>::sProtoPropertiesCount = 0;
246 :
247 : template<class LC>
248 : typename ListBase<LC>::Methods ListBase<LC>::sProtoMethods[] = {
249 : { s_VOID_id, NULL, 0 }
250 : };
251 : template<class LC>
252 : size_t ListBase<LC>::sProtoMethodsCount = 0;
253 :
254 : template<class LC>
255 19032 : ListBase<LC> ListBase<LC>::instance;
256 :
257 : bool
258 0 : DefineConstructor(JSContext *cx, JSObject *obj, DefineInterface aDefine, nsresult *aResult)
259 : {
260 : bool enabled;
261 0 : bool defined = !!aDefine(cx, XPCWrappedNativeScope::FindInJSObjectScope(cx, obj), &enabled);
262 0 : NS_ASSERTION(!defined || enabled,
263 : "We defined a constructor but the new bindings are disabled?");
264 0 : *aResult = defined ? NS_OK : NS_ERROR_FAILURE;
265 0 : return enabled;
266 : }
267 :
268 : template<class LC>
269 : typename ListBase<LC>::ListType*
270 11835 : ListBase<LC>::getNative(JSObject *obj)
271 : {
272 11835 : return static_cast<ListType*>(js::GetProxyPrivate(obj).toPrivate());
273 : }
274 :
275 : template<class LC>
276 : typename ListBase<LC>::ListType*
277 20938 : ListBase<LC>::getListObject(JSObject *obj)
278 : {
279 20938 : if (xpc::WrapperFactory::IsXrayWrapper(obj))
280 0 : obj = js::UnwrapObject(obj);
281 20938 : JS_ASSERT(objIsList(obj));
282 20938 : return getNative(obj);
283 : }
284 :
285 : template<class LC>
286 : js::Shape *
287 12691 : ListBase<LC>::getProtoShape(JSObject *obj)
288 : {
289 12691 : JS_ASSERT(objIsList(obj));
290 12691 : return (js::Shape *) js::GetProxyExtra(obj, JSPROXYSLOT_PROTOSHAPE).toPrivate();
291 : }
292 :
293 : template<class LC>
294 : void
295 8636 : ListBase<LC>::setProtoShape(JSObject *obj, js::Shape *shape)
296 : {
297 8636 : JS_ASSERT(objIsList(obj));
298 8636 : js::SetProxyExtra(obj, JSPROXYSLOT_PROTOSHAPE, PrivateValue(shape));
299 8636 : }
300 :
301 : static JSBool
302 0 : UnwrapSecurityWrapper(JSContext *cx, JSObject *obj, JSObject *callee, JSObject **unwrapped)
303 : {
304 0 : JS_ASSERT(XPCWrapper::IsSecurityWrapper(obj));
305 :
306 0 : if (callee && JS_GetGlobalForObject(cx, obj) == JS_GetGlobalForObject(cx, callee)) {
307 0 : *unwrapped = js::UnwrapObject(obj);
308 : } else {
309 0 : *unwrapped = XPCWrapper::Unwrap(cx, obj);
310 0 : if (!*unwrapped)
311 0 : return Throw(cx, NS_ERROR_XPC_SECURITY_MANAGER_VETO);
312 : }
313 0 : return true;
314 : }
315 :
316 : template<class LC>
317 : bool
318 3704 : ListBase<LC>::instanceIsListObject(JSContext *cx, JSObject *obj, JSObject *callee)
319 : {
320 3704 : if (XPCWrapper::IsSecurityWrapper(obj) && !UnwrapSecurityWrapper(cx, obj, callee, &obj))
321 0 : return false;
322 :
323 3704 : if (!objIsList(obj)) {
324 : // FIXME: Throw a proper DOM exception.
325 0 : JS_ReportError(cx, "type error: wrong object");
326 0 : return false;
327 : }
328 3704 : return true;
329 : }
330 :
331 : template<class LC>
332 : JSBool
333 0 : ListBase<LC>::length_getter(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
334 : {
335 0 : if (!instanceIsListObject(cx, obj, NULL))
336 0 : return false;
337 : PRUint32 length;
338 0 : getListObject(obj)->GetLength(&length);
339 0 : JS_ASSERT(int32_t(length) >= 0);
340 0 : *vp = UINT_TO_JSVAL(length);
341 0 : return true;
342 : }
343 :
344 : template<class LC>
345 : bool
346 : ListBase<LC>::getItemAt(ListType *list, uint32_t i, IndexGetterType &item)
347 : {
348 : JS_STATIC_ASSERT(!hasIndexGetter);
349 : return false;
350 : }
351 :
352 : template<class LC>
353 : bool
354 0 : ListBase<LC>::setItemAt(JSContext *cx, ListType *list, uint32_t i, IndexSetterType item)
355 : {
356 : JS_STATIC_ASSERT(!hasIndexSetter);
357 0 : return false;
358 : }
359 :
360 : template<class LC>
361 : bool
362 0 : ListBase<LC>::getNamedItem(ListType *list, const nsAString& aName, NameGetterType &item)
363 : {
364 : JS_STATIC_ASSERT(!hasNameGetter);
365 0 : return false;
366 : }
367 :
368 : template<class LC>
369 : bool
370 0 : ListBase<LC>::setNamedItem(JSContext *cx, ListType *list, const nsAString& aName,
371 : NameSetterType item)
372 : {
373 : JS_STATIC_ASSERT(!hasNameSetter);
374 0 : return false;
375 : }
376 :
377 : template<class LC>
378 : bool
379 0 : ListBase<LC>::namedItem(JSContext *cx, JSObject *obj, jsval *name, NameGetterType &result,
380 : bool *hasResult)
381 : {
382 : xpc_qsDOMString nameString(cx, *name, name,
383 : xpc_qsDOMString::eDefaultNullBehavior,
384 0 : xpc_qsDOMString::eDefaultUndefinedBehavior);
385 0 : if (!nameString.IsValid())
386 0 : return false;
387 0 : *hasResult = getNamedItem(getListObject(obj), nameString, result);
388 0 : return true;
389 : }
390 :
391 : JSBool
392 0 : interface_hasInstance(JSContext *cx, JSObject *obj, const JS::Value *vp, JSBool *bp)
393 : {
394 0 : if (vp->isObject()) {
395 : jsval prototype;
396 0 : if (!JS_GetPropertyById(cx, obj, s_prototype_id, &prototype) ||
397 0 : JSVAL_IS_PRIMITIVE(prototype)) {
398 : JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL,
399 0 : JSMSG_THROW_TYPE_ERROR);
400 0 : return false;
401 : }
402 :
403 0 : JSObject *other = &vp->toObject();
404 0 : if (instanceIsProxy(other)) {
405 0 : ProxyHandler *handler = static_cast<ProxyHandler*>(js::GetProxyHandler(other));
406 0 : if (handler->isInstanceOf(JSVAL_TO_OBJECT(prototype))) {
407 0 : *bp = true;
408 : } else {
409 0 : JSObject *protoObj = JSVAL_TO_OBJECT(prototype);
410 0 : JSObject *proto = other;
411 0 : while ((proto = JS_GetPrototype(proto))) {
412 0 : if (proto == protoObj) {
413 0 : *bp = true;
414 0 : return true;
415 : }
416 : }
417 0 : *bp = false;
418 : }
419 :
420 0 : return true;
421 : }
422 : }
423 :
424 0 : *bp = false;
425 0 : return true;
426 : }
427 :
428 : enum {
429 : USE_CACHE = 0,
430 : CHECK_CACHE = 1,
431 : DONT_USE_CACHE = 2
432 : };
433 :
434 : static JSBool
435 : InvalidateProtoShape_add(JSContext *cx, JSObject *obj, jsid id, jsval *vp);
436 : static JSBool
437 : InvalidateProtoShape_set(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp);
438 :
439 : js::Class sInterfacePrototypeClass = {
440 : "Object",
441 : JSCLASS_HAS_RESERVED_SLOTS(1),
442 : InvalidateProtoShape_add, /* addProperty */
443 : JS_PropertyStub, /* delProperty */
444 : JS_PropertyStub, /* getProperty */
445 : InvalidateProtoShape_set, /* setProperty */
446 : JS_EnumerateStub,
447 : JS_ResolveStub,
448 : JS_ConvertStub
449 : };
450 :
451 : static JSBool
452 852 : InvalidateProtoShape_add(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
453 : {
454 852 : if (JSID_IS_STRING(id) && JS_InstanceOf(cx, obj, Jsvalify(&sInterfacePrototypeClass), NULL))
455 852 : js::SetReservedSlot(obj, 0, PrivateUint32Value(CHECK_CACHE));
456 852 : return JS_TRUE;
457 : }
458 :
459 : static JSBool
460 0 : InvalidateProtoShape_set(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp)
461 : {
462 0 : return InvalidateProtoShape_add(cx, obj, id, vp);
463 : }
464 :
465 : template<class LC>
466 : JSObject *
467 4321 : ListBase<LC>::getPrototype(JSContext *cx, XPCWrappedNativeScope *scope)
468 : {
469 : nsDataHashtable<nsDepCharHashKey, JSObject*> &cache =
470 4321 : scope->GetCachedDOMPrototypes();
471 :
472 : JSObject *interfacePrototype;
473 4321 : if (cache.IsInitialized()) {
474 4062 : if (cache.Get(sInterfaceClass.name, &interfacePrototype)) {
475 4047 : xpc_UnmarkGrayObject(interfacePrototype);
476 4047 : return interfacePrototype;
477 : }
478 259 : } else if (!cache.Init()) {
479 0 : return NULL;
480 : }
481 :
482 274 : JSObject* proto = Base::getPrototype(cx, scope);
483 274 : if (!proto)
484 0 : return NULL;
485 :
486 274 : JSObject *global = scope->GetGlobalJSObject();
487 274 : interfacePrototype = JS_NewObject(cx, Jsvalify(&sInterfacePrototypeClass), proto, global);
488 274 : if (!interfacePrototype)
489 0 : return NULL;
490 :
491 548 : for (size_t n = 0; n < sProtoPropertiesCount; ++n) {
492 274 : JS_ASSERT(sProtoProperties[n].getter);
493 274 : jsid id = sProtoProperties[n].id;
494 274 : unsigned attrs = JSPROP_ENUMERATE | JSPROP_SHARED;
495 274 : if (!sProtoProperties[n].setter)
496 274 : attrs |= JSPROP_READONLY;
497 274 : if (!JS_DefinePropertyById(cx, interfacePrototype, id, JSVAL_VOID,
498 : sProtoProperties[n].getter, sProtoProperties[n].setter, attrs))
499 0 : return NULL;
500 : }
501 :
502 578 : for (size_t n = 0; n < sProtoMethodsCount; ++n) {
503 304 : jsid id = sProtoMethods[n].id;
504 : JSFunction *fun = JS_NewFunctionById(cx, sProtoMethods[n].native, sProtoMethods[n].nargs,
505 304 : 0, js::GetObjectParent(interfacePrototype), id);
506 304 : if (!fun)
507 0 : return NULL;
508 304 : JSObject *funobj = JS_GetFunctionObject(fun);
509 304 : if (!JS_DefinePropertyById(cx, interfacePrototype, id, OBJECT_TO_JSVAL(funobj),
510 : NULL, NULL, JSPROP_ENUMERATE))
511 0 : return NULL;
512 : }
513 :
514 274 : JSObject *interface = JS_NewObject(cx, Jsvalify(&sInterfaceClass), NULL, global);
515 274 : if (!interface)
516 0 : return NULL;
517 :
518 274 : if (!JS_LinkConstructorAndPrototype(cx, interface, interfacePrototype))
519 0 : return NULL;
520 :
521 274 : if (!JS_DefineProperty(cx, global, sInterfaceClass.name, OBJECT_TO_JSVAL(interface), NULL,
522 : NULL, 0))
523 0 : return NULL;
524 :
525 : // This needs to happen after we've set all our own properties on interfacePrototype, to
526 : // overwrite the value set by InvalidateProtoShape_add when we set our own properties.
527 274 : js::SetReservedSlot(interfacePrototype, 0, PrivateUint32Value(USE_CACHE));
528 :
529 274 : if (!cache.Put(sInterfaceClass.name, interfacePrototype))
530 0 : return NULL;
531 :
532 274 : return interfacePrototype;
533 : }
534 :
535 : template<class LC>
536 : JSObject *
537 4321 : ListBase<LC>::create(JSContext *cx, XPCWrappedNativeScope *scope, ListType *aList,
538 : nsWrapperCache* aWrapperCache, bool *triedToWrap)
539 : {
540 4321 : *triedToWrap = true;
541 :
542 4321 : JSObject *parent = WrapNativeParent(cx, scope->GetGlobalJSObject(), aList->GetParentObject());
543 4321 : if (!parent)
544 0 : return NULL;
545 :
546 8642 : JSAutoEnterCompartment ac;
547 4321 : if (js::GetGlobalForObjectCrossCompartment(parent) != scope->GetGlobalJSObject()) {
548 0 : if (!ac.enter(cx, parent))
549 0 : return NULL;
550 :
551 0 : scope = XPCWrappedNativeScope::FindInJSObjectScope(cx, parent);
552 : }
553 :
554 4321 : JSObject *proto = getPrototype(cx, scope, triedToWrap);
555 4321 : if (!proto && !*triedToWrap)
556 0 : aWrapperCache->ClearIsProxy();
557 4321 : if (!proto)
558 0 : return NULL;
559 : JSObject *obj = NewProxyObject(cx, &ListBase<LC>::instance,
560 4321 : PrivateValue(aList), proto, parent);
561 4321 : if (!obj)
562 0 : return NULL;
563 :
564 4321 : NS_ADDREF(aList);
565 4321 : setProtoShape(obj, NULL);
566 :
567 4321 : aWrapperCache->SetWrapper(obj);
568 :
569 4321 : return obj;
570 : }
571 :
572 : static JSObject *
573 12691 : getExpandoObject(JSObject *obj)
574 : {
575 12691 : NS_ASSERTION(instanceIsProxy(obj), "expected a DOM proxy object");
576 12691 : Value v = js::GetProxyExtra(obj, JSPROXYSLOT_EXPANDO);
577 12691 : return v.isUndefined() ? NULL : v.toObjectOrNull();
578 : }
579 :
580 : static int32_t
581 0 : IdToInt32(JSContext *cx, jsid id)
582 : {
583 0 : JSAutoRequest ar(cx);
584 :
585 : jsval idval;
586 : double array_index;
587 : int32_t i;
588 0 : if (!::JS_IdToValue(cx, id, &idval) ||
589 0 : !::JS_ValueToNumber(cx, idval, &array_index) ||
590 0 : !::JS_DoubleIsInt32(array_index, &i)) {
591 0 : return -1;
592 : }
593 :
594 0 : return i;
595 : }
596 :
597 : static inline int32_t
598 15490 : GetArrayIndexFromId(JSContext *cx, jsid id)
599 : {
600 15490 : if (NS_LIKELY(JSID_IS_INT(id)))
601 2799 : return JSID_TO_INT(id);
602 12691 : if (NS_LIKELY(id == s_length_id))
603 8979 : return -1;
604 3712 : if (NS_LIKELY(JSID_IS_ATOM(id))) {
605 3712 : JSAtom *atom = JSID_TO_ATOM(id);
606 3712 : jschar s = *js::GetAtomChars(atom);
607 3712 : if (NS_LIKELY((unsigned)s >= 'a' && (unsigned)s <= 'z'))
608 3712 : return -1;
609 :
610 : uint32_t i;
611 0 : JSLinearString *str = js::AtomToLinearString(JSID_TO_ATOM(id));
612 0 : return js::StringIsArrayIndex(str, &i) ? i : -1;
613 : }
614 0 : return IdToInt32(cx, id);
615 : }
616 :
617 : static void
618 0 : FillPropertyDescriptor(JSPropertyDescriptor *desc, JSObject *obj, jsval v, bool readonly)
619 : {
620 0 : desc->obj = obj;
621 0 : desc->value = v;
622 0 : desc->attrs = (readonly ? JSPROP_READONLY : 0) | JSPROP_ENUMERATE;
623 0 : desc->getter = NULL;
624 0 : desc->setter = NULL;
625 0 : desc->shortid = 0;
626 0 : }
627 :
628 : template<class LC>
629 : bool
630 0 : ListBase<LC>::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set,
631 : JSPropertyDescriptor *desc)
632 : {
633 0 : if (set) {
634 : if (hasIndexSetter) {
635 0 : int32_t index = GetArrayIndexFromId(cx, id);
636 0 : if (index >= 0) {
637 0 : FillPropertyDescriptor(desc, proxy, JSVAL_VOID, false);
638 0 : return true;
639 : }
640 : }
641 :
642 : if (hasNameSetter && JSID_IS_STRING(id)) {
643 : FillPropertyDescriptor(desc, proxy, JSVAL_VOID, false);
644 : return true;
645 : }
646 : } else {
647 : if (hasIndexGetter) {
648 0 : int32_t index = GetArrayIndexFromId(cx, id);
649 0 : if (index >= 0) {
650 0 : IndexGetterType result;
651 0 : if (!getItemAt(getListObject(proxy), PRUint32(index), result))
652 0 : return true;
653 :
654 : jsval v;
655 0 : if (!Wrap(cx, proxy, result, &v))
656 0 : return false;
657 0 : FillPropertyDescriptor(desc, proxy, v, !hasIndexSetter);
658 0 : return true;
659 : }
660 : }
661 : }
662 :
663 : JSObject *expando;
664 0 : if (!xpc::WrapperFactory::IsXrayWrapper(proxy) && (expando = getExpandoObject(proxy))) {
665 0 : unsigned flags = (set ? JSRESOLVE_ASSIGNING : 0) | JSRESOLVE_QUALIFIED;
666 0 : if (!JS_GetPropertyDescriptorById(cx, expando, id, flags, desc))
667 0 : return false;
668 0 : if (desc->obj) {
669 : // Pretend the property lives on the wrapper.
670 0 : desc->obj = proxy;
671 0 : return true;
672 : }
673 : }
674 :
675 0 : if (hasNameGetter && !set && JSID_IS_STRING(id) && !hasPropertyOnPrototype(cx, proxy, id)) {
676 0 : jsval name = STRING_TO_JSVAL(JSID_TO_STRING(id));
677 : bool hasResult;
678 0 : NameGetterType result;
679 0 : if (!namedItem(cx, proxy, &name, result, &hasResult))
680 0 : return false;
681 0 : if (hasResult) {
682 : jsval v;
683 0 : if (!Wrap(cx, proxy, result, &v))
684 0 : return false;
685 0 : FillPropertyDescriptor(desc, proxy, v, !hasNameSetter);
686 0 : return true;
687 : }
688 : }
689 :
690 0 : desc->obj = NULL;
691 0 : return true;
692 : }
693 :
694 : template<class LC>
695 : bool
696 0 : ListBase<LC>::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set,
697 : JSPropertyDescriptor *desc)
698 : {
699 0 : if (!getOwnPropertyDescriptor(cx, proxy, id, set, desc))
700 0 : return false;
701 0 : if (desc->obj)
702 0 : return true;
703 0 : if (xpc::WrapperFactory::IsXrayWrapper(proxy))
704 0 : return resolveNativeName(cx, proxy, id, desc);
705 0 : JSObject *proto = js::GetObjectProto(proxy);
706 0 : if (!proto) {
707 0 : desc->obj = NULL;
708 0 : return true;
709 : }
710 0 : return JS_GetPropertyDescriptorById(cx, proto, id, JSRESOLVE_QUALIFIED, desc);
711 : }
712 :
713 : JSClass ExpandoClass = {
714 : "DOM proxy binding expando object",
715 : JSCLASS_HAS_PRIVATE,
716 : JS_PropertyStub,
717 : JS_PropertyStub,
718 : JS_PropertyStub,
719 : JS_StrictPropertyStub,
720 : JS_EnumerateStub,
721 : JS_ResolveStub,
722 : JS_ConvertStub
723 : };
724 :
725 : template<class LC>
726 : JSObject *
727 0 : ListBase<LC>::ensureExpandoObject(JSContext *cx, JSObject *obj)
728 : {
729 0 : NS_ASSERTION(instanceIsProxy(obj), "expected a DOM proxy object");
730 0 : JSObject *expando = getExpandoObject(obj);
731 0 : if (!expando) {
732 0 : expando = JS_NewObjectWithGivenProto(cx, &ExpandoClass, nsnull,
733 : js::GetObjectParent(obj));
734 0 : if (!expando)
735 0 : return NULL;
736 :
737 0 : JSCompartment *compartment = js::GetObjectCompartment(obj);
738 : xpc::CompartmentPrivate *priv =
739 0 : static_cast<xpc::CompartmentPrivate *>(JS_GetCompartmentPrivate(compartment));
740 0 : if (!priv->RegisterDOMExpandoObject(expando))
741 0 : return NULL;
742 :
743 0 : js::SetProxyExtra(obj, JSPROXYSLOT_EXPANDO, ObjectValue(*expando));
744 0 : JS_SetPrivate(expando, js::GetProxyPrivate(obj).toPrivate());
745 : }
746 0 : return expando;
747 : }
748 :
749 : template<class LC>
750 : bool
751 0 : ListBase<LC>::defineProperty(JSContext *cx, JSObject *proxy, jsid id, JSPropertyDescriptor *desc)
752 : {
753 : if (hasIndexSetter) {
754 0 : int32_t index = GetArrayIndexFromId(cx, id);
755 0 : if (index >= 0) {
756 0 : nsCOMPtr<nsISupports> ref;
757 : IndexSetterType value;
758 : jsval v;
759 : return Unwrap(cx, desc->value, &value, getter_AddRefs(ref), &v) &&
760 0 : setItemAt(cx, getListObject(proxy), index, value);
761 : }
762 : }
763 :
764 : if (hasNameSetter && JSID_IS_STRING(id)) {
765 : jsval name = STRING_TO_JSVAL(JSID_TO_STRING(id));
766 : xpc_qsDOMString nameString(cx, name, &name,
767 : xpc_qsDOMString::eDefaultNullBehavior,
768 : xpc_qsDOMString::eDefaultUndefinedBehavior);
769 : if (!nameString.IsValid())
770 : return false;
771 :
772 : nsCOMPtr<nsISupports> ref;
773 : NameSetterType value;
774 : jsval v;
775 : if (!Unwrap(cx, desc->value, &value, getter_AddRefs(ref), &v))
776 : return false;
777 : return setNamedItem(cx, getListObject(proxy), nameString, value);
778 : }
779 :
780 0 : if (xpc::WrapperFactory::IsXrayWrapper(proxy))
781 0 : return true;
782 :
783 0 : JSObject *expando = ensureExpandoObject(cx, proxy);
784 0 : if (!expando)
785 0 : return false;
786 :
787 : return JS_DefinePropertyById(cx, expando, id, desc->value, desc->getter, desc->setter,
788 0 : desc->attrs);
789 : }
790 :
791 : template<class LC>
792 : bool
793 0 : ListBase<LC>::getOwnPropertyNames(JSContext *cx, JSObject *proxy, AutoIdVector &props)
794 : {
795 : PRUint32 length;
796 0 : getListObject(proxy)->GetLength(&length);
797 0 : JS_ASSERT(int32_t(length) >= 0);
798 0 : for (int32_t i = 0; i < int32_t(length); ++i) {
799 0 : if (!props.append(INT_TO_JSID(i)))
800 0 : return false;
801 : }
802 :
803 : JSObject *expando;
804 0 : if (!xpc::WrapperFactory::IsXrayWrapper(proxy) && (expando = getExpandoObject(proxy)) &&
805 : !js::GetPropertyNames(cx, expando, JSITER_OWNONLY | JSITER_HIDDEN, &props))
806 0 : return false;
807 :
808 : // FIXME: Add named items
809 0 : return true;
810 : }
811 :
812 : template<class LC>
813 : bool
814 0 : ListBase<LC>::delete_(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
815 : {
816 0 : JSBool b = true;
817 :
818 : JSObject *expando;
819 0 : if (!xpc::WrapperFactory::IsXrayWrapper(proxy) && (expando = getExpandoObject(proxy))) {
820 : jsval v;
821 0 : if (!JS_DeletePropertyById2(cx, expando, id, &v) ||
822 : !JS_ValueToBoolean(cx, v, &b)) {
823 0 : return false;
824 : }
825 : }
826 :
827 0 : *bp = !!b;
828 0 : return true;
829 : }
830 :
831 : template<class LC>
832 : bool
833 0 : ListBase<LC>::enumerate(JSContext *cx, JSObject *proxy, AutoIdVector &props)
834 : {
835 0 : JSObject *proto = JS_GetPrototype(proxy);
836 : return getOwnPropertyNames(cx, proxy, props) &&
837 0 : (!proto || js::GetPropertyNames(cx, proto, 0, &props));
838 : }
839 :
840 : template<class LC>
841 : bool
842 0 : ListBase<LC>::fix(JSContext *cx, JSObject *proxy, Value *vp)
843 : {
844 0 : vp->setUndefined();
845 0 : return true;
846 : }
847 :
848 : template<class LC>
849 : bool
850 0 : ListBase<LC>::hasOwn(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
851 : {
852 : if (hasIndexGetter) {
853 0 : int32_t index = GetArrayIndexFromId(cx, id);
854 0 : if (index >= 0) {
855 0 : IndexGetterType result;
856 0 : *bp = getItemAt(getListObject(proxy), PRUint32(index), result);
857 0 : return true;
858 : }
859 : }
860 :
861 0 : JSObject *expando = getExpandoObject(proxy);
862 0 : if (expando) {
863 0 : JSBool b = true;
864 0 : JSBool ok = JS_HasPropertyById(cx, expando, id, &b);
865 0 : *bp = !!b;
866 0 : if (!ok || *bp)
867 0 : return ok;
868 : }
869 :
870 0 : if (hasNameGetter && JSID_IS_STRING(id) && !hasPropertyOnPrototype(cx, proxy, id)) {
871 0 : jsval name = STRING_TO_JSVAL(JSID_TO_STRING(id));
872 0 : NameGetterType result;
873 0 : return namedItem(cx, proxy, &name, result, bp);
874 : }
875 :
876 0 : *bp = false;
877 0 : return true;
878 : }
879 :
880 : template<class LC>
881 : bool
882 0 : ListBase<LC>::has(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
883 : {
884 0 : if (!hasOwn(cx, proxy, id, bp))
885 0 : return false;
886 : // We have the property ourselves; no need to worry about our
887 : // prototype chain.
888 0 : if (*bp)
889 0 : return true;
890 :
891 : // OK, now we have to look at the proto
892 0 : JSObject *proto = js::GetObjectProto(proxy);
893 0 : if (!proto)
894 0 : return true;
895 :
896 : JSBool protoHasProp;
897 0 : bool ok = JS_HasPropertyById(cx, proto, id, &protoHasProp);
898 0 : if (ok)
899 0 : *bp = protoHasProp;
900 0 : return ok;
901 : }
902 :
903 : template<class LC>
904 : bool
905 8027 : ListBase<LC>::protoIsClean(JSContext *cx, JSObject *proto, bool *isClean)
906 : {
907 : JSPropertyDescriptor desc;
908 16054 : for (size_t n = 0; n < sProtoPropertiesCount; ++n) {
909 8027 : jsid id = sProtoProperties[n].id;
910 8027 : if (!JS_GetPropertyDescriptorById(cx, proto, id, JSRESOLVE_QUALIFIED, &desc))
911 0 : return false;
912 : JSStrictPropertyOp setter =
913 8027 : sProtoProperties[n].setter ? sProtoProperties[n].setter : InvalidateProtoShape_set;
914 8027 : if (desc.obj != proto || desc.getter != sProtoProperties[n].getter ||
915 : desc.setter != setter) {
916 0 : *isClean = false;
917 0 : return true;
918 : }
919 : }
920 :
921 18854 : for (size_t n = 0; n < sProtoMethodsCount; ++n) {
922 10827 : jsid id = sProtoMethods[n].id;
923 10827 : if (!JS_GetPropertyDescriptorById(cx, proto, id, JSRESOLVE_QUALIFIED, &desc))
924 0 : return false;
925 10827 : if (desc.obj != proto || desc.getter || JSVAL_IS_PRIMITIVE(desc.value) ||
926 : n >= js::GetObjectSlotSpan(proto) || js::GetObjectSlot(proto, n + 1) != desc.value ||
927 : !JS_IsNativeFunction(JSVAL_TO_OBJECT(desc.value), sProtoMethods[n].native)) {
928 0 : *isClean = false;
929 0 : return true;
930 : }
931 : }
932 :
933 8027 : *isClean = true;
934 8027 : return true;
935 : }
936 :
937 : template<class LC>
938 : bool
939 4315 : ListBase<LC>::shouldCacheProtoShape(JSContext *cx, JSObject *proto, bool *shouldCache)
940 : {
941 4315 : bool ok = protoIsClean(cx, proto, shouldCache);
942 4315 : if (!ok || !*shouldCache)
943 0 : return ok;
944 :
945 4315 : js::SetReservedSlot(proto, 0, PrivateUint32Value(USE_CACHE));
946 :
947 4315 : JSObject *protoProto = js::GetObjectProto(proto);
948 4315 : if (!protoProto) {
949 0 : *shouldCache = false;
950 0 : return true;
951 : }
952 :
953 4315 : return Base::shouldCacheProtoShape(cx, protoProto, shouldCache);
954 : }
955 :
956 : template<class LC>
957 : bool
958 0 : ListBase<LC>::resolveNativeName(JSContext *cx, JSObject *proxy, jsid id, JSPropertyDescriptor *desc)
959 : {
960 0 : JS_ASSERT(xpc::WrapperFactory::IsXrayWrapper(proxy));
961 :
962 0 : for (size_t n = 0; n < sProtoPropertiesCount; ++n) {
963 0 : if (id == sProtoProperties[n].id) {
964 0 : desc->attrs = JSPROP_ENUMERATE | JSPROP_SHARED;
965 0 : if (!sProtoProperties[n].setter)
966 0 : desc->attrs |= JSPROP_READONLY;
967 0 : desc->obj = proxy;
968 0 : desc->setter = sProtoProperties[n].setter;
969 0 : desc->getter = sProtoProperties[n].getter;
970 0 : return true;
971 : }
972 : }
973 :
974 0 : for (size_t n = 0; n < sProtoMethodsCount; ++n) {
975 0 : if (id == sProtoMethods[n].id) {
976 : JSFunction *fun = JS_NewFunctionById(cx, sProtoMethods[n].native,
977 0 : sProtoMethods[n].nargs, 0, proxy, id);
978 0 : if (!fun)
979 0 : return false;
980 0 : JSObject *funobj = JS_GetFunctionObject(fun);
981 0 : desc->value.setObject(*funobj);
982 0 : desc->attrs = JSPROP_ENUMERATE;
983 0 : desc->obj = proxy;
984 0 : desc->setter = nsnull;
985 0 : desc->getter = nsnull;
986 0 : return true;
987 : }
988 : }
989 :
990 0 : return Base::resolveNativeName(cx, proxy, id, desc);
991 : }
992 :
993 : template<class LC>
994 : bool
995 3712 : ListBase<LC>::nativeGet(JSContext *cx, JSObject *proxy, JSObject *proto, jsid id, bool *found, Value *vp)
996 : {
997 3712 : uint32_t cache = js::GetReservedSlot(proto, 0).toPrivateUint32();
998 3712 : if (cache == CHECK_CACHE) {
999 : bool isClean;
1000 0 : if (!protoIsClean(cx, proto, &isClean))
1001 0 : return false;
1002 0 : if (!isClean) {
1003 0 : js::SetReservedSlot(proto, 0, PrivateUint32Value(DONT_USE_CACHE));
1004 0 : return true;
1005 : }
1006 0 : js::SetReservedSlot(proto, 0, PrivateUint32Value(USE_CACHE));
1007 : }
1008 3712 : else if (cache == DONT_USE_CACHE) {
1009 0 : return true;
1010 : }
1011 : else {
1012 : #ifdef DEBUG
1013 : bool isClean;
1014 3712 : JS_ASSERT(protoIsClean(cx, proto, &isClean) && isClean);
1015 : #endif
1016 : }
1017 :
1018 7424 : for (size_t n = 0; n < sProtoPropertiesCount; ++n) {
1019 3712 : if (id == sProtoProperties[n].id) {
1020 0 : *found = true;
1021 0 : if (!vp)
1022 0 : return true;
1023 :
1024 0 : return sProtoProperties[n].getter(cx, proxy, id, vp);
1025 : }
1026 : }
1027 3728 : for (size_t n = 0; n < sProtoMethodsCount; ++n) {
1028 3720 : if (id == sProtoMethods[n].id) {
1029 3704 : *found = true;
1030 3704 : if (!vp)
1031 0 : return true;
1032 :
1033 3704 : *vp = js::GetObjectSlot(proto, n + 1);
1034 3704 : JS_ASSERT(JS_IsNativeFunction(&vp->toObject(), sProtoMethods[n].native));
1035 3704 : return true;
1036 : }
1037 : }
1038 :
1039 8 : JSObject *protoProto = js::GetObjectProto(proto);
1040 8 : if (!protoProto) {
1041 0 : *found = false;
1042 0 : return true;
1043 : }
1044 :
1045 8 : return Base::nativeGet(cx, proxy, protoProto, id, found, vp);
1046 : }
1047 :
1048 : template<class LC>
1049 : bool
1050 12691 : ListBase<LC>::getPropertyOnPrototype(JSContext *cx, JSObject *proxy, jsid id, bool *found,
1051 : JS::Value *vp)
1052 : {
1053 12691 : JSObject *proto = js::GetObjectProto(proxy);
1054 12691 : if (!proto)
1055 0 : return true;
1056 :
1057 : bool hit;
1058 12691 : if (getProtoShape(proxy) != js::GetObjectShape(proto)) {
1059 4315 : if (!shouldCacheProtoShape(cx, proto, &hit))
1060 0 : return false;
1061 4315 : if (hit)
1062 4315 : setProtoShape(proxy, js::GetObjectShape(proto));
1063 : } else {
1064 8376 : hit = true;
1065 : }
1066 :
1067 12691 : if (hit) {
1068 12691 : if (id == s_length_id) {
1069 8979 : if (vp) {
1070 : PRUint32 length;
1071 8979 : getListObject(proxy)->GetLength(&length);
1072 8979 : JS_ASSERT(int32_t(length) >= 0);
1073 8979 : vp->setInt32(length);
1074 : }
1075 8979 : *found = true;
1076 8979 : return true;
1077 : }
1078 3712 : if (!nativeGet(cx, proxy, proto, id, found, vp))
1079 0 : return false;
1080 3712 : if (*found)
1081 3704 : return true;
1082 : }
1083 :
1084 : JSBool hasProp;
1085 8 : if (!JS_HasPropertyById(cx, proto, id, &hasProp))
1086 0 : return false;
1087 :
1088 8 : *found = hasProp;
1089 8 : if (!hasProp || !vp)
1090 0 : return true;
1091 :
1092 8 : return JS_ForwardGetPropertyTo(cx, proto, id, proxy, vp);
1093 : }
1094 :
1095 : template<class LC>
1096 : bool
1097 0 : ListBase<LC>::hasPropertyOnPrototype(JSContext *cx, JSObject *proxy, jsid id)
1098 : {
1099 0 : JSAutoEnterCompartment ac;
1100 0 : if (xpc::WrapperFactory::IsXrayWrapper(proxy)) {
1101 0 : proxy = js::UnwrapObject(proxy);
1102 0 : if (!ac.enter(cx, proxy))
1103 0 : return false;
1104 : }
1105 0 : JS_ASSERT(objIsList(proxy));
1106 :
1107 : bool found;
1108 : // We ignore an error from getPropertyOnPrototype.
1109 0 : return !getPropertyOnPrototype(cx, proxy, id, &found, NULL) || found;
1110 : }
1111 :
1112 : template<class LC>
1113 : bool
1114 15490 : ListBase<LC>::get(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, Value *vp)
1115 : {
1116 15490 : NS_ASSERTION(!xpc::WrapperFactory::IsXrayWrapper(proxy),
1117 : "Should not have a XrayWrapper here");
1118 :
1119 15490 : bool getFromExpandoObject = true;
1120 :
1121 : if (hasIndexGetter) {
1122 15490 : int32_t index = GetArrayIndexFromId(cx, id);
1123 15490 : if (index >= 0) {
1124 0 : IndexGetterType result;
1125 2799 : if (getItemAt(getListObject(proxy), PRUint32(index), result))
1126 2799 : return Wrap(cx, proxy, result, vp);
1127 :
1128 : // Even if we don't have this index, we don't forward the
1129 : // get on to our expando object.
1130 0 : getFromExpandoObject = false;
1131 : }
1132 : }
1133 :
1134 12691 : if (getFromExpandoObject) {
1135 12691 : JSObject *expando = getExpandoObject(proxy);
1136 12691 : if (expando) {
1137 : JSBool hasProp;
1138 0 : if (!JS_HasPropertyById(cx, expando, id, &hasProp))
1139 0 : return false;
1140 :
1141 0 : if (hasProp)
1142 0 : return JS_GetPropertyById(cx, expando, id, vp);
1143 : }
1144 : }
1145 :
1146 : bool found;
1147 12691 : if (!getPropertyOnPrototype(cx, proxy, id, &found, vp))
1148 0 : return false;
1149 :
1150 12691 : if (found)
1151 12691 : return true;
1152 :
1153 0 : if (hasNameGetter && JSID_IS_STRING(id)) {
1154 0 : jsval name = STRING_TO_JSVAL(JSID_TO_STRING(id));
1155 : bool hasResult;
1156 0 : NameGetterType result;
1157 0 : if (!namedItem(cx, proxy, &name, result, &hasResult))
1158 0 : return false;
1159 0 : if (hasResult)
1160 0 : return Wrap(cx, proxy, result, vp);
1161 : }
1162 :
1163 0 : vp->setUndefined();
1164 0 : return true;
1165 : }
1166 :
1167 : template<class LC>
1168 : bool
1169 1135 : ListBase<LC>::getElementIfPresent(JSContext *cx, JSObject *proxy, JSObject *receiver,
1170 : uint32_t index, Value *vp, bool *present)
1171 : {
1172 1135 : NS_ASSERTION(!xpc::WrapperFactory::IsXrayWrapper(proxy),
1173 : "Should not have a XrayWrapper here");
1174 :
1175 : if (hasIndexGetter) {
1176 0 : IndexGetterType result;
1177 1135 : *present = getItemAt(getListObject(proxy), index, result);
1178 1135 : if (*present)
1179 1135 : return Wrap(cx, proxy, result, vp);
1180 : }
1181 :
1182 : jsid id;
1183 0 : if (!JS_IndexToId(cx, index, &id))
1184 0 : return false;
1185 :
1186 : // if hasIndexGetter, we skip the expando object
1187 : if (!hasIndexGetter) {
1188 : JSObject *expando = getExpandoObject(proxy);
1189 : if (expando) {
1190 : JSBool isPresent;
1191 : if (!JS_GetElementIfPresent(cx, expando, index, expando, vp, &isPresent))
1192 : return false;
1193 : if (isPresent) {
1194 : *present = true;
1195 : return true;
1196 : }
1197 : }
1198 : }
1199 :
1200 : // No need to worry about name getters here, so just check the proto.
1201 :
1202 0 : JSObject *proto = js::GetObjectProto(proxy);
1203 0 : if (proto) {
1204 : JSBool isPresent;
1205 0 : if (!JS_GetElementIfPresent(cx, proto, index, proxy, vp, &isPresent))
1206 0 : return false;
1207 0 : *present = isPresent;
1208 0 : return true;
1209 : }
1210 :
1211 0 : *present = false;
1212 : // Can't Debug_SetValueRangeToCrashOnTouch because it's not public
1213 0 : return true;
1214 : }
1215 :
1216 : template<class LC>
1217 : bool
1218 0 : ListBase<LC>::set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, bool strict,
1219 : Value *vp)
1220 : {
1221 0 : return ProxyHandler::set(cx, proxy, proxy, id, strict, vp);
1222 : }
1223 :
1224 : template<class LC>
1225 : bool
1226 0 : ListBase<LC>::keys(JSContext *cx, JSObject *proxy, AutoIdVector &props)
1227 : {
1228 0 : return ProxyHandler::keys(cx, proxy, props);
1229 : }
1230 :
1231 : template<class LC>
1232 : bool
1233 0 : ListBase<LC>::iterate(JSContext *cx, JSObject *proxy, unsigned flags, Value *vp)
1234 : {
1235 0 : if (flags == JSITER_FOR_OF) {
1236 0 : JSObject *iterobj = JS_NewElementIterator(cx, proxy);
1237 0 : if (!iterobj)
1238 0 : return false;
1239 0 : vp->setObject(*iterobj);
1240 0 : return true;
1241 : }
1242 0 : return ProxyHandler::iterate(cx, proxy, flags, vp);
1243 : }
1244 :
1245 : template<class LC>
1246 : bool
1247 0 : ListBase<LC>::hasInstance(JSContext *cx, JSObject *proxy, const Value *vp, bool *bp)
1248 : {
1249 0 : *bp = vp->isObject() && js::GetObjectClass(&vp->toObject()) == &sInterfaceClass;
1250 0 : return true;
1251 : }
1252 :
1253 : template<class LC>
1254 : JSString *
1255 4 : ListBase<LC>::obj_toString(JSContext *cx, JSObject *proxy)
1256 : {
1257 4 : const char *clazz = sInterfaceClass.name;
1258 4 : size_t nchars = 9 + strlen(clazz); /* 9 for "[object ]" */
1259 4 : jschar *chars = (jschar *)JS_malloc(cx, (nchars + 1) * sizeof(jschar));
1260 4 : if (!chars)
1261 0 : return NULL;
1262 :
1263 4 : const char *prefix = "[object ";
1264 4 : nchars = 0;
1265 40 : while ((chars[nchars] = (jschar)*prefix) != 0)
1266 32 : nchars++, prefix++;
1267 64 : while ((chars[nchars] = (jschar)*clazz) != 0)
1268 56 : nchars++, clazz++;
1269 4 : chars[nchars++] = ']';
1270 4 : chars[nchars] = 0;
1271 :
1272 4 : JSString *str = JS_NewUCString(cx, chars, nchars);
1273 4 : if (!str)
1274 0 : JS_free(cx, chars);
1275 4 : return str;
1276 : }
1277 :
1278 : template<class LC>
1279 : void
1280 4321 : ListBase<LC>::finalize(JSContext *cx, JSObject *proxy)
1281 : {
1282 4321 : ListType *list = getListObject(proxy);
1283 : nsWrapperCache *cache;
1284 4321 : CallQueryInterface(list, &cache);
1285 4321 : if (cache) {
1286 4321 : cache->ClearWrapper();
1287 : }
1288 4321 : XPCJSRuntime *rt = nsXPConnect::GetRuntimeInstance();
1289 4321 : if (rt) {
1290 4321 : rt->DeferredRelease(nativeToSupports(list));
1291 : }
1292 : else {
1293 0 : NS_RELEASE(list);
1294 : }
1295 4321 : }
1296 :
1297 :
1298 : JSObject*
1299 274 : NoBase::getPrototype(JSContext *cx, XPCWrappedNativeScope *scope)
1300 : {
1301 : // We need to pass the object prototype to JS_NewObject. If we pass NULL then the JS engine
1302 : // will look up a prototype on the global by using the class' name and we'll recurse into
1303 : // getPrototype.
1304 274 : return JS_GetObjectPrototype(cx, scope->GetGlobalJSObject());
1305 : }
1306 :
1307 :
1308 : }
1309 : }
1310 : }
1311 : #include "dombindings_gen.cpp"
|