Bug 648801 (new DOM list bindings) - Prepare for more DOM list implementations. r=bz/jst/mrbkap.
authorPeter Van der Beken <peterv@propagandism.org>
Thu, 22 Sep 2011 08:50:16 -0700
changeset 79091 7b6a905eaecc52597a06cef4cc36e5df169a4c27
parent 79090 0679aba1d739466801045fa39a0e73be392f8e45
child 79092 6909fd27d2b722e963413a0bdb1c116f3fbe4cc3
push id506
push userclegnitto@mozilla.com
push dateWed, 09 Nov 2011 02:03:18 +0000
treeherdermozilla-aurora@63587fc7bb93 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz, jst, mrbkap
bugs648801
milestone10.0a1
Bug 648801 (new DOM list bindings) - Prepare for more DOM list implementations. r=bz/jst/mrbkap.
content/base/src/nsContentList.cpp
content/html/content/src/nsHTMLFormElement.cpp
content/html/content/src/nsHTMLTableElement.cpp
js/src/xpconnect/src/dombindings.cpp
js/src/xpconnect/src/dombindings.h
--- a/content/base/src/nsContentList.cpp
+++ b/content/base/src/nsContentList.cpp
@@ -501,17 +501,17 @@ nsContentList::~nsContentList()
     (*mDestroyFunc)(mData);
   }
 }
 
 JSObject*
 nsContentList::WrapObject(JSContext *cx, XPCWrappedNativeScope *scope,
                           bool *triedToWrap)
 {
-  return mozilla::dom::binding::HTMLCollection::create(cx, scope, this, this,
+  return mozilla::dom::binding::HTMLCollection::create(cx, scope, this,
                                                        triedToWrap);
 }
 
 DOMCI_DATA(ContentList, nsContentList)
 
 // QueryInterface implementation for nsContentList
 NS_INTERFACE_TABLE_HEAD(nsContentList)
   NS_NODELIST_OFFSET_AND_INTERFACE_TABLE_BEGIN(nsContentList)
--- a/content/html/content/src/nsHTMLFormElement.cpp
+++ b/content/html/content/src/nsHTMLFormElement.cpp
@@ -160,17 +160,18 @@ public:
    * @return NS_OK or NS_ERROR_OUT_OF_MEMORY.
    */
   nsresult GetSortedControls(nsTArray<nsGenericHTMLFormElement*>& aControls) const;
 
   // nsWrapperCache
   virtual JSObject* WrapObject(JSContext *cx, XPCWrappedNativeScope *scope,
                                bool *triedToWrap)
   {
-    return mozilla::dom::binding::HTMLCollection::create(cx, scope, this, this, triedToWrap);
+    return mozilla::dom::binding::HTMLCollection::create(cx, scope, this,
+                                                         triedToWrap);
   }
 
   nsHTMLFormElement* mForm;  // WEAK - the form owns me
 
   nsTArray<nsGenericHTMLFormElement*> mElements;  // Holds WEAK references - bug 36639
 
   // This array holds on to all form controls that are not contained
   // in mElements (form.elements in JS, see ShouldBeInFormControl()).
--- a/content/html/content/src/nsHTMLTableElement.cpp
+++ b/content/html/content/src/nsHTMLTableElement.cpp
@@ -82,17 +82,18 @@ public:
   NS_IMETHOD    ParentDestroyed();
 
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(TableRowsCollection)
 
   // nsWrapperCache
   virtual JSObject* WrapObject(JSContext *cx, XPCWrappedNativeScope *scope,
                                bool *triedToWrap)
   {
-    return mozilla::dom::binding::HTMLCollection::create(cx, scope, this, this, triedToWrap);
+    return mozilla::dom::binding::HTMLCollection::create(cx, scope, this,
+                                                         triedToWrap);
   }
 
 protected:
   // Those rows that are not in table sections
   nsHTMLTableElement* mParent;
   nsRefPtr<nsContentList> mOrphanRows;  
 };
 
--- a/js/src/xpconnect/src/dombindings.cpp
+++ b/js/src/xpconnect/src/dombindings.cpp
@@ -154,34 +154,87 @@ WrapNativeParent(JSContext *cx, JSObject
         return obj;
     }
 
     qsObjectHelper helper(p, cache);
     jsval v;
     return XPCOMObjectToJsval(cx, scope, helper, false, &v) ? JSVAL_TO_OBJECT(v) : NULL;
 }
 
+template<class T>
 static bool
-WrapObject(JSContext *cx, JSObject *scope, nsISupports *result,
-           nsWrapperCache *cache, jsval *vp)
+Wrap(JSContext *cx, JSObject *scope, T *p, nsWrapperCache *cache, jsval *vp)
 {
     if (xpc_FastGetCachedWrapper(cache, scope, vp))
         return true;
-    qsObjectHelper helper(result, cache);
+    qsObjectHelper helper(p, cache);
     return XPCOMObjectToJsval(cx, scope, helper, true, vp);
 }
 
 template<class T>
-ListBase<T> ListBase<T>::instance;
+static inline bool
+Wrap(JSContext *cx, JSObject *scope, T *p, jsval *vp)
+{
+    return Wrap(cx, scope, p, GetWrapperCache(p), vp);
+}
+
+template<>
+inline bool
+Wrap(JSContext *cx, JSObject *scope, NoType *p, jsval *vp)
+{
+    NS_RUNTIMEABORT("We try to wrap the result from calling a noop?");
+    return false;
+}
+
+template<class T>
+inline bool
+Wrap(JSContext *cx, JSObject *scope, nsCOMPtr<T> &p, jsval *vp)
+{
+    return Wrap(cx, scope, p.get(), vp);
+}
+
+static inline bool
+Wrap(JSContext *cx, JSObject *scope, nsISupportsResult &result, jsval *vp)
+{
+    return Wrap(cx, scope, result.mResult, result.mCache, vp);
+}
+
+static inline bool
+Wrap(JSContext *cx, JSObject *scope, nsString &result, jsval *vp)
+{
+    return xpc_qsStringToJsval(cx, result, vp);
+}
+
+template<class T>
+bool
+Unwrap(JSContext *cx, jsval v, T **ppArg, nsISupports **ppArgRef, jsval *vp)
+{
+    nsresult rv = xpc_qsUnwrapArg(cx, v, ppArg, ppArgRef, vp);
+    if (NS_FAILED(rv))
+        return Throw(cx, rv);
+    return true;
+}
+
+template<>
+bool
+Unwrap(JSContext *cx, jsval v, NoType **ppArg, nsISupports **ppArgRef, jsval *vp)
+{
+    NS_RUNTIMEABORT("We try to unwrap an argument for a noop?");
+    return false;
+}
+
+
+template<class LC>
+ListBase<LC> ListBase<LC>::instance;
 
 void
 Register(nsDOMClassInfoData *aData)
 {
 #define REGISTER_PROTO(_dom_class) \
-    aData[eDOMClassInfo_##_dom_class##_id].mDefineDOMInterface = _dom_class##Base::getPrototype
+    aData[eDOMClassInfo_##_dom_class##_id].mDefineDOMInterface = _dom_class::getPrototype
 
     REGISTER_PROTO(NodeList);
     REGISTER_PROTO(HTMLCollection);
 
 #undef REGISTER_PROTO
 }
 
 bool
@@ -190,45 +243,45 @@ DefineConstructor(JSContext *cx, JSObjec
     bool enabled;
     bool defined = !!aDefine(cx, XPCWrappedNativeScope::FindInJSObjectScope(cx, obj), &enabled);
     NS_ASSERTION(!defined || enabled,
                  "We defined a constructor but the new bindings are disabled?");
     *aResult = defined ? NS_OK : NS_ERROR_FAILURE;
     return enabled;
 }
 
-template<class T>
-T*
-ListBase<T>::getListObject(JSObject *obj)
+template<class LC>
+typename ListBase<LC>::ListType*
+ListBase<LC>::getListObject(JSObject *obj)
 {
     if (xpc::WrapperFactory::IsXrayWrapper(obj))
         obj = js::UnwrapObject(obj);
     JS_ASSERT(objIsList(obj));
-    return static_cast<T *>(js::GetProxyPrivate(obj).toPrivate());
+    return static_cast<ListType *>(js::GetProxyPrivate(obj).toPrivate());
 }
 
-template<class T>
+template<class LC>
 uint32
-ListBase<T>::getProtoShape(JSObject *obj)
+ListBase<LC>::getProtoShape(JSObject *obj)
 {
     JS_ASSERT(objIsList(obj));
     return js::GetProxyExtra(obj, JSPROXYSLOT_PROTOSHAPE).toPrivateUint32();
 }
 
-template<class T>
+template<class LC>
 void
-ListBase<T>::setProtoShape(JSObject *obj, uint32 shape)
+ListBase<LC>::setProtoShape(JSObject *obj, uint32 shape)
 {
     JS_ASSERT(objIsList(obj));
     js::SetProxyExtra(obj, JSPROXYSLOT_PROTOSHAPE, PrivateUint32Value(shape));
 }
 
-template<class T>
+template<class LC>
 bool
-ListBase<T>::instanceIsListObject(JSContext *cx, JSObject *obj, JSObject *callee)
+ListBase<LC>::instanceIsListObject(JSContext *cx, JSObject *obj, JSObject *callee)
 {
     if (XPCWrapper::IsSecurityWrapper(obj)) {
         if (callee && js::GetObjectGlobal(obj) == js::GetObjectGlobal(callee)) {
             obj = js::UnwrapObject(obj);
         }
         else {
             obj = XPCWrapper::Unwrap(cx, obj);
             if (!obj)
@@ -239,91 +292,94 @@ ListBase<T>::instanceIsListObject(JSCont
     if (!objIsList(obj)) {
         // FIXME: Throw a proper DOM exception.
         JS_ReportError(cx, "type error: wrong object");
         return false;
     }
     return true;
 }
 
-template<class T>
+template<class LC>
 JSBool
-ListBase<T>::length_getter(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
+ListBase<LC>::length_getter(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
 {
     if (!instanceIsListObject(cx, obj, NULL))
         return false;
     PRUint32 length;
     getListObject(obj)->GetLength(&length);
     JS_ASSERT(int32(length) >= 0);
     *vp = UINT_TO_JSVAL(length);
     return true;
 }
 
-template<class T>
+template<class LC>
+bool
+ListBase<LC>::getNamedItem(ListType *list, const nsAString& aName, NameGetterType &item)
+{
+    JS_STATIC_ASSERT(!hasNameGetter);
+    return false;
+}
+
+template<class LC>
 JSBool
-ListBase<T>::item(JSContext *cx, uintN argc, jsval *vp)
+ListBase<LC>::item(JSContext *cx, uintN argc, jsval *vp)
 {
     JSObject *obj = JS_THIS_OBJECT(cx, vp);
     JSObject *callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
     if (!obj || !instanceIsListObject(cx, obj, callee))
         return false;
     if (argc < 1)
         return Throw(cx, NS_ERROR_XPC_NOT_ENOUGH_ARGS);
     jsval *argv = JS_ARGV(cx, vp);
     uint32 u;
     if (!JS_ValueToECMAUint32(cx, argv[0], &u))
         return false;
-    T *list = getListObject(obj);
-    nsIContent *result = list->GetNodeAt(u);
-    return WrapObject(cx, obj, result, result, vp);
+    IndexGetterType result;
+    if (!getItemAt(getListObject(obj), u, result)) {
+        *vp = JSVAL_NULL;
+        return JS_TRUE;
+    }
+    return Wrap(cx, obj, result, vp);
 }
 
 
-template<>
-nsISupports*
-HTMLCollectionBase::getNamedItem(nsIHTMLCollection *list, const nsAString& aName,
-                                 nsWrapperCache **aCache);
-
-template<>
+template<class LC>
 bool
-HTMLCollectionBase::namedItem(JSContext *cx, JSObject *obj, jsval *name, nsISupports **result,
-                              nsWrapperCache **cache, bool *hasResult)
+ListBase<LC>::namedItem(JSContext *cx, JSObject *obj, jsval *name, NameGetterType &result,
+                        bool *hasResult)
 {
     xpc_qsDOMString nameString(cx, *name, name,
                                xpc_qsDOMString::eDefaultNullBehavior,
                                xpc_qsDOMString::eDefaultUndefinedBehavior);
     if (!nameString.IsValid())
         return false;
-    nsIHTMLCollection *collection = getListObject(obj);
-    *result = getNamedItem(collection, nameString, cache);
-    *hasResult = !!*result;
+    *hasResult = getNamedItem(getListObject(obj), nameString, result);
     return true;
 }
 
-template<>
+template<class LC>
 JSBool
-HTMLCollectionBase::namedItem(JSContext *cx, uintN argc, jsval *vp)
+ListBase<LC>::namedItem(JSContext *cx, uintN argc, jsval *vp)
 {
     JSObject *obj = JS_THIS_OBJECT(cx, vp);
     JSObject *callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
     if (!obj || !instanceIsListObject(cx, obj, callee))
         return false;
     if (argc < 1)
         return Throw(cx, NS_ERROR_XPC_NOT_ENOUGH_ARGS);
     jsval *argv = JS_ARGV(cx, vp);
     bool hasResult;
-    nsISupports *result;
-    nsWrapperCache *cache;
-    if (!namedItem(cx, obj, &argv[0], &result, &cache, &hasResult))
+    NameGetterType result;
+    if (!namedItem(cx, obj, &argv[0], result, &hasResult))
         return JS_FALSE;
     if (!hasResult) {
         *vp = JSVAL_NULL;
         return JS_TRUE;
     }
-    return WrapObject(cx, obj, result, cache, vp);
+    return Wrap(cx, obj, result, vp);
 }
 
 JSBool
 interface_hasInstance(JSContext *cx, JSObject *obj, const js::Value *vp, JSBool *bp)
 {
     if (vp->isObject()) {
         jsval prototype;
         if (!JS_GetPropertyById(cx, obj, s_prototype_id, &prototype) ||
@@ -354,19 +410,19 @@ interface_hasInstance(JSContext *cx, JSO
             return JS_TRUE;
         }
     }
 
     *bp = JS_FALSE;
     return JS_TRUE;
 }
 
-template<class T>
+template<class LC>
 JSObject *
-ListBase<T>::getPrototype(JSContext *cx, XPCWrappedNativeScope *scope, bool *enabled)
+ListBase<LC>::getPrototype(JSContext *cx, XPCWrappedNativeScope *scope, bool *enabled)
 {
     if(!scope->NewDOMBindingsEnabled()) {
         *enabled = false;
         return NULL;
     }
 
     *enabled = true;
 
@@ -391,16 +447,17 @@ ListBase<T>::getPrototype(JSContext *cx,
     if (!js_GetClassPrototype(cx, global, JSProto_Object, &proto))
         return NULL;
 
     interfacePrototype = JS_NewObject(cx, NULL, proto, global);
     if (!interfacePrototype)
         return NULL;
 
     for (size_t n = 0; n < NS_ARRAY_LENGTH(sProtoProperties); ++n) {
+        JS_ASSERT(sProtoProperties[n].getter);
         jsid id = sProtoProperties[n].id;
         uintN attrs = JSPROP_ENUMERATE | JSPROP_SHARED;
         if (!sProtoProperties[n].setter)
             attrs |= JSPROP_READONLY;
         if (!JS_DefinePropertyById(cx, interfacePrototype, id, JSVAL_VOID,
                                    sProtoProperties[n].getter, sProtoProperties[n].setter, attrs))
             return NULL;
     }
@@ -432,45 +489,44 @@ ListBase<T>::getPrototype(JSContext *cx,
         return NULL;
 
     if (!cache.Put(sInterfaceClass.name, interfacePrototype))
         return NULL;
 
     return interfacePrototype;
 }
 
-template<class T>
+template<class LC>
 JSObject *
-ListBase<T>::create(JSContext *cx, XPCWrappedNativeScope *scope, T *aNodeList,
-                    nsWrapperCache* aWrapperCache, bool *triedToWrap)
+ListBase<LC>::create(JSContext *cx, XPCWrappedNativeScope *scope, ListType *aList,
+                     nsWrapperCache* aWrapperCache, bool *triedToWrap)
 {
     *triedToWrap = true;
 
-    JSObject *parent = WrapNativeParent(cx, scope->GetGlobalJSObject(), aNodeList->GetParentObject());
+    JSObject *parent = WrapNativeParent(cx, scope->GetGlobalJSObject(), aList->GetParentObject());
     if (!parent)
         return NULL;
 
     JSAutoEnterCompartment ac;
     if (js::GetObjectGlobal(parent) != scope->GetGlobalJSObject()) {
         if (!ac.enter(cx, parent))
             return NULL;
 
         scope = XPCWrappedNativeScope::FindInJSObjectScope(cx, parent);
     }
 
     JSObject *proto = getPrototype(cx, scope, triedToWrap);
     if (!proto)
         return NULL;
-    JSObject *obj = NewProxyObject(cx, &ListBase<T>::instance,
-                                   PrivateValue(aNodeList),
-                                   proto, parent);
+    JSObject *obj = NewProxyObject(cx, &ListBase<LC>::instance,
+                                   PrivateValue(aList), proto, parent);
     if (!obj)
         return NULL;
 
-    NS_ADDREF(aNodeList);
+    NS_ADDREF(aList);
     setProtoShape(obj, -1);
 
     aWrapperCache->SetWrapper(obj);
 
     return obj;
 }
 
 static JSObject *
@@ -509,83 +565,83 @@ GetArrayIndexFromId(JSContext *cx, jsid 
         JSAtom *atom = JSID_TO_ATOM(id);
         jschar s = *atom->chars();
         if (NS_LIKELY((unsigned)s >= 'a' && (unsigned)s <= 'z'))
             return -1;
     }
     return IdToInt32(cx, id);
 }
 
-template<class T>
+template<class LC>
 bool
-ListBase<T>::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set,
-                                      PropertyDescriptor *desc)
+ListBase<LC>::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set,
+                                       PropertyDescriptor *desc)
 {
     JSObject *expando;
     if (!xpc::WrapperFactory::IsXrayWrapper(proxy) && (expando = getExpandoObject(proxy))) {
         uintN flags = (set ? JSRESOLVE_ASSIGNING : 0) | JSRESOLVE_QUALIFIED;
         if (!JS_GetPropertyDescriptorById(cx, expando, id, flags, desc))
             return false;
         if (desc->obj) {
             // Pretend the property lives on the wrapper.
             desc->obj = proxy;
             return true;
         }
     }
 
-    if (hasNamedItem(id)) {
+    if (hasNameGetter && JSID_IS_STRING(id) && !hasNative(cx, proxy, id)) {
         jsval name = STRING_TO_JSVAL(JSID_TO_STRING(id));
         bool hasResult;
-        nsISupports *result;
-        nsWrapperCache *cache;
-        if (!namedItem(cx, proxy, &name, &result, &cache, &hasResult))
+        NameGetterType result;
+        if (!namedItem(cx, proxy, &name, result, &hasResult))
             return false;
         if (hasResult) {
             jsval v;
-            if (!WrapObject(cx, proxy, result, cache, &v))
+            if (!Wrap(cx, proxy, result, &v))
                 return false;
             desc->obj = proxy;
             desc->value = v;
             desc->attrs = JSPROP_READONLY | JSPROP_ENUMERATE;
             desc->getter = NULL;
             desc->setter = NULL;
             desc->shortid = 0;
         }
         else {
             desc->obj = NULL;
         }
         return true;
     }
 
-    int32 index = GetArrayIndexFromId(cx, id);
-    if (index >= 0) {
-        T *list = getListObject(proxy);
-        nsIContent *result = list->GetNodeAt(PRUint32(index));
-        if (result) {
-            jsval v;
-            if (!WrapObject(cx, proxy, result, result, &v))
-                return false;
-            desc->obj = proxy;
-            desc->value = v;
-            desc->attrs = JSPROP_READONLY | JSPROP_ENUMERATE;
-            desc->getter = NULL;
-            desc->setter = NULL;
-            desc->shortid = 0;
-            return true;
+    if (hasIndexGetter) {
+        int32 index = GetArrayIndexFromId(cx, id);
+        if (index >= 0) {
+            IndexGetterType result;
+            if (getItemAt(getListObject(proxy), PRUint32(index), result)) {
+                jsval v;
+                if (!Wrap(cx, proxy, result, &v))
+                    return false;
+                desc->obj = proxy;
+                desc->value = v;
+                desc->attrs = JSPROP_READONLY | JSPROP_ENUMERATE;
+                desc->getter = NULL;
+                desc->setter = NULL;
+                desc->shortid = 0;
+                return true;
+            }
         }
     }
 
     desc->obj = NULL;
     return true;
 }
 
-template<class T>
+template<class LC>
 bool
-ListBase<T>::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set,
-                                   PropertyDescriptor *desc)
+ListBase<LC>::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set,
+                                    PropertyDescriptor *desc)
 {
     if (!getOwnPropertyDescriptor(cx, proxy, id, set, desc))
         return false;
     if (desc->obj)
         return true;
     if (xpc::WrapperFactory::IsXrayWrapper(proxy))
         return resolveNativeName(cx, proxy, id, desc);
     return JS_GetPropertyDescriptorById(cx, js::GetObjectProto(proxy), id, JSRESOLVE_QUALIFIED,
@@ -599,19 +655,19 @@ JSClass ExpandoClass = {
     JS_PropertyStub,
     JS_PropertyStub,
     JS_StrictPropertyStub,
     JS_EnumerateStub,
     JS_ResolveStub,
     JS_ConvertStub
 };
 
-template<class T>
+template<class LC>
 JSObject *
-ListBase<T>::ensureExpandoObject(JSContext *cx, JSObject *obj)
+ListBase<LC>::ensureExpandoObject(JSContext *cx, JSObject *obj)
 {
     NS_ASSERTION(instanceIsProxy(obj), "expected a DOM proxy object");
     JSObject *expando = getExpandoObject(obj);
     if (!expando) {
         expando = JS_NewObjectWithGivenProto(cx, &ExpandoClass, nsnull,
                                              js::GetObjectParent(obj));
         if (!expando)
             return NULL;
@@ -623,35 +679,34 @@ ListBase<T>::ensureExpandoObject(JSConte
             return NULL;
 
         js::SetProxyExtra(obj, JSPROXYSLOT_EXPANDO, ObjectValue(*expando));
         expando->setPrivate(js::GetProxyPrivate(obj).toPrivate());
     }
     return expando;
 }
 
-template<class T>
+template<class LC>
 bool
-ListBase<T>::defineProperty(JSContext *cx, JSObject *proxy, jsid id,
-                            PropertyDescriptor *desc)
+ListBase<LC>::defineProperty(JSContext *cx, JSObject *proxy, jsid id, PropertyDescriptor *desc)
 {
     if (xpc::WrapperFactory::IsXrayWrapper(proxy))
         return true;
 
     JSObject *expando = ensureExpandoObject(cx, proxy);
     if (!expando)
         return false;
 
     return JS_DefinePropertyById(cx, expando, id, desc->value, desc->getter, desc->setter,
                                  desc->attrs);
 }
 
-template<class T>
+template<class LC>
 bool
-ListBase<T>::getOwnPropertyNames(JSContext *cx, JSObject *proxy, AutoIdVector &props)
+ListBase<LC>::getOwnPropertyNames(JSContext *cx, JSObject *proxy, AutoIdVector &props)
 {
     JSObject *expando;
     if (!xpc::WrapperFactory::IsXrayWrapper(proxy) && (expando = getExpandoObject(proxy)) &&
         !GetPropertyNames(cx, expando, JSITER_OWNONLY | JSITER_HIDDEN, &props))
         return false;
 
     PRUint32 length;
     getListObject(proxy)->GetLength(&length);
@@ -659,93 +714,95 @@ ListBase<T>::getOwnPropertyNames(JSConte
     for (int32 i = 0; i < int32(length); ++i) {
         if (!props.append(INT_TO_JSID(i)))
             return false;
     }
     // FIXME: Add named items?
     return true;
 }
 
-template<class T>
+template<class LC>
 bool
-ListBase<T>::delete_(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
+ListBase<LC>::delete_(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
 {
     JSBool b = true;
 
     JSObject *expando;
     if (!xpc::WrapperFactory::IsXrayWrapper(proxy) && (expando = getExpandoObject(proxy))) {
         jsval v;
         if (!JS_DeletePropertyById2(cx, expando, id, &v) ||
             !JS_ValueToBoolean(cx, v, &b)) {
             return false;
         }
     }
 
     *bp = !!b;
     return true;
 }
 
-template<class T>
+template<class LC>
 bool
-ListBase<T>::enumerate(JSContext *cx, JSObject *proxy, AutoIdVector &props)
+ListBase<LC>::enumerate(JSContext *cx, JSObject *proxy, AutoIdVector &props)
 {
     // FIXME: enumerate proto as well
     return getOwnPropertyNames(cx, proxy, props);
 }
 
-template<class T>
+template<class LC>
 bool
-ListBase<T>::fix(JSContext *cx, JSObject *proxy, Value *vp)
+ListBase<LC>::fix(JSContext *cx, JSObject *proxy, Value *vp)
 {
     vp->setUndefined();
     return true;
 }
 
-template<class T>
+template<class LC>
 bool
-ListBase<T>::hasOwn(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
+ListBase<LC>::hasOwn(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
 {
     JSObject *expando = getExpandoObject(proxy);
     if (expando) {
         JSBool b = JS_TRUE;
         JSBool ok = JS_HasPropertyById(cx, expando, id, &b);
         *bp = !!b;
         if (!ok || *bp)
             return ok;
     }
 
-    if (hasNamedItem(id)) {
+    if (hasNameGetter && JSID_IS_STRING(id) && !hasNative(cx, proxy, id)) {
         jsval name = STRING_TO_JSVAL(JSID_TO_STRING(id));
-        nsISupports *result;
-        nsWrapperCache *cache;
-        return namedItem(cx, proxy, &name, &result, &cache, bp);
+        NameGetterType result;
+        return namedItem(cx, proxy, &name, result, bp);
     }
 
-    int32 index = GetArrayIndexFromId(cx, id);
-    if (index >= 0) {
-        if (getListObject(proxy)->GetNodeAt(PRUint32(index))) {
-            *bp = true;
-            return true;
+    if (hasIndexGetter) {
+        int32 index = GetArrayIndexFromId(cx, id);
+        if (index >= 0) {
+            IndexGetterType result;
+            if (getItemAt(getListObject(proxy), PRUint32(index), result)) {
+                *bp = true;
+                return true;
+            }
         }
     }
 
     *bp = false;
     return true;
 }
 
-template<class T>
+template<class LC>
 bool
-ListBase<T>::has(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
+ListBase<LC>::has(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
 {
     return ProxyHandler::has(cx, proxy, id, bp);
 }
 
-template<class T>
+template<class LC>
 bool
-ListBase<T>::cacheProtoShape(JSContext *cx, JSObject *proxy, JSObject *proto)
+ListBase<LC>::cacheProtoShape(JSContext *cx, JSObject *proxy, JSObject *proto)
 {
     JSPropertyDescriptor desc;
     for (size_t n = 0; n < NS_ARRAY_LENGTH(sProtoProperties); ++n) {
         jsid id = sProtoProperties[n].id;
         if (!JS_GetPropertyDescriptorById(cx, proto, id, JSRESOLVE_QUALIFIED, &desc))
             return false;
         if (desc.obj != proto || desc.getter != sProtoProperties[n].getter ||
             desc.setter != sProtoProperties[n].setter)
@@ -762,36 +819,36 @@ ListBase<T>::cacheProtoShape(JSContext *
             return true; // don't cache
         }
     }
 
     setProtoShape(proxy, js::GetObjectShape(proto));
     return true;
 }
 
-template<class T>
+template<class LC>
 bool
-ListBase<T>::checkForCacheHit(JSContext *cx, JSObject *proxy, JSObject *receiver, JSObject *proto,
-                              jsid id, Value *vp, bool *hitp)
+ListBase<LC>::checkForCacheHit(JSContext *cx, JSObject *proxy, JSObject *receiver,
+                               JSObject *proto, jsid id, Value *vp, bool *hitp)
 {
     if (getProtoShape(proxy) != js::GetObjectShape(proto)) {
         if (!cacheProtoShape(cx, proxy, proto))
             return false;
         if (getProtoShape(proxy) != js::GetObjectShape(proto)) {
             *hitp = false;
             return JS_GetPropertyById(cx, proto, id, vp);
         }
     }
     *hitp = true;
     return true;
 }
 
-template<class T>
+template<class LC>
 bool
-ListBase<T>::resolveNativeName(JSContext *cx, JSObject *proxy, jsid id, PropertyDescriptor *desc)
+ListBase<LC>::resolveNativeName(JSContext *cx, JSObject *proxy, jsid id, PropertyDescriptor *desc)
 {
     JS_ASSERT(xpc::WrapperFactory::IsXrayWrapper(proxy));
 
     for (size_t n = 0; n < NS_ARRAY_LENGTH(sProtoProperties); ++n) {
         if (id == sProtoProperties[n].id) {
             desc->attrs = JSPROP_ENUMERATE | JSPROP_SHARED;
             if (!sProtoProperties[n].setter)
                 desc->attrs |= JSPROP_READONLY;
@@ -816,105 +873,131 @@ ListBase<T>::resolveNativeName(JSContext
             desc->getter = nsnull;
             return true;
         }
     }
 
     return true;
 }
 
-template<class T>
+template<class LC>
 bool
-ListBase<T>::get(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, Value *vp)
+ListBase<LC>::nativeGet(JSContext *cx, JSObject *proxy, JSObject *proto, jsid id, bool *found, Value *vp)
+{
+    for (size_t n = 0; n < NS_ARRAY_LENGTH(sProtoProperties); ++n) {
+        if (id == sProtoProperties[n].id) {
+            *found = true;
+            if (!vp)
+                return true;
+
+            return sProtoProperties[n].getter(cx, proxy, id, vp);
+        }
+    }
+    for (size_t n = 0; n < NS_ARRAY_LENGTH(sProtoMethods); ++n) {
+        if (id == sProtoMethods[n].id) {
+            *found = true;
+            if (!vp)
+                return true;
+
+            *vp = js::GetSlot(proto, n);
+            JS_ASSERT(JS_IsNativeFunction(&vp->toObject(), sProtoMethods[n].native));
+            return true;
+        }
+    }
+
+    return true;
+}
+
+template<class LC>
+bool
+ListBase<LC>::get(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, Value *vp)
 {
     JSObject *expando = getExpandoObject(proxy);
     if (expando) {
         JSBool hasProp;
         if (!JS_HasPropertyById(cx, expando, id, &hasProp))
             return false;
         if (hasProp)
             return JS_GetPropertyById(cx, expando, id, vp);
     }
 
-    if (hasNamedItem(id)) {
+    if (hasNameGetter && JSID_IS_STRING(id) && !hasNative(cx, proxy, id)) {
         jsval name = STRING_TO_JSVAL(JSID_TO_STRING(id));
         bool hasResult;
-        nsISupports *result;
-        nsWrapperCache *cache;
-        if (!namedItem(cx, proxy, &name, &result, &cache, &hasResult))
+        NameGetterType result;
+        if (!namedItem(cx, proxy, &name, result, &hasResult))
             return false;
         if (hasResult)
-            return WrapObject(cx, proxy, result, cache, vp);
+            return Wrap(cx, proxy, result, vp);
     }
 
-    int32 index = GetArrayIndexFromId(cx, id);
-    if (index >= 0) {
-        T *list = getListObject(proxy);
-        nsIContent *result = list->GetNodeAt(PRUint32(index));
-        if (result)
-            return WrapObject(cx, proxy, result, result, vp);
+    if (hasIndexGetter) {
+        int32 index = GetArrayIndexFromId(cx, id);
+        if (index >= 0) {
+            IndexGetterType result;
+            if (getItemAt(getListObject(proxy), PRUint32(index), result))
+                return Wrap(cx, proxy, result, vp);
+        }
     }
 
     JSObject *proto = js::GetObjectProto(proxy);
     bool hit;
     if (!checkForCacheHit(cx, proxy, receiver, proto, id, vp, &hit))
         return false;
     if (hit) {
         if (id == s_length_id) {
             PRUint32 length;
             getListObject(proxy)->GetLength(&length);
             JS_ASSERT(int32(length) >= 0);
             vp->setInt32(length);
             return true;
         }
-        for (size_t n = 0; n < NS_ARRAY_LENGTH(sProtoMethods); ++n) {
-            if (id == sProtoMethods[n].id) {
-                *vp = js::GetSlot(proto, n);
-                JS_ASSERT(JS_IsNativeFunction(&vp->toObject(), sProtoMethods[n].native));
-                return true;
-            }
-        }
+        bool found;
+        if (!nativeGet(cx, proxy, proto, id, &found, vp))
+            return false;
+        if (found)
+            return true;
     }
 
     return JS_GetPropertyById(cx, proto, id, vp);
 }
 
-template<class T>
+template<class LC>
 bool
-ListBase<T>::set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, bool strict,
-                 Value *vp)
+ListBase<LC>::set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, bool strict,
+                  Value *vp)
 {
     return ProxyHandler::set(cx, proxy, proxy, id, strict, vp);
 }
 
-template<class T>
+template<class LC>
 bool
-ListBase<T>::keys(JSContext *cx, JSObject *proxy, AutoIdVector &props)
+ListBase<LC>::keys(JSContext *cx, JSObject *proxy, AutoIdVector &props)
 {
     return ProxyHandler::keys(cx, proxy, props);
 }
 
-template<class T>
+template<class LC>
 bool
-ListBase<T>::iterate(JSContext *cx, JSObject *proxy, uintN flags, Value *vp)
+ListBase<LC>::iterate(JSContext *cx, JSObject *proxy, uintN flags, Value *vp)
 {
     return ProxyHandler::iterate(cx, proxy, flags, vp);
 }
 
-template<class T>
+template<class LC>
 bool
-ListBase<T>::hasInstance(JSContext *cx, JSObject *proxy, const Value *vp, bool *bp)
+ListBase<LC>::hasInstance(JSContext *cx, JSObject *proxy, const Value *vp, bool *bp)
 {
     *bp = vp->isObject() && js::GetObjectClass(&vp->toObject()) == &sInterfaceClass;
     return true;
 }
 
-template<class T>
+template<class LC>
 JSString *
-ListBase<T>::obj_toString(JSContext *cx, JSObject *proxy)
+ListBase<LC>::obj_toString(JSContext *cx, JSObject *proxy)
 {
     const char *clazz = sInterfaceClass.name;
     size_t nchars = 9 + strlen(clazz); /* 9 for "[object ]" */
     jschar *chars = (jschar *)JS_malloc(cx, (nchars + 1) * sizeof(jschar));
     if (!chars)
         return NULL;
 
     const char *prefix = "[object ";
@@ -927,41 +1010,39 @@ ListBase<T>::obj_toString(JSContext *cx,
     chars[nchars] = 0;
 
     JSString *str = JS_NewUCString(cx, chars, nchars);
     if (!str)
         JS_free(cx, chars);
     return str;
 }
 
-template<class T>
+template<class LC>
 void
-ListBase<T>::finalize(JSContext *cx, JSObject *proxy)
+ListBase<LC>::finalize(JSContext *cx, JSObject *proxy)
 {
-    T *list = getListObject(proxy);
+    ListType *list = getListObject(proxy);
     nsWrapperCache *cache;
     CallQueryInterface(list, &cache);
     if (cache) {
         cache->ClearWrapper();
     }
     NS_RELEASE(list);
 }
 
 
 // NodeList
 
-JSObject *
-NodeList::create(JSContext *cx, XPCWrappedNativeScope *scope, nsINodeList *aNodeList,
-                 bool *triedToWrap)
-{
-    return NodeListBase::create(cx, scope, aNodeList, aNodeList, triedToWrap);
-}
+template
+JSObject*
+NodeList::create(JSContext *cx, XPCWrappedNativeScope *scope, NodeList::ListType *aList,
+                  nsWrapperCache* aWrapperCache, bool *triedToWrap);
 
 template<>
-Class NodeListBase::sInterfaceClass = {
+Class NodeList::sInterfaceClass = {
     "NodeList",
     0,
     JS_PropertyStub,        /* addProperty */
     JS_PropertyStub,        /* delProperty */
     JS_PropertyStub,        /* getProperty */
     JS_StrictPropertyStub,  /* setProperty */
     JS_EnumerateStub,
     JS_ResolveStub,
@@ -971,61 +1052,47 @@ Class NodeListBase::sInterfaceClass = {
     NULL,                   /* checkAccess */
     NULL,                   /* call */
     NULL,                   /* construct */
     NULL,                   /* xdrObject */
     interface_hasInstance
 };
 
 template<>
-NodeListBase::Properties NodeListBase::sProtoProperties[] = {
+NodeList::Properties NodeList::sProtoProperties[] = {
     { s_length_id, length_getter, NULL }
 };
 
 template<>
-NodeListBase::Methods NodeListBase::sProtoMethods[] = {
+NodeList::Methods NodeList::sProtoMethods[] = {
     { s_item_id, &item, 1 }
 };
 
 template<>
-nsISupports*
-NodeListBase::getNamedItem(nsINodeList *list, const nsAString& aName, nsWrapperCache **aCache)
-{
-    return NULL;
-}
-
-template<>
 bool
-NodeListBase::hasNamedItem(jsid id)
+NodeList::getItemAt(nsINodeList *list, uint32 i, nsIContent *&item)
 {
-    return false;
-}
-
-template<>
-bool
-NodeListBase::namedItem(JSContext *cx, JSObject *obj, jsval *name, nsISupports **result,
-                        nsWrapperCache **cache, bool *hasResult)
-{
-    *hasResult = false;
-    return true;
+    return !!(item = list->GetNodeAt(i));
 }
 
 
 // HTMLCollection
 
-JSObject *
+template
+JSObject*
 HTMLCollection::create(JSContext *cx, XPCWrappedNativeScope *scope,
-                       nsIHTMLCollection *aHTMLCollection,
-                       nsWrapperCache *aWrapperCache, bool *triedToWrap)
-{
-    return HTMLCollectionBase::create(cx, scope, aHTMLCollection, aWrapperCache, triedToWrap);
-}
+                       HTMLCollection::ListType *aList, nsWrapperCache* aWrapperCache,
+                       bool *triedToWrap);
+
+template
+nsIHTMLCollection*
+HTMLCollection::getListObject(JSObject *obj);
 
 template<>
-Class HTMLCollectionBase::sInterfaceClass = {
+Class HTMLCollection::sInterfaceClass = {
     "HTMLCollection",
     0,
     JS_PropertyStub,        /* addProperty */
     JS_PropertyStub,        /* delProperty */
     JS_PropertyStub,        /* getProperty */
     JS_StrictPropertyStub,  /* setProperty */
     JS_EnumerateStub,
     JS_ResolveStub,
@@ -1035,41 +1102,38 @@ Class HTMLCollectionBase::sInterfaceClas
     NULL,                   /* checkAccess */
     NULL,                   /* call */
     NULL,                   /* construct */
     NULL,                   /* xdrObject */
     interface_hasInstance
 };
 
 template<>
-HTMLCollectionBase::Properties HTMLCollectionBase::sProtoProperties[] = {
+HTMLCollection::Properties HTMLCollection::sProtoProperties[] = {
     { s_length_id, length_getter, NULL }
 };
 
 template<>
-HTMLCollectionBase::Methods HTMLCollectionBase::sProtoMethods[] = {
+HTMLCollection::Methods HTMLCollection::sProtoMethods[] = {
     { s_item_id, &item, 1 },
     { s_namedItem_id, &namedItem, 1 }
 };
 
 template<>
-nsISupports*
-HTMLCollectionBase::getNamedItem(nsIHTMLCollection *list, const nsAString& aName,
-                                 nsWrapperCache **aCache)
+bool
+HTMLCollection::getItemAt(nsIHTMLCollection *list, uint32 i, nsIContent *&item)
 {
-    return list->GetNamedItem(aName, aCache);
+    return !!(item = list->GetNodeAt(i));
 }
 
 template<>
 bool
-HTMLCollectionBase::hasNamedItem(jsid id)
+HTMLCollection::getNamedItem(nsIHTMLCollection *list, const nsAString& aName,
+                             nsISupportsResult &item)
 {
-    return JSID_IS_STRING(id);
+    item.mResult = list->GetNamedItem(aName, &item.mCache);
+    return !!item.mResult;
 }
 
-template
-nsIHTMLCollection*
-HTMLCollectionBase::getListObject(JSObject *obj);
-
 
 }
 }
 }
--- a/js/src/xpconnect/src/dombindings.h
+++ b/js/src/xpconnect/src/dombindings.h
@@ -39,17 +39,17 @@
 
 #ifndef dombindings_h
 #define dombindings_h
 
 #include "jsapi.h"
 #include "jsproxy.h"
 #include "xpcpublic.h"
 
-class nsINode;
+class nsIContent;
 class nsINodeList;
 class nsIHTMLCollection;
 
 namespace mozilla {
 namespace dom {
 namespace binding {
 
 inline nsWrapperCache*
@@ -75,21 +75,71 @@ protected:
     ProxyHandler() : js::ProxyHandler(ProxyFamily())
     {
     }
 
 public:
     virtual bool isInstanceOf(JSObject *prototype) = 0;
 };
 
-template<class T>
+class NoType;
+class NoOp {
+public:
+    typedef NoType* T;
+    enum {
+        hasOp = 0
+    };
+};
+
+template<typename Type>
+class Op {
+public:
+    typedef Type T;
+    enum {
+        hasOp = 1
+    };
+};
+
+template<typename Type>
+class Getter : public Op<Type>
+{
+};
+
+template<class Getter>
+class Ops
+{
+public:
+    typedef Getter G;
+};
+
+typedef Ops<NoOp> NoOps;
+ 
+template<class ListType, class IndexOps, class NameOps=NoOps>
+class ListClass {
+public:
+    typedef ListType LT;
+    typedef IndexOps IO;
+    typedef NameOps NO;
+};
+
+template<class LC>
 class ListBase : public ProxyHandler {
+protected:
+    typedef typename LC::LT ListType;
+    typedef typename LC::IO::G::T IndexGetterType;
+    typedef typename LC::NO::G::T NameGetterType;
+    enum {
+        hasIndexGetter = LC::IO::G::hasOp,
+        hasNameGetter = LC::NO::G::hasOp
+    };
+
+private:
     friend void Register(nsDOMClassInfoData *aData);
 
-    static ListBase<T> instance;
+    static ListBase<LC> instance;
 
     static js::Class sInterfaceClass;
 
     struct Properties {
         jsid &id;
         JSPropertyOp getter;
         JSStrictPropertyOp setter;
     };
@@ -97,46 +147,58 @@ class ListBase : public ProxyHandler {
         jsid &id;
         JSNative native;
         uintN nargs;
     };
 
     static Properties sProtoProperties[];
     static Methods sProtoMethods[];
 
-    static bool instanceIsListObject(JSContext *cx, JSObject *obj, JSObject *callee);
     static JSObject *getPrototype(JSContext *cx, XPCWrappedNativeScope *scope, bool *enabled);
 
     static JSObject *ensureExpandoObject(JSContext *cx, JSObject *obj);
 
     static uint32 getProtoShape(JSObject *obj);
     static void setProtoShape(JSObject *obj, uint32 shape);
 
     static JSBool length_getter(JSContext *cx, JSObject *obj, jsid id, jsval *vp);
     static JSBool item(JSContext *cx, uintN argc, jsval *vp);
     static JSBool namedItem(JSContext *cx, uintN argc, jsval *vp);
 
-    static bool hasNamedItem(jsid id);
-    static bool namedItem(JSContext *cx, JSObject *obj, jsval *name, nsISupports **result,
-                          nsWrapperCache **cache, bool *hasResult);
+    static inline bool getItemAt(ListType *list, uint32 i, IndexGetterType &item);
 
-    static nsISupports *getNamedItem(T *list, const nsAString& aName, nsWrapperCache **aCache);
+    static inline bool namedItem(JSContext *cx, JSObject *obj, jsval *name, NameGetterType &result,
+                                 bool *hasResult);
+
+    static inline bool getNamedItem(ListType *list, const nsAString& aName, NameGetterType &item);
 
     static bool cacheProtoShape(JSContext *cx, JSObject *proxy, JSObject *proto);
     static bool checkForCacheHit(JSContext *cx, JSObject *proxy, JSObject *receiver, JSObject *proto,
                                  jsid id, js::Value *vp, bool *hitp);
 
+    static bool hasNative(JSContext *cx, JSObject *proxy, jsid id)
+    {
+        bool found;
+        // We ignore an error from nativeGet.
+        return !nativeGet(cx, proxy, js::GetObjectProto(proxy), id, &found, NULL) || found;
+    }
+
     static bool resolveNativeName(JSContext *cx, JSObject *proxy, jsid id,
                                   js::PropertyDescriptor *desc);
 
-protected:
-    static JSObject *create(JSContext *cx, XPCWrappedNativeScope *scope, T *,
+    static JSObject *create(JSContext *cx, XPCWrappedNativeScope *scope, ListType *aList,
                             nsWrapperCache* aWrapperCache, bool *triedToWrap);
 
 public:
+    template <typename I>
+    static JSObject *create(JSContext *cx, XPCWrappedNativeScope *scope, I *aList, bool *triedToWrap)
+    {
+        return create(cx, scope, aList, GetWrapperCache(aList), triedToWrap);
+    }
+    
     bool getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set,
                                js::PropertyDescriptor *desc);
     bool getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set,
                                   js::PropertyDescriptor *desc);
     bool defineProperty(JSContext *cx, JSObject *proxy, jsid id,
                         js::PropertyDescriptor *desc);
     bool getOwnPropertyNames(JSContext *cx, JSObject *proxy, js::AutoIdVector &props);
     bool delete_(JSContext *cx, JSObject *proxy, jsid id, bool *bp);
@@ -154,36 +216,39 @@ public:
     /* Spidermonkey extensions. */
     bool hasInstance(JSContext *cx, JSObject *proxy, const js::Value *vp, bool *bp);
     JSString *obj_toString(JSContext *cx, JSObject *proxy);
     void finalize(JSContext *cx, JSObject *proxy);
 
     static bool objIsList(JSObject *obj) {
         return js::IsProxy(obj) && js::GetProxyHandler(obj) == &instance;
     }
+    static bool instanceIsListObject(JSContext *cx, JSObject *obj, JSObject *callee);
     virtual bool isInstanceOf(JSObject *prototype)
     {
         return js::GetObjectClass(prototype) == &sInterfaceClass;
     }
-    static T *getListObject(JSObject *obj);
+    static ListType *getListObject(JSObject *obj);
+
+    static bool nativeGet(JSContext *cx, JSObject *proxy, JSObject *proto, jsid id, bool *found,
+                          js::Value *vp);
 };
 
-typedef ListBase<nsINodeList> NodeListBase;
-class NodeList : public NodeListBase
+struct nsISupportsResult
 {
-public:
-    static JSObject *create(JSContext *cx, XPCWrappedNativeScope *scope,
-                            nsINodeList *aNodeList, bool *triedToWrap);
+    nsISupportsResult()
+    {
+    }
+    nsISupports *mResult;
+    nsWrapperCache *mCache;
 };
-typedef ListBase<nsIHTMLCollection> HTMLCollectionBase;
-class HTMLCollection : public HTMLCollectionBase
-{
-public:
-    static JSObject *create(JSContext *cx, XPCWrappedNativeScope *scope,
-                            nsIHTMLCollection *aHTMLCollection,
-                            nsWrapperCache *aWrapperCache, bool *triedToWrap);
-};
+
+typedef ListClass<nsINodeList, Ops<Getter<nsIContent*> > > NodeListClass;
+typedef ListBase<NodeListClass> NodeList;
+
+typedef ListClass<nsIHTMLCollection, Ops<Getter<nsIContent*> >, Ops<Getter<nsISupportsResult> > > HTMLCollectionClass;
+typedef ListBase<HTMLCollectionClass> HTMLCollection;
 
 }
 }
 }
 
 #endif /* dombindings_h */