Fix for bug 763643 (Implement static operations and attributes for WebIDL). r=bz.
authorPeter Van der Beken <peterv@propagandism.org>
Fri, 19 Oct 2012 09:34:28 +0200
changeset 111882 c6fb95ba0a208324a194c2820368882539cdd556
parent 111881 2183064c4b7b0aa8b437e022402b83dedeab0c23
child 111883 00c8ee61e262578493e0b21a9019014d63002f61
push id93
push usernmatsakis@mozilla.com
push dateWed, 31 Oct 2012 21:26:57 +0000
reviewersbz
bugs763643
milestone19.0a1
Fix for bug 763643 (Implement static operations and attributes for WebIDL). r=bz.
dom/bindings/BindingUtils.cpp
dom/bindings/Codegen.py
dom/bindings/DOMJSClass.h
dom/bindings/parser/WebIDL.py
dom/bindings/test/TestBindingHeader.h
dom/bindings/test/TestCodeGen.webidl
dom/bindings/test/TestExampleGen.webidl
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -213,28 +213,39 @@ CreateInterfaceObject(JSContext* cx, JSO
   }
 
   if (properties) {
     if (properties->staticMethods &&
         !DefinePrefable(cx, constructor, properties->staticMethods)) {
       return nullptr;
     }
 
+    if (properties->staticAttributes &&
+        !DefinePrefable(cx, constructor, properties->staticAttributes)) {
+      return nullptr;
+    }
+
     if (properties->constants &&
         !DefinePrefable(cx, constructor, properties->constants)) {
       return nullptr;
     }
   }
 
   if (chromeOnlyProperties) {
     if (chromeOnlyProperties->staticMethods &&
         !DefinePrefable(cx, constructor, chromeOnlyProperties->staticMethods)) {
       return nullptr;
     }
 
+    if (chromeOnlyProperties->staticAttributes &&
+        !DefinePrefable(cx, constructor,
+                        chromeOnlyProperties->staticAttributes)) {
+      return nullptr;
+    }
+
     if (chromeOnlyProperties->constants &&
         !DefinePrefable(cx, constructor, chromeOnlyProperties->constants)) {
       return nullptr;
     }
   }
 
   if (proto && !JS_LinkConstructorAndPrototype(cx, constructor, proto)) {
     return NULL;
@@ -317,18 +328,21 @@ CreateInterfaceObjects(JSContext* cx, JS
   MOZ_ASSERT(protoClass || constructorClass || constructor,
              "Need at least one class or a constructor!");
   MOZ_ASSERT(!((properties &&
                 (properties->methods || properties->attributes)) ||
                (chromeOnlyProperties &&
                 (chromeOnlyProperties->methods ||
                  chromeOnlyProperties->attributes))) || protoClass,
              "Methods or properties but no protoClass!");
-  MOZ_ASSERT(!((properties && properties->staticMethods) ||
-               (chromeOnlyProperties && chromeOnlyProperties->staticMethods)) ||
+  MOZ_ASSERT(!((properties &&
+                (properties->staticMethods || properties->staticAttributes)) ||
+               (chromeOnlyProperties &&
+                (chromeOnlyProperties->staticMethods ||
+                 chromeOnlyProperties->staticAttributes))) ||
              constructorClass || constructor,
              "Static methods but no constructorClass or constructor!");
   MOZ_ASSERT(bool(name) == bool(constructorClass || constructor),
              "Must have name precisely when we have an interface object");
   MOZ_ASSERT(!constructorClass || !constructor);
   MOZ_ASSERT(!protoClass == !protoCache,
              "If, and only if, there is an interface prototype object we need "
              "to cache it");
@@ -632,17 +646,29 @@ XrayResolveProperty(JSContext* cx, JSObj
             desc->getter = nullptr;
            return true;
           }
         }
       }
     }
   }
 
-  if (type != eInterface) {
+  if (type == eInterface) {
+    if (nativeProperties->staticAttributes) {
+      if (!XrayResolveAttribute(cx, wrapper, id,
+                                nativeProperties->staticAttributes,
+                                nativeProperties->staticAttributeIds,
+                                nativeProperties->staticAttributeSpecs, desc)) {
+        return false;
+      }
+      if (desc->obj) {
+        return true;
+      }
+    }
+  } else {
     if (nativeProperties->attributes) {
       if (!XrayResolveAttribute(cx, wrapper, id,
                                 nativeProperties->attributes,
                                 nativeProperties->attributeIds,
                                 nativeProperties->attributeSpecs, desc)) {
         return false;
       }
       if (desc->obj) {
@@ -831,17 +857,25 @@ XrayEnumerateProperties(JS::AutoIdVector
               !props.append(methodIds[i])) {
             return false;
           }
         }
       }
     }
   }
 
-  if (type != eInterface) {
+  if (type == eInterface) {
+    if (nativeProperties->staticAttributes &&
+        !XrayEnumerateAttributes(nativeProperties->staticAttributes,
+                                 nativeProperties->staticAttributeIds,
+                                 nativeProperties->staticAttributeSpecs,
+                                 props)) {
+      return false;
+    }
+  } else {
     if (nativeProperties->attributes &&
         !XrayEnumerateAttributes(nativeProperties->attributes,
                                  nativeProperties->attributeIds,
                                  nativeProperties->attributeSpecs, props)) {
       return false;
     }
     if (nativeProperties->unforgeableAttributes &&
         !XrayEnumerateAttributes(nativeProperties->unforgeableAttributes,
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -1068,26 +1068,28 @@ class MethodDefiner(PropertyDefiner):
         PropertyDefiner.__init__(self, descriptor, name)
 
         # FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=772822
         #       We should be able to check for special operations without an
         #       identifier. For now we check if the name starts with __
         methods = [m for m in descriptor.interface.members if
                    m.isMethod() and m.isStatic() == static and
                    not m.isIdentifierLess()]
-        self.chrome = [{"name": m.identifier.name,
-                        "length": methodLength(m),
-                        "flags": "JSPROP_ENUMERATE",
-                        "pref": PropertyDefiner.getControllingPref(m) }
-                       for m in methods if isChromeOnly(m)]
-        self.regular = [{"name": m.identifier.name,
-                         "length": methodLength(m),
-                         "flags": "JSPROP_ENUMERATE",
-                         "pref": PropertyDefiner.getControllingPref(m) }
-                        for m in methods if not isChromeOnly(m)]
+        self.chrome = []
+        self.regular = []
+        for m in methods:
+            method = { "name": m.identifier.name,
+                       "methodInfo": not m.isStatic(),
+                       "length": methodLength(m),
+                       "flags": "JSPROP_ENUMERATE",
+                       "pref": PropertyDefiner.getControllingPref(m) }
+            if isChromeOnly(m):
+                self.chrome.append(method)
+            else:
+                self.regular.append(method)
 
         # FIXME Check for an existing iterator on the interface first.
         if any(m.isGetter() and m.isIndexed() for m in methods):
             self.regular.append({"name": 'iterator',
                                  "methodInfo": False,
                                  "nativeName": "JS_ArrayIterator",
                                  "length": 0,
                                  "flags": "JSPROP_ENUMERATE",
@@ -1141,25 +1143,37 @@ class MethodDefiner(PropertyDefiner):
         return self.generatePrefableArray(
             array, name,
             '  JS_FNINFO("%s", %s, %s, %s, %s)',
             '  JS_FS_END',
             'JSFunctionSpec',
             pref, specData, doIdArrays)
 
 class AttrDefiner(PropertyDefiner):
-    def __init__(self, descriptor, name, unforgeable):
+    def __init__(self, descriptor, name, static, unforgeable=False):
+        assert not (static and unforgeable)
         PropertyDefiner.__init__(self, descriptor, name)
         self.name = name
-        attributes = [m for m in descriptor.interface.members
-                      if m.isAttr() and m.isUnforgeable() == unforgeable]
+        attributes = [m for m in descriptor.interface.members if
+                      m.isAttr() and m.isStatic() == static and
+                      m.isUnforgeable() == unforgeable]
         self.chrome = [m for m in attributes if isChromeOnly(m)]
         self.regular = [m for m in attributes if not isChromeOnly(m)]
+        self.static = static
         self.unforgeable = unforgeable
 
+        if static:
+            if not descriptor.interface.hasInterfaceObject():
+                # static attributes go on the interface object
+                assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
+        else:
+            if not descriptor.interface.hasInterfacePrototypeObject():
+                # non-static attributes go on the interface prototype object
+                assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
+
         if unforgeable and len(attributes) != 0 and descriptor.proxy:
             raise TypeError("Unforgeable properties are not supported on "
                             "proxy bindings without [NamedPropertiesObject].  "
                             "And not even supported on the ones with "
                             "[NamedPropertiesObject] yet, but we should fix "
                             "that, since they're safe there.")
 
     def generateArray(self, array, name, doIdArrays):
@@ -1167,30 +1181,36 @@ class AttrDefiner(PropertyDefiner):
             return ""
 
         def flags(attr):
             unforgeable = " | JSPROP_PERMANENT" if self.unforgeable else ""
             return ("JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_NATIVE_ACCESSORS" +
                     unforgeable)
 
         def getter(attr):
-            native = ("genericLenientGetter" if attr.hasLenientThis()
-                      else "genericGetter")
-            return ("{(JSPropertyOp)%(native)s, &%(name)s_getterinfo}"
-                    % {"name" : attr.identifier.name,
-                       "native" : native})
+            if self.static:
+                accessor = 'get_' + attr.identifier.name
+                jitinfo = "nullptr"
+            else:
+                accessor = ("genericLenientGetter" if attr.hasLenientThis()
+                            else "genericGetter")
+                jitinfo = "&%s_getterinfo" % attr.identifier.name
+            return "{ (JSPropertyOp)%s, %s }" % (accessor, jitinfo)
 
         def setter(attr):
-            if attr.readonly and attr.getExtendedAttribute("PutForwards") is None:
-                return "JSOP_NULLWRAPPER"
-            native = ("genericLenientSetter" if attr.hasLenientThis()
-                      else "genericSetter")
-            return ("{(JSStrictPropertyOp)%(native)s, &%(name)s_setterinfo}"
-                    % {"name" : attr.identifier.name,
-                       "native" : native})
+            if self.static:
+                accessor = 'set_' + attr.identifier.name
+                jitinfo = "nullptr"
+            else:
+                if attr.readonly and attr.getExtendedAttribute("PutForwards") is None:
+                    return "JSOP_NULLWRAPPER"
+                accessor = ("genericLenientSetter" if attr.hasLenientThis()
+                            else "genericSetter")
+                jitinfo = "&%s_setterinfo" % attr.identifier.name
+            return "{ (JSStrictPropertyOp)%s, %s }" % (accessor, jitinfo)
 
         def specData(attr):
             return (attr.identifier.name, flags(attr), getter(attr),
                     setter(attr))
 
         return self.generatePrefableArray(
             array, name,
             '  { "%s", 0, %s, %s, %s}',
@@ -1221,28 +1241,30 @@ class ConstDefiner(PropertyDefiner):
             array, name,
             '  { "%s", %s }',
             '  { 0, JSVAL_VOID }',
             'ConstantSpec',
             PropertyDefiner.getControllingPref, specData, doIdArrays)
 
 class PropertyArrays():
     def __init__(self, descriptor):
-        self.staticMethods = MethodDefiner(descriptor, "StaticMethods", True)
-        self.staticAttrs = None
-        self.methods = MethodDefiner(descriptor, "Methods", False)
-        self.attrs = AttrDefiner(descriptor, "Attributes", unforgeable=False)
+        self.staticMethods = MethodDefiner(descriptor, "StaticMethods",
+                                           static=True)
+        self.staticAttrs = AttrDefiner(descriptor, "StaticAttributes",
+                                       static=True)
+        self.methods = MethodDefiner(descriptor, "Methods", static=False)
+        self.attrs = AttrDefiner(descriptor, "Attributes", static=False)
         self.unforgeableAttrs = AttrDefiner(descriptor, "UnforgeableAttributes",
-                                            unforgeable=True)
+                                            static=False, unforgeable=True)
         self.consts = ConstDefiner(descriptor, "Constants")
 
     @staticmethod
     def arrayNames():
-        return [ "staticMethods", "methods", "attrs", "unforgeableAttrs",
-                 "consts" ]
+        return [ "staticMethods", "staticAttrs", "methods", "attrs",
+                 "unforgeableAttrs", "consts" ]
 
     @staticmethod
     def xrayRelevantArrayNames():
         return [ "methods", "attrs", "unforgeableAttrs", "consts" ]
 
     def hasChromeOnly(self):
         return any(getattr(self, a).hasChromeOnly() for a in self.arrayNames())
     def hasNonChromeOnly(self):
@@ -3676,19 +3698,23 @@ class CGMethodCall(CGThing):
         return self.cgRoot.define()
 
 class CGGetterCall(CGPerSignatureCall):
     """
     A class to generate a native object getter call for a particular IDL
     getter.
     """
     def __init__(self, returnType, nativeMethodName, descriptor, attr):
-        CGPerSignatureCall.__init__(self, returnType, [], [],
-                                    nativeMethodName, False, descriptor,
-                                    attr, getter=True)
+        if attr.isStatic():
+            argsPre = [ "global" ]
+        else:
+            argsPre = []
+        CGPerSignatureCall.__init__(self, returnType, argsPre, [],
+                                    nativeMethodName, attr.isStatic(),
+                                    descriptor, attr, getter=True)
 
 class FakeArgument():
     """
     A class that quacks like an IDLArgument.  This is used to make
     setters look like method calls or for special operations.
     """
     def __init__(self, type, interfaceMember):
         self.type = type
@@ -3705,20 +3731,24 @@ class FakeArgument():
         self.identifier = FakeIdentifier()
 
 class CGSetterCall(CGPerSignatureCall):
     """
     A class to generate a native object setter call for a particular IDL
     setter.
     """
     def __init__(self, argType, nativeMethodName, descriptor, attr):
-        CGPerSignatureCall.__init__(self, None, [],
+        if attr.isStatic():
+            argsPre = [ "global" ]
+        else:
+            argsPre = []
+        CGPerSignatureCall.__init__(self, None, argsPre,
                                     [FakeArgument(argType, attr)],
-                                    nativeMethodName, False, descriptor, attr,
-                                    setter=True)
+                                    nativeMethodName, attr.isStatic(),
+                                    descriptor, attr, setter=True)
     def wrap_return_value(self):
         # We have no return value
         return "\nreturn true;"
     def getArgc(self):
         return "1"
     def getArgvDecl(self):
         # We just get our stuff from our last arg no matter what
         return ""
@@ -3747,31 +3777,70 @@ class CGAbstractBindingMethod(CGAbstract
         else:
             self.unwrapFailureCode = unwrapFailureCode
 
     def definition_body(self):
         # Our descriptor might claim that we're not castable, simply because
         # we're someone's consequential interface.  But for this-unwrapping, we
         # know that we're the real deal.  So fake a descriptor here for
         # consumption by FailureFatalCastableObjectUnwrapper.
-        unwrapThis = CGIndenter(CGGeneric(
+        getThis = CGGeneric("""js::RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
+if (!obj) {
+  return false;
+}
+
+%s* self;""" % self.descriptor.nativeType)
+        unwrapThis = CGGeneric(
             str(CastableObjectUnwrapper(
                         FakeCastableDescriptor(self.descriptor),
-                        "obj", "self", self.unwrapFailureCode))))
-        return CGList([ self.getThis(), unwrapThis,
+                        "obj", "self", self.unwrapFailureCode)))
+        return CGList([ CGIndenter(getThis), CGIndenter(unwrapThis),
                         self.generate_code() ], "\n").define()
 
-    def getThis(self):
-        return CGIndenter(
-            CGGeneric("js::RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));\n"
-                      "if (!obj) {\n"
-                      "  return false;\n"
-                      "}\n"
-                      "\n"
-                      "%s* self;" % self.descriptor.nativeType))
+    def generate_code(self):
+        assert(False) # Override me
+
+class CGAbstractStaticBindingMethod(CGAbstractStaticMethod):
+    """
+    Common class to generate the JSNatives for all our static methods, getters
+    and setters.  This will generate the function declaration and unwrap the
+    global object.  Subclasses are expected to override the generate_code
+    function to do the rest of the work.  This function should return a
+    CGThing which is already properly indented.
+    """
+    def __init__(self, descriptor, name, args):
+        CGAbstractStaticMethod.__init__(self, descriptor, name, "JSBool", args)
+
+    def definition_body(self):
+        isMainThread = toStringBool(not self.descriptor.workers)
+        unwrap = CGGeneric("""js::RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
+if (!obj) {
+  return false;
+}
+
+if (js::IsWrapper(obj)) {
+  obj = XPCWrapper::Unwrap(cx, obj, false);
+  if (!obj) {
+    return Throw<%s>(cx, NS_ERROR_XPC_SECURITY_MANAGER_VETO);
+  }
+}
+
+nsISupports* global;
+xpc_qsSelfRef globalRef;
+{
+  JS::Value val;
+  val.setObjectOrNull(JS_GetGlobalForObject(cx, obj));
+  nsresult rv = xpc_qsUnwrapArg<nsISupports>(cx, val, &global, &globalRef.ptr,
+                                             &val);
+  if (NS_FAILED(rv)) {
+    return Throw<%s>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);
+  }
+}""" % (isMainThread, isMainThread))
+        return CGList([ CGIndenter(unwrap),
+                        self.generate_code() ], "\n\n").define()
 
     def generate_code(self):
         assert(False) # Override me
 
 def MakeNativeName(name):
     return name[0].upper() + name[1:]
 
 class CGGenericMethod(CGAbstractBindingMethod):
@@ -3808,16 +3877,33 @@ class CGSpecializedMethod(CGAbstractStat
         return CGMethodCall([], nativeName, self.method.isStatic(),
                             self.descriptor, self.method).define()
 
     @staticmethod
     def makeNativeName(descriptor, method):
         name = method.identifier.name
         return MakeNativeName(descriptor.binaryNames.get(name, name))
 
+class CGStaticMethod(CGAbstractStaticBindingMethod):
+    """
+    A class for generating the C++ code for an IDL static method.
+    """
+    def __init__(self, descriptor, method):
+        self.method = method
+        name = method.identifier.name
+        args = [Argument('JSContext*', 'cx'), Argument('unsigned', 'argc'),
+                Argument('JS::Value*', 'vp')]
+        CGAbstractStaticBindingMethod.__init__(self, descriptor, name, args)
+
+    def generate_code(self):
+        nativeName = CGSpecializedMethod.makeNativeName(self.descriptor,
+                                                        self.method)
+        return CGMethodCall([ "global" ], nativeName, True, self.descriptor,
+                            self.method)
+
 class CGGenericGetter(CGAbstractBindingMethod):
     """
     A class for generating the C++ code for an IDL attribute getter.
     """
     def __init__(self, descriptor, lenientThis=False):
         args = [Argument('JSContext*', 'cx'), Argument('unsigned', 'argc'),
                 Argument('JS::Value*', 'vp')]
         if lenientThis:
@@ -3866,16 +3952,33 @@ class CGSpecializedGetter(CGAbstractStat
         (_, resultOutParam) = getRetvalDeclarationForType(attr.type, descriptor,
                                                           False)
         infallible = ('infallible' in
                       descriptor.getExtendedAttributes(attr, getter=True))
         if resultOutParam or attr.type.nullable() or not infallible:
             nativeName = "Get" + nativeName
         return nativeName
 
+class CGStaticGetter(CGAbstractStaticBindingMethod):
+    """
+    A class for generating the C++ code for an IDL static attribute getter.
+    """
+    def __init__(self, descriptor, attr):
+        self.attr = attr
+        name = 'get_' + attr.identifier.name
+        args = [Argument('JSContext*', 'cx'), Argument('unsigned', 'argc'),
+                Argument('JS::Value*', 'vp')]
+        CGAbstractStaticBindingMethod.__init__(self, descriptor, name, args)
+
+    def generate_code(self):
+        nativeName = CGSpecializedGetter.makeNativeName(self.descriptor,
+                                                        self.attr)
+        return CGIndenter(CGGetterCall(self.attr.type, nativeName,
+                                       self.descriptor, self.attr))
+
 class CGGenericSetter(CGAbstractBindingMethod):
     """
     A class for generating the C++ code for an IDL attribute setter.
     """
     def __init__(self, descriptor, lenientThis=False):
         args = [Argument('JSContext*', 'cx'), Argument('unsigned', 'argc'),
                 Argument('JS::Value*', 'vp')]
         if lenientThis:
@@ -3924,16 +4027,39 @@ class CGSpecializedSetter(CGAbstractStat
         return CGIndenter(CGSetterCall(self.attr.type, nativeName,
                                        self.descriptor, self.attr)).define()
 
     @staticmethod
     def makeNativeName(descriptor, attr):
         name = attr.identifier.name
         return "Set" + MakeNativeName(descriptor.binaryNames.get(name, name))
 
+class CGStaticSetter(CGAbstractStaticBindingMethod):
+    """
+    A class for generating the C++ code for an IDL static attribute setter.
+    """
+    def __init__(self, descriptor, attr):
+        self.attr = attr
+        name = 'set_' + attr.identifier.name
+        args = [Argument('JSContext*', 'cx'), Argument('unsigned', 'argc'),
+                Argument('JS::Value*', 'vp')]
+        CGAbstractStaticBindingMethod.__init__(self, descriptor, name, args)
+
+    def generate_code(self):
+        nativeName = CGSpecializedSetter.makeNativeName(self.descriptor,
+                                                        self.attr)
+        argv = CGGeneric("""JS::Value* argv = JS_ARGV(cx, vp);
+JS::Value undef = JS::UndefinedValue();
+if (argc == 0) {
+  argv = &undef;
+}""")
+        call = CGSetterCall(self.attr.type, nativeName, self.descriptor,
+                            self.attr)
+        return CGIndenter(CGList([ argv, call ], "\n"))
+
 class CGSpecializedForwardingSetter(CGSpecializedSetter):
     """
     A class for generating the code for a specialized attribute setter with
     PutForwards that the JIT can call with lower overhead.
     """
     def __init__(self, descriptor, attr):
         CGSpecializedSetter.__init__(self, descriptor, attr)
 
@@ -5329,40 +5455,52 @@ def stripTrailingWhitespace(text):
 class CGDescriptor(CGThing):
     def __init__(self, descriptor):
         CGThing.__init__(self)
 
         assert not descriptor.concrete or descriptor.interface.hasInterfacePrototypeObject()
 
         cgThings = []
         if descriptor.interface.hasInterfacePrototypeObject():
+            # These are set to true if at least one non-static
+            # method/getter/setter exist on the interface.
             (hasMethod, hasGetter, hasLenientGetter,
              hasSetter, hasLenientSetter) = False, False, False, False, False
             for m in descriptor.interface.members:
-                if (m.isMethod() and not m.isStatic() and
+                if (m.isMethod() and
                     (not m.isIdentifierLess() or m == descriptor.operations['Stringifier'])):
-                    cgThings.append(CGSpecializedMethod(descriptor, m))
-                    cgThings.append(CGMemberJITInfo(descriptor, m))
-                    hasMethod = True
+                    if m.isStatic():
+                        cgThings.append(CGStaticMethod(descriptor, m))
+                    else:
+                        cgThings.append(CGSpecializedMethod(descriptor, m))
+                        cgThings.append(CGMemberJITInfo(descriptor, m))
+                        hasMethod = True
                 elif m.isAttr():
-                    cgThings.append(CGSpecializedGetter(descriptor, m))
-                    if m.hasLenientThis():
-                        hasLenientGetter = True
+                    if m.isStatic():
+                        cgThings.append(CGStaticGetter(descriptor, m))
                     else:
-                        hasGetter = True
+                        cgThings.append(CGSpecializedGetter(descriptor, m))
+                        if m.hasLenientThis():
+                            hasLenientGetter = True
+                        else:
+                            hasGetter = True
                     if not m.readonly:
-                        cgThings.append(CGSpecializedSetter(descriptor, m))
-                        if m.hasLenientThis():
-                            hasLenientSetter = True
+                        if m.isStatic():
+                            cgThings.append(CGStaticSetter(descriptor, m))
                         else:
-                            hasSetter = True
+                            cgThings.append(CGSpecializedSetter(descriptor, m))
+                            if m.hasLenientThis():
+                                hasLenientSetter = True
+                            else:
+                                hasSetter = True
                     elif m.getExtendedAttribute("PutForwards"):
                         cgThings.append(CGSpecializedForwardingSetter(descriptor, m))
                         hasSetter = True
-                    cgThings.append(CGMemberJITInfo(descriptor, m))
+                    if not m.isStatic():
+                        cgThings.append(CGMemberJITInfo(descriptor, m))
             if hasMethod: cgThings.append(CGGenericMethod(descriptor))
             if hasGetter: cgThings.append(CGGenericGetter(descriptor))
             if hasLenientGetter: cgThings.append(CGGenericGetter(descriptor,
                                                                  lenientThis=True))
             if hasSetter: cgThings.append(CGGenericSetter(descriptor))
             if hasLenientSetter: cgThings.append(CGGenericSetter(descriptor,
                                                                  lenientThis=True))
 
@@ -6023,17 +6161,17 @@ class CGExampleMember(CGThing):
         self.extendedAttrs = extendedAttrs
         self.resultAlreadyAddRefed = isResultAlreadyAddRefed(self.descriptor,
                                                              self.extendedAttrs)
 
     def define(self):
         static = "static " if self.member.isStatic() else ""
         # Mark our getters, which are attrs that have a non-void return type,
         # as const.
-        if self.member.isAttr() and not self.signatures[0][0].isVoid():
+        if not self.member.isStatic() and self.member.isAttr() and not self.signatures[0][0].isVoid():
             const = " const"
         else:
             const = ""
         return "\n".join("%s%s %s(%s)%s;" %
                          (static,
                           self.getReturnType(s[0], False),
                           self.name,
                           self.getArgs(s[0], s[1]),
@@ -6553,15 +6691,15 @@ struct PrototypeIDMap;
 
         unions = UnionConversions(config.getDescriptors())
 
         # Wrap all of that in our namespaces.
         curr = CGNamespace.build(['mozilla', 'dom'], unions)
 
         curr = CGWrapper(curr, post='\n')
 
-        curr = CGHeaders([], [], ["nsDebug.h", "mozilla/dom/UnionTypes.h", "nsDOMQS.h"], [], curr)
+        curr = CGHeaders([], [], ["nsDebug.h", "mozilla/dom/UnionTypes.h", "nsDOMQS.h", "XPCWrapper.h"], [], curr)
 
         # Add include guards.
         curr = CGIncludeGuard('UnionConversions', curr)
 
         # Done.
         return curr
--- a/dom/bindings/DOMJSClass.h
+++ b/dom/bindings/DOMJSClass.h
@@ -66,16 +66,19 @@ struct Prefable {
   T* specs;
 };
 
 struct NativeProperties
 {
   Prefable<JSFunctionSpec>* staticMethods;
   jsid* staticMethodIds;
   JSFunctionSpec* staticMethodsSpecs;
+  Prefable<JSPropertySpec>* staticAttributes;
+  jsid* staticAttributeIds;
+  JSPropertySpec* staticAttributeSpecs;
   Prefable<JSFunctionSpec>* methods;
   jsid* methodIds;
   JSFunctionSpec* methodsSpecs;
   Prefable<JSPropertySpec>* attributes;
   jsid* attributeIds;
   JSPropertySpec* attributeSpecs;
   Prefable<JSPropertySpec>* unforgeableAttributes;
   jsid* unforgeableAttributeIds;
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -31,28 +31,35 @@ def parseInt(literal):
             string = string[1:]
     else:
         base = 10
 
     value = int(string, base)
     return value * sign
 
 # Magic for creating enums
-def M_add_class_attribs(attribs):
+def M_add_class_attribs(attribs, start):
     def foo(name, bases, dict_):
         for v, k in enumerate(attribs):
-            dict_[k] = v
+            dict_[k] = start + v
         assert 'length' not in dict_
-        dict_['length'] = len(attribs)
+        dict_['length'] = start + len(attribs)
         return type(name, bases, dict_)
     return foo
 
-def enum(*names):
-    class Foo(object):
-        __metaclass__ = M_add_class_attribs(names)
+def enum(*names, **kw):
+    if len(kw) == 1:
+        base = kw['base'].__class__
+        start = base.length
+    else:
+        assert len(kw) == 0
+        base = object
+        start = 0
+    class Foo(base):
+        __metaclass__ = M_add_class_attribs(names, start)
         def __setattr__(self, name, value):  # this makes it read-only
             raise NotImplementedError
     return Foo()
 
 class WebIDLError(Exception):
     def __init__(self, message, locations, warning=False):
         self.message = message
         self.locations = [str(loc) for loc in locations]
@@ -1923,16 +1930,21 @@ class IDLNullValue(IDLObject):
 class IDLInterfaceMember(IDLObjectWithIdentifier):
 
     Tags = enum(
         'Const',
         'Attr',
         'Method'
     )
 
+    Special = enum(
+        'Static',
+        'Stringifier'
+    )
+
     def __init__(self, location, identifier, tag):
         IDLObjectWithIdentifier.__init__(self, location, None, identifier)
         self.tag = tag
         self._extendedAttrDict = {}
 
     def isMethod(self):
         return self.tag == IDLInterfaceMember.Tags.Method
 
@@ -1986,28 +1998,29 @@ class IDLConst(IDLInterfaceMember):
         assert coercedValue
 
         self.value = coercedValue
 
     def validate(self):
         pass
 
 class IDLAttribute(IDLInterfaceMember):
-    def __init__(self, location, identifier, type, readonly, inherit,
-                 static=False):
+    def __init__(self, location, identifier, type, readonly, inherit=False,
+                 static=False, stringifier=False):
         IDLInterfaceMember.__init__(self, location, identifier,
                                     IDLInterfaceMember.Tags.Attr)
 
         assert isinstance(type, IDLType)
         self.type = type
         self.readonly = readonly
         self.inherit = inherit
         self.static = static
         self.lenientThis = False
         self._unforgeable = False
+        self.stringifier = stringifier
 
         if readonly and inherit:
             raise WebIDLError("An attribute cannot be both 'readonly' and 'inherit'",
                               [self.location])
 
     def isStatic(self):
         return self.static
 
@@ -2260,24 +2273,22 @@ class IDLMethodOverload:
         self.returnType = returnType
         # Clone the list of arguments, just in case
         self.arguments = list(arguments)
         self.location = location
 
 class IDLMethod(IDLInterfaceMember, IDLScope):
 
     Special = enum(
-        'None',
         'Getter',
         'Setter',
         'Creator',
         'Deleter',
         'LegacyCaller',
-        'Stringifier',
-        'Static'
+        base=IDLInterfaceMember.Special
     )
 
     TypeSuffixModifier = enum(
         'None',
         'QMark',
         'Brackets'
     )
 
@@ -3091,26 +3102,42 @@ class Parser(Tokenizer):
 
     def p_AttributeOrOperation(self, p):
         """
             AttributeOrOperation : Attribute
                                  | Operation
         """
         p[0] = p[1]
 
+    def p_AttributeWithQualifier(self, p):
+        """
+            Attribute : Qualifier AttributeRest
+        """
+        static = IDLInterfaceMember.Special.Static in p[1]
+        stringifier = IDLInterfaceMember.Special.Stringifier in p[1]
+        (location, identifier, type, readonly) = p[2]
+        p[0] = IDLAttribute(location, identifier, type, readonly, static=static,
+                            stringifier=stringifier)
+
     def p_Attribute(self, p):
         """
-            Attribute : Inherit ReadOnly ATTRIBUTE Type IDENTIFIER SEMICOLON
-        """
-        location = self.getLocation(p, 3)
-        inherit = p[1]
-        readonly = p[2]
-        t = p[4]
-        identifier = IDLUnresolvedIdentifier(self.getLocation(p, 5), p[5])
-        p[0] = IDLAttribute(location, identifier, t, readonly, inherit)
+            Attribute : Inherit AttributeRest
+        """
+        (location, identifier, type, readonly) = p[2]
+        p[0] = IDLAttribute(location, identifier, type, readonly, inherit=p[1])
+
+    def p_AttributeRest(self, p):
+        """
+            AttributeRest : ReadOnly ATTRIBUTE Type IDENTIFIER SEMICOLON
+        """
+        location = self.getLocation(p, 2)
+        readonly = p[1]
+        t = p[3]
+        identifier = IDLUnresolvedIdentifier(self.getLocation(p, 4), p[4])
+        p[0] = (location, identifier, t, readonly)
 
     def p_ReadOnly(self, p):
         """
             ReadOnly : READONLY
         """
         p[0] = True
 
     def p_ReadOnlyEmpty(self, p):
@@ -3137,27 +3164,31 @@ class Parser(Tokenizer):
         """
         qualifiers = p[1]
 
         # Disallow duplicates in the qualifier set
         if not len(set(qualifiers)) == len(qualifiers):
             raise WebIDLError("Duplicate qualifiers are not allowed",
                               [self.getLocation(p, 1)])
 
-        static = True if IDLMethod.Special.Static in p[1] else False
+        static = IDLInterfaceMember.Special.Static in p[1]
         # If static is there that's all that's allowed.  This is disallowed
         # by the parser, so we can assert here.
         assert not static or len(qualifiers) == 1
 
+        stringifier = IDLInterfaceMember.Special.Stringifier in p[1]
+        # If stringifier is there that's all that's allowed.  This is disallowed
+        # by the parser, so we can assert here.
+        assert not stringifier or len(qualifiers) == 1
+
         getter = True if IDLMethod.Special.Getter in p[1] else False
         setter = True if IDLMethod.Special.Setter in p[1] else False
         creator = True if IDLMethod.Special.Creator in p[1] else False
         deleter = True if IDLMethod.Special.Deleter in p[1] else False
         legacycaller = True if IDLMethod.Special.LegacyCaller in p[1] else False
-        stringifier = True if IDLMethod.Special.Stringifier in p[1] else False
 
         if getter or deleter:
             if setter or creator:
                 raise WebIDLError("getter and deleter are incompatible with setter and creator",
                                   [self.getLocation(p, 1)])
 
         (returnType, identifier, arguments) = p[2]
 
@@ -3255,25 +3286,32 @@ class Parser(Tokenizer):
                  "stringifier" if stringifier else ""), allowDoubleUnderscore=True)
 
         method = IDLMethod(self.getLocation(p, 2), identifier, returnType, arguments,
                            static=static, getter=getter, setter=setter, creator=creator,
                            deleter=deleter, specialType=specialType,
                            legacycaller=legacycaller, stringifier=stringifier)
         p[0] = method
 
-    def p_QualifiersStatic(self, p):
-        """
-            Qualifiers : STATIC
-        """
-        p[0] = [IDLMethod.Special.Static]
-
-    def p_QualifiersSpecials(self, p):
-        """
-            Qualifiers : Specials
+    def p_QualifierStatic(self, p):
+        """
+            Qualifier : STATIC
+        """
+        p[0] = [IDLInterfaceMember.Special.Static]
+
+    def p_QualifierStringifier(self, p):
+        """
+            Qualifier : STRINGIFIER
+        """
+        p[0] = [IDLInterfaceMember.Special.Stringifier]
+
+    def p_Qualifiers(self, p):
+        """
+            Qualifiers : Qualifier
+                       | Specials
         """
         p[0] = p[1]
 
     def p_Specials(self, p):
         """
             Specials : Special Specials
         """
         p[0] = [p[1]]
@@ -3310,22 +3348,16 @@ class Parser(Tokenizer):
         p[0] = IDLMethod.Special.Deleter
 
     def p_SpecialLegacyCaller(self, p):
         """
             Special : LEGACYCALLER
         """
         p[0] = IDLMethod.Special.LegacyCaller
 
-    def p_SpecialStringifier(self, p):
-        """
-            Special : STRINGIFIER
-        """
-        p[0] = IDLMethod.Special.Stringifier
-
     def p_OperationRest(self, p):
         """
             OperationRest : ReturnType OptionalIdentifier LPAREN ArgumentList RPAREN SEMICOLON
         """
         p[0] = (p[1], p[2], p[4])
 
     def p_OptionalIdentifier(self, p):
         """
--- a/dom/bindings/test/TestBindingHeader.h
+++ b/dom/bindings/test/TestBindingHeader.h
@@ -423,16 +423,21 @@ public:
   void PassDictContainingDict(const DictContainingDict&);
   void PassDictContainingSequence(const DictContainingSequence&);
 
   // Typedefs
   void ExerciseTypedefInterfaces1(TestInterface&);
   already_AddRefed<TestInterface> ExerciseTypedefInterfaces2(TestInterface*);
   void ExerciseTypedefInterfaces3(TestInterface&);
 
+  // Static methods and attributes
+  static void StaticMethod(nsISupports*, bool);
+  static bool StaticAttribute(nsISupports*);
+  static void SetStaticAttribute(nsISupports*, bool);
+
   // Miscellania
   int32_t AttrWithLenientThis();
   void SetAttrWithLenientThis(int32_t);
   uint32_t UnforgeableAttr();
   uint32_t UnforgeableAttr2();
   void PassRenamedInterface(nsRenamedInterface&);
   TestInterface* PutForwardsAttr();
   TestInterface* PutForwardsAttr2();
--- a/dom/bindings/test/TestCodeGen.webidl
+++ b/dom/bindings/test/TestCodeGen.webidl
@@ -329,16 +329,20 @@ interface TestInterface {
   void doClamp([Clamp] byte arg);
 
   // Typedefs
   const myLong myLongConstant = 5;
   void exerciseTypedefInterfaces1(AnotherNameForTestInterface arg);
   AnotherNameForTestInterface exerciseTypedefInterfaces2(NullableTestInterface arg);
   void exerciseTypedefInterfaces3(YetAnotherNameForTestInterface arg);
 
+  // Static methods and attributes
+  static attribute boolean staticAttribute;
+  static void staticMethod(boolean arg);
+
   // Miscellania
   [LenientThis] attribute long attrWithLenientThis;
   [Unforgeable] readonly attribute long unforgeableAttr;
   [Unforgeable, ChromeOnly] readonly attribute long unforgeableAttr2;
   void passRenamedInterface(TestRenamedInterface arg);
   [PutForwards=writableByte] readonly attribute TestInterface putForwardsAttr;
   [PutForwards=writableByte, LenientThis] readonly attribute TestInterface putForwardsAttr2;
   [PutForwards=writableByte, ChromeOnly] readonly attribute TestInterface putForwardsAttr3;
--- a/dom/bindings/test/TestExampleGen.webidl
+++ b/dom/bindings/test/TestExampleGen.webidl
@@ -296,16 +296,20 @@ interface TestExampleInterface {
   void doClamp([Clamp] byte arg);
 
   // Typedefs
   const myLong myLongConstant = 5;
   void exerciseTypedefInterfaces1(AnotherNameForTestInterface arg);
   AnotherNameForTestInterface exerciseTypedefInterfaces2(NullableTestInterface arg);
   void exerciseTypedefInterfaces3(YetAnotherNameForTestInterface arg);
 
+  // Static methods and attributes
+  static attribute boolean staticAttribute;
+  static void staticMethod(boolean arg);
+
   // Miscellania
   [LenientThis] attribute long attrWithLenientThis;
   [Unforgeable] readonly attribute long unforgeableAttr;
   [Unforgeable, ChromeOnly] readonly attribute long unforgeableAttr2;
   void passRenamedInterface(TestRenamedInterface arg);
   [PutForwards=writableByte] readonly attribute TestExampleInterface putForwardsAttr;
   [PutForwards=writableByte, LenientThis] readonly attribute TestExampleInterface putForwardsAttr2;
   [PutForwards=writableByte, ChromeOnly] readonly attribute TestExampleInterface putForwardsAttr3;