aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/script/dom/bindings/codegen/CodegenRust.py520
-rw-r--r--components/script/dom/bindings/codegen/Configuration.py40
-rw-r--r--components/script/dom/bindings/codegen/parser/WebIDL.py4
-rw-r--r--components/script/dom/bindings/codegen/parser/callback-location.patch22
-rwxr-xr-xcomponents/script/dom/bindings/codegen/parser/update.sh1
-rw-r--r--components/script/dom/bindings/iterable.rs161
-rw-r--r--components/script/dom/bindings/mod.rs1
-rw-r--r--components/script/dom/mod.rs2
-rw-r--r--components/script/dom/testbindingiterable.rs43
-rw-r--r--components/script/dom/testbindingpairiterable.rs54
-rw-r--r--components/script/dom/webidls/IterableIterator.webidl16
-rw-r--r--components/script/dom/webidls/TestBindingIterable.webidl14
-rw-r--r--components/script/dom/webidls/TestBindingPairIterable.webidl12
-rw-r--r--tests/wpt/mozilla/meta/MANIFEST.json6
-rw-r--r--tests/wpt/mozilla/meta/mozilla/iterable.html.ini3
-rw-r--r--tests/wpt/mozilla/tests/mozilla/iterable.html95
16 files changed, 847 insertions, 147 deletions
diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py
index 7e80e6c9969..f155362415d 100644
--- a/components/script/dom/bindings/codegen/CodegenRust.py
+++ b/components/script/dom/bindings/codegen/CodegenRust.py
@@ -21,6 +21,7 @@ from WebIDL import (
IDLType,
IDLInterfaceMember,
IDLUndefinedValue,
+ IDLWrapperType,
)
from Configuration import (
@@ -29,6 +30,7 @@ from Configuration import (
getTypesFromCallback,
getTypesFromDescriptor,
getTypesFromDictionary,
+ iteratorNativeType
)
AUTOGENERATED_WARNING_COMMENT = \
@@ -719,7 +721,9 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
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)
@@ -942,7 +946,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
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())
@@ -1518,6 +1522,46 @@ class MethodDefiner(PropertyDefiner):
"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']
@@ -1738,7 +1782,7 @@ 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.
"""
@@ -1752,17 +1796,19 @@ class CGImports(CGWrapper):
]
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 = []
@@ -1774,13 +1820,30 @@ class CGImports(CGWrapper):
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()
@@ -1796,19 +1859,39 @@ class CGImports(CGWrapper):
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')
@@ -1868,7 +1951,7 @@ def DOMClass(descriptor):
# 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:
@@ -2111,7 +2194,14 @@ def UnionTypes(descriptors, dictionaries, callbacks, config):
# 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():
@@ -2442,7 +2532,7 @@ class CGIDLInterface(CGThing):
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
@@ -2549,6 +2639,8 @@ assert!((*cache)[PrototypeList::Constructor::%(id)s as usize].is_null());
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:
@@ -2628,6 +2720,55 @@ assert!((*cache)[PrototypeList::Constructor::%(id)s as usize].is_null());
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)
@@ -2709,7 +2850,7 @@ class CGGetProtoObjectMethod(CGGetPerInterfaceObject):
"""
def __init__(self, descriptor):
CGGetPerInterfaceObject.__init__(self, descriptor, "GetProtoObject",
- "PrototypeList::ID", pub=descriptor.hasDescendants())
+ "PrototypeList::ID", pub=True)
def definition_body(self):
return CGList([
@@ -2727,7 +2868,7 @@ class CGGetConstructorObjectMethod(CGGetPerInterfaceObject):
def __init__(self, descriptor):
CGGetPerInterfaceObject.__init__(self, descriptor, "GetConstructorObject",
"PrototypeList::Constructor",
- pub=descriptor.hasDescendants())
+ pub=True)
def definition_body(self):
return CGList([
@@ -2966,11 +3107,21 @@ class CGPerSignatureCall(CGThing):
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):
@@ -5008,6 +5159,7 @@ class CGInterfaceTrait(CGThing):
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)
@@ -5068,6 +5220,7 @@ class CGInterfaceTrait(CGThing):
post="}")
else:
self.cgRoot = CGGeneric("")
+ self.empty = not methods
def define(self):
return self.cgRoot.define()
@@ -5083,18 +5236,136 @@ class CGWeakReferenceableTrait(CGThing):
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:
@@ -5156,9 +5427,11 @@ class CGDescriptor(CGThing):
cgThings.append(CGNamespace.build([descriptor.name + "Constants"],
CGConstant(constMembers),
public=True))
+ reexports.append(descriptor.name + 'Constants')
- if descriptor.interface.hasInterfaceObject():
+ if descriptor.interface.hasInterfaceObject() and descriptor.register:
cgThings.append(CGDefineDOMInterfaceMethod(descriptor))
+ reexports.append('DefineDOMInterface')
cgThings.append(CGConstructorEnabled(descriptor))
if descriptor.proxy:
@@ -5194,6 +5467,7 @@ class CGDescriptor(CGThing):
pass
cgThings.append(CGWrapMethod(descriptor))
+ reexports.append('Wrap')
haveUnscopables = False
if not descriptor.interface.isCallback():
@@ -5206,7 +5480,12 @@ class CGDescriptor(CGThing):
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))
@@ -5214,11 +5493,13 @@ class CGDescriptor(CGThing):
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()
@@ -5468,8 +5749,8 @@ class CGRegisterProxyHandlersMethod(CGAbstractMethod):
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")
@@ -5542,7 +5823,7 @@ class CGBindingRoot(CGThing):
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),
@@ -5553,102 +5834,8 @@ class CGBindingRoot(CGThing):
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)
@@ -6250,6 +6437,53 @@ class CallbackSetter(CallbackMember):
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))
@@ -6286,12 +6520,12 @@ class GlobalGenRoots():
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)>"
@@ -6340,15 +6574,17 @@ class GlobalGenRoots():
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
@@ -6356,9 +6592,13 @@ class GlobalGenRoots():
@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
diff --git a/components/script/dom/bindings/codegen/Configuration.py b/components/script/dom/bindings/codegen/Configuration.py
index 4074736a462..c8f92472618 100644
--- a/components/script/dom/bindings/codegen/Configuration.py
+++ b/components/script/dom/bindings/codegen/Configuration.py
@@ -4,7 +4,7 @@
import os
-from WebIDL import IDLExternalInterface, IDLInterface, WebIDLError
+from WebIDL import IDLExternalInterface, IDLInterface, IDLWrapperType, WebIDLError
class Configuration:
@@ -89,6 +89,8 @@ class Configuration:
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)
@@ -177,13 +179,26 @@ class Descriptor(DescriptorProvider):
# 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
@@ -192,10 +207,15 @@ class Descriptor(DescriptorProvider):
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)
@@ -377,7 +397,8 @@ class Descriptor(DescriptorProvider):
# 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):
@@ -404,6 +425,8 @@ def getTypesFromDictionary(dictionary):
"""
Get all member types for this dictionary
"""
+ if isinstance(dictionary, IDLWrapperType):
+ dictionary = dictionary.inner
types = []
curDict = dictionary
while curDict:
@@ -421,3 +444,10 @@ def getTypesFromCallback(callback):
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)
diff --git a/components/script/dom/bindings/codegen/parser/WebIDL.py b/components/script/dom/bindings/codegen/parser/WebIDL.py
index 54d510781a1..f3195050908 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)
@@ -6534,7 +6534,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])
diff --git a/components/script/dom/bindings/codegen/parser/callback-location.patch b/components/script/dom/bindings/codegen/parser/callback-location.patch
new file mode 100644
index 00000000000..ab6b0ae9b9b
--- /dev/null
+++ b/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
diff --git a/components/script/dom/bindings/codegen/parser/update.sh b/components/script/dom/bindings/codegen/parser/update.sh
index 76a99d9cecb..25aeefc5830 100755
--- a/components/script/dom/bindings/codegen/parser/update.sh
+++ b/components/script/dom/bindings/codegen/parser/update.sh
@@ -2,6 +2,7 @@ wget https://hg.mozilla.org/mozilla-central/raw-file/tip/dom/bindings/parser/Web
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
diff --git a/components/script/dom/bindings/iterable.rs b/components/script/dom/bindings/iterable.rs
new file mode 100644
index 00000000000..20eb84bffde
--- /dev/null
+++ b/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(())
+}
diff --git a/components/script/dom/bindings/mod.rs b/components/script/dom/bindings/mod.rs
index 19797a473d0..439016ee15b 100644
--- a/components/script/dom/bindings/mod.rs
+++ b/components/script/dom/bindings/mod.rs
@@ -136,6 +136,7 @@ pub mod global;
pub mod guard;
pub mod inheritance;
pub mod interface;
+pub mod iterable;
pub mod js;
pub mod num;
pub mod proxyhandler;
diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs
index 432246d814f..07e4d6227be 100644
--- a/components/script/dom/mod.rs
+++ b/components/script/dom/mod.rs
@@ -384,6 +384,8 @@ 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;
diff --git a/components/script/dom/testbindingiterable.rs b/components/script/dom/testbindingiterable.rs
new file mode 100644
index 00000000000..1e462a98531
--- /dev/null
+++ b/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
+ }
+}
diff --git a/components/script/dom/testbindingpairiterable.rs b/components/script/dom/testbindingpairiterable.rs
new file mode 100644
index 00000000000..9bceedd4980
--- /dev/null
+++ b/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));
+ }
+}
diff --git a/components/script/dom/webidls/IterableIterator.webidl b/components/script/dom/webidls/IterableIterator.webidl
new file mode 100644
index 00000000000..d975aa5645d
--- /dev/null
+++ b/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;
+};
diff --git a/components/script/dom/webidls/TestBindingIterable.webidl b/components/script/dom/webidls/TestBindingIterable.webidl
new file mode 100644
index 00000000000..c9e61074eed
--- /dev/null
+++ b/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>;
+};
diff --git a/components/script/dom/webidls/TestBindingPairIterable.webidl b/components/script/dom/webidls/TestBindingPairIterable.webidl
new file mode 100644
index 00000000000..a7bc66c1be3
--- /dev/null
+++ b/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>;
+};
diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json
index 322e742d7a1..4f9e37cfa05 100644
--- a/tests/wpt/mozilla/meta/MANIFEST.json
+++ b/tests/wpt/mozilla/meta/MANIFEST.json
@@ -6858,6 +6858,12 @@
"url": "/_mozilla/mozilla/interfaces.worker"
}
],
+ "mozilla/iterable.html": [
+ {
+ "path": "mozilla/iterable.html",
+ "url": "/_mozilla/mozilla/iterable.html"
+ }
+ ],
"mozilla/lenient_this.html": [
{
"path": "mozilla/lenient_this.html",
diff --git a/tests/wpt/mozilla/meta/mozilla/iterable.html.ini b/tests/wpt/mozilla/meta/mozilla/iterable.html.ini
new file mode 100644
index 00000000000..2316a8b3984
--- /dev/null
+++ b/tests/wpt/mozilla/meta/mozilla/iterable.html.ini
@@ -0,0 +1,3 @@
+[iterable.html]
+ type: testharness
+ prefs: [dom.testbinding.enabled:true]
diff --git a/tests/wpt/mozilla/tests/mozilla/iterable.html b/tests/wpt/mozilla/tests/mozilla/iterable.html
new file mode 100644
index 00000000000..d4fe1259b01
--- /dev/null
+++ b/tests/wpt/mozilla/tests/mozilla/iterable.html
@@ -0,0 +1,95 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Value and pair iterable bindings</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ function collect(iter) {
+ var collection = [];
+ for (element of iter) {
+ collection.push(element);
+ }
+ return collection;
+ }
+
+ test(function() {
+ var t = new TestBindingIterable();
+ var empty = true;
+ t.forEach(function() { empty = false; });
+ assert_true(empty);
+ }, "Empty value iterator");
+
+ test(function() {
+ var t = new TestBindingIterable();
+ function is_iterator(o) {
+ return o[Symbol.iterator]() === o;
+ }
+ assert_true(is_iterator(t.keys()));
+ assert_true(is_iterator(t.values()));
+ assert_true(is_iterator(t.entries()));
+ }, "Iterable iterators are iterators");
+
+ test(function() {
+ var t = new TestBindingIterable();
+ t.add("first");
+ t.add("second");
+ t.add("third");
+ assert_array_equals(collect(t.keys()), [0, 1, 2]);
+ assert_array_equals(collect(t.values()), ["first", "second", "third"]);
+ var expected = [[0, "first"], [1, "second"], [2, "third"]];
+ var i = 0;
+ for (entry of t.entries()) {
+ assert_array_equals(entry, expected[i++]);
+ }
+
+ t.add("fourth");
+ assert_array_equals(collect(t.keys()), [0, 1, 2, 3]);
+ assert_array_equals(collect(t.values()), ["first", "second", "third", "fourth"]);
+ var expected = [[0, "first"], [1, "second"], [2, "third"], [3, "fourth"]];
+ var i = 0;
+ for (entry of t.entries()) {
+ assert_array_equals(entry, expected[i++]);
+ }
+ }, "Iterators iterate over values");
+
+ test(function() {
+ var t = new TestBindingPairIterable();
+ var empty = true;
+ t.forEach(function() { empty = false; });
+ assert_true(empty);
+ }, "Empty pair iterator");
+
+ test(function() {
+ var t = new TestBindingPairIterable();
+ function is_iterator(o) {
+ return o[Symbol.iterator]() === o;
+ }
+ assert_true(is_iterator(t.keys()));
+ assert_true(is_iterator(t.values()));
+ assert_true(is_iterator(t.entries()));
+ }, "Pair iterable iterators are iterators");
+
+ test(function() {
+ var t = new TestBindingPairIterable();
+ t.add("first", 0);
+ t.add("second", 1);
+ t.add("third", 2);
+ assert_array_equals(collect(t.keys()), ["first", "second", "third"]);
+ assert_array_equals(collect(t.values()), [0, 1, 2]);
+ var expected = [["first", 0], ["second", 1], ["third", 2]];
+ var i = 0;
+ for (entry of t.entries()) {
+ assert_array_equals(entry, expected[i++]);
+ }
+
+ t.add("fourth", 3);
+ assert_array_equals(collect(t.keys()), ["first", "second", "third", "fourth"]);
+ assert_array_equals(collect(t.values()), [0, 1, 2, 3]);
+ var expected = [["first", 0], ["second", 1], ["third", 2], ["fourth", 3]];
+ var i = 0;
+ for (entry of t.entries()) {
+ assert_array_equals(entry, expected[i++]);
+ }
+ }, "Pair iterators iterate over key/value pairs");
+
+</script>