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 111746 c6fb95ba0a208324a194c2820368882539cdd556
parent 111745 2183064c4b7b0aa8b437e022402b83dedeab0c23
child 111750 00c8ee61e262578493e0b21a9019014d63002f61
push id17238
push userpvanderbeken@mozilla.com
push dateTue, 30 Oct 2012 10:41:27 +0000
treeherdermozilla-inbound@c6fb95ba0a20 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs763643
milestone19.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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;