Bug 1270601 part 1. Add Web IDL parser support for IDL namespace syntax. r=peterv
authorBoris Zbarsky <bzbarsky@mit.edu>
Thu, 02 Jun 2016 10:34:39 -0400
changeset 341196 9482e25918a393e0203019d6c697ee4ae5eb4485
parent 341195 2fe18e6af534b6514486207fd7eef64513e7ae8e
child 341197 1d761dc47c2d532b247288233025ae91593f83a3
push id1183
push userraliiev@mozilla.com
push dateMon, 05 Sep 2016 20:01:49 +0000
treeherdermozilla-release@3148731bed45 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspeterv
bugs1270601
milestone49.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
Bug 1270601 part 1. Add Web IDL parser support for IDL namespace syntax. r=peterv
dom/bindings/parser/WebIDL.py
dom/bindings/parser/tests/test_namespace.py
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -153,16 +153,19 @@ class IDLObject(object):
         self.userData = dict()
 
     def filename(self):
         return self.location.filename()
 
     def isInterface(self):
         return False
 
+    def isNamespace(self):
+        return False
+
     def isEnum(self):
         return False
 
     def isCallback(self):
         return False
 
     def isType(self):
         return False
@@ -580,30 +583,30 @@ class IDLExternalInterface(IDLObjectWith
 
     def isNavigatorProperty(self):
         return False
 
     def _getDependentObjects(self):
         return set()
 
 
-class IDLPartialInterface(IDLObject):
-    def __init__(self, location, name, members, nonPartialInterface):
+class IDLPartialInterfaceOrNamespace(IDLObject):
+    def __init__(self, location, name, members, nonPartialInterfaceOrNamespace):
         assert isinstance(name, IDLUnresolvedIdentifier)
 
         IDLObject.__init__(self, location)
         self.identifier = name
         self.members = members
         # propagatedExtendedAttrs are the ones that should get
         # propagated to our non-partial interface.
         self.propagatedExtendedAttrs = []
         self._haveSecureContextExtendedAttribute = False
-        self._nonPartialInterface = nonPartialInterface
+        self._nonPartialInterfaceOrNamespace = nonPartialInterfaceOrNamespace
         self._finished = False
-        nonPartialInterface.addPartialInterface(self)
+        nonPartialInterfaceOrNamespace.addPartialInterface(self)
 
     def addExtendedAttributes(self, attrs):
         for attr in attrs:
             identifier = attr.identifier()
 
             if identifier in ["Constructor", "NamedConstructor"]:
                 self.propagatedExtendedAttrs.append(attr)
             elif identifier == "SecureContext":
@@ -630,28 +633,32 @@ class IDLPartialInterface(IDLObject):
                                   "interface" % identifier,
                                   [attr.location])
 
     def finish(self, scope):
         if self._finished:
             return
         self._finished = True
         if (not self._haveSecureContextExtendedAttribute and
-            self._nonPartialInterface.getExtendedAttribute("SecureContext")):
+            self._nonPartialInterfaceOrNamespace.getExtendedAttribute("SecureContext")):
             # This gets propagated to all our members.
             for member in self.members:
                 if member.getExtendedAttribute("SecureContext"):
                     raise WebIDLError("[SecureContext] specified on both a "
                                       "partial interface member and on the "
                                       "non-partial interface",
-                                      [member.location, self._nonPartialInterface.location])
-                member.addExtendedAttributes([IDLExtendedAttribute(self._nonPartialInterface.location, ("SecureContext",))])
-        # Need to make sure our non-partial interface gets finished so it can
-        # report cases when we only have partial interfaces.
-        self._nonPartialInterface.finish(scope)
+                                      [member.location,
+                                       self._nonPartialInterfaceOrNamespace.location])
+                member.addExtendedAttributes(
+                    [IDLExtendedAttribute(self._nonPartialInterfaceOrNamespace.location,
+                                          ("SecureContext",))])
+        # Need to make sure our non-partial interface or namespace gets
+        # finished so it can report cases when we only have partial
+        # interfaces/namespaces.
+        self._nonPartialInterfaceOrNamespace.finish(scope)
 
     def validate(self):
         pass
 
 
 def convertExposedAttrToGlobalNameSet(exposedAttr, targetSet):
     assert len(targetSet) == 0
     if exposedAttr.hasValue():
@@ -661,17 +668,17 @@ def convertExposedAttrToGlobalNameSet(ex
         targetSet.update(exposedAttr.args())
 
 
 def globalNameSetToExposureSet(globalScope, nameSet, exposureSet):
     for name in nameSet:
         exposureSet.update(globalScope.globalNameMapping[name])
 
 
-class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
+class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
     def __init__(self, location, parentScope, name, parent, members,
                  isKnownNonPartial):
         assert isinstance(parentScope, IDLScope)
         assert isinstance(name, IDLUnresolvedIdentifier)
         assert isKnownNonPartial or not parent
         assert isKnownNonPartial or len(members) == 0
 
         self.parent = None
@@ -707,19 +714,16 @@ class IDLInterface(IDLObjectWithScope, I
         self.iterableInterface = None
 
         IDLObjectWithScope.__init__(self, location, parentScope, name)
         IDLExposureMixins.__init__(self, location)
 
         if isKnownNonPartial:
             self.setNonPartial(location, parent, members)
 
-    def __str__(self):
-        return "Interface '%s'" % self.identifier.name
-
     def ctor(self):
         identifier = IDLUnresolvedIdentifier(self.location, "constructor",
                                              allowForbidden=True)
         try:
             return self._lookupIdentifier(identifier)
         except:
             return None
 
@@ -805,16 +809,30 @@ class IDLInterface(IDLObjectWithScope, I
                                self.parent.identifier.name),
                               [self.location])
         assert not parent or isinstance(parent, IDLInterface)
 
         self.parent = parent
 
         assert iter(self.members)
 
+        if self.isNamespace():
+            assert not self.parent
+            for m in self.members:
+                if m.isAttr() or m.isMethod():
+                    if m.isStatic():
+                        raise WebIDLError("Don't mark things explicitly static "
+                                          "in namespaces",
+                                          [self.location, m.location])
+                    # Just mark all our methods/attributes as static.  The other
+                    # option is to duplicate the relevant InterfaceMembers
+                    # production bits but modified to produce static stuff to
+                    # start with, but that sounds annoying.
+                    m.forceStatic()
+
         if self.parent:
             self.parent.finish(scope)
             self.parent._hasChildInterfaces = True
 
             self.totalMembersInSlots = self.parent.totalMembersInSlots
 
             # Interfaces with [Global] or [PrimaryGlobal] must not
             # have anything inherit from them
@@ -1319,19 +1337,16 @@ class IDLInterface(IDLObjectWithScope, I
             else:
                 assert iterableDecl.isPairIterator()
                 if indexedGetter:
                     raise WebIDLError("Interface with pair iterator supports "
                                       "indexed properties",
                                       [self.location, iterableDecl.location,
                                        indexedGetter.location])
 
-    def isInterface(self):
-        return True
-
     def isExternal(self):
         return False
 
     def setIsConsequentialInterfaceOf(self, other):
         self._consequential = True
         self.interfacesBasedOnSelf.add(other)
 
     def isConsequential(self):
@@ -1372,17 +1387,182 @@ class IDLInterface(IDLObjectWithScope, I
         return any(m.isConst() for m in self.members)
 
     def hasInterfaceObject(self):
         if self.isCallback():
             return self.hasConstants()
         return not hasattr(self, "_noInterfaceObject")
 
     def hasInterfacePrototypeObject(self):
-        return not self.isCallback() and self.getUserData('hasConcreteDescendant', False)
+        return (not self.isCallback() and not self.isNamespace()
+                and self.getUserData('hasConcreteDescendant', False))
+
+    def addImplementedInterface(self, implementedInterface):
+        assert(isinstance(implementedInterface, IDLInterface))
+        self.implementedInterfaces.add(implementedInterface)
+
+    def getInheritedInterfaces(self):
+        """
+        Returns a list of the interfaces this interface inherits from
+        (not including this interface itself).  The list is in order
+        from most derived to least derived.
+        """
+        assert(self._finished)
+        if not self.parent:
+            return []
+        parentInterfaces = self.parent.getInheritedInterfaces()
+        parentInterfaces.insert(0, self.parent)
+        return parentInterfaces
+
+    def getConsequentialInterfaces(self):
+        assert(self._finished)
+        # The interfaces we implement directly
+        consequentialInterfaces = set(self.implementedInterfaces)
+
+        # And their inherited interfaces
+        for iface in self.implementedInterfaces:
+            consequentialInterfaces |= set(iface.getInheritedInterfaces())
+
+        # And now collect up the consequential interfaces of all of those
+        temp = set()
+        for iface in consequentialInterfaces:
+            temp |= iface.getConsequentialInterfaces()
+
+        return consequentialInterfaces | temp
+
+    def findInterfaceLoopPoint(self, otherInterface):
+        """
+        Finds an interface, amongst our ancestors and consequential interfaces,
+        that inherits from otherInterface or implements otherInterface
+        directly.  If there is no such interface, returns None.
+        """
+        if self.parent:
+            if self.parent == otherInterface:
+                return self
+            loopPoint = self.parent.findInterfaceLoopPoint(otherInterface)
+            if loopPoint:
+                return loopPoint
+        if otherInterface in self.implementedInterfaces:
+            return self
+        for iface in self.implementedInterfaces:
+            loopPoint = iface.findInterfaceLoopPoint(otherInterface)
+            if loopPoint:
+                return loopPoint
+        return None
+
+    def getExtendedAttribute(self, name):
+        return self._extendedAttrDict.get(name, None)
+
+    def setNonPartial(self, location, parent, members):
+        assert not parent or isinstance(parent, IDLIdentifierPlaceholder)
+        if self._isKnownNonPartial:
+            raise WebIDLError("Two non-partial definitions for the "
+                              "same %s" %
+                              ("interface" if self.isInterface()
+                               else "namespace"),
+                              [location, self.location])
+        self._isKnownNonPartial = True
+        # Now make it look like we were parsed at this new location, since
+        # that's the place where the interface is "really" defined
+        self.location = location
+        assert not self.parent
+        self.parent = parent
+        # Put the new members at the beginning
+        self.members = members + self.members
+
+    def addPartialInterface(self, partial):
+        assert self.identifier.name == partial.identifier.name
+        self._partialInterfaces.append(partial)
+
+    def getJSImplementation(self):
+        classId = self.getExtendedAttribute("JSImplementation")
+        if not classId:
+            return classId
+        assert isinstance(classId, list)
+        assert len(classId) == 1
+        return classId[0]
+
+    def isJSImplemented(self):
+        return bool(self.getJSImplementation())
+
+    def isProbablyShortLivingObject(self):
+        current = self
+        while current:
+            if current.getExtendedAttribute("ProbablyShortLivingObject"):
+                return True
+            current = current.parent
+        return False
+
+    def isNavigatorProperty(self):
+        naviProp = self.getExtendedAttribute("NavigatorProperty")
+        if not naviProp:
+            return False
+        assert len(naviProp) == 1
+        assert isinstance(naviProp, list)
+        assert len(naviProp[0]) != 0
+        return True
+
+    def getNavigatorProperty(self):
+        naviProp = self.getExtendedAttribute("NavigatorProperty")
+        if not naviProp:
+            return None
+        assert len(naviProp) == 1
+        assert isinstance(naviProp, list)
+        assert len(naviProp[0]) != 0
+        conditionExtendedAttributes = self._extendedAttrDict.viewkeys() & IDLInterfaceOrNamespace.conditionExtendedAttributes
+        attr = IDLAttribute(self.location,
+                            IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"), naviProp[0]),
+                            IDLUnresolvedType(self.location, IDLUnresolvedIdentifier(self.location, self.identifier.name)),
+                            True,
+                            extendedAttrDict={ a: self._extendedAttrDict[a] for a in conditionExtendedAttributes },
+                            navigatorObjectGetter=True)
+        attr._exposureGlobalNames = self._exposureGlobalNames
+        # We're abusing Constant a little bit here, because we need Cached. The
+        # getter will create a new object every time, but we're never going to
+        # clear the cached value.
+        extendedAttrs = [ IDLExtendedAttribute(self.location, ("Throws", )),
+                          IDLExtendedAttribute(self.location, ("Cached", )),
+                          IDLExtendedAttribute(self.location, ("Constant", )) ]
+        attr.addExtendedAttributes(extendedAttrs)
+        return attr
+
+    def hasChildInterfaces(self):
+        return self._hasChildInterfaces
+
+    def isOnGlobalProtoChain(self):
+        return self._isOnGlobalProtoChain
+
+    def _getDependentObjects(self):
+        deps = set(self.members)
+        deps.update(self.implementedInterfaces)
+        if self.parent:
+            deps.add(self.parent)
+        return deps
+
+    def hasMembersInSlots(self):
+        return self._ownMembersInSlots != 0
+
+    conditionExtendedAttributes = [ "Pref", "ChromeOnly", "Func", "AvailableIn",
+                                    "SecureContext",
+                                    "CheckAnyPermissions",
+                                    "CheckAllPermissions" ]
+    def isExposedConditionally(self):
+        return any(self.getExtendedAttribute(a) for a in self.conditionExtendedAttributes)
+
+class IDLInterface(IDLInterfaceOrNamespace):
+    def __init__(self, location, parentScope, name, parent, members,
+                 isKnownNonPartial):
+        IDLInterfaceOrNamespace.__init__(self, location, parentScope, name,
+                                         parent, members, isKnownNonPartial)
+
+    def __str__(self):
+        return "Interface '%s'" % self.identifier.name
+
+    def isInterface(self):
+        return True
 
     def addExtendedAttributes(self, attrs):
         for attr in attrs:
             identifier = attr.identifier()
 
             # Special cased attrs
             if identifier == "TreatNonCallableAsNull":
                 raise WebIDLError("TreatNonCallableAsNull cannot be specified on interfaces",
@@ -1551,165 +1731,46 @@ class IDLInterface(IDLObjectWithScope, I
                                       [attr.location])
             else:
                 raise WebIDLError("Unknown extended attribute %s on interface" % identifier,
                                   [attr.location])
 
             attrlist = attr.listValue()
             self._extendedAttrDict[identifier] = attrlist if len(attrlist) else True
 
-    def addImplementedInterface(self, implementedInterface):
-        assert(isinstance(implementedInterface, IDLInterface))
-        self.implementedInterfaces.add(implementedInterface)
-
-    def getInheritedInterfaces(self):
-        """
-        Returns a list of the interfaces this interface inherits from
-        (not including this interface itself).  The list is in order
-        from most derived to least derived.
-        """
-        assert(self._finished)
-        if not self.parent:
-            return []
-        parentInterfaces = self.parent.getInheritedInterfaces()
-        parentInterfaces.insert(0, self.parent)
-        return parentInterfaces
-
-    def getConsequentialInterfaces(self):
-        assert(self._finished)
-        # The interfaces we implement directly
-        consequentialInterfaces = set(self.implementedInterfaces)
-
-        # And their inherited interfaces
-        for iface in self.implementedInterfaces:
-            consequentialInterfaces |= set(iface.getInheritedInterfaces())
-
-        # And now collect up the consequential interfaces of all of those
-        temp = set()
-        for iface in consequentialInterfaces:
-            temp |= iface.getConsequentialInterfaces()
-
-        return consequentialInterfaces | temp
-
-    def findInterfaceLoopPoint(self, otherInterface):
-        """
-        Finds an interface, amongst our ancestors and consequential interfaces,
-        that inherits from otherInterface or implements otherInterface
-        directly.  If there is no such interface, returns None.
-        """
-        if self.parent:
-            if self.parent == otherInterface:
-                return self
-            loopPoint = self.parent.findInterfaceLoopPoint(otherInterface)
-            if loopPoint:
-                return loopPoint
-        if otherInterface in self.implementedInterfaces:
-            return self
-        for iface in self.implementedInterfaces:
-            loopPoint = iface.findInterfaceLoopPoint(otherInterface)
-            if loopPoint:
-                return loopPoint
-        return None
-
-    def getExtendedAttribute(self, name):
-        return self._extendedAttrDict.get(name, None)
-
-    def setNonPartial(self, location, parent, members):
-        assert not parent or isinstance(parent, IDLIdentifierPlaceholder)
-        if self._isKnownNonPartial:
-            raise WebIDLError("Two non-partial definitions for the "
-                              "same interface",
-                              [location, self.location])
-        self._isKnownNonPartial = True
-        # Now make it look like we were parsed at this new location, since
-        # that's the place where the interface is "really" defined
-        self.location = location
-        assert not self.parent
-        self.parent = parent
-        # Put the new members at the beginning
-        self.members = members + self.members
-
-    def addPartialInterface(self, partial):
-        assert self.identifier.name == partial.identifier.name
-        self._partialInterfaces.append(partial)
-
-    def getJSImplementation(self):
-        classId = self.getExtendedAttribute("JSImplementation")
-        if not classId:
-            return classId
-        assert isinstance(classId, list)
-        assert len(classId) == 1
-        return classId[0]
-
-    def isJSImplemented(self):
-        return bool(self.getJSImplementation())
-
-    def isProbablyShortLivingObject(self):
-        current = self
-        while current:
-            if current.getExtendedAttribute("ProbablyShortLivingObject"):
-                return True
-            current = current.parent
-        return False
-
-    def isNavigatorProperty(self):
-        naviProp = self.getExtendedAttribute("NavigatorProperty")
-        if not naviProp:
-            return False
-        assert len(naviProp) == 1
-        assert isinstance(naviProp, list)
-        assert len(naviProp[0]) != 0
+
+class IDLNamespace(IDLInterfaceOrNamespace):
+    def __init__(self, location, parentScope, name, members, isKnownNonPartial):
+        IDLInterfaceOrNamespace.__init__(self, location, parentScope, name,
+                                         None, members, isKnownNonPartial)
+
+    def __str__(self):
+        return "Namespace '%s'" % self.identifier.name
+
+    def isNamespace(self):
         return True
 
-    def getNavigatorProperty(self):
-        naviProp = self.getExtendedAttribute("NavigatorProperty")
-        if not naviProp:
-            return None
-        assert len(naviProp) == 1
-        assert isinstance(naviProp, list)
-        assert len(naviProp[0]) != 0
-        conditionExtendedAttributes = self._extendedAttrDict.viewkeys() & IDLInterface.conditionExtendedAttributes
-        attr = IDLAttribute(self.location,
-                            IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"), naviProp[0]),
-                            IDLUnresolvedType(self.location, IDLUnresolvedIdentifier(self.location, self.identifier.name)),
-                            True,
-                            extendedAttrDict={ a: self._extendedAttrDict[a] for a in conditionExtendedAttributes },
-                            navigatorObjectGetter=True)
-        attr._exposureGlobalNames = self._exposureGlobalNames
-        # We're abusing Constant a little bit here, because we need Cached. The
-        # getter will create a new object every time, but we're never going to
-        # clear the cached value.
-        extendedAttrs = [ IDLExtendedAttribute(self.location, ("Throws", )),
-                          IDLExtendedAttribute(self.location, ("Cached", )),
-                          IDLExtendedAttribute(self.location, ("Constant", )) ]
-        attr.addExtendedAttributes(extendedAttrs)
-        return attr
-
-    def hasChildInterfaces(self):
-        return self._hasChildInterfaces
-
-    def isOnGlobalProtoChain(self):
-        return self._isOnGlobalProtoChain
-
-    def _getDependentObjects(self):
-        deps = set(self.members)
-        deps.update(self.implementedInterfaces)
-        if self.parent:
-            deps.add(self.parent)
-        return deps
-
-    def hasMembersInSlots(self):
-        return self._ownMembersInSlots != 0
-
-    conditionExtendedAttributes = [ "Pref", "ChromeOnly", "Func", "AvailableIn",
-                                    "SecureContext",
-                                    "CheckAnyPermissions",
-                                    "CheckAllPermissions" ]
-    def isExposedConditionally(self):
-        return any(self.getExtendedAttribute(a) for a in self.conditionExtendedAttributes)
+    def addExtendedAttributes(self, attrs):
+        # The set of things namespaces support is small enough it's simpler
+        # to factor out into a separate method than it is to sprinkle
+        # isNamespace() checks all through
+        # IDLInterfaceOrNamespace.addExtendedAttributes.
+        for attr in attrs:
+            identifier = attr.identifier()
+
+            if identifier == "Exposed":
+                convertExposedAttrToGlobalNameSet(attr,
+                                                  self._exposureGlobalNames)
+            else:
+                raise WebIDLError("Unknown extended attribute %s on namespace" %
+                                  identifier,
+                                  [attr.location])
+
+            attrlist = attr.listValue()
+            self._extendedAttrDict[identifier] = attrlist if len(attrlist) else True
 
 
 class IDLDictionary(IDLObjectWithScope):
     def __init__(self, location, parentScope, name, parent, members):
         assert isinstance(parentScope, IDLScope)
         assert isinstance(name, IDLUnresolvedIdentifier)
         assert not parent or isinstance(parent, IDLIdentifierPlaceholder)
 
@@ -3925,17 +3986,17 @@ class IDLAttribute(IDLInterfaceMember):
         IDLInterfaceMember.__init__(self, location, identifier,
                                     IDLInterfaceMember.Tags.Attr,
                                     extendedAttrDict=extendedAttrDict)
 
         assert isinstance(type, IDLType)
         self.type = type
         self.readonly = readonly
         self.inherit = inherit
-        self.static = static
+        self._static = static
         self.lenientThis = False
         self._unforgeable = False
         self.stringifier = stringifier
         self.enforceRange = False
         self.clamp = False
         self.slotIndices = None
         assert maplikeOrSetlike is None or isinstance(maplikeOrSetlike, IDLMaplikeOrSetlike)
         self.maplikeOrSetlike = maplikeOrSetlike
@@ -3947,17 +4008,20 @@ class IDLAttribute(IDLInterfaceMember):
             raise WebIDLError("The identifier of a static attribute must not be 'prototype'",
                               [location])
 
         if readonly and inherit:
             raise WebIDLError("An attribute cannot be both 'readonly' and 'inherit'",
                               [self.location])
 
     def isStatic(self):
-        return self.static
+        return self._static
+
+    def forceStatic(self):
+        self._static = True
 
     def __str__(self):
         return "'%s' attribute '%s'" % (self.type, self.identifier)
 
     def finish(self, scope):
         IDLInterfaceMember.finish(self, scope)
 
         if not self.type.isComplete():
@@ -4560,16 +4624,19 @@ class IDLMethod(IDLInterfaceMember, IDLS
             assert len(self._overloads) == 1
             overload = self._overloads[0]
             assert len(overload.arguments) == 0
             assert overload.returnType == BuiltinTypes[IDLBuiltinType.Types.object]
 
     def isStatic(self):
         return self._static
 
+    def forceStatic(self):
+        self._static = True
+
     def isGetter(self):
         return self._getter
 
     def isSetter(self):
         return self._setter
 
     def isCreator(self):
         return self._creator
@@ -5139,17 +5206,18 @@ class Tokenizer(object):
         "=": "EQUALS",
         "<": "LT",
         ">": "GT",
         "ArrayBuffer": "ARRAYBUFFER",
         "SharedArrayBuffer": "SHAREDARRAYBUFFER",
         "or": "OR",
         "maplike": "MAPLIKE",
         "setlike": "SETLIKE",
-        "iterable": "ITERABLE"
+        "iterable": "ITERABLE",
+        "namespace": "NAMESPACE"
         }
 
     tokens.extend(keywords.values())
 
     def t_error(self, t):
         raise WebIDLError("Unrecognized Input",
                           [Location(lexer=self.lexer,
                                     lineno=self.lexer.lineno,
@@ -5236,17 +5304,18 @@ class Parser(Tokenizer):
         """
             Definitions :
         """
         p[0] = []
 
     def p_Definition(self, p):
         """
             Definition : CallbackOrInterface
-                       | PartialInterface
+                       | Namespace
+                       | Partial
                        | Dictionary
                        | Exception
                        | Enum
                        | Typedef
                        | ImplementsStatement
         """
         p[0] = p[1]
         assert p[1]  # We might not have implemented something ...
@@ -5270,43 +5339,64 @@ class Parser(Tokenizer):
     def p_CallbackRestOrInterface(self, p):
         """
             CallbackRestOrInterface : CallbackRest
                                     | Interface
         """
         assert p[1]
         p[0] = p[1]
 
+    def handleNonPartialObject(self, location, identifier, constructor,
+                               constructorArgs, nonPartialArgs):
+        """
+        This handles non-partial objects (interfaces and namespaces) by
+        checking for an existing partial object, and promoting it to
+        non-partial as needed.  The return value is the non-partial object.
+
+        constructorArgs are all the args for the constructor except the last
+        one: isKnownNonPartial.
+
+        nonPartialArgs are the args for the setNonPartial call.
+        """
+        # The name of the class starts with "IDL", so strip that off.
+        # Also, starts with a capital letter after that, so nix that
+        # as well.
+        prettyname = constructor.__name__[3:].lower()
+
+        try:
+            existingObj = self.globalScope()._lookupIdentifier(identifier)
+            if existingObj:
+                if not isinstance(existingObj, constructor):
+                    raise WebIDLError("%s has the same name as "
+                                      "non-%s object" %
+                                      (prettyname.capitalize(), prettyname),
+                                      [location, existingObj.location])
+                existingObj.setNonPartial(*nonPartialArgs)
+                return existingObj
+        except Exception, ex:
+            if isinstance(ex, WebIDLError):
+                raise ex
+            pass
+
+        # True for isKnownNonPartial
+        return constructor(*(constructorArgs + [True]))
+
     def p_Interface(self, p):
         """
             Interface : INTERFACE IDENTIFIER Inheritance LBRACE InterfaceMembers RBRACE SEMICOLON
         """
         location = self.getLocation(p, 1)
         identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
         members = p[5]
         parent = p[3]
 
-        try:
-            existingObj = self.globalScope()._lookupIdentifier(identifier)
-            if existingObj:
-                p[0] = existingObj
-                if not isinstance(p[0], IDLInterface):
-                    raise WebIDLError("Interface has the same name as "
-                                      "non-interface object",
-                                      [location, p[0].location])
-                p[0].setNonPartial(location, parent, members)
-                return
-        except Exception, ex:
-            if isinstance(ex, WebIDLError):
-                raise ex
-            pass
-
-        iface = IDLInterface(location, self.globalScope(), identifier, parent,
-                            members, isKnownNonPartial=True)
-        p[0] = iface
+        p[0] = self.handleNonPartialObject(
+            location, identifier, IDLInterface,
+            [location, self.globalScope(), identifier, parent, members],
+            [location, parent, members])
 
     def p_InterfaceForwardDecl(self, p):
         """
             Interface : INTERFACE IDENTIFIER SEMICOLON
         """
         location = self.getLocation(p, 1)
         identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
 
@@ -5321,44 +5411,110 @@ class Parser(Tokenizer):
                 return
         except Exception, ex:
             if isinstance(ex, WebIDLError):
                 raise ex
             pass
 
         p[0] = IDLExternalInterface(location, self.globalScope(), identifier)
 
-    def p_PartialInterface(self, p):
-        """
-            PartialInterface : PARTIAL INTERFACE IDENTIFIER LBRACE InterfaceMembers RBRACE SEMICOLON
-        """
-        location = self.getLocation(p, 2)
-        identifier = IDLUnresolvedIdentifier(self.getLocation(p, 3), p[3])
-        members = p[5]
-
-        nonPartialInterface = None
+    def p_Namespace(self, p):
+        """
+            Namespace : NAMESPACE IDENTIFIER LBRACE InterfaceMembers RBRACE SEMICOLON
+        """
+        location = self.getLocation(p, 1)
+        identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
+        members = p[4]
+
+        p[0] = self.handleNonPartialObject(
+            location, identifier, IDLNamespace,
+            [location, self.globalScope(), identifier, members],
+            [location, None, members])
+
+    def p_Partial(self, p):
+        """
+            Partial : PARTIAL PartialDefinition
+        """
+        p[0] = p[2]
+
+    def p_PartialDefinition(self, p):
+        """
+            PartialDefinition : PartialInterface
+                              | PartialNamespace
+        """
+        p[0] = p[1]
+
+    def handlePartialObject(self, location, identifier, nonPartialConstructor,
+                            nonPartialConstructorArgs,
+                            partialConstructorArgs):
+        """
+        This handles partial objects (interfaces and namespaces) by checking for
+        an existing non-partial object, and adding ourselves to it as needed.
+        The return value is our partial object.  For now we just use
+        IDLPartialInterfaceOrNamespace for partial objects.
+
+        nonPartialConstructorArgs are all the args for the non-partial
+        constructor except the last two: members and isKnownNonPartial.
+
+        partialConstructorArgs are the arguments for the
+        IDLPartialInterfaceOrNamespace constructor, except the last one (the
+        non-partial object).
+        """
+        # The name of the class starts with "IDL", so strip that off.
+        # Also, starts with a capital letter after that, so nix that
+        # as well.
+        prettyname = nonPartialConstructor.__name__[3:].lower()
+
+        nonPartialObject = None
         try:
-            nonPartialInterface = self.globalScope()._lookupIdentifier(identifier)
-            if nonPartialInterface:
-                if not isinstance(nonPartialInterface, IDLInterface):
-                    raise WebIDLError("Partial interface has the same name as "
-                                      "non-interface object",
-                                      [location, nonPartialInterface.location])
+            nonPartialObject = self.globalScope()._lookupIdentifier(identifier)
+            if nonPartialObject:
+                if not isinstance(nonPartialObject, nonPartialConstructor):
+                    raise WebIDLError("Partial %s has the same name as "
+                                      "non-%s object" %
+                                      (prettyname, prettyname),
+                                      [location, nonPartialObject.location])
         except Exception, ex:
             if isinstance(ex, WebIDLError):
                 raise ex
             pass
 
-        if not nonPartialInterface:
-            nonPartialInterface = IDLInterface(location, self.globalScope(),
-                                               identifier, None,
-                                               [], isKnownNonPartial=False)
-        partialInterface = IDLPartialInterface(location, identifier, members,
-                                               nonPartialInterface)
-        p[0] = partialInterface
+        if not nonPartialObject:
+            nonPartialObject = nonPartialConstructor(
+                # No members, False for isKnownNonPartial
+                *(nonPartialConstructorArgs + [[], False]))
+        partialInterface = IDLPartialInterfaceOrNamespace(
+            *(partialConstructorArgs + [nonPartialObject]))
+        return partialInterface
+
+    def p_PartialInterface(self, p):
+        """
+            PartialInterface : INTERFACE IDENTIFIER LBRACE InterfaceMembers RBRACE SEMICOLON
+        """
+        location = self.getLocation(p, 1)
+        identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
+        members = p[4]
+
+        p[0] = self.handlePartialObject(
+            location, identifier, IDLInterface,
+            [location, self.globalScope(), identifier, None],
+            [location, identifier, members])
+
+    def p_PartialNamespace(self, p):
+        """
+            PartialNamespace : NAMESPACE IDENTIFIER LBRACE InterfaceMembers RBRACE SEMICOLON
+        """
+        location = self.getLocation(p, 1)
+        identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
+        members = p[4]
+
+        p[0] = self.handlePartialObject(
+            location, identifier, IDLNamespace,
+            [location, self.globalScope(), identifier],
+            [location, identifier, members])
 
     def p_Inheritance(self, p):
         """
             Inheritance : COLON ScopedName
         """
         p[0] = IDLIdentifierPlaceholder(self.getLocation(p, 2), p[2])
 
     def p_InheritanceEmpty(self, p):
@@ -6009,16 +6165,17 @@ class Parser(Tokenizer):
                          | SERIALIZER
                          | SETLIKE
                          | SETTER
                          | STATIC
                          | STRINGIFIER
                          | JSONIFIER
                          | TYPEDEF
                          | UNRESTRICTED
+                         | NAMESPACE
         """
         p[0] = p[1]
 
     def p_AttributeName(self, p):
         """
             AttributeName : IDENTIFIER
                           | REQUIRED
         """
@@ -6609,28 +6766,30 @@ class Parser(Tokenizer):
                               [self._filename])
         else:
             raise WebIDLError("invalid syntax", [Location(self.lexer, p.lineno, p.lexpos, self._filename)])
 
     def __init__(self, outputdir='', lexer=None):
         Tokenizer.__init__(self, outputdir, lexer)
 
         logger = SqueakyCleanLogger()
-        self.parser = yacc.yacc(module=self,
-                                outputdir=outputdir,
-                                tabmodule='webidlyacc',
-                                errorlog=logger
-                                # Pickling the grammar is a speedup in
-                                # some cases (older Python?) but a
-                                # significant slowdown in others.
-                                # We're not pickling for now, until it
-                                # becomes a speedup again.
-                                # , picklefile='WebIDLGrammar.pkl'
-                                )
-        logger.reportGrammarErrors()
+        try:
+            self.parser = yacc.yacc(module=self,
+                                    outputdir=outputdir,
+                                    tabmodule='webidlyacc',
+                                    errorlog=logger
+                                    # Pickling the grammar is a speedup in
+                                    # some cases (older Python?) but a
+                                    # significant slowdown in others.
+                                    # We're not pickling for now, until it
+                                    # becomes a speedup again.
+                                    # , picklefile='WebIDLGrammar.pkl'
+            )
+        finally:
+            logger.reportGrammarErrors()
 
         self._globalScope = IDLScope(BuiltinLocation("<Global Scope>"), None, None)
         # To make our test harness work, pretend like we have a primary global already.
         # Note that we _don't_ set _globalScope.primaryGlobalAttr,
         # so we'll still be able to detect multiple PrimaryGlobal extended attributes.
         self._globalScope.primaryGlobalName = "FakeTestPrimaryGlobal"
         self._globalScope.globalNames.add("FakeTestPrimaryGlobal")
         self._globalScope.globalNameMapping["FakeTestPrimaryGlobal"].add("FakeTestPrimaryGlobal")
@@ -6691,20 +6850,21 @@ class Parser(Tokenizer):
 
         iterableIteratorIface = None
         for iface in interfaceStatements:
             navigatorProperty = iface.getNavigatorProperty()
             if navigatorProperty:
                 # We're generating a partial interface to add a readonly
                 # property to the Navigator interface for every interface
                 # annotated with NavigatorProperty.
-                partialInterface = IDLPartialInterface(iface.location,
-                                                       IDLUnresolvedIdentifier(iface.location, "Navigator"),
-                                                       [ navigatorProperty ],
-                                                       navigatorInterface)
+                partialInterface = IDLPartialInterfaceOrNamespace(
+                    iface.location,
+                    IDLUnresolvedIdentifier(iface.location, "Navigator"),
+                    [ navigatorProperty ],
+                    navigatorInterface)
                 self._productions.append(partialInterface)
 
             iterable = None
             # We haven't run finish() on the interface yet, so we don't know
             # whether our interface is maplike/setlike/iterable or not. This
             # means we have to loop through the members to see if we have an
             # iterable member.
             for m in iface.members:
new file mode 100644
--- /dev/null
+++ b/dom/bindings/parser/tests/test_namespace.py
@@ -0,0 +1,223 @@
+def WebIDLTest(parser, harness):
+    parser.parse(
+        """
+        namespace MyNamespace {
+          attribute any foo;
+          any bar();
+        };
+        """)
+
+    results = parser.finish()
+    harness.check(len(results), 1, "Should have a thing.")
+    harness.ok(results[0].isNamespace(), "Our thing should be a namespace");
+    harness.check(len(results[0].members), 2,
+                  "Should have two things in our namespace")
+    harness.ok(results[0].members[0].isAttr(), "First member is attribute")
+    harness.ok(results[0].members[0].isStatic(), "Attribute should be static")
+    harness.ok(results[0].members[1].isMethod(), "Second member is method")
+    harness.ok(results[0].members[1].isStatic(), "Operation should be static")
+
+    parser = parser.reset()
+    parser.parse(
+        """
+        namespace MyNamespace {
+          attribute any foo;
+        };
+        partial namespace MyNamespace {
+          any bar();
+        };
+        """)
+
+    results = parser.finish()
+    harness.check(len(results), 2, "Should have things.")
+    harness.ok(results[0].isNamespace(), "Our thing should be a namespace");
+    harness.check(len(results[0].members), 2,
+                  "Should have two things in our namespace")
+    harness.ok(results[0].members[0].isAttr(), "First member is attribute")
+    harness.ok(results[0].members[0].isStatic(), "Attribute should be static");
+    harness.ok(results[0].members[1].isMethod(), "Second member is method")
+    harness.ok(results[0].members[1].isStatic(), "Operation should be static");
+
+    parser = parser.reset()
+    parser.parse(
+        """
+        partial namespace MyNamespace {
+          any bar();
+        };
+        namespace MyNamespace {
+          attribute any foo;
+        };
+        """)
+
+    results = parser.finish()
+    harness.check(len(results), 2, "Should have things.")
+    harness.ok(results[1].isNamespace(), "Our thing should be a namespace");
+    harness.check(len(results[1].members), 2,
+                  "Should have two things in our namespace")
+    harness.ok(results[1].members[0].isAttr(), "First member is attribute")
+    harness.ok(results[1].members[0].isStatic(), "Attribute should be static");
+    harness.ok(results[1].members[1].isMethod(), "Second member is method")
+    harness.ok(results[1].members[1].isStatic(), "Operation should be static");
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse(
+            """
+            namespace MyNamespace {
+              static attribute any foo;
+            };
+        """)
+
+        results = parser.finish()
+    except Exception, x:
+        threw = True
+    harness.ok(threw, "Should have thrown.")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse(
+            """
+            namespace MyNamespace {
+              static any bar();
+            };
+        """)
+
+        results = parser.finish()
+    except Exception, x:
+        threw = True
+    harness.ok(threw, "Should have thrown.")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse(
+            """
+            namespace MyNamespace {
+              any bar();
+            };
+
+            interface MyNamespace {
+              any baz();
+            };
+        """)
+
+        results = parser.finish()
+    except Exception, x:
+        threw = True
+    harness.ok(threw, "Should have thrown.")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse(
+            """
+            interface MyNamespace {
+              any baz();
+            };
+
+            namespace MyNamespace {
+              any bar();
+            };
+        """)
+
+        results = parser.finish()
+    except Exception, x:
+        threw = True
+    harness.ok(threw, "Should have thrown.")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse(
+            """
+            namespace MyNamespace {
+              any baz();
+            };
+
+            namespace MyNamespace {
+              any bar();
+            };
+        """)
+
+        results = parser.finish()
+    except Exception, x:
+        threw = True
+    harness.ok(threw, "Should have thrown.")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse(
+            """
+            partial namespace MyNamespace {
+              any baz();
+            };
+
+            interface MyNamespace {
+              any bar();
+            };
+        """)
+
+        results = parser.finish()
+    except Exception, x:
+        threw = True
+    harness.ok(threw, "Should have thrown.")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse(
+            """
+            namespace MyNamespace {
+              any bar();
+            };
+
+            partial interface MyNamespace {
+              any baz();
+            };
+        """)
+
+        results = parser.finish()
+    except Exception, x:
+        threw = True
+    harness.ok(threw, "Should have thrown.")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse(
+            """
+            partial interface MyNamespace {
+              any baz();
+            };
+
+            namespace MyNamespace {
+              any bar();
+            };
+        """)
+
+        results = parser.finish()
+    except Exception, x:
+        threw = True
+    harness.ok(threw, "Should have thrown.")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse(
+            """
+            interface MyNamespace {
+              any bar();
+            };
+
+            partial namespace MyNamespace {
+              any baz();
+            };
+        """)
+
+        results = parser.finish()
+    except Exception, x:
+        threw = True
+    harness.ok(threw, "Should have thrown.")