Bug 1414372: Introduce interface mixins r=bzbarsky
authorKagami Sascha Rosylight <saschanaz@outlook.com>
Thu, 15 Aug 2019 16:53:49 +0000
changeset 488285 1b097ae05490a3b2c5e1c125160b0adea3515fe2
parent 488284 8e9d1223d391da60c16ea3e4087123f6c3b9f3a4
child 488286 e56e6ed91e5af309cbca9ce38252f03530d81b79
push id36440
push userncsoregi@mozilla.com
push dateFri, 16 Aug 2019 03:57:48 +0000
treeherdermozilla-central@a58b7dc85887 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbzbarsky
bugs1414372
milestone70.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 1414372: Introduce interface mixins r=bzbarsky Add IDLInterfaceMixin with a new superclass shared with existing IDLInterfaceOrNamespace. Differential Revision: https://phabricator.services.mozilla.com/D38802
dom/bindings/Configuration.py
dom/bindings/parser/WebIDL.py
dom/bindings/parser/tests/test_interfacemixin.py
dom/webidl/CanvasRenderingContext2D.webidl
dom/webidl/Fetch.webidl
dom/webidl/IDBKeyRange.webidl
dom/webidl/Request.webidl
dom/webidl/Response.webidl
--- a/dom/bindings/Configuration.py
+++ b/dom/bindings/Configuration.py
@@ -77,17 +77,17 @@ class Configuration(DescriptorProvider):
                 continue
             iface = thing
             # Our build system doesn't support dep builds involving
             # addition/removal of partial interfaces that appear in a different
             # .webidl file than the interface they are extending.  Make sure we
             # don't have any of those.  See similar block above for "implements"
             # statements!
             if not iface.isExternal():
-                for partialIface in iface.getPartialInterfaces():
+                for partialIface in iface.getPartials():
                     if (partialIface.filename() != iface.filename() and
                         # Unfortunately, NavigatorProperty does exactly the
                         # thing we're trying to prevent here.  I'm not sure how
                         # to deal with that, short of effectively requiring a
                         # clobber when NavigatorProperty is added/removed and
                         # whitelisting the things it outputs here as
                         # restrictively as I can.
                         (partialIface.identifier.name != "Navigator" or
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -309,17 +309,17 @@ class IDLScope(IDLObject):
 
         # Default to throwing, derived classes can override.
         conflictdesc = "\n\t%s at %s\n\t%s at %s" % (originalObject,
                                                      originalObject.location,
                                                      newObject,
                                                      newObject.location)
 
         raise WebIDLError(
-            "Multiple unresolvable definitions of identifier '%s' in scope '%s%s"
+            "Multiple unresolvable definitions of identifier '%s' in scope '%s'%s"
             % (identifier.name, str(self), conflictdesc), [])
 
     def _lookupIdentifier(self, identifier):
         return self._dict[identifier.name]
 
     def lookupIdentifier(self, identifier):
         assert isinstance(identifier, IDLIdentifier)
         assert identifier.scope == self
@@ -600,17 +600,17 @@ class IDLPartialInterfaceOrNamespace(IDL
         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._nonPartialInterfaceOrNamespace = nonPartialInterfaceOrNamespace
         self._finished = False
-        nonPartialInterfaceOrNamespace.addPartialInterface(self)
+        nonPartialInterfaceOrNamespace.addPartial(self)
 
     def addExtendedAttributes(self, attrs):
         for attr in attrs:
             identifier = attr.identifier()
 
             if identifier in ["Constructor", "NamedConstructor"]:
                 self.propagatedExtendedAttrs.append(attr)
             elif identifier == "SecureContext":
@@ -671,40 +671,222 @@ def convertExposedAttrToGlobalNameSet(ex
         assert exposedAttr.hasArgs()
         targetSet.update(exposedAttr.args())
 
 
 def globalNameSetToExposureSet(globalScope, nameSet, exposureSet):
     for name in nameSet:
         exposureSet.update(globalScope.globalNameMapping[name])
 
-
-class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
+class IDLInterfaceOrInterfaceMixinOrNamespace(IDLObjectWithScope, IDLExposureMixins):
+    def __init__(self, location, parentScope, name):
+        assert isinstance(parentScope, IDLScope)
+        assert isinstance(name, IDLUnresolvedIdentifier)
+
+        self._finished = False
+        self.members = []
+        self._partials = []
+        self._extendedAttrDict = {}
+        self._isKnownNonPartial = False
+
+        IDLObjectWithScope.__init__(self, location, parentScope, name)
+        IDLExposureMixins.__init__(self, location)
+
+    def finish(self, scope):
+        if not self._isKnownNonPartial:
+            raise WebIDLError("%s does not have a non-partial declaration" %
+                              str(self), [self.location])
+
+        IDLExposureMixins.finish(self, scope)
+
+        # Now go ahead and merge in our partials.
+        for partial in self._partials:
+            partial.finish(scope)
+            self.addExtendedAttributes(partial.propagatedExtendedAttrs)
+            self.members.extend(partial.members)
+
+    def resolveIdentifierConflict(self, scope, identifier, originalObject, newObject):
+        assert isinstance(scope, IDLScope)
+        assert isinstance(originalObject, IDLInterfaceMember)
+        assert isinstance(newObject, IDLInterfaceMember)
+
+        retval = IDLScope.resolveIdentifierConflict(self, scope, identifier,
+                                                    originalObject, newObject)
+
+        # Might be a ctor, which isn't in self.members
+        if newObject in self.members:
+            self.members.remove(newObject)
+        return retval
+
+    def typeName(self):
+        if self.isInterface():
+            return "interface"
+        if self.isNamespace():
+            return "namespace"
+        return "interface mixin"
+
+    def getExtendedAttribute(self, name):
+        return self._extendedAttrDict.get(name, None)
+
+    def setNonPartial(self, location, members):
+        if self._isKnownNonPartial:
+            raise WebIDLError("Two non-partial definitions for the "
+                              "same %s" % self.typeName(),
+                              [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
+        # Put the new members at the beginning
+        self.members = members + self.members
+    
+    def addPartial(self, partial):
+        assert self.identifier.name == partial.identifier.name
+        self._partials.append(partial)
+
+    def getPartials(self):
+        # Don't let people mutate our guts.
+        return list(self._partials)
+
+    def finishMembers(self, scope):
+        # Assuming we've merged in our partials, set the _exposureGlobalNames on
+        # any members that don't have it set yet.  Note that any partial
+        # interfaces that had [Exposed] set have already set up
+        # _exposureGlobalNames on all the members coming from them, so this is
+        # just implementing the "members default to interface or interface mixin
+        # that defined them" and "partial interfaces or interface mixins default
+        # to interface or interface mixin they're a partial for" rules from the
+        # spec.
+        for m in self.members:
+            # If m, or the partial m came from, had [Exposed]
+            # specified, it already has a nonempty exposure global names set.
+            if len(m._exposureGlobalNames) == 0:
+                m._exposureGlobalNames.update(self._exposureGlobalNames)
+
+        # resolve() will modify self.members, so we need to iterate
+        # over a copy of the member list here.
+        for member in list(self.members):
+            member.resolve(self)
+
+        for member in self.members:
+            member.finish(scope)
+
+        # Now that we've finished our members, which has updated their exposure
+        # sets, make sure they aren't exposed in places where we are not.
+        for member in self.members:
+            if not member.exposureSet.issubset(self.exposureSet):
+                raise WebIDLError("Interface or interface mixin member has"
+                                  "larger exposure set than its container",
+                                  [member.location, self.location])
+
+
+class IDLInterfaceMixin(IDLInterfaceOrInterfaceMixinOrNamespace):
+    def __init__(self, location, parentScope, name, members, isKnownNonPartial):
+        self.actualExposureGlobalNames = set()
+
+        assert isKnownNonPartial or len(members) == 0
+        IDLInterfaceOrInterfaceMixinOrNamespace.__init__(self, location, parentScope, name)
+
+        if isKnownNonPartial:
+            self.setNonPartial(location, members)
+
+    def __str__(self):
+        return "Interface mixin '%s'" % self.identifier.name
+    
+    def finish(self, scope):
+        if self._finished:
+            return
+        self._finished = True
+
+        # Expose to the globals of interfaces that includes this mixin if this
+        # mixin has no explicit [Exposed] so that its members can be exposed
+        # based on the base interface exposure set.
+        # Make sure this is done before IDLExposureMixins.finish call to
+        # prevent exposing to PrimaryGlobal by default.
+        hasImplicitExposure = len(self._exposureGlobalNames) == 0
+        if hasImplicitExposure:
+            self._exposureGlobalNames.update(self.actualExposureGlobalNames)
+
+        IDLInterfaceOrInterfaceMixinOrNamespace.finish(self, scope)
+
+        self.finishMembers(scope)
+
+    def validate(self):
+        for member in self.members:
+
+            if member.isAttr():
+                if member.inherit:
+                    raise WebIDLError("Interface mixin member cannot include "
+                                      "an inherited attribute",
+                                      [member.location, self.location])
+                if member.isStatic():
+                    raise WebIDLError("Interface mixin member cannot include "
+                                      "a static member",
+                                      [member.location, self.location])
+
+            if member.isMethod():
+                if member.isStatic():
+                    raise WebIDLError("Interface mixin member cannot include "
+                                      "a static operation",
+                                      [member.location, self.location])
+                if (member.isGetter() or
+                    member.isSetter() or
+                    member.isDeleter() or
+                    member.isLegacycaller()):
+                    raise WebIDLError("Interface mixin member cannot include a "
+                                      "special operation",
+                                      [member.location, self.location])
+
+    def addExtendedAttributes(self, attrs):
+        for attr in attrs:
+            identifier = attr.identifier()
+
+            if identifier == "SecureContext":
+                if not attr.noArguments():
+                    raise WebIDLError("[%s] must take no arguments" % identifier,
+                                      [attr.location])
+                # This gets propagated to all our members.
+                for member in self.members:
+                    if member.getExtendedAttribute("SecureContext"):
+                        raise WebIDLError("[SecureContext] specified on both "
+                                          "an interface mixin member and on"
+                                          "the interface mixin itself",
+                                          [member.location, attr.location])
+                    member.addExtendedAttributes([attr])
+            elif identifier == "Exposed":
+                convertExposedAttrToGlobalNameSet(attr,
+                                                  self._exposureGlobalNames)
+            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 _getDependentObjects(self):
+        return set(self.members)
+
+
+class IDLInterfaceOrNamespace(IDLInterfaceOrInterfaceMixinOrNamespace):
     def __init__(self, location, parentScope, name, parent, members,
                  isKnownNonPartial, toStringTag):
-        assert isinstance(parentScope, IDLScope)
-        assert isinstance(name, IDLUnresolvedIdentifier)
         assert isKnownNonPartial or not parent
         assert isKnownNonPartial or len(members) == 0
 
         self.parent = None
         self._callback = False
-        self._finished = False
-        self.members = []
         self.maplikeOrSetlikeOrIterable = None
-        self._partialInterfaces = []
-        self._extendedAttrDict = {}
         # namedConstructors needs deterministic ordering because bindings code
         # outputs the constructs in the order that namedConstructors enumerates
         # them.
         self.namedConstructors = list()
         self.legacyWindowAliases = []
         self.implementedInterfaces = set()
+        self.includedMixins = set()
         self._consequential = False
-        self._isKnownNonPartial = False
         # self.interfacesBasedOnSelf is the set of interfaces that inherit from
         # self or have self as a consequential interface, including self itself.
         # Used for distinguishability checking.
         self.interfacesBasedOnSelf = set([self])
         # self.interfacesImplementingSelf is the set of interfaces that directly
         # have self as a consequential interface
         self.interfacesImplementingSelf = set()
         self._hasChildInterfaces = False
@@ -715,18 +897,17 @@ class IDLInterfaceOrNamespace(IDLObjectW
         # Tracking of the number of own own members we have in slots
         self._ownMembersInSlots = 0
         # If this is an iterator interface, we need to know what iterable
         # interface we're iterating for in order to get its nativeType.
         self.iterableInterface = None
 
         self.toStringTag = toStringTag
 
-        IDLObjectWithScope.__init__(self, location, parentScope, name)
-        IDLExposureMixins.__init__(self, location)
+        IDLInterfaceOrInterfaceMixinOrNamespace.__init__(self, location, parentScope, name)
 
         if isKnownNonPartial:
             self.setNonPartial(location, parent, members)
 
     def ctor(self):
         identifier = IDLUnresolvedIdentifier(self.location, "constructor",
                                              allowForbidden=True)
         try:
@@ -736,58 +917,34 @@ class IDLInterfaceOrNamespace(IDLObjectW
 
     def isIterable(self):
         return (self.maplikeOrSetlikeOrIterable and
                 self.maplikeOrSetlikeOrIterable.isIterable())
 
     def isIteratorInterface(self):
         return self.iterableInterface is not None
 
-    def resolveIdentifierConflict(self, scope, identifier, originalObject, newObject):
-        assert isinstance(scope, IDLScope)
-        assert isinstance(originalObject, IDLInterfaceMember)
-        assert isinstance(newObject, IDLInterfaceMember)
-
-        retval = IDLScope.resolveIdentifierConflict(self, scope, identifier,
-                                                    originalObject, newObject)
-
-        # Might be a ctor, which isn't in self.members
-        if newObject in self.members:
-            self.members.remove(newObject)
-        return retval
-
     def finish(self, scope):
         if self._finished:
             return
 
         self._finished = True
 
-        if not self._isKnownNonPartial:
-            raise WebIDLError("Interface %s does not have a non-partial "
-                              "declaration" % self.identifier.name,
-                              [self.location])
-
-        IDLExposureMixins.finish(self, scope)
+        IDLInterfaceOrInterfaceMixinOrNamespace.finish(self, scope)
 
         if len(self.legacyWindowAliases) > 0:
             if not self.hasInterfaceObject():
                 raise WebIDLError("Interface %s unexpectedly has [LegacyWindowAlias] "
                                   "and [NoInterfaceObject] together" % self.identifier.name,
                                   [self.location])
             if not self.isExposedInWindow():
                 raise WebIDLError("Interface %s has [LegacyWindowAlias] "
                                   "but not exposed in Window" % self.identifier.name,
                                   [self.location])
 
-        # Now go ahead and merge in our partial interfaces.
-        for partial in self._partialInterfaces:
-            partial.finish(scope)
-            self.addExtendedAttributes(partial.propagatedExtendedAttrs)
-            self.members.extend(partial.members)
-
         # Generate maplike/setlike interface members. Since generated members
         # need to be treated like regular interface members, do this before
         # things like exposure setting.
         for member in self.members:
             if member.isMaplikeOrSetlikeOrIterable():
                 # Check that we only have one interface declaration (currently
                 # there can only be one maplike/setlike declaration per
                 # interface)
@@ -799,29 +956,16 @@ class IDLInterfaceOrNamespace(IDLObjectW
                                        self.maplikeOrSetlikeOrIterable.maplikeOrSetlikeOrIterableType),
                                       [self.maplikeOrSetlikeOrIterable.location,
                                        member.location])
                 self.maplikeOrSetlikeOrIterable = member
                 # If we've got a maplike or setlike declaration, we'll be building all of
                 # our required methods in Codegen. Generate members now.
                 self.maplikeOrSetlikeOrIterable.expand(self.members, self.isJSImplemented())
 
-        # Now that we've merged in our partial interfaces, set the
-        # _exposureGlobalNames on any members that don't have it set yet.  Note
-        # that any partial interfaces that had [Exposed] set have already set up
-        # _exposureGlobalNames on all the members coming from them, so this is
-        # just implementing the "members default to interface that defined them"
-        # and "partial interfaces default to interface they're a partial for"
-        # rules from the spec.
-        for m in self.members:
-            # If m, or the partial interface m came from, had [Exposed]
-            # specified, it already has a nonempty exposure global names set.
-            if len(m._exposureGlobalNames) == 0:
-                m._exposureGlobalNames.update(self._exposureGlobalNames)
-
         assert not self.parent or isinstance(self.parent, IDLIdentifierPlaceholder)
         parent = self.parent.finish(scope) if self.parent else None
         if parent and isinstance(parent, IDLExternalInterface):
             raise WebIDLError("%s inherits from %s which does not have "
                               "a definition" %
                               (self.identifier.name,
                                self.parent.identifier.name),
                               [self.location])
@@ -907,48 +1051,33 @@ class IDLInterfaceOrNamespace(IDLObjectW
                                   "[SecureContext] but inherits from "
                                   "interface %s which does" %
                                   (self.identifier.name,
                                    self.parent.identifier.name),
                                   [self.location, self.parent.location])
 
         for iface in self.implementedInterfaces:
             iface.finish(scope)
+        for mixin in self.includedMixins:
+            mixin.finish(scope)
 
         cycleInGraph = self.findInterfaceLoopPoint(self)
         if cycleInGraph:
             raise WebIDLError("Interface %s has itself as ancestor or "
                               "implemented interface" % self.identifier.name,
                               [self.location, cycleInGraph.location])
 
         if self.isCallback():
             # "implements" should have made sure we have no
             # consequential interfaces.
             assert len(self.getConsequentialInterfaces()) == 0
             # And that we're not consequential.
             assert not self.isConsequential()
 
-        # Now resolve() and finish() our members before importing the
-        # ones from our implemented interfaces.
-
-        # resolve() will modify self.members, so we need to iterate
-        # over a copy of the member list here.
-        for member in list(self.members):
-            member.resolve(self)
-
-        for member in self.members:
-            member.finish(scope)
-
-        # Now that we've finished our members, which has updated their exposure
-        # sets, make sure they aren't exposed in places where we are not.
-        for member in self.members:
-            if not member.exposureSet.issubset(self.exposureSet):
-                raise WebIDLError("Interface member has larger exposure set "
-                                  "than the interface itself",
-                                  [member.location, self.location])
+        self.finishMembers(scope)
 
         ctor = self.ctor()
         if ctor is not None:
             assert len(ctor._exposureGlobalNames) == 0
             ctor._exposureGlobalNames.update(self._exposureGlobalNames)
             ctor.finish(scope)
 
         for ctor in self.namedConstructors:
@@ -991,16 +1120,20 @@ class IDLInterfaceOrNamespace(IDLObjectW
                     if additionalMember.identifier.name == member.identifier.name:
                         raise WebIDLError(
                             "Multiple definitions of %s on %s coming from 'implements' statements" %
                             (member.identifier.name, self),
                             [additionalMember.location, member.location])
             self.members.extend(additionalMembers)
             iface.interfacesImplementingSelf.add(self)
 
+        for mixin in sorted(self.includedMixins,
+                            key=lambda x: x.identifier.name):
+            self.members.extend(mixin.members)
+
         for ancestor in self.getInheritedInterfaces():
             ancestor.interfacesBasedOnSelf.add(self)
             if (ancestor.maplikeOrSetlikeOrIterable is not None and
                 self.maplikeOrSetlikeOrIterable is not None):
                 raise WebIDLError("Cannot have maplike/setlike on %s that "
                                   "inherits %s, which is already "
                                   "maplike/setlike" %
                                   (self.identifier.name,
@@ -1425,16 +1558,20 @@ class IDLInterfaceOrNamespace(IDLObjectW
     def hasInterfacePrototypeObject(self):
         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 addIncludedMixin(self, includedMixin):
+        assert(isinstance(includedMixin, IDLInterfaceMixin))
+        self.includedMixins.add(includedMixin)
+
     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:
@@ -1473,44 +1610,21 @@ class IDLInterfaceOrNamespace(IDLObjectW
                 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
+        IDLInterfaceOrInterfaceMixinOrNamespace.setNonPartial(self, location, members)
         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 getPartialInterfaces(self):
-        # Don't let people mutate our guts.
-        return list(self._partialInterfaces)
 
     def getJSImplementation(self):
         classId = self.getExtendedAttribute("JSImplementation")
         if not classId:
             return classId
         assert isinstance(classId, list)
         assert len(classId) == 1
         return classId[0]
@@ -1563,16 +1677,17 @@ class IDLInterfaceOrNamespace(IDLObjectW
         return self._hasChildInterfaces
 
     def isOnGlobalProtoChain(self):
         return self._isOnGlobalProtoChain
 
     def _getDependentObjects(self):
         deps = set(self.members)
         deps.update(self.implementedInterfaces)
+        deps.update(self.includedMixins)
         if self.parent:
             deps.add(self.parent)
         return deps
 
     def hasMembersInSlots(self):
         return self._ownMembersInSlots != 0
 
     conditionExtendedAttributes = [ "Pref", "ChromeOnly", "Func",
@@ -5413,16 +5528,59 @@ class IDLImplementsStatement(IDLObject):
         pass
 
     def addExtendedAttributes(self, attrs):
         if len(attrs) != 0:
             raise WebIDLError("There are no extended attributes that are "
                               "allowed on implements statements",
                               [attrs[0].location, self.location])
 
+class IDLIncludesStatement(IDLObject):
+    def __init__(self, location, interface, mixin):
+        IDLObject.__init__(self, location)
+        self.interface = interface
+        self.mixin = mixin
+        self._finished = False
+
+    def finish(self, scope):
+        if self._finished:
+            return
+        self._finished = True
+        assert(isinstance(self.interface, IDLIdentifierPlaceholder))
+        assert(isinstance(self.mixin, IDLIdentifierPlaceholder))
+        interface = self.interface.finish(scope)
+        mixin = self.mixin.finish(scope)
+        # NOTE: we depend on not setting self.interface and
+        # self.mixin here to keep track of the original
+        # locations.
+        if not isinstance(interface, IDLInterface):
+            raise WebIDLError("Left-hand side of 'includes' is not an "
+                              "interface",
+                              [self.interface.location])
+        if interface.isCallback():
+            raise WebIDLError("Left-hand side of 'includes' is a callback "
+                              "interface",
+                              [self.interface.location])
+        if not isinstance(mixin, IDLInterfaceMixin):
+            raise WebIDLError("Right-hand side of 'includes' is not an "
+                              "interface mixin",
+                              [self.mixin.location])
+        mixin.actualExposureGlobalNames.update(interface._exposureGlobalNames)
+        interface.addIncludedMixin(mixin)
+        self.interface = interface
+        self.mixin = mixin
+
+    def validate(self):
+        pass
+
+    def addExtendedAttributes(self, attrs):
+        if len(attrs) != 0:
+            raise WebIDLError("There are no extended attributes that are "
+                              "allowed on includes statements",
+                              [attrs[0].location, self.location])
 
 class IDLExtendedAttribute(IDLObject):
     """
     A class to represent IDL extended attributes so we can give them locations
     """
     def __init__(self, location, tuple):
         IDLObject.__init__(self, location)
         self._tuple = tuple
@@ -5509,22 +5667,24 @@ class Tokenizer(object):
         r'[^\t\n\r 0-9A-Z_a-z]'
         t.type = self.keywords.get(t.value, 'OTHER')
         return t
 
     keywords = {
         "module": "MODULE",
         "interface": "INTERFACE",
         "partial": "PARTIAL",
+        "mixin": "MIXIN",
         "dictionary": "DICTIONARY",
         "exception": "EXCEPTION",
         "enum": "ENUM",
         "callback": "CALLBACK",
         "typedef": "TYPEDEF",
         "implements": "IMPLEMENTS",
+        "includes": "INCLUDES",
         "const": "CONST",
         "null": "NULL",
         "true": "TRUE",
         "false": "FALSE",
         "serializer": "SERIALIZER",
         "stringifier": "STRINGIFIER",
         "unrestricted": "UNRESTRICTED",
         "attribute": "ATTRIBUTE",
@@ -5669,49 +5829,50 @@ class Parser(Tokenizer):
     def p_DefinitionsEmpty(self, p):
         """
             Definitions :
         """
         p[0] = []
 
     def p_Definition(self, p):
         """
-            Definition : CallbackOrInterface
+            Definition : CallbackOrInterfaceOrMixin
                        | Namespace
                        | Partial
                        | Dictionary
                        | Exception
                        | Enum
                        | Typedef
                        | ImplementsStatement
+                       | IncludesStatement
         """
         p[0] = p[1]
         assert p[1]  # We might not have implemented something ...
 
-    def p_CallbackOrInterfaceCallback(self, p):
-        """
-            CallbackOrInterface : CALLBACK CallbackRestOrInterface
+    def p_CallbackOrInterfaceOrMixinCallback(self, p):
+        """
+            CallbackOrInterfaceOrMixin : CALLBACK CallbackRestOrInterface
         """
         if p[2].isInterface():
             assert isinstance(p[2], IDLInterface)
             p[2].setCallback(True)
 
         p[0] = p[2]
 
-    def p_CallbackOrInterfaceInterface(self, p):
-        """
-            CallbackOrInterface : Interface
-        """
-        p[0] = p[1]
+    def p_CallbackOrInterfaceOrMixinInterfaceOrMixin(self, p):
+        """
+            CallbackOrInterfaceOrMixin : INTERFACE InterfaceOrMixin
+        """
+        p[0] = p[2]
 
     def p_CallbackRestOrInterface(self, p):
         """
             CallbackRestOrInterface : CallbackRest
                                     | CallbackConstructorRest
-                                    | Interface
+                                    | CallbackInterface
         """
         assert p[1]
         p[0] = p[1]
 
     def handleNonPartialObject(self, location, identifier, constructor,
                                constructorArgs, nonPartialArgs):
         """
         This handles non-partial objects (interfaces, namespaces and
@@ -5742,36 +5903,49 @@ class Parser(Tokenizer):
         except Exception as 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
+    def p_InterfaceOrMixin(self, p):
+        """
+            InterfaceOrMixin : InterfaceRest
+                             | MixinRest
+        """
+        p[0] = p[1]
+
+    def p_CallbackInterface(self, p):
+        """
+            CallbackInterface : INTERFACE InterfaceRest
+        """
+        p[0] = p[2]
+
+    def p_InterfaceRest(self, p):
+        """
+            InterfaceRest : 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]
+        identifier = IDLUnresolvedIdentifier(location, p[1])
+        members = p[4]
+        parent = p[2]
 
         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
+            InterfaceRest : IDENTIFIER SEMICOLON
         """
         location = self.getLocation(p, 1)
-        identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
+        identifier = IDLUnresolvedIdentifier(location, p[1])
 
         try:
             if self.globalScope()._lookupIdentifier(identifier):
                 p[0] = self.globalScope()._lookupIdentifier(identifier)
                 if not isinstance(p[0], IDLExternalInterface):
                     raise WebIDLError("Name collision between external "
                                       "interface declaration for identifier "
                                       "%s and %s" % (identifier.name, p[0]),
@@ -5779,16 +5953,29 @@ class Parser(Tokenizer):
                 return
         except Exception as ex:
             if isinstance(ex, WebIDLError):
                 raise ex
             pass
 
         p[0] = IDLExternalInterface(location, self.globalScope(), identifier)
 
+    def p_MixinRest(self, p):
+        """
+            MixinRest : MIXIN IDENTIFIER LBRACE MixinMembers RBRACE SEMICOLON
+        """
+        location = self.getLocation(p, 1)
+        identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
+        members = p[4]
+
+        p[0] = self.handleNonPartialObject(
+            location, identifier, IDLInterfaceMixin,
+            [location, self.globalScope(), identifier, members],
+            [location, members])
+
     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]
 
@@ -5798,20 +5985,25 @@ class Parser(Tokenizer):
             [location, None, members])
 
     def p_Partial(self, p):
         """
             Partial : PARTIAL PartialDefinition
         """
         p[0] = p[2]
 
+    def p_PartialDefinitionInterface(self, p):
+        """
+            PartialDefinition : INTERFACE PartialInterfaceOrPartialMixin
+        """
+        p[0] = p[2]
+
     def p_PartialDefinition(self, p):
         """
-            PartialDefinition : PartialInterface
-                              | PartialNamespace
+            PartialDefinition : PartialNamespace
                               | PartialDictionary
         """
         p[0] = p[1]
 
     def handlePartialObject(self, location, identifier, nonPartialConstructor,
                             nonPartialConstructorArgs,
                             partialConstructorArgs):
         """
@@ -5844,42 +6036,63 @@ class Parser(Tokenizer):
         except Exception as ex:
             if isinstance(ex, WebIDLError):
                 raise ex
             pass
 
         if not nonPartialObject:
             nonPartialObject = nonPartialConstructor(
                 # No members, False for isKnownNonPartial
-                *(nonPartialConstructorArgs + [[], False]))
+                *(nonPartialConstructorArgs), members=[], isKnownNonPartial=False)
 
         partialObject = None
         if isinstance(nonPartialObject, IDLDictionary):
             partialObject = IDLPartialDictionary(
                 *(partialConstructorArgs + [nonPartialObject]))
-        elif isinstance(nonPartialObject, (IDLInterface, IDLNamespace)):
+        elif isinstance(nonPartialObject, (IDLInterface, IDLInterfaceMixin, IDLNamespace)):
             partialObject = IDLPartialInterfaceOrNamespace(
                 *(partialConstructorArgs + [nonPartialObject]))
         else:
             raise WebIDLError("Unknown partial object type %s" %
-                    type(partialObject))
+                    type(partialObject),
+                    [location])
 
         return partialObject
 
-    def p_PartialInterface(self, p):
-        """
-            PartialInterface : INTERFACE IDENTIFIER LBRACE InterfaceMembers RBRACE SEMICOLON
+    def p_PartialInterfaceOrPartialMixin(self, p):
+        """
+            PartialInterfaceOrPartialMixin : PartialInterfaceRest
+                                           | PartialMixinRest
+        """
+        p[0] = p[1]
+
+    def p_PartialInterfaceRest(self, p):
+        """
+            PartialInterfaceRest : IDENTIFIER LBRACE InterfaceMembers RBRACE SEMICOLON
+        """
+        location = self.getLocation(p, 1)
+        identifier = IDLUnresolvedIdentifier(location, p[1])
+        members = p[3]
+
+        p[0] = self.handlePartialObject(
+            location, identifier, IDLInterface,
+            [location, self.globalScope(), identifier, None],
+            [location, identifier, members])
+
+    def p_PartialMixinRest(self, p):
+        """
+            PartialMixinRest : MIXIN IDENTIFIER LBRACE MixinMembers 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, IDLInterfaceMixin,
+            [location, self.globalScope(), identifier],
             [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])
@@ -5914,17 +6127,17 @@ class Parser(Tokenizer):
             Inheritance :
         """
         pass
 
     def p_InterfaceMembers(self, p):
         """
             InterfaceMembers : ExtendedAttributeList InterfaceMember InterfaceMembers
         """
-        p[0] = [p[2]] if p[2] else []
+        p[0] = [p[2]]
 
         assert not p[1] or p[2]
         p[2].addExtendedAttributes(p[1])
 
         p[0].extend(p[3])
 
     def p_InterfaceMembersEmpty(self, p):
         """
@@ -5934,16 +6147,42 @@ class Parser(Tokenizer):
 
     def p_InterfaceMember(self, p):
         """
             InterfaceMember : Const
                             | AttributeOrOperationOrMaplikeOrSetlikeOrIterable
         """
         p[0] = p[1]
 
+        
+    def p_MixinMembersEmpty(self, p):
+        """
+            MixinMembers :
+        """
+        p[0] = []
+
+    def p_MixinMembers(self, p):
+        """
+            MixinMembers : ExtendedAttributeList MixinMember MixinMembers
+        """
+        p[0] = [p[2]]
+
+        assert not p[1] or p[2]
+        p[2].addExtendedAttributes(p[1])
+
+        p[0].extend(p[3])
+
+    def p_MixinMember(self, p):
+        """
+            MixinMember : Const
+                        | Attribute
+                        | Operation
+        """
+        p[0] = p[1]
+
     def p_Dictionary(self, p):
         """
             Dictionary : DICTIONARY IDENTIFIER Inheritance LBRACE DictionaryMembers RBRACE SEMICOLON
         """
         location = self.getLocation(p, 1)
         identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
         members = p[5]
         p[0] = IDLDictionary(location, self.globalScope(), identifier, p[3], members)
@@ -6110,16 +6349,25 @@ class Parser(Tokenizer):
             ImplementsStatement : ScopedName IMPLEMENTS ScopedName SEMICOLON
         """
         assert(p[2] == "implements")
         implementor = IDLIdentifierPlaceholder(self.getLocation(p, 1), p[1])
         implementee = IDLIdentifierPlaceholder(self.getLocation(p, 3), p[3])
         p[0] = IDLImplementsStatement(self.getLocation(p, 1), implementor,
                                       implementee)
 
+    def p_IncludesStatement(self, p):
+        """
+            IncludesStatement : ScopedName INCLUDES ScopedName SEMICOLON
+        """
+        assert(p[2] == "includes")
+        interface = IDLIdentifierPlaceholder(self.getLocation(p, 1), p[1])
+        mixin = IDLIdentifierPlaceholder(self.getLocation(p, 3), p[3])
+        p[0] = IDLIncludesStatement(self.getLocation(p, 1), interface, mixin)
+
     def p_Const(self, p):
         """
             Const : CONST ConstType IDENTIFIER EQUALS ConstValue SEMICOLON
         """
         location = self.getLocation(p, 1)
         type = p[2]
         identifier = IDLUnresolvedIdentifier(self.getLocation(p, 3), p[3])
         value = p[5]
@@ -7239,20 +7487,27 @@ class Parser(Tokenizer):
                 self._productions.append(itr_iface)
                 iterable.iteratorType = IDLWrapperType(iface.location, itr_iface)
 
         # Then, finish all the IDLImplementsStatements.  In particular, we
         # have to make sure we do those before we do the IDLInterfaces.
         # XXX khuey hates this bit and wants to nuke it from orbit.
         implementsStatements = [p for p in self._productions if
                                 isinstance(p, IDLImplementsStatement)]
+        # Make sure we finish IDLIncludesStatements before we finish the
+        # IDLInterfaces.
+        includesStatements = [p for p in self._productions if
+                                isinstance(p, IDLIncludesStatement)]
         otherStatements = [p for p in self._productions if
-                           not isinstance(p, IDLImplementsStatement)]
+                           not isinstance(p, (IDLImplementsStatement,
+                                              IDLIncludesStatement))]
         for production in implementsStatements:
             production.finish(self.globalScope())
+        for production in includesStatements:
+            production.finish(self.globalScope())
         for production in otherStatements:
             production.finish(self.globalScope())
 
         # Do any post-finish validation we need to do
         for production in self._productions:
             production.validate()
 
         # De-duplicate self._productions, without modifying its order.
new file mode 100644
--- /dev/null
+++ b/dom/bindings/parser/tests/test_interfacemixin.py
@@ -0,0 +1,373 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+    parser.parse("interface mixin Foo { };")
+    results = parser.finish()
+    harness.ok(True, "Empty interface mixin parsed without error.")
+    harness.check(len(results), 1, "Should be one production")
+    harness.ok(isinstance(results[0], WebIDL.IDLInterfaceMixin),
+               "Should be an IDLInterfaceMixin")
+    mixin = results[0]
+    harness.check(mixin.identifier.QName(), "::Foo", "Interface mixin has the right QName")
+    harness.check(mixin.identifier.name, "Foo", "Interface mixin has the right name")
+
+    parser = parser.reset()
+    parser.parse("""
+        interface mixin QNameBase {
+            const long foo = 3;
+        };
+    """)
+    results = parser.finish()
+    harness.check(len(results), 1, "Should be one productions")
+    harness.ok(isinstance(results[0], WebIDL.IDLInterfaceMixin),
+               "Should be an IDLInterfaceMixin")
+    harness.check(len(results[0].members), 1, "Expect 1 productions")
+    mixin = results[0]
+    harness.check(mixin.members[0].identifier.QName(), "::QNameBase::foo",
+                  "Member has the right QName")
+
+    parser = parser.reset()
+    parser.parse("""
+        interface mixin A {
+            readonly attribute boolean x;
+            void foo();
+        };
+        partial interface mixin A {
+            readonly attribute boolean y;
+            void foo(long arg);
+        };
+    """)
+    results = parser.finish()
+    harness.check(len(results), 2,
+                  "Should have two results with partial interface mixin")
+    mixin = results[0]
+    harness.check(len(mixin.members), 3,
+                  "Should have three members with partial interface mixin")
+    harness.check(mixin.members[0].identifier.name, "x",
+                  "First member should be x with partial interface mixin")
+    harness.check(mixin.members[1].identifier.name, "foo",
+                  "Second member should be foo with partial interface mixin")
+    harness.check(len(mixin.members[1].signatures()), 2,
+                  "Should have two foo signatures with partial interface mixin")
+    harness.check(mixin.members[2].identifier.name, "y",
+                  "Third member should be y with partial interface mixin")
+
+    parser = parser.reset()
+    parser.parse("""
+        partial interface mixin A {
+            readonly attribute boolean y;
+            void foo(long arg);
+        };
+        interface mixin A {
+            readonly attribute boolean x;
+            void foo();
+        };
+    """)
+    results = parser.finish()
+    harness.check(len(results), 2,
+                  "Should have two results with reversed partial interface mixin")
+    mixin = results[1]
+    harness.check(len(mixin.members), 3,
+                  "Should have three members with reversed partial interface mixin")
+    harness.check(mixin.members[0].identifier.name, "x",
+                  "First member should be x with reversed partial interface mixin")
+    harness.check(mixin.members[1].identifier.name, "foo",
+                  "Second member should be foo with reversed partial interface mixin")
+    harness.check(len(mixin.members[1].signatures()), 2,
+                  "Should have two foo signatures with reversed partial interface mixin")
+    harness.check(mixin.members[2].identifier.name, "y",
+                  "Third member should be y with reversed partial interface mixin")
+
+    parser = parser.reset()
+    parser.parse("""
+        interface Interface {};
+        interface mixin Mixin {
+            attribute short x;
+        };
+        Interface includes Mixin;
+    """)
+    results = parser.finish()
+    iface = results[0]
+    harness.check(len(iface.members), 1, "Should merge members from mixins")
+    harness.check(iface.members[0].identifier.name, "x",
+                  "Should merge members from mixins")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            interface mixin A {
+                readonly attribute boolean x;
+            };
+            interface mixin A {
+                readonly attribute boolean y;
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+    harness.ok(threw,
+               "Should not allow two non-partial interface mixins with the same name")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            partial interface mixin A {
+                readonly attribute boolean x;
+            };
+            partial interface mixin A {
+                readonly attribute boolean y;
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+    harness.ok(threw,
+               "Must have a non-partial interface mixin for a given name")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            dictionary A {
+                boolean x;
+            };
+            partial interface mixin A {
+                readonly attribute boolean y;
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+    harness.ok(threw,
+               "Should not allow a name collision between partial interface "
+               "mixin and other object")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            dictionary A {
+                boolean x;
+            };
+            interface mixin A {
+                readonly attribute boolean y;
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+    harness.ok(threw,
+               "Should not allow a name collision between interface mixin "
+               "and other object")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            interface mixin A {
+                readonly attribute boolean x;
+            };
+            interface A;
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+    harness.ok(threw,
+               "Should not allow a name collision between external interface "
+               "and interface mixin")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            [SomeRandomAnnotation]
+            interface mixin A {
+                readonly attribute boolean y;
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+    harness.ok(threw,
+               "Should not allow unknown extended attributes on interface mixins")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            interface mixin A {
+                getter double (DOMString propertyName);
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+    harness.ok(threw,
+               "Should not allow getters on interface mixins")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            interface mixin A {
+                setter void (DOMString propertyName, double propertyValue);
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+    harness.ok(threw,
+               "Should not allow setters on interface mixins")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            interface mixin A {
+                deleter void (DOMString propertyName);
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+    harness.ok(threw,
+               "Should not allow deleters on interface mixins")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            interface mixin A {
+                legacycaller double compute(double x);
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+    harness.ok(threw,
+               "Should not allow legacycallers on interface mixins")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            interface mixin A {
+                inherit attribute x;
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+    harness.ok(threw,
+               "Should not allow inherited attribute on interface mixins")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            interface Interface {};
+            interface NotMixin {
+                attribute short x;
+            };
+            Interface includes NotMixin;
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+    harness.ok(threw,
+               "Should fail if the right side does not point an interface mixin")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            interface mixin NotInterface {};
+            interface mixin Mixin {
+                attribute short x;
+            };
+            NotInterface includes Mixin;
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+    harness.ok(threw,
+               "Should fail if the left side does not point an interface")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            interface mixin Mixin {
+                iterable<DOMString>;
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+    harness.ok(threw,
+               "Should fail if an interface mixin includes iterable")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            interface mixin Mixin {
+                setlike<DOMString>;
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+    harness.ok(threw,
+               "Should fail if an interface mixin includes setlike")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            interface mixin Mixin {
+                maplike<DOMString, DOMString>;
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+    harness.ok(threw,
+               "Should fail if an interface mixin includes maplike")
+
+    parser = parser.reset()
+    parser.parse("""
+        [Global] interface Window {};
+        [Global] interface Worker {};
+        [Exposed=Window]
+        interface Base {};
+        interface mixin Mixin {
+            Base returnSelf();
+        };
+        Base includes Mixin;
+    """)
+    results = parser.finish()
+    base = results[2]
+    attr = base.members[0]
+    harness.check(attr.exposureSet, set(["Window"]),
+                  "Should expose on globals where the base interfaces are exposed")
+
+    parser = parser.reset()
+    parser.parse("""
+        [Global] interface Window {};
+        [Global] interface Worker {};
+        [Exposed=Window]
+        interface Base {};
+        [Exposed=Window]
+        interface mixin Mixin {
+            attribute short a;
+        };
+        Base includes Mixin;
+    """)
+    results = parser.finish()
+    base = results[2]
+    attr = base.members[0]
+    harness.check(attr.exposureSet, set(["Window"]),
+                 "Should follow [Exposed] on interface mixin")
--- a/dom/webidl/CanvasRenderingContext2D.webidl
+++ b/dom/webidl/CanvasRenderingContext2D.webidl
@@ -117,44 +117,42 @@ interface CanvasRenderingContext2D {
   /**
    * This causes a context that is currently using a hardware-accelerated
    * backend to fallback to a software one. All state should be preserved.
    */
   [ChromeOnly]
   void demote();
 };
 
-CanvasRenderingContext2D implements CanvasState;
-CanvasRenderingContext2D implements CanvasTransform;
-CanvasRenderingContext2D implements CanvasCompositing;
-CanvasRenderingContext2D implements CanvasImageSmoothing;
-CanvasRenderingContext2D implements CanvasFillStrokeStyles;
-CanvasRenderingContext2D implements CanvasShadowStyles;
-CanvasRenderingContext2D implements CanvasFilters;
-CanvasRenderingContext2D implements CanvasRect;
-CanvasRenderingContext2D implements CanvasDrawPath;
-CanvasRenderingContext2D implements CanvasUserInterface;
-CanvasRenderingContext2D implements CanvasText;
-CanvasRenderingContext2D implements CanvasDrawImage;
-CanvasRenderingContext2D implements CanvasImageData;
-CanvasRenderingContext2D implements CanvasPathDrawingStyles;
-CanvasRenderingContext2D implements CanvasTextDrawingStyles;
-CanvasRenderingContext2D implements CanvasPathMethods;
-CanvasRenderingContext2D implements CanvasHitRegions;
+CanvasRenderingContext2D includes CanvasState;
+CanvasRenderingContext2D includes CanvasTransform;
+CanvasRenderingContext2D includes CanvasCompositing;
+CanvasRenderingContext2D includes CanvasImageSmoothing;
+CanvasRenderingContext2D includes CanvasFillStrokeStyles;
+CanvasRenderingContext2D includes CanvasShadowStyles;
+CanvasRenderingContext2D includes CanvasFilters;
+CanvasRenderingContext2D includes CanvasRect;
+CanvasRenderingContext2D includes CanvasDrawPath;
+CanvasRenderingContext2D includes CanvasUserInterface;
+CanvasRenderingContext2D includes CanvasText;
+CanvasRenderingContext2D includes CanvasDrawImage;
+CanvasRenderingContext2D includes CanvasImageData;
+CanvasRenderingContext2D includes CanvasPathDrawingStyles;
+CanvasRenderingContext2D includes CanvasTextDrawingStyles;
+CanvasRenderingContext2D includes CanvasPathMethods;
+CanvasRenderingContext2D includes CanvasHitRegions;
 
 
-[NoInterfaceObject]
-interface CanvasState {
+interface mixin CanvasState {
   // state
   void save(); // push state on state stack
   void restore(); // pop state stack and restore state
 };
 
-[NoInterfaceObject]
-interface CanvasTransform {
+interface mixin CanvasTransform {
   // transformations (default transform is the identity matrix)
   [Throws, LenientFloat]
   void scale(double x, double y);
   [Throws, LenientFloat]
   void rotate(double angle);
   [Throws, LenientFloat]
   void translate(double x, double y);
   [Throws, LenientFloat]
@@ -164,71 +162,64 @@ interface CanvasTransform {
   [Throws, LenientFloat]
   void setTransform(double a, double b, double c, double d, double e, double f);
   [Throws]
   void setTransform(optional DOMMatrix2DInit transform = {});
   [Throws]
   void resetTransform();
 };
 
-[NoInterfaceObject]
-interface CanvasCompositing {
+interface mixin CanvasCompositing {
   attribute unrestricted double globalAlpha; // (default 1.0)
   [Throws]
   attribute DOMString globalCompositeOperation; // (default source-over)
 };
 
-[NoInterfaceObject]
-interface CanvasImageSmoothing {
+interface mixin CanvasImageSmoothing {
   // drawing images
   attribute boolean imageSmoothingEnabled;
 };
 
-[NoInterfaceObject]
-interface CanvasFillStrokeStyles {
+interface mixin CanvasFillStrokeStyles {
   // colors and styles (see also the CanvasPathDrawingStyles interface)
   attribute (DOMString or CanvasGradient or CanvasPattern) strokeStyle; // (default black)
   attribute (DOMString or CanvasGradient or CanvasPattern) fillStyle; // (default black)
   [NewObject]
   CanvasGradient createLinearGradient(double x0, double y0, double x1, double y1);
   [NewObject, Throws]
   CanvasGradient createRadialGradient(double x0, double y0, double r0, double x1, double y1, double r1);
   [NewObject, Throws]
   CanvasPattern? createPattern(CanvasImageSource image, [TreatNullAs=EmptyString] DOMString repetition);
 };
 
-[NoInterfaceObject]
-interface CanvasShadowStyles {
+interface mixin CanvasShadowStyles {
   [LenientFloat]
   attribute double shadowOffsetX; // (default 0)
   [LenientFloat]
   attribute double shadowOffsetY; // (default 0)
   [LenientFloat]
   attribute double shadowBlur; // (default 0)
   attribute DOMString shadowColor; // (default transparent black)
 };
 
-[NoInterfaceObject]
-interface CanvasFilters {
+interface mixin CanvasFilters {
   [Pref="canvas.filters.enabled", SetterThrows]
   attribute DOMString filter; // (default empty string = no filter)
 };
 
-[NoInterfaceObject]
-interface CanvasRect {
+interface mixin CanvasRect {
   [LenientFloat]
   void clearRect(double x, double y, double w, double h);
   [LenientFloat]
   void fillRect(double x, double y, double w, double h);
   [LenientFloat]
   void strokeRect(double x, double y, double w, double h);
 };
 
-[NoInterfaceObject]
-interface CanvasDrawPath {
+interface mixin CanvasDrawPath {
   // path API (see also CanvasPathMethods)
   void beginPath();
   void fill(optional CanvasWindingRule winding = "nonzero");
   void fill(Path2D path, optional CanvasWindingRule winding = "nonzero");
   void stroke();
   void stroke(Path2D path);
   void clip(optional CanvasWindingRule winding = "nonzero");
   void clip(Path2D path, optional CanvasWindingRule winding = "nonzero");
@@ -238,87 +229,80 @@ interface CanvasDrawPath {
   [NeedsSubjectPrincipal] // Only required because overloads can't have different extended attributes.
   boolean isPointInPath(Path2D path, unrestricted double x, unrestricted double y, optional CanvasWindingRule winding = "nonzero");
   [NeedsSubjectPrincipal]
   boolean isPointInStroke(double x, double y);
   [NeedsSubjectPrincipal] // Only required because overloads can't have different extended attributes.
   boolean isPointInStroke(Path2D path, unrestricted double x, unrestricted double y);
 };
 
-[NoInterfaceObject]
-interface CanvasUserInterface {
+interface mixin CanvasUserInterface {
   [Pref="canvas.focusring.enabled", Throws] void drawFocusIfNeeded(Element element);
 // NOT IMPLEMENTED  void scrollPathIntoView();
 // NOT IMPLEMENTED  void scrollPathIntoView(Path path);
 };
 
-[NoInterfaceObject]
-interface CanvasText {
+interface mixin CanvasText {
   // text (see also the CanvasPathDrawingStyles interface)
   [Throws, LenientFloat]
   void fillText(DOMString text, double x, double y, optional double maxWidth);
   [Throws, LenientFloat]
   void strokeText(DOMString text, double x, double y, optional double maxWidth);
   [NewObject, Throws]
   TextMetrics measureText(DOMString text);
 };
 
-[NoInterfaceObject]
-interface CanvasDrawImage {
+interface mixin CanvasDrawImage {
   [Throws, LenientFloat]
   void drawImage(CanvasImageSource image, double dx, double dy);
   [Throws, LenientFloat]
   void drawImage(CanvasImageSource image, double dx, double dy, double dw, double dh);
   [Throws, LenientFloat]
   void drawImage(CanvasImageSource image, double sx, double sy, double sw, double sh, double dx, double dy, double dw, double dh);
 };
 
-[NoInterfaceObject]
-interface CanvasImageData {
+interface mixin CanvasImageData {
   // pixel manipulation
   [NewObject, Throws]
   ImageData createImageData(double sw, double sh);
   [NewObject, Throws]
   ImageData createImageData(ImageData imagedata);
   [NewObject, Throws, NeedsSubjectPrincipal]
   ImageData getImageData(double sx, double sy, double sw, double sh);
   [Throws]
   void putImageData(ImageData imagedata, double dx, double dy);
   [Throws]
   void putImageData(ImageData imagedata, double dx, double dy, double dirtyX, double dirtyY, double dirtyWidth, double dirtyHeight);
 };
 
-[NoInterfaceObject]
-interface CanvasPathDrawingStyles {
+interface mixin CanvasPathDrawingStyles {
   // line caps/joins
   [LenientFloat]
   attribute double lineWidth; // (default 1)
   attribute DOMString lineCap; // "butt", "round", "square" (default "butt")
   [GetterThrows]
   attribute DOMString lineJoin; // "round", "bevel", "miter" (default "miter")
   [LenientFloat]
   attribute double miterLimit; // (default 10)
 
   // dashed lines
   [LenientFloat, Throws] void setLineDash(sequence<double> segments); // default empty
   sequence<double> getLineDash();
   [LenientFloat] attribute double lineDashOffset;
 };
 
-[NoInterfaceObject]
-interface CanvasTextDrawingStyles {
+interface mixin CanvasTextDrawingStyles {
   // text
   [SetterThrows]
   attribute DOMString font; // (default 10px sans-serif)
   attribute DOMString textAlign; // "start", "end", "left", "right", "center" (default: "start")
   attribute DOMString textBaseline; // "top", "hanging", "middle", "alphabetic", "ideographic", "bottom" (default: "alphabetic")
 };
 
-[NoInterfaceObject]
-interface CanvasPathMethods {
+interface mixin CanvasPathMethods {
   // shared path API methods
   void closePath();
   [LenientFloat]
   void moveTo(double x, double y);
   [LenientFloat]
   void lineTo(double x, double y);
   [LenientFloat]
   void quadraticCurveTo(double cpx, double cpy, double x, double y);
@@ -335,18 +319,17 @@ interface CanvasPathMethods {
 
   [Throws, LenientFloat]
   void arc(double x, double y, double radius, double startAngle, double endAngle, optional boolean anticlockwise = false);
 
   [Throws, LenientFloat]
   void ellipse(double x, double y, double radiusX, double radiusY, double rotation, double startAngle, double endAngle, optional boolean anticlockwise = false);
 };
 
-[NoInterfaceObject]
-interface CanvasHitRegions {
+interface mixin CanvasHitRegions {
   // hit regions
   [Pref="canvas.hitregions.enabled", Throws] void addHitRegion(optional HitRegionOptions options = {});
   [Pref="canvas.hitregions.enabled"] void removeHitRegion(DOMString id);
   [Pref="canvas.hitregions.enabled"] void clearHitRegions();
 };
 
 interface CanvasGradient {
   // opaque object
@@ -392,9 +375,9 @@ interface TextMetrics {
 [Pref="canvas.path.enabled",
  Constructor,
  Constructor(Path2D other),
  Constructor(DOMString pathString)]
 interface Path2D
 {
   [Throws] void addPath(Path2D path, optional DOMMatrix2DInit transform = {});
 };
-Path2D implements CanvasPathMethods;
+Path2D includes CanvasPathMethods;
--- a/dom/webidl/Fetch.webidl
+++ b/dom/webidl/Fetch.webidl
@@ -5,18 +5,17 @@
  *
  * The origin of this IDL file is
  * http://fetch.spec.whatwg.org/
  */
 
 typedef object JSON;
 typedef (Blob or BufferSource or FormData or URLSearchParams or USVString) BodyInit;
 
-[NoInterfaceObject, Exposed=(Window,Worker)]
-interface Body {
+interface mixin Body {
   [Throws]
   readonly attribute boolean bodyUsed;
   [Throws]
   Promise<ArrayBuffer> arrayBuffer();
   [Throws]
   Promise<Blob> blob();
   [Throws]
   Promise<FormData> formData();
--- a/dom/webidl/IDBKeyRange.webidl
+++ b/dom/webidl/IDBKeyRange.webidl
@@ -15,17 +15,17 @@ interface IDBKeyRange {
   readonly attribute any     lower;
   [Throws]
   readonly attribute any     upper;
   [Constant]
   readonly attribute boolean lowerOpen;
   [Constant]
   readonly attribute boolean upperOpen;
   [Throws]
-  boolean includes(any key);
+  boolean _includes(any key);
 
 
   [NewObject, Throws]
   static IDBKeyRange only (any value);
   [NewObject, Throws]
   static IDBKeyRange lowerBound (any lower, optional boolean open = false);
   [NewObject, Throws]
   static IDBKeyRange upperBound (any upper, optional boolean open = false);
--- a/dom/webidl/Request.webidl
+++ b/dom/webidl/Request.webidl
@@ -36,17 +36,17 @@ interface Request {
 
   [Throws,
    NewObject] Request clone();
 
   // Bug 1124638 - Allow chrome callers to set the context.
   [ChromeOnly]
   void overrideContentPolicyType(nsContentPolicyType context);
 };
-Request implements Body;
+Request includes Body;
 
 dictionary RequestInit {
   ByteString method;
   HeadersInit headers;
   BodyInit? body;
   USVString referrer;
   ReferrerPolicy referrerPolicy;
   RequestMode mode;
--- a/dom/webidl/Response.webidl
+++ b/dom/webidl/Response.webidl
@@ -28,17 +28,17 @@ interface Response {
   [Throws,
    NewObject] Response clone();
 
   [ChromeOnly, NewObject, Throws] Response cloneUnfiltered();
 
   // For testing only.
   [ChromeOnly] readonly attribute boolean hasCacheInfoChannel;
 };
-Response implements Body;
+Response includes Body;
 
 // This should be part of Body but we don't want to expose body to request yet.
 // See bug 1387483.
 partial interface Response {
   [GetterThrows, Pref="javascript.options.streams"]
   readonly attribute ReadableStream? body;
 };