servo: Merge #12819 - Support pair and value iterable WebIDL bindings (from jdm:iterable2); r=nox
authorJosh Matthews <josh@joshmatthews.net>
Wed, 24 Aug 2016 12:47:53 -0500
changeset 339565 fd86563b32d7012e2f729d4ef27da517fb23a916
parent 339564 352a3025e6eb583e083ac6ef1da80d2d31a2396d
child 339566 3fd4373a34f0a74573f15bc0b97d8b89ad9140e8
push id31307
push usergszorc@mozilla.com
push dateSat, 04 Feb 2017 00:59:06 +0000
treeherdermozilla-central@94079d43835f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnox
servo: Merge #12819 - Support pair and value iterable WebIDL bindings (from jdm:iterable2); r=nox The actual iterator implementation and JSAPI calls related to setting up the interface are ported directly from Gecko's Codegen.py, IterableIterator.h, and IterableIterator.webidl. The changes to support multiple interfaces in one file are required because the internal iterator interface the parser generates gets associated with the original interface's WebIDL file. It seemed like a good time to address #571 in that case. --- - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [X] These changes fix #12628 and fix #571. - [X] There are tests for these changes Source-Repo: https://github.com/servo/servo Source-Revision: 1370fa5e3b38f3000c0b1439177cc7b7b81d380e
servo/components/script/dom/bindings/codegen/CodegenRust.py
servo/components/script/dom/bindings/codegen/Configuration.py
servo/components/script/dom/bindings/codegen/parser/WebIDL.py
servo/components/script/dom/bindings/codegen/parser/callback-location.patch
servo/components/script/dom/bindings/codegen/parser/update.sh
servo/components/script/dom/bindings/iterable.rs
servo/components/script/dom/bindings/mod.rs
servo/components/script/dom/mod.rs
servo/components/script/dom/testbindingiterable.rs
servo/components/script/dom/testbindingpairiterable.rs
servo/components/script/dom/webidls/IterableIterator.webidl
servo/components/script/dom/webidls/TestBindingIterable.webidl
servo/components/script/dom/webidls/TestBindingPairIterable.webidl
--- a/servo/components/script/dom/bindings/codegen/CodegenRust.py
+++ b/servo/components/script/dom/bindings/codegen/CodegenRust.py
@@ -16,24 +16,26 @@ import functools
 
 from WebIDL import (
     BuiltinTypes,
     IDLBuiltinType,
     IDLNullValue,
     IDLType,
     IDLInterfaceMember,
     IDLUndefinedValue,
+    IDLWrapperType,
 )
 
 from Configuration import (
     MemberIsUnforgeable,
     getModuleFromObject,
     getTypesFromCallback,
     getTypesFromDescriptor,
     getTypesFromDictionary,
+    iteratorNativeType
 )
 
 AUTOGENERATED_WARNING_COMMENT = \
     "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n\n"
 FINALIZE_HOOK_NAME = '_finalize'
 TRACE_HOOK_NAME = '_trace'
 CONSTRUCT_HOOK_NAME = '_constructor'
 HASINSTANCE_HOOK_NAME = '_hasInstance'
@@ -714,17 +716,19 @@ def getJSToNativeConversionInfo(type, de
         return templateBody
 
     assert not (isEnforceRange and isClamp)  # These are mutually exclusive
 
     if type.isArray():
         raise TypeError("Can't handle array arguments yet")
 
     if type.isSequence():
-        innerInfo = getJSToNativeConversionInfo(innerSequenceType(type), descriptorProvider)
+        innerInfo = getJSToNativeConversionInfo(innerSequenceType(type),
+                                                descriptorProvider,
+                                                isMember=isMember)
         declType = CGWrapper(innerInfo.declType, pre="Vec<", post=">")
         config = getConversionConfigForType(type, isEnforceRange, isClamp, treatNullAs)
 
         if type.nullable():
             declType = CGWrapper(declType, pre="Option<", post=" >")
 
         templateBody = ("match FromJSValConvertible::from_jsval(cx, ${val}, %s) {\n"
                         "    Ok(ConversionResult::Success(value)) => value,\n"
@@ -937,17 +941,17 @@ def getJSToNativeConversionInfo(type, de
 
     if type.isCallback():
         assert not isEnforceRange and not isClamp
         assert not type.treatNonCallableAsNull()
         assert not type.treatNonObjectAsNull() or type.nullable()
         assert not type.treatNonObjectAsNull() or not type.treatNonCallableAsNull()
 
         callback = type.unroll().callback
-        declType = CGGeneric('%s::%s' % (getModuleFromObject(callback), callback.identifier.name))
+        declType = CGGeneric(callback.identifier.name)
         finalDeclType = CGTemplatedType("Rc", declType)
 
         conversion = CGCallbackTempRoot(declType.define())
 
         if type.nullable():
             declType = CGTemplatedType("Option", declType)
             finalDeclType = CGTemplatedType("Option", finalDeclType)
             conversion = CGWrapper(conversion, pre="Some(", post=")")
@@ -1513,16 +1517,56 @@ class MethodDefiner(PropertyDefiner):
         # FIXME Check for an existing iterator on the interface first.
         if any(m.isGetter() and m.isIndexed() for m in methods):
             self.regular.append({"name": '@@iterator',
                                  "methodInfo": False,
                                  "selfHostedName": "ArrayValues",
                                  "length": 0,
                                  "condition": "Condition::Satisfied"})
 
+        # Generate the keys/values/entries aliases for value iterables.
+        maplikeOrSetlikeOrIterable = descriptor.interface.maplikeOrSetlikeOrIterable
+        if (not static and not unforgeable and
+            (maplikeOrSetlikeOrIterable and
+             maplikeOrSetlikeOrIterable.isIterable() and
+             maplikeOrSetlikeOrIterable.isValueIterator())):
+            # Add our keys/values/entries/forEach
+            self.regular.append({
+                "name": "keys",
+                "methodInfo": False,
+                "selfHostedName": "ArrayKeys",
+                "length": 0,
+                "condition": PropertyDefiner.getControllingCondition(m,
+                                                                     descriptor)
+            })
+            self.regular.append({
+                "name": "values",
+                "methodInfo": False,
+                "selfHostedName": "ArrayValues",
+                "length": 0,
+                "condition": PropertyDefiner.getControllingCondition(m,
+                                                                     descriptor)
+            })
+            self.regular.append({
+                "name": "entries",
+                "methodInfo": False,
+                "selfHostedName": "ArrayEntries",
+                "length": 0,
+                "condition": PropertyDefiner.getControllingCondition(m,
+                                                                     descriptor)
+            })
+            self.regular.append({
+                "name": "forEach",
+                "methodInfo": False,
+                "selfHostedName": "ArrayForEach",
+                "length": 0,
+                "condition": PropertyDefiner.getControllingCondition(m,
+                                                                     descriptor)
+            })
+
         isUnforgeableInterface = bool(descriptor.interface.getExtendedAttribute("Unforgeable"))
         if not static and unforgeable == isUnforgeableInterface:
             stringifier = descriptor.operations['Stringifier']
             if stringifier:
                 self.regular.append({
                     "name": "toString",
                     "nativeName": stringifier.identifier.name,
                     "length": 0,
@@ -1733,87 +1777,126 @@ class CGWrapper(CGThing):
                 defn.replace("\n", "\n" + (" " * len(self.pre))))
         return self.pre + defn + self.post
 
 
 class CGImports(CGWrapper):
     """
     Generates the appropriate import/use statements.
     """
-    def __init__(self, child, descriptors, callbacks, imports, config, ignored_warnings=None):
+    def __init__(self, child, descriptors, callbacks, dictionaries, enums, imports, config, ignored_warnings=None):
         """
         Adds a set of imports.
         """
         if ignored_warnings is None:
             ignored_warnings = [
                 'non_camel_case_types',
                 'non_upper_case_globals',
                 'unused_imports',
                 'unused_variables',
                 'unused_assignments',
             ]
 
         def componentTypes(type):
-            if type.nullable():
+            if type.isType() and type.nullable():
                 type = type.unroll()
             if type.isUnion():
                 return type.flatMemberTypes
+            if type.isDictionary():
+                return [type] + getTypesFromDictionary(type)
             return [type]
 
         def isImportable(type):
             if not type.isType():
-                assert type.isInterface()
-                return not type.isCallback()
-            return type.isNonCallbackInterface() and not type.builtin
+                assert type.isInterface() or type.isDictionary() or type.isEnum()
+                return True
+            return not (type.builtin or type.isSequence() or type.isUnion())
 
         def relatedTypesForSignatures(method):
             types = []
             for (returnType, arguments) in method.signatures():
                 types += componentTypes(returnType)
                 for arg in arguments:
                     types += componentTypes(arg.type)
             return types
 
         def getIdentifier(t):
             if t.isType():
-                return t.inner.identifier
-            assert t.isInterface()
+                if t.nullable():
+                    t = t.inner
+                if t.isCallback():
+                    return t.callback.identifier
+                return t.identifier
+            assert t.isInterface() or t.isDictionary() or t.isEnum()
             return t.identifier
 
+        def removeWrapperAndNullableTypes(types):
+            normalized = []
+            for t in types:
+                while (t.isType() and t.nullable()) or isinstance(t, IDLWrapperType):
+                    t = t.inner
+                if isImportable(t):
+                    normalized += [t]
+            return normalized
+
         types = []
         for d in descriptors:
-            types += [d.interface]
+            if not d.interface.isCallback():
+                types += [d.interface]
+
+            if d.interface.isIteratorInterface():
+                types += [d.interface.iterableInterface]
 
             members = d.interface.members + d.interface.namedConstructors
             constructor = d.interface.ctor()
             if constructor:
                 members += [constructor]
 
             if d.proxy:
                 members += [o for o in d.operations.values() if o]
 
             for m in members:
                 if m.isMethod():
                     types += relatedTypesForSignatures(m)
                 elif m.isAttr():
                     types += componentTypes(m.type)
 
+        # Import the type names used in the callbacks that are being defined.
         for c in callbacks:
             types += relatedTypesForSignatures(c)
 
+        # Import the type names used in the dictionaries that are being defined.
+        for d in dictionaries:
+            types += componentTypes(d)
+
+        # Normalize the types we've collected and remove any ones which can't be imported.
+        types = removeWrapperAndNullableTypes(types)
+
         descriptorProvider = config.getDescriptorProvider()
+        extras = []
         for t in types:
-            if isImportable(t):
+            # Importing these types in the same module that defines them is an error.
+            if t in dictionaries or t in enums:
+                continue
+            if t.isInterface():
                 descriptor = descriptorProvider.getDescriptor(getIdentifier(t).name)
-                imports += ['%s' % descriptor.path]
+                extras += [descriptor.path]
+                if descriptor.interface.parent:
+                    parentName = getIdentifier(descriptor.interface.parent).name
+                    descriptor = descriptorProvider.getDescriptor(parentName)
+                    extras += [descriptor.path, descriptor.bindingPath]
+            else:
+                if t.isEnum():
+                    extras += [getModuleFromObject(t) + '::' + getIdentifier(t).name + 'Values']
+                extras += [getModuleFromObject(t) + '::' + getIdentifier(t).name]
 
         statements = []
         if len(ignored_warnings) > 0:
             statements.append('#![allow(%s)]' % ','.join(ignored_warnings))
-        statements.extend('use %s;' % i for i in sorted(set(imports)))
+        statements.extend('use %s;' % i for i in sorted(set(imports + extras)))
 
         CGWrapper.__init__(self, child,
                            pre='\n'.join(statements) + '\n\n')
 
 
 class CGIfWrapper(CGWrapper):
     def __init__(self, condition, child):
         pre = CGWrapper(CGGeneric(condition), pre="if ", post=" {\n",
@@ -1863,17 +1946,17 @@ def DOMClassTypeId(desc):
 def DOMClass(descriptor):
     protoList = ['PrototypeList::ID::' + proto for proto in descriptor.prototypeChain]
     # Pad out the list to the right length with ID::Last so we
     # guarantee that all the lists are the same length.  ID::Last
     # is never the ID of any prototype, so it's safe to use as
     # padding.
     protoList.extend(['PrototypeList::ID::Last'] * (descriptor.config.maxProtoChainLength - len(protoList)))
     prototypeChainString = ', '.join(protoList)
-    heapSizeOf = 'heap_size_of_raw_self_and_children::<%s>' % descriptor.interface.identifier.name
+    heapSizeOf = 'heap_size_of_raw_self_and_children::<%s>' % descriptor.concreteType
     if descriptor.isGlobal():
         globals_ = camel_to_upper_snake(descriptor.name)
     else:
         globals_ = 'EMPTY'
     return """\
 DOMClass {
     interface_chain: [ %s ],
     type_id: %s,
@@ -2106,17 +2189,24 @@ def UnionTypes(descriptors, dictionaries
             unionStructs[name] = CGList([
                 CGUnionStruct(t, provider),
                 CGUnionConversionStruct(t, provider)
             ])
 
     # Sort unionStructs by key, retrieve value
     unionStructs = (i[1] for i in sorted(unionStructs.items(), key=operator.itemgetter(0)))
 
-    return CGImports(CGList(unionStructs, "\n\n"), [], [], imports, config, ignored_warnings=[])
+    return CGImports(CGList(unionStructs, "\n\n"),
+                     descriptors=[],
+                     callbacks=[],
+                     dictionaries=[],
+                     enums=[],
+                     imports=imports,
+                     config=config,
+                     ignored_warnings=[])
 
 
 class Argument():
     """
     A class for outputting the type and name of an argument
     """
     def __init__(self, argType, name, default=None, mutable=False):
         self.argType = argType
@@ -2437,17 +2527,17 @@ class CGIDLInterface(CGThing):
     Class for codegen of an implementation of the IDLInterface trait.
     """
     def __init__(self, descriptor):
         CGThing.__init__(self)
         self.descriptor = descriptor
 
     def define(self):
         interface = self.descriptor.interface
-        name = self.descriptor.name
+        name = self.descriptor.concreteType
         if (interface.getUserData("hasConcreteDescendant", False) or
                 interface.getUserData("hasProxyDescendant", False)):
             depth = self.descriptor.prototypeDepth
             check = "class.interface_chain[%s] == PrototypeList::ID::%s" % (depth, name)
         elif self.descriptor.proxy:
             check = "class as *const _ == &Class as *const _"
         else:
             check = "class as *const _ == &Class.dom_class as *const _"
@@ -2544,16 +2634,18 @@ assert!((*cache)[PrototypeList::Construc
 <*mut JSObject>::post_barrier((*cache).as_mut_ptr().offset(PrototypeList::Constructor::%(id)s as isize),
                               ptr::null_mut(),
                               interface.get());
 """ % {"id": name, "name": str_to_const_array(name)})
 
         if len(self.descriptor.prototypeChain) == 1:
             if self.descriptor.interface.getExtendedAttribute("ExceptionClass"):
                 getPrototypeProto = "prototype_proto.set(JS_GetErrorPrototype(cx))"
+            elif self.descriptor.interface.isIteratorInterface():
+                getPrototypeProto = "prototype_proto.set(JS_GetIteratorPrototype(cx))"
             else:
                 getPrototypeProto = "prototype_proto.set(JS_GetObjectPrototype(cx, global))"
         else:
             getPrototypeProto = ("%s::GetProtoObject(cx, global, prototype_proto.handle_mut())" %
                                  toBindingNamespace(self.descriptor.prototypeChain[-2]))
 
         code = [CGGeneric("""\
 rooted!(in(cx) let mut prototype_proto = ptr::null_mut());
@@ -2623,16 +2715,65 @@ assert!(!interface.is_null());""" % prop
                 code.append(CGGeneric("""\
 assert!((*cache)[PrototypeList::Constructor::%(id)s as usize].is_null());
 (*cache)[PrototypeList::Constructor::%(id)s as usize] = interface.get();
 <*mut JSObject>::post_barrier((*cache).as_mut_ptr().offset(PrototypeList::Constructor::%(id)s as isize),
                               ptr::null_mut(),
                               interface.get());
 """ % properties))
 
+        aliasedMembers = [m for m in self.descriptor.interface.members if m.isMethod() and m.aliases]
+        if aliasedMembers:
+            def defineAlias(alias):
+                if alias == "@@iterator":
+                    symbolJSID = "RUST_SYMBOL_TO_JSID(GetWellKnownSymbol(cx, SymbolCode::iterator))"
+                    getSymbolJSID = CGGeneric(fill("rooted!(in(cx) let iteratorId = ${symbolJSID});",
+                                                   symbolJSID=symbolJSID))
+                    defineFn = "JS_DefinePropertyById2"
+                    prop = "iteratorId.handle()"
+                elif alias.startswith("@@"):
+                    raise TypeError("Can't handle any well-known Symbol other than @@iterator")
+                else:
+                    getSymbolJSID = None
+                    defineFn = "JS_DefineProperty"
+                    prop = '"%s"' % alias
+                return CGList([
+                    getSymbolJSID,
+                    # XXX If we ever create non-enumerable properties that can
+                    #     be aliased, we should consider making the aliases
+                    #     match the enumerability of the property being aliased.
+                    CGGeneric(fill(
+                        """
+                        assert!(${defineFn}(cx, prototype.handle(), ${prop}, aliasedVal.handle(),
+                                            JSPROP_ENUMERATE, None, None));
+                        """,
+                        defineFn=defineFn,
+                        prop=prop))
+                ], "\n")
+
+            def defineAliasesFor(m):
+                return CGList([
+                    CGGeneric(fill(
+                        """
+                        assert!(JS_GetProperty(cx, prototype.handle(),
+                                               b\"${prop}\0\" as *const u8 as *const _,
+                                               aliasedVal.handle_mut()));
+                        """,
+                        prop=m.identifier.name))
+                ] + [defineAlias(alias) for alias in sorted(m.aliases)])
+
+            defineAliases = CGList([
+                CGGeneric(fill("""
+                    // Set up aliases on the interface prototype object we just created.
+
+                    """)),
+                CGGeneric("rooted!(in(cx) let mut aliasedVal = UndefinedValue());\n\n")
+            ] + [defineAliasesFor(m) for m in sorted(aliasedMembers)])
+            code.append(defineAliases)
+
         constructors = self.descriptor.interface.namedConstructors
         if constructors:
             decl = "let named_constructors: [(NonNullJSNative, &'static [u8], u32); %d]" % len(constructors)
             specs = []
             for constructor in constructors:
                 hook = CONSTRUCT_HOOK_NAME + "_" + constructor.identifier.name
                 name = str_to_const_array(constructor.identifier.name)
                 length = methodLength(constructor)
@@ -2704,17 +2845,17 @@ assert!(!rval.get().is_null());
 
 
 class CGGetProtoObjectMethod(CGGetPerInterfaceObject):
     """
     A method for getting the interface prototype object.
     """
     def __init__(self, descriptor):
         CGGetPerInterfaceObject.__init__(self, descriptor, "GetProtoObject",
-                                         "PrototypeList::ID", pub=descriptor.hasDescendants())
+                                         "PrototypeList::ID", pub=True)
 
     def definition_body(self):
         return CGList([
             CGGeneric("""\
 /* Get the interface prototype object for this class.  This will create the
    object as needed. */"""),
             CGGetPerInterfaceObject.definition_body(self),
         ])
@@ -2722,17 +2863,17 @@ class CGGetProtoObjectMethod(CGGetPerInt
 
 class CGGetConstructorObjectMethod(CGGetPerInterfaceObject):
     """
     A method for getting the interface constructor object.
     """
     def __init__(self, descriptor):
         CGGetPerInterfaceObject.__init__(self, descriptor, "GetConstructorObject",
                                          "PrototypeList::Constructor",
-                                         pub=descriptor.hasDescendants())
+                                         pub=True)
 
     def definition_body(self):
         return CGList([
             CGGeneric("""\
 /* Get the interface object for this class.  This will create the object as
    needed. */"""),
             CGGetPerInterfaceObject.definition_body(self),
         ])
@@ -2961,21 +3102,31 @@ class CGPerSignatureCall(CGThing):
                                              self.getArgc(), self.descriptor,
                                              invalidEnumValueFatal=not setter) for
                          i in range(argConversionStartsAt, self.argCount)])
 
         errorResult = None
         if self.isFallible():
             errorResult = " false"
 
-        cgThings.append(CGCallGenerator(
-            errorResult,
-            self.getArguments(), self.argsPre, returnType,
-            self.extendedAttributes, descriptor, nativeMethodName,
-            static))
+        if idlNode.isMethod() and idlNode.isMaplikeOrSetlikeOrIterableMethod():
+            if idlNode.maplikeOrSetlikeOrIterable.isMaplike() or \
+               idlNode.maplikeOrSetlikeOrIterable.isSetlike():
+                raise TypeError('Maplike/Setlike methods are not supported yet')
+            else:
+                cgThings.append(CGIterableMethodGenerator(descriptor,
+                                                          idlNode.maplikeOrSetlikeOrIterable,
+                                                          idlNode.identifier.name))
+        else:
+            cgThings.append(CGCallGenerator(
+                errorResult,
+                self.getArguments(), self.argsPre, returnType,
+                self.extendedAttributes, descriptor, nativeMethodName,
+                static))
+
         self.cgRoot = CGList(cgThings, "\n")
 
     def getArgs(self):
         return "args" if self.argCount > 0 else ""
 
     def getArgc(self):
         return "argc"
 
@@ -5003,16 +5154,17 @@ class CGInterfaceTrait(CGThing):
                 yield "cx", "*mut JSContext"
 
             if argument:
                 yield "value", argument_type(descriptor, argument)
 
         def members():
             for m in descriptor.interface.members:
                 if (m.isMethod() and not m.isStatic() and
+                        not m.isMaplikeOrSetlikeOrIterableMethod() and
                         (not m.isIdentifierLess() or m.isStringifier())):
                     name = CGSpecializedMethod.makeNativeName(descriptor, m)
                     infallible = 'infallible' in descriptor.getExtendedAttributes(m)
                     for idx, (rettype, arguments) in enumerate(m.signatures()):
                         arguments = method_arguments(descriptor, rettype, arguments)
                         rettype = return_type(descriptor, rettype, infallible)
                         yield name + ('_' * idx), arguments, rettype
                 elif m.isAttr() and not m.isStatic():
@@ -5063,43 +5215,162 @@ class CGInterfaceTrait(CGThing):
             for name, arguments, rettype in members()
         ]
         if methods:
             self.cgRoot = CGWrapper(CGIndenter(CGList(methods, "")),
                                     pre="pub trait %sMethods {\n" % descriptor.interface.identifier.name,
                                     post="}")
         else:
             self.cgRoot = CGGeneric("")
+        self.empty = not methods
 
     def define(self):
         return self.cgRoot.define()
 
 
 class CGWeakReferenceableTrait(CGThing):
     def __init__(self, descriptor):
         CGThing.__init__(self)
         assert descriptor.weakReferenceable
         self.code = "impl WeakReferenceable for %s {}" % descriptor.interface.identifier.name
 
     def define(self):
         return self.code
 
 
+def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries=None, enums=None):
+    if not callbacks:
+        callbacks = []
+    if not dictionaries:
+        dictionaries = []
+    if not enums:
+        enums = []
+
+    return CGImports(cgthings, descriptors, callbacks, dictionaries, enums, [
+        'js',
+        'js::{JS_CALLEE, JSCLASS_GLOBAL_SLOT_COUNT}',
+        'js::{JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL, JSCLASS_RESERVED_SLOTS_MASK}',
+        'js::error::throw_type_error',
+        'js::jsapi::{JSJitInfo_AliasSet, JSJitInfo_ArgType, AutoIdVector, CallArgs, FreeOp}',
+        'js::jsapi::{JSITER_SYMBOLS, JSPROP_ENUMERATE, JSPROP_PERMANENT, JSPROP_READONLY, JSPROP_SHARED}',
+        'js::jsapi::{JSCLASS_RESERVED_SLOTS_SHIFT, JSITER_HIDDEN, JSITER_OWNONLY}',
+        'js::jsapi::{GetPropertyKeys, Handle, Call, GetWellKnownSymbol}',
+        'js::jsapi::{HandleId, HandleObject, HandleValue, HandleValueArray}',
+        'js::jsapi::{INTERNED_STRING_TO_JSID, IsCallable, JS_CallFunctionValue}',
+        'js::jsapi::{JS_CopyPropertiesFrom, JS_ForwardGetPropertyTo}',
+        'js::jsapi::{JS_GetClass, JS_GetErrorPrototype, JS_GetFunctionPrototype}',
+        'js::jsapi::{JS_GetGlobalForObject, JS_GetObjectPrototype, JS_GetProperty}',
+        'js::jsapi::{JS_GetPropertyById, JS_GetPropertyDescriptorById, JS_GetReservedSlot}',
+        'js::jsapi::{JS_HasProperty, JS_HasPropertyById, JS_InitializePropertiesFromCompatibleNativeObject}',
+        'js::jsapi::{JS_AtomizeAndPinString, JS_NewObject, JS_NewObjectWithGivenProto}',
+        'js::jsapi::{JS_NewObjectWithoutMetadata, JS_SetProperty, JS_DefinePropertyById2}',
+        'js::jsapi::{JS_SplicePrototype, JS_SetReservedSlot, JSAutoCompartment}',
+        'js::jsapi::{JSContext, JSClass, JSFreeOp, JSFunctionSpec, JS_GetIteratorPrototype}',
+        'js::jsapi::{JSJitGetterCallArgs, JSJitInfo, JSJitMethodCallArgs, JSJitSetterCallArgs}',
+        'js::jsapi::{JSNative, JSObject, JSNativeWrapper, JSPropertySpec}',
+        'js::jsapi::{JSString, JSTracer, JSType, JSTypedMethodJitInfo, JSValueType}',
+        'js::jsapi::{ObjectOpResult, JSJitInfo_OpType, MutableHandle, MutableHandleObject}',
+        'js::jsapi::{MutableHandleValue, PropertyDescriptor, RootedObject}',
+        'js::jsapi::{SymbolCode, jsid}',
+        'js::jsval::JSVal',
+        'js::jsval::{ObjectValue, ObjectOrNullValue, PrivateValue}',
+        'js::jsval::{NullValue, UndefinedValue}',
+        'js::glue::{CallJitMethodOp, CallJitGetterOp, CallJitSetterOp, CreateProxyHandler}',
+        'js::glue::{GetProxyPrivate, NewProxyObject, ProxyTraps}',
+        'js::glue::{RUST_JSID_IS_STRING, int_to_jsid, RUST_SYMBOL_TO_JSID}',
+        'js::glue::AppendToAutoIdVector',
+        'js::rust::{GCMethods, define_methods, define_properties}',
+        'dom',
+        'dom::bindings',
+        'dom::bindings::codegen::InterfaceObjectMap',
+        'dom::bindings::global::{GlobalRef, global_root_from_object, global_root_from_reflector}',
+        'dom::bindings::interface::{InterfaceConstructorBehavior, NonCallbackInterfaceObjectClass}',
+        'dom::bindings::interface::{create_callback_interface_object, create_interface_prototype_object}',
+        'dom::bindings::interface::{create_named_constructors, create_noncallback_interface_object}',
+        'dom::bindings::interface::{define_guarded_methods, define_guarded_properties}',
+        'dom::bindings::interface::{ConstantSpec, NonNullJSNative}',
+        'dom::bindings::interface::ConstantVal::{IntVal, UintVal}',
+        'dom::bindings::interface::is_exposed_in',
+        'dom::bindings::iterable::{IteratorType, Iterable}',
+        'dom::bindings::js::{JS, Root, RootedReference}',
+        'dom::bindings::js::{OptionalRootedReference}',
+        'dom::bindings::reflector::{Reflectable}',
+        'dom::bindings::utils::{DOMClass, DOMJSClass}',
+        'dom::bindings::utils::{DOM_PROTO_UNFORGEABLE_HOLDER_SLOT, JSCLASS_DOM_GLOBAL}',
+        'dom::bindings::utils::{ProtoOrIfaceArray, create_dom_global}',
+        'dom::bindings::utils::{enumerate_global, finalize_global, find_enum_string_index}',
+        'dom::bindings::utils::{generic_getter, generic_lenient_getter, generic_lenient_setter}',
+        'dom::bindings::utils::{generic_method, generic_setter, get_array_index_from_id}',
+        'dom::bindings::utils::{get_dictionary_property, get_property_on_prototype}',
+        'dom::bindings::utils::{get_proto_or_iface_array, has_property_on_prototype}',
+        'dom::bindings::utils::{is_platform_object, resolve_global, set_dictionary_property, trace_global}',
+        'dom::bindings::trace::{JSTraceable, RootedTraceable}',
+        'dom::bindings::callback::{CallbackContainer,CallbackInterface,CallbackFunction}',
+        'dom::bindings::callback::{CallSetup,ExceptionHandling}',
+        'dom::bindings::callback::wrap_call_this_object',
+        'dom::bindings::conversions::{ConversionBehavior, ConversionResult, DOM_OBJECT_SLOT}',
+        'dom::bindings::conversions::{IDLInterface, is_array_like}',
+        'dom::bindings::conversions::{FromJSValConvertible, StringificationBehavior}',
+        'dom::bindings::conversions::{ToJSValConvertible, jsid_to_str, native_from_handlevalue}',
+        'dom::bindings::conversions::{native_from_object, private_from_object, root_from_object}',
+        'dom::bindings::conversions::{root_from_handleobject, root_from_handlevalue}',
+        'dom::bindings::codegen::{PrototypeList, RegisterBindings, UnionTypes}',
+        'dom::bindings::error::{Fallible, Error, ErrorResult}',
+        'dom::bindings::error::Error::JSFailed',
+        'dom::bindings::error::throw_dom_exception',
+        'dom::bindings::guard::{Condition, Guard}',
+        'dom::bindings::proxyhandler',
+        'dom::bindings::proxyhandler::{ensure_expando_object, fill_property_descriptor}',
+        'dom::bindings::proxyhandler::{get_expando_object, get_property_descriptor}',
+        'dom::bindings::num::Finite',
+        'dom::bindings::str::{ByteString, DOMString, USVString}',
+        'dom::bindings::weakref::{DOM_WEAK_SLOT, WeakBox, WeakReferenceable}',
+        'dom::browsingcontext::BrowsingContext',
+        'mem::heap_size_of_raw_self_and_children',
+        'libc',
+        'util::prefs::PREFS',
+        'script_runtime::{store_panic_result, maybe_take_panic_result}',
+        'std::borrow::ToOwned',
+        'std::cmp',
+        'std::mem',
+        'std::num',
+        'std::os',
+        'std::panic::{self, AssertUnwindSafe}',
+        'std::ptr',
+        'std::str',
+        'std::rc',
+        'std::rc::Rc',
+        'std::default::Default',
+        'std::ffi::CString',
+    ], config)
+
+
 class CGDescriptor(CGThing):
-    def __init__(self, descriptor):
+    def __init__(self, descriptor, config, soleDescriptor):
         CGThing.__init__(self)
 
         assert not descriptor.concrete or not descriptor.interface.isCallback()
 
+        reexports = []
+
+        def reexportedName(name):
+            if name.startswith(descriptor.name):
+                return name
+            if not soleDescriptor:
+                return '%s as %s%s' % (name, descriptor.name, name)
+            return name
+
         cgThings = []
         if not descriptor.interface.isCallback():
             cgThings.append(CGGetProtoObjectMethod(descriptor))
+            reexports.append('GetProtoObject')
         if (descriptor.interface.hasInterfaceObject() and
                 descriptor.shouldHaveGetConstructorObjectMethod()):
             cgThings.append(CGGetConstructorObjectMethod(descriptor))
+            reexports.append('GetConstructorObject')
 
         unscopableNames = []
         for m in descriptor.interface.members:
             if (m.isMethod() and
                     (not m.isIdentifierLess() or m == descriptor.operations["Stringifier"])):
                 if m.getExtendedAttribute("Unscopable"):
                     assert not m.isStatic()
                     unscopableNames.append(m.identifier.name)
@@ -5151,19 +5422,21 @@ class CGDescriptor(CGThing):
             cgThings.append(CGPrototypeJSClass(descriptor))
 
         # If there are no constant members, don't make a module for constants
         constMembers = [m for m in descriptor.interface.members if m.isConst()]
         if constMembers:
             cgThings.append(CGNamespace.build([descriptor.name + "Constants"],
                                               CGConstant(constMembers),
                                               public=True))
-
-        if descriptor.interface.hasInterfaceObject():
+            reexports.append(descriptor.name + 'Constants')
+
+        if descriptor.interface.hasInterfaceObject() and descriptor.register:
             cgThings.append(CGDefineDOMInterfaceMethod(descriptor))
+            reexports.append('DefineDOMInterface')
             cgThings.append(CGConstructorEnabled(descriptor))
 
         if descriptor.proxy:
             cgThings.append(CGDefineProxyHandler(descriptor))
 
         if descriptor.concrete:
             if descriptor.proxy:
                 # cgThings.append(CGProxyIsProxy(descriptor))
@@ -5189,41 +5462,49 @@ class CGDescriptor(CGThing):
                 # cgThings.append(CGDOMJSProxyHandler(descriptor))
                 # cgThings.append(CGIsMethod(descriptor))
                 pass
             else:
                 cgThings.append(CGDOMJSClass(descriptor))
                 pass
 
             cgThings.append(CGWrapMethod(descriptor))
+            reexports.append('Wrap')
 
         haveUnscopables = False
         if not descriptor.interface.isCallback():
             if unscopableNames:
                 haveUnscopables = True
                 cgThings.append(
                     CGList([CGGeneric("const unscopable_names: &'static [&'static [u8]] = &["),
                             CGIndenter(CGList([CGGeneric(str_to_const_array(name)) for
                                                name in unscopableNames], ",\n")),
                             CGGeneric("];\n")], "\n"))
             if descriptor.concrete or descriptor.hasDescendants():
                 cgThings.append(CGIDLInterface(descriptor))
-            cgThings.append(CGInterfaceTrait(descriptor))
+
+            interfaceTrait = CGInterfaceTrait(descriptor)
+            cgThings.append(interfaceTrait)
+            if not interfaceTrait.empty:
+                reexports.append('%sMethods' % descriptor.name)
+
             if descriptor.weakReferenceable:
                 cgThings.append(CGWeakReferenceableTrait(descriptor))
 
         properties = PropertyArrays(descriptor)
         cgThings.append(CGGeneric(str(properties)))
         cgThings.append(CGCreateInterfaceObjectsMethod(descriptor, properties, haveUnscopables))
 
-        cgThings = CGList(cgThings, "\n")
-        # self.cgRoot = CGWrapper(CGNamespace(toBindingNamespace(descriptor.name),
-        #                                     cgThings),
-        #                         post='\n')
-        self.cgRoot = cgThings
+        cgThings = generate_imports(config, CGList(cgThings, '\n'), [descriptor])
+        cgThings = CGWrapper(CGNamespace(toBindingNamespace(descriptor.name),
+                                         cgThings, public=True),
+                             post='\n')
+        reexports = ', '.join(map(lambda name: reexportedName(name), reexports))
+        self.cgRoot = CGList([CGGeneric('pub use self::%sBinding::{%s};' % (descriptor.name, reexports)),
+                              cgThings], '\n')
 
     def define(self):
         return self.cgRoot.define()
 
 
 class CGNonNamespacedEnum(CGThing):
     def __init__(self, enumName, names, first, comment="", deriving="", repr=""):
         # Account for first value
@@ -5463,18 +5744,18 @@ class CGRegisterProxyHandlersMethod(CGAb
     def __init__(self, descriptors):
         docs = "Create the global vtables used by the generated DOM bindings to implement JS proxies."
         CGAbstractMethod.__init__(self, None, 'RegisterProxyHandlers', 'void', [],
                                   unsafe=True, pub=True, docs=docs)
         self.descriptors = descriptors
 
     def definition_body(self):
         return CGList([
-            CGGeneric("proxy_handlers[Proxies::%s as usize] = codegen::Bindings::%sBinding::DefineProxyHandler();"
-                      % (desc.name, desc.name))
+            CGGeneric("proxy_handlers[Proxies::%s as usize] = Bindings::%s::DefineProxyHandler();"
+                      % (desc.name, '::'.join([desc.name + 'Binding'] * 2)))
             for desc in self.descriptors
         ], "\n")
 
 
 class CGRegisterProxyHandlers(CGThing):
     def __init__(self, config):
         descriptors = config.getDescriptors(proxy=True)
         length = len(descriptors)
@@ -5537,123 +5818,29 @@ class CGBindingRoot(CGThing):
                          for d in dictionaries])
 
         # Do codegen for all the callbacks.
         cgthings.extend(CGList([CGCallbackFunction(c, config.getDescriptorProvider()),
                                 CGCallbackFunctionImpl(c)], "\n")
                         for c in mainCallbacks)
 
         # Do codegen for all the descriptors
-        cgthings.extend([CGDescriptor(x) for x in descriptors])
+        cgthings.extend([CGDescriptor(x, config, len(descriptors) == 1) for x in descriptors])
 
         # Do codegen for all the callback interfaces.
         cgthings.extend(CGList([CGCallbackInterface(x),
                                 CGCallbackFunctionImpl(x.interface)], "\n")
                         for x in callbackDescriptors)
 
         # And make sure we have the right number of newlines at the end
         curr = CGWrapper(CGList(cgthings, "\n\n"), post="\n\n")
 
         # Add imports
-        curr = CGImports(curr, descriptors + callbackDescriptors, mainCallbacks, [
-            'js',
-            'js::{JS_CALLEE, JSCLASS_GLOBAL_SLOT_COUNT}',
-            'js::{JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL, JSCLASS_RESERVED_SLOTS_MASK}',
-            'js::error::throw_type_error',
-            'js::jsapi::{JSJitInfo_AliasSet, JSJitInfo_ArgType, AutoIdVector, CallArgs, FreeOp}',
-            'js::jsapi::{JSITER_SYMBOLS, JSPROP_ENUMERATE, JSPROP_PERMANENT, JSPROP_READONLY, JSPROP_SHARED}',
-            'js::jsapi::{JSCLASS_RESERVED_SLOTS_SHIFT, JSITER_HIDDEN, JSITER_OWNONLY}',
-            'js::jsapi::{GetPropertyKeys, Handle}',
-            'js::jsapi::{HandleId, HandleObject, HandleValue, HandleValueArray}',
-            'js::jsapi::{INTERNED_STRING_TO_JSID, IsCallable, JS_CallFunctionValue}',
-            'js::jsapi::{JS_CopyPropertiesFrom, JS_ForwardGetPropertyTo}',
-            'js::jsapi::{JS_GetClass, JS_GetErrorPrototype, JS_GetFunctionPrototype}',
-            'js::jsapi::{JS_GetGlobalForObject, JS_GetObjectPrototype, JS_GetProperty}',
-            'js::jsapi::{JS_GetPropertyById, JS_GetPropertyDescriptorById, JS_GetReservedSlot}',
-            'js::jsapi::{JS_HasProperty, JS_HasPropertyById, JS_InitializePropertiesFromCompatibleNativeObject}',
-            'js::jsapi::{JS_AtomizeAndPinString, JS_NewObject, JS_NewObjectWithGivenProto}',
-            'js::jsapi::{JS_NewObjectWithoutMetadata, JS_SetProperty}',
-            'js::jsapi::{JS_SplicePrototype, JS_SetReservedSlot, JSAutoCompartment}',
-            'js::jsapi::{JSContext, JSClass, JSFreeOp, JSFunctionSpec}',
-            'js::jsapi::{JSJitGetterCallArgs, JSJitInfo, JSJitMethodCallArgs, JSJitSetterCallArgs}',
-            'js::jsapi::{JSNative, JSObject, JSNativeWrapper, JSPropertySpec}',
-            'js::jsapi::{JSString, JSTracer, JSType, JSTypedMethodJitInfo, JSValueType}',
-            'js::jsapi::{ObjectOpResult, JSJitInfo_OpType, MutableHandle, MutableHandleObject}',
-            'js::jsapi::{MutableHandleValue, PropertyDescriptor, RootedObject}',
-            'js::jsapi::{SymbolCode, jsid}',
-            'js::jsval::JSVal',
-            'js::jsval::{ObjectValue, ObjectOrNullValue, PrivateValue}',
-            'js::jsval::{NullValue, UndefinedValue}',
-            'js::glue::{CallJitMethodOp, CallJitGetterOp, CallJitSetterOp, CreateProxyHandler}',
-            'js::glue::{GetProxyPrivate, NewProxyObject, ProxyTraps}',
-            'js::glue::{RUST_JSID_IS_STRING, int_to_jsid}',
-            'js::glue::AppendToAutoIdVector',
-            'js::rust::{GCMethods, define_methods, define_properties}',
-            'dom::bindings',
-            'dom::bindings::codegen::InterfaceObjectMap',
-            'dom::bindings::global::{GlobalRef, global_root_from_object, global_root_from_reflector}',
-            'dom::bindings::interface::{InterfaceConstructorBehavior, NonCallbackInterfaceObjectClass}',
-            'dom::bindings::interface::{create_callback_interface_object, create_interface_prototype_object}',
-            'dom::bindings::interface::{create_named_constructors, create_noncallback_interface_object}',
-            'dom::bindings::interface::{define_guarded_methods, define_guarded_properties}',
-            'dom::bindings::interface::{ConstantSpec, NonNullJSNative}',
-            'dom::bindings::interface::ConstantVal::{IntVal, UintVal}',
-            'dom::bindings::interface::is_exposed_in',
-            'dom::bindings::js::{JS, Root, RootedReference}',
-            'dom::bindings::js::{OptionalRootedReference}',
-            'dom::bindings::reflector::{Reflectable}',
-            'dom::bindings::utils::{DOMClass, DOMJSClass}',
-            'dom::bindings::utils::{DOM_PROTO_UNFORGEABLE_HOLDER_SLOT, JSCLASS_DOM_GLOBAL}',
-            'dom::bindings::utils::{ProtoOrIfaceArray, create_dom_global}',
-            'dom::bindings::utils::{enumerate_global, finalize_global, find_enum_string_index}',
-            'dom::bindings::utils::{generic_getter, generic_lenient_getter, generic_lenient_setter}',
-            'dom::bindings::utils::{generic_method, generic_setter, get_array_index_from_id}',
-            'dom::bindings::utils::{get_dictionary_property, get_property_on_prototype}',
-            'dom::bindings::utils::{get_proto_or_iface_array, has_property_on_prototype}',
-            'dom::bindings::utils::{is_platform_object, resolve_global, set_dictionary_property, trace_global}',
-            'dom::bindings::trace::{JSTraceable, RootedTraceable}',
-            'dom::bindings::callback::{CallbackContainer,CallbackInterface,CallbackFunction}',
-            'dom::bindings::callback::{CallSetup,ExceptionHandling}',
-            'dom::bindings::callback::wrap_call_this_object',
-            'dom::bindings::conversions::{ConversionBehavior, ConversionResult, DOM_OBJECT_SLOT}',
-            'dom::bindings::conversions::{IDLInterface, is_array_like}',
-            'dom::bindings::conversions::{FromJSValConvertible, StringificationBehavior}',
-            'dom::bindings::conversions::{ToJSValConvertible, jsid_to_str, native_from_handlevalue}',
-            'dom::bindings::conversions::{native_from_object, private_from_object, root_from_object}',
-            'dom::bindings::conversions::{root_from_handleobject, root_from_handlevalue}',
-            'dom::bindings::codegen::{PrototypeList, RegisterBindings, UnionTypes}',
-            'dom::bindings::codegen::Bindings::*',
-            'dom::bindings::error::{Fallible, Error, ErrorResult}',
-            'dom::bindings::error::Error::JSFailed',
-            'dom::bindings::error::throw_dom_exception',
-            'dom::bindings::guard::{Condition, Guard}',
-            'dom::bindings::proxyhandler',
-            'dom::bindings::proxyhandler::{ensure_expando_object, fill_property_descriptor}',
-            'dom::bindings::proxyhandler::{get_expando_object, get_property_descriptor}',
-            'dom::bindings::num::Finite',
-            'dom::bindings::str::{ByteString, DOMString, USVString}',
-            'dom::bindings::weakref::{DOM_WEAK_SLOT, WeakBox, WeakReferenceable}',
-            'dom::browsingcontext::BrowsingContext',
-            'mem::heap_size_of_raw_self_and_children',
-            'libc',
-            'util::prefs::PREFS',
-            'script_runtime::{store_panic_result, maybe_take_panic_result}',
-            'std::borrow::ToOwned',
-            'std::cmp',
-            'std::mem',
-            'std::num',
-            'std::os',
-            'std::panic::{self, AssertUnwindSafe}',
-            'std::ptr',
-            'std::str',
-            'std::rc',
-            'std::rc::Rc',
-            'std::default::Default',
-            'std::ffi::CString',
-        ], config)
+        curr = generate_imports(config, curr, callbackDescriptors, mainCallbacks,
+                                dictionaries, enums)
 
         # Add the auto-generated comment.
         curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
 
         # Store the final result.
         self.root = curr
 
     def define(self):
@@ -6245,16 +6432,63 @@ class CallbackSetter(CallbackMember):
             'if (!JS_SetProperty(cx, mCallback, "${attrName}", ${argv})) {\n'
             '    return Err(JSFailed);\n'
             '}\n').substitute(replacements)
 
     def getArgcDecl(self):
         return None
 
 
+class CGIterableMethodGenerator(CGGeneric):
+    """
+    Creates methods for iterable interfaces. Unwrapping/wrapping
+    will be taken care of by the usual method generation machinery in
+    CGMethodCall/CGPerSignatureCall. Functionality is filled in here instead of
+    using CGCallGenerator.
+    """
+    def __init__(self, descriptor, iterable, methodName):
+        if methodName == "forEach":
+            CGGeneric.__init__(self, fill(
+                """
+                if !IsCallable(arg0) {
+                  throw_type_error(cx, "Argument 1 of ${ifaceName}.forEach is not callable.");
+                  return false;
+                }
+                rooted!(in(cx) let arg0 = ObjectValue(&*arg0));
+                rooted!(in(cx) let mut call_arg1 = UndefinedValue());
+                rooted!(in(cx) let mut call_arg2 = UndefinedValue());
+                let mut call_args = vec![UndefinedValue(), UndefinedValue(), ObjectValue(&**_obj)];
+                rooted!(in(cx) let mut ignoredReturnVal = UndefinedValue());
+                for i in 0..(*this).get_iterable_length() {
+                  (*this).get_value_at_index(i).to_jsval(cx, call_arg1.handle_mut());
+                  (*this).get_key_at_index(i).to_jsval(cx, call_arg2.handle_mut());
+                  call_args[0] = call_arg1.handle().get();
+                  call_args[1] = call_arg2.handle().get();
+                  let call_args = HandleValueArray { length_: 3, elements_: call_args.as_ptr() };
+                  if !Call(cx, arg1, arg0.handle(), &call_args,
+                           ignoredReturnVal.handle_mut()) {
+                    return false;
+                  }
+                }
+
+                let result = ();
+                """,
+                ifaceName=descriptor.interface.identifier.name))
+            return
+        CGGeneric.__init__(self, fill(
+            """
+            let result = ${iterClass}::new(&*this,
+                                           IteratorType::${itrMethod},
+                                           super::${ifaceName}IteratorBinding::Wrap);
+            """,
+            iterClass=iteratorNativeType(descriptor, True),
+            ifaceName=descriptor.interface.identifier.name,
+            itrMethod=methodName.title()))
+
+
 def camel_to_upper_snake(s):
     return "_".join(m.group(0).upper() for m in re.finditer("[A-Z][a-z]*", s))
 
 
 class GlobalGenRoots():
     """
     Roots for global codegen.
 
@@ -6281,22 +6515,22 @@ class GlobalGenRoots():
             CGGeneric("const %s = %#x," % args)
             for args in flags
         ], "\n")), pre="pub flags Globals: u8 {\n", post="\n}")
         globals_ = CGWrapper(CGIndenter(global_flags), pre="bitflags! {\n", post="\n}")
 
         pairs = []
         for d in config.getDescriptors(hasInterfaceObject=True):
             binding = toBindingNamespace(d.name)
-            pairs.append((d.name, binding))
+            pairs.append((d.name, binding, binding))
             for ctor in d.interface.namedConstructors:
-                pairs.append((ctor.identifier.name, binding))
+                pairs.append((ctor.identifier.name, binding, binding))
         pairs.sort(key=operator.itemgetter(0))
         mappings = [
-            CGGeneric('b"%s" => codegen::Bindings::%s::DefineDOMInterface as unsafe fn(_, _),' % pair)
+            CGGeneric('b"%s" => codegen::Bindings::%s::%s::DefineDOMInterface as unsafe fn(_, _),' % pair)
             for pair in pairs
         ]
         mapType = "phf::Map<&'static [u8], unsafe fn(*mut JSContext, HandleObject)>"
         phf = CGWrapper(
             CGIndenter(CGList(mappings, "\n")),
             pre="pub static MAP: %s = phf_map! {\n" % mapType,
             post="\n};\n")
 
@@ -6335,35 +6569,41 @@ class GlobalGenRoots():
 
     @staticmethod
     def RegisterBindings(config):
         # TODO - Generate the methods we want
         code = CGList([
             CGRegisterProxyHandlers(config),
         ], "\n")
 
-        return CGImports(code, [], [], [
-            'dom::bindings::codegen',
+        return CGImports(code, descriptors=[], callbacks=[], dictionaries=[], enums=[], imports=[
+            'dom::bindings::codegen::Bindings',
             'dom::bindings::codegen::PrototypeList::Proxies',
             'libc',
-        ], config, ignored_warnings=[])
+        ], config=config, ignored_warnings=[])
 
     @staticmethod
     def InterfaceTypes(config):
-        descriptors = [d.name for d in config.getDescriptors(register=True, isCallback=False)]
+        descriptors = [d.name for d in config.getDescriptors(register=True,
+                                                             isCallback=False,
+                                                             isIteratorInterface=False)]
         curr = CGList([CGGeneric("pub use dom::%s::%s;\n" % (name.lower(), name)) for name in descriptors])
         curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
         return curr
 
     @staticmethod
     def Bindings(config):
 
-        descriptors = (set(d.name + "Binding" for d in config.getDescriptors(register=True)) |
-                       set(getModuleFromObject(d) for d in config.callbacks) |
-                       set(getModuleFromObject(d) for d in config.getDictionaries()))
+        def leafModule(d):
+            return getModuleFromObject(d).split('::')[-1]
+
+        descriptors = config.getDescriptors(register=True, isIteratorInterface=False)
+        descriptors = (set(d.name + "Binding" for d in descriptors) |
+                       set(leafModule(d) for d in config.callbacks) |
+                       set(leafModule(d) for d in config.getDictionaries()))
         curr = CGList([CGGeneric("pub mod %s;\n" % name) for name in sorted(descriptors)])
         curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
         return curr
 
     @staticmethod
     def InheritTypes(config):
 
         descriptors = config.getDescriptors(register=True, isCallback=False)
--- a/servo/components/script/dom/bindings/codegen/Configuration.py
+++ b/servo/components/script/dom/bindings/codegen/Configuration.py
@@ -1,15 +1,15 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 import os
 
-from WebIDL import IDLExternalInterface, IDLInterface, WebIDLError
+from WebIDL import IDLExternalInterface, IDLInterface, IDLWrapperType, WebIDLError
 
 
 class Configuration:
     """
     Represents global configuration state based on IDL parse data and
     the configuration file.
     """
     def __init__(self, filename, parseData):
@@ -84,16 +84,18 @@ class Configuration:
             elif key == 'isCallback':
                 getter = lambda x: x.interface.isCallback()
             elif key == 'isJSImplemented':
                 getter = lambda x: x.interface.isJSImplemented()
             elif key == 'isGlobal':
                 getter = lambda x: x.isGlobal()
             elif key == 'isExposedConditionally':
                 getter = lambda x: x.interface.isExposedConditionally()
+            elif key == 'isIteratorInterface':
+                getter = lambda x: x.interface.isIteratorInterface()
             else:
                 getter = lambda x: getattr(x, key)
             curr = filter(lambda x: getter(x) == val, curr)
         return curr
 
     def getEnums(self, webIDLFile):
         return filter(lambda e: e.filename() == webIDLFile, self.enums)
 
@@ -172,35 +174,53 @@ class Descriptor(DescriptorProvider):
         if not self.isExposedConditionally():
             if interface.parent and interface.parent.isExposedConditionally():
                 raise TypeError("%s is not conditionally exposed but inherits from "
                                 "%s which is" %
                                 (interface.identifier.name, interface.parent.identifier.name))
 
         # Read the desc, and fill in the relevant defaults.
         ifaceName = self.interface.identifier.name
-        typeName = desc.get('nativeType', ifaceName)
+        nativeTypeDefault = ifaceName
+
+        # For generated iterator interfaces for other iterable interfaces, we
+        # just use IterableIterator as the native type, templated on the
+        # nativeType of the iterable interface. That way we can have a
+        # templated implementation for all the duplicated iterator
+        # functionality.
+        if self.interface.isIteratorInterface():
+            itrName = self.interface.iterableInterface.identifier.name
+            itrDesc = self.getDescriptor(itrName)
+            nativeTypeDefault = iteratorNativeType(itrDesc)
+
+        typeName = desc.get('nativeType', nativeTypeDefault)
 
         # Callback types do not use JS smart pointers, so we should not use the
         # built-in rooting mechanisms for them.
         if self.interface.isCallback():
             self.needsRooting = False
-            ty = "%sBinding::%s" % (ifaceName, ifaceName)
+            ty = 'dom::bindings::codegen::Bindings::%sBinding::%s' % (ifaceName, ifaceName)
+            pathDefault = ty
             self.returnType = "Rc<%s>" % ty
             self.argumentType = "???"
             self.nativeType = ty
         else:
             self.needsRooting = True
             self.returnType = "Root<%s>" % typeName
             self.argumentType = "&%s" % typeName
             self.nativeType = "*const %s" % typeName
+            if self.interface.isIteratorInterface():
+                pathDefault = 'dom::bindings::iterable::IterableIterator'
+            else:
+                pathDefault = 'dom::types::%s' % typeName
 
         self.concreteType = typeName
         self.register = desc.get('register', True)
-        self.path = desc.get('path', 'dom::types::%s' % typeName)
+        self.path = desc.get('path', pathDefault)
+        self.bindingPath = 'dom::bindings::codegen::Bindings::%s' % ('::'.join([ifaceName + 'Binding'] * 2))
         self.outerObjectHook = desc.get('outerObjectHook', 'None')
         self.proxy = False
         self.weakReferenceable = desc.get('weakReferenceable', False)
 
         # If we're concrete, we need to crawl our ancestor interfaces and mark
         # them as having a concrete descendant.
         self.concrete = (not self.interface.isCallback() and
                          not self.interface.getExtendedAttribute("Abstract"))
@@ -372,17 +392,18 @@ class Descriptor(DescriptorProvider):
         of some sort.
         """
         return bool(self.interface.getExtendedAttribute("Global") or
                     self.interface.getExtendedAttribute("PrimaryGlobal"))
 
 
 # Some utility methods
 def getModuleFromObject(object):
-    return os.path.basename(object.location.filename()).split('.webidl')[0] + 'Binding'
+    return ('dom::bindings::codegen::Bindings::' +
+            os.path.basename(object.location.filename()).split('.webidl')[0] + 'Binding')
 
 
 def getTypesFromDescriptor(descriptor):
     """
     Get all argument and return types for all members of the descriptor
     """
     members = [m for m in descriptor.interface.members]
     if descriptor.interface.ctor():
@@ -399,16 +420,18 @@ def getTypesFromDescriptor(descriptor):
     types.extend(a.type for a in members if a.isAttr())
     return types
 
 
 def getTypesFromDictionary(dictionary):
     """
     Get all member types for this dictionary
     """
+    if isinstance(dictionary, IDLWrapperType):
+        dictionary = dictionary.inner
     types = []
     curDict = dictionary
     while curDict:
         types.extend([m.type for m in curDict.members])
         curDict = curDict.parent
     return types
 
 
@@ -416,8 +439,15 @@ def getTypesFromCallback(callback):
     """
     Get the types this callback depends on: its return type and the
     types of its arguments.
     """
     sig = callback.signatures()[0]
     types = [sig[0]]  # Return type
     types.extend(arg.type for arg in sig[1])  # Arguments
     return types
+
+
+def iteratorNativeType(descriptor, infer=False):
+    assert descriptor.interface.isIterable()
+    iterableDecl = descriptor.interface.maplikeOrSetlikeOrIterable
+    assert iterableDecl.isPairIterator()
+    return "IterableIterator%s" % ("" if infer else '<%s>' % descriptor.interface.identifier.name)
--- a/servo/components/script/dom/bindings/codegen/parser/WebIDL.py
+++ b/servo/components/script/dom/bindings/codegen/parser/WebIDL.py
@@ -2165,17 +2165,17 @@ class IDLUnresolvedType(IDLType):
         if obj.isTypedef():
             assert self.name.name == obj.identifier.name
             typedefType = IDLTypedefType(self.location, obj.innerType,
                                          obj.identifier)
             assert not typedefType.isComplete()
             return typedefType.complete(scope)
         elif obj.isCallback() and not obj.isInterface():
             assert self.name.name == obj.identifier.name
-            return IDLCallbackType(self.location, obj)
+            return IDLCallbackType(obj.location, obj)
 
         if self._promiseInnerType and not self._promiseInnerType.isComplete():
             self._promiseInnerType = self._promiseInnerType.complete(scope)
 
         name = self.name.resolve(scope, None)
         return IDLWrapperType(self.location, obj, self._promiseInnerType)
 
     def isDistinguishableFrom(self, other):
@@ -6529,17 +6529,17 @@ class Parser(Tokenizer):
         try:
             if self.globalScope()._lookupIdentifier(p[1]):
                 obj = self.globalScope()._lookupIdentifier(p[1])
                 assert not obj.isType()
                 if obj.isTypedef():
                     type = IDLTypedefType(self.getLocation(p, 1), obj.innerType,
                                           obj.identifier.name)
                 elif obj.isCallback() and not obj.isInterface():
-                    type = IDLCallbackType(self.getLocation(p, 1), obj)
+                    type = IDLCallbackType(obj.location, obj)
                 else:
                     type = IDLWrapperType(self.getLocation(p, 1), p[1])
                 p[0] = self.handleModifiers(type, p[2])
                 return
         except:
             pass
 
         type = IDLUnresolvedType(self.getLocation(p, 1), p[1])
new file mode 100644
--- /dev/null
+++ b/servo/components/script/dom/bindings/codegen/parser/callback-location.patch
@@ -0,0 +1,22 @@
+diff --git a/components/script/dom/bindings/codegen/parser/WebIDL.py b/components/script/dom/bindings/codegen/parser/WebIDL.py
+index da32340..81c52b7 100644
+--- a/components/script/dom/bindings/codegen/parser/WebIDL.py
++++ b/components/script/dom/bindings/codegen/parser/WebIDL.py
+@@ -2170,7 +2170,7 @@ class IDLUnresolvedType(IDLType):
+             return typedefType.complete(scope)
+         elif obj.isCallback() and not obj.isInterface():
+             assert self.name.name == obj.identifier.name
+-            return IDLCallbackType(self.location, obj)
++            return IDLCallbackType(obj.location, obj)
+
+         if self._promiseInnerType and not self._promiseInnerType.isComplete():
+             self._promiseInnerType = self._promiseInnerType.complete(scope)
+@@ -6521,7 +6521,7 @@ class Parser(Tokenizer):
+                     type = IDLTypedefType(self.getLocation(p, 1), obj.innerType,
+                                           obj.identifier.name)
+                 elif obj.isCallback() and not obj.isInterface():
+-                    type = IDLCallbackType(self.getLocation(p, 1), obj)
++                    type = IDLCallbackType(obj.location, obj)
+                 else:
+                     type = IDLWrapperType(self.getLocation(p, 1), p[1])
+                 p[0] = self.handleModifiers(type, p[2])
\ No newline at end of file
--- a/servo/components/script/dom/bindings/codegen/parser/update.sh
+++ b/servo/components/script/dom/bindings/codegen/parser/update.sh
@@ -1,10 +1,11 @@
 wget https://hg.mozilla.org/mozilla-central/raw-file/tip/dom/bindings/parser/WebIDL.py -O WebIDL.py
 patch < abstract.patch
 patch < debug.patch
 patch < pref-main-thread.patch
+patch < callback-location.patch
 
 wget https://hg.mozilla.org/mozilla-central/archive/tip.tar.gz/dom/bindings/parser/tests/ -O tests.tar.gz
 rm -r tests
 mkdir tests
 tar xvpf tests.tar.gz  -C tests --strip-components=5
 rm tests.tar.gz WebIDL.py.orig
new file mode 100644
--- /dev/null
+++ b/servo/components/script/dom/bindings/iterable.rs
@@ -0,0 +1,161 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#![allow(unsafe_code)]
+
+//! Implementation of `iterable<...>` and `iterable<..., ...>` WebIDL declarations.
+
+use dom::bindings::codegen::Bindings::IterableIteratorBinding::IterableKeyAndValueResult;
+use dom::bindings::codegen::Bindings::IterableIteratorBinding::IterableKeyOrValueResult;
+use dom::bindings::error::Fallible;
+use dom::bindings::global::GlobalRef;
+use dom::bindings::js::{JS, Root};
+use dom::bindings::reflector::{Reflector, Reflectable, reflect_dom_object};
+use dom::bindings::trace::JSTraceable;
+use js::conversions::ToJSValConvertible;
+use js::jsapi::{JSContext, JSObject, MutableHandleValue, MutableHandleObject, HandleValue};
+use js::jsval::UndefinedValue;
+use std::cell::Cell;
+use std::ptr;
+
+/// The values that an iterator will iterate over.
+#[derive(JSTraceable, HeapSizeOf)]
+pub enum IteratorType {
+    /// The keys of the iterable object.
+    Keys,
+    /// The values of the iterable object.
+    Values,
+    /// The keys and values of the iterable object combined.
+    Entries,
+}
+
+/// A DOM object that can be iterated over using a pair value iterator.
+pub trait Iterable {
+    /// The type of the key of the iterator pair.
+    type Key: ToJSValConvertible;
+    /// The type of the value of the iterator pair.
+    type Value: ToJSValConvertible;
+    /// Return the number of entries that can be iterated over.
+    fn get_iterable_length(&self) -> u32;
+    /// Return the value at the provided index.
+    fn get_value_at_index(&self, index: u32) -> Self::Value;
+    /// Return the key at the provided index.
+    fn get_key_at_index(&self, index: u32) -> Self::Key;
+}
+
+/// An iterator over the iterable entries of a given DOM interface.
+//FIXME: #12811 prevents dom_struct with type parameters
+//#[dom_struct]
+#[must_root]
+#[privatize]
+#[derive(JSTraceable)]
+#[derive(HeapSizeOf)]
+pub struct IterableIterator<T: Reflectable + JSTraceable + Iterable> {
+    reflector: Reflector,
+    iterable: JS<T>,
+    type_: IteratorType,
+    index: Cell<u32>,
+}
+
+impl<T: Reflectable + JSTraceable + Iterable> Reflectable for IterableIterator<T> {
+    fn reflector<'a>(&'a self) -> &'a Reflector {
+        &self.reflector
+    }
+    fn init_reflector(&mut self, obj: *mut JSObject) {
+        self.reflector.set_jsobject(obj);
+    }
+}
+
+impl<T: Reflectable + JSTraceable + Iterable> ToJSValConvertible for IterableIterator<T> {
+    #[allow(unsafe_code)]
+    unsafe fn to_jsval(&self,
+                       cx: *mut JSContext,
+                       rval: MutableHandleValue) {
+        let object = Reflectable::reflector(self).get_jsobject();
+        object.to_jsval(cx, rval)
+    }
+}
+
+impl<T: Reflectable + JSTraceable + Iterable> IterableIterator<T> {
+    /// Create a new iterator instance for the provided iterable DOM interface.
+    pub fn new(iterable: &T,
+               type_: IteratorType,
+               wrap: fn(*mut JSContext, GlobalRef, Box<IterableIterator<T>>)
+                     -> Root<Self>) -> Root<Self> {
+        let iterator = box IterableIterator {
+            reflector: Reflector::new(),
+            type_: type_,
+            iterable: JS::from_ref(iterable),
+            index: Cell::new(0),
+        };
+        let global = iterable.global();
+        reflect_dom_object(iterator, global.r(), wrap)
+    }
+
+    /// Return the next value from the iterable object.
+    #[allow(non_snake_case)]
+    pub fn Next(&self, cx: *mut JSContext) -> Fallible<*mut JSObject> {
+        let index = self.index.get();
+        rooted!(in(cx) let mut value = UndefinedValue());
+        rooted!(in(cx) let mut rval = ptr::null_mut());
+        if index >= self.iterable.get_iterable_length() {
+            return dict_return(cx, rval.handle_mut(), true, value.handle())
+                .map(|_| rval.handle().get());
+        }
+        let result = match self.type_ {
+            IteratorType::Keys => {
+                unsafe {
+                    self.iterable.get_key_at_index(index).to_jsval(cx, value.handle_mut());
+                }
+                dict_return(cx, rval.handle_mut(), false, value.handle())
+            }
+            IteratorType::Values => {
+                unsafe {
+                    self.iterable.get_value_at_index(index).to_jsval(cx, value.handle_mut());
+                }
+                dict_return(cx, rval.handle_mut(), false, value.handle())
+            }
+            IteratorType::Entries => {
+                rooted!(in(cx) let mut key = UndefinedValue());
+                unsafe {
+                    self.iterable.get_key_at_index(index).to_jsval(cx, key.handle_mut());
+                    self.iterable.get_value_at_index(index).to_jsval(cx, value.handle_mut());
+                }
+                key_and_value_return(cx, rval.handle_mut(), key.handle(), value.handle())
+            }
+        };
+        self.index.set(index + 1);
+        result.map(|_| rval.handle().get())
+    }
+}
+
+fn dict_return(cx: *mut JSContext,
+               result: MutableHandleObject,
+               done: bool,
+               value: HandleValue) -> Fallible<()> {
+    let mut dict = unsafe { IterableKeyOrValueResult::empty(cx) };
+    dict.done = done;
+    dict.value = value.get();
+    rooted!(in(cx) let mut dict_value = UndefinedValue());
+    unsafe {
+        dict.to_jsval(cx, dict_value.handle_mut());
+    }
+    result.set(dict_value.to_object());
+    Ok(())
+}
+
+fn key_and_value_return(cx: *mut JSContext,
+                        result: MutableHandleObject,
+                        key: HandleValue,
+                        value: HandleValue) -> Fallible<()> {
+    let mut dict = unsafe { IterableKeyAndValueResult::empty(cx) };
+    dict.done = false;
+    dict.value = Some(vec![key.get(), value.get()]);
+    rooted!(in(cx) let mut dict_value = UndefinedValue());
+    unsafe {
+        dict.to_jsval(cx, dict_value.handle_mut());
+    }
+    result.set(dict_value.to_object());
+    Ok(())
+}
--- a/servo/components/script/dom/bindings/mod.rs
+++ b/servo/components/script/dom/bindings/mod.rs
@@ -131,16 +131,17 @@
 pub mod callback;
 pub mod cell;
 pub mod conversions;
 pub mod error;
 pub mod global;
 pub mod guard;
 pub mod inheritance;
 pub mod interface;
+pub mod iterable;
 pub mod js;
 pub mod num;
 pub mod proxyhandler;
 pub mod refcounted;
 pub mod reflector;
 pub mod str;
 pub mod structuredclone;
 pub mod trace;
--- a/servo/components/script/dom/mod.rs
+++ b/servo/components/script/dom/mod.rs
@@ -379,16 +379,18 @@ pub mod serviceworkerglobalscope;
 pub mod serviceworkerregistration;
 pub mod servohtmlparser;
 pub mod servoxmlparser;
 pub mod storage;
 pub mod storageevent;
 pub mod stylesheet;
 pub mod stylesheetlist;
 pub mod testbinding;
+pub mod testbindingiterable;
+pub mod testbindingpairiterable;
 pub mod testbindingproxy;
 pub mod text;
 pub mod textdecoder;
 pub mod textencoder;
 pub mod touch;
 pub mod touchevent;
 pub mod touchlist;
 pub mod treewalker;
new file mode 100644
--- /dev/null
+++ b/servo/components/script/dom/testbindingiterable.rs
@@ -0,0 +1,43 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// check-tidy: no specs after this line
+
+use dom::bindings::cell::DOMRefCell;
+use dom::bindings::codegen::Bindings::TestBindingIterableBinding::{self, TestBindingIterableMethods};
+use dom::bindings::error::Fallible;
+use dom::bindings::global::GlobalRef;
+use dom::bindings::js::Root;
+use dom::bindings::reflector::{Reflector, reflect_dom_object};
+use dom::bindings::str::DOMString;
+
+#[dom_struct]
+pub struct TestBindingIterable {
+    reflector: Reflector,
+    vals: DOMRefCell<Vec<DOMString>>,
+}
+
+impl TestBindingIterable {
+    fn new(global: GlobalRef) -> Root<TestBindingIterable> {
+        reflect_dom_object(box TestBindingIterable {
+            reflector: Reflector::new(),
+            vals: DOMRefCell::new(vec![]),
+        }, global, TestBindingIterableBinding::Wrap)
+    }
+
+    pub fn Constructor(global: GlobalRef) -> Fallible<Root<TestBindingIterable>> {
+        Ok(TestBindingIterable::new(global))
+    }
+}
+
+impl TestBindingIterableMethods for TestBindingIterable {
+    fn Add(&self, v: DOMString) { self.vals.borrow_mut().push(v); }
+    fn Length(&self) -> u32 { self.vals.borrow().len() as u32 }
+    fn GetItem(&self, n: u32) -> DOMString { self.vals.borrow().get(n as usize).unwrap().clone() }
+    fn IndexedGetter(&self, n: u32, found: &mut bool) -> DOMString {
+        let s = self.GetItem(n);
+        *found = true;
+        s
+    }
+}
new file mode 100644
--- /dev/null
+++ b/servo/components/script/dom/testbindingpairiterable.rs
@@ -0,0 +1,54 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// check-tidy: no specs after this line
+
+use dom::bindings::cell::DOMRefCell;
+use dom::bindings::codegen::Bindings::TestBindingPairIterableBinding;
+use dom::bindings::codegen::Bindings::TestBindingPairIterableBinding::TestBindingPairIterableMethods;
+use dom::bindings::error::Fallible;
+use dom::bindings::global::GlobalRef;
+use dom::bindings::iterable::Iterable;
+use dom::bindings::js::Root;
+use dom::bindings::reflector::{Reflector, reflect_dom_object};
+use dom::bindings::str::DOMString;
+
+#[dom_struct]
+pub struct TestBindingPairIterable {
+    reflector: Reflector,
+    map: DOMRefCell<Vec<(DOMString, u32)>>,
+}
+
+impl Iterable for TestBindingPairIterable {
+    type Key = DOMString;
+    type Value = u32;
+    fn get_iterable_length(&self) -> u32 {
+        self.map.borrow().len() as u32
+    }
+    fn get_value_at_index(&self, index: u32) -> u32 {
+        self.map.borrow().iter().nth(index as usize).map(|a| &a.1).unwrap().clone()
+    }
+    fn get_key_at_index(&self, index: u32) -> DOMString {
+        self.map.borrow().iter().nth(index as usize).map(|a| &a.0).unwrap().clone()
+    }
+}
+
+impl TestBindingPairIterable {
+    fn new(global: GlobalRef) -> Root<TestBindingPairIterable> {
+        reflect_dom_object(box TestBindingPairIterable {
+            reflector: Reflector::new(),
+            map: DOMRefCell::new(vec![]),
+        }, global, TestBindingPairIterableBinding::TestBindingPairIterableWrap)
+    }
+
+    pub fn Constructor(global: GlobalRef) -> Fallible<Root<TestBindingPairIterable>> {
+        Ok(TestBindingPairIterable::new(global))
+    }
+}
+
+impl TestBindingPairIterableMethods for TestBindingPairIterable {
+    fn Add(&self, key: DOMString, value: u32) {
+        self.map.borrow_mut().push((key, value));
+    }
+}
new file mode 100644
--- /dev/null
+++ b/servo/components/script/dom/webidls/IterableIterator.webidl
@@ -0,0 +1,16 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// This interface is entirely internal to Servo, and should not be accessible to
+// web pages.
+
+dictionary IterableKeyOrValueResult {
+  any value;
+  boolean done = false;
+};
+
+dictionary IterableKeyAndValueResult {
+  sequence<any> value;
+  boolean done = false;
+};
new file mode 100644
--- /dev/null
+++ b/servo/components/script/dom/webidls/TestBindingIterable.webidl
@@ -0,0 +1,14 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// This interface is entirely internal to Servo, and should not be accessible to
+// web pages.
+
+[Pref="dom.testbinding.enabled", Exposed=(Window,Worker), Constructor]
+interface TestBindingIterable {
+  void add(DOMString arg);
+  readonly attribute unsigned long length;
+  getter DOMString getItem(unsigned long index);
+  iterable<DOMString>;
+};
new file mode 100644
--- /dev/null
+++ b/servo/components/script/dom/webidls/TestBindingPairIterable.webidl
@@ -0,0 +1,12 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// This interface is entirely internal to Servo, and should not be accessible to
+// web pages.
+
+[Pref="dom.testbinding.enabled", Exposed=(Window,Worker), Constructor]
+interface TestBindingPairIterable {
+  void add(DOMString key, unsigned long value);
+  iterable<DOMString, unsigned long>;
+};