aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/components/script/dom/bindings/codegen/CodegenRust.py566
-rw-r--r--src/components/script/dom/bindings/codegen/GlobalGen.py5
-rw-r--r--src/components/script/dom/bindings/conversions.rs52
-rw-r--r--src/components/script/dom/bindings/utils.rs9
-rw-r--r--src/components/script/dom/htmlselectelement.rs5
-rw-r--r--src/components/script/dom/webidls/HTMLSelectElement.webidl4
-rw-r--r--src/components/script/script.rs2
-rw-r--r--src/test/html/content/harness.js18
-rw-r--r--src/test/html/content/test_exception.html7
-rw-r--r--src/test/html/content/test_union.html26
10 files changed, 631 insertions, 63 deletions
diff --git a/src/components/script/dom/bindings/codegen/CodegenRust.py b/src/components/script/dom/bindings/codegen/CodegenRust.py
index da0ad3dd8df..ca04b873aab 100644
--- a/src/components/script/dom/bindings/codegen/CodegenRust.py
+++ b/src/components/script/dom/bindings/codegen/CodegenRust.py
@@ -92,9 +92,13 @@ class CastableObjectUnwrapper():
codeOnFailure is the code to run if unwrapping fails.
"""
- def __init__(self, descriptor, source, target, codeOnFailure, isOptional=False):
+ def __init__(self, descriptor, source, target, codeOnFailure, isOptional=False,
+ preUnwrapped=None, postUnwrapped=None):
assert descriptor.castable
+ unwrappedVal = "val"
+ if preUnwrapped or postUnwrapped:
+ unwrappedVal = preUnwrapped + unwrappedVal + postUnwrapped
self.substitution = { "type" : descriptor.nativeType,
"depth": descriptor.interface.inheritanceDepth(),
"prototype": "PrototypeList::id::" + descriptor.name,
@@ -102,7 +106,7 @@ class CastableObjectUnwrapper():
"source" : source,
"target" : target,
"codeOnFailure" : CGIndenter(CGGeneric(codeOnFailure), 4).define(),
- "unwrapped_val" : "Some(val)" if isOptional else "val",
+ "unwrapped_val" : ("Some(%s)" % unwrappedVal) if isOptional else unwrappedVal,
"unwrapFn": "unwrap_jsmanaged" if 'JS' in descriptor.nativeType else "unwrap_object"}
if descriptor.hasXPConnectImpls:
# We don't use xpc_qsUnwrapThis because it will always throw on
@@ -447,7 +451,9 @@ def getJSToNativeConversionTemplate(type, descriptorProvider, failureCode=None,
isClamp=False,
exceptionCode=None,
isCallbackReturnValue=False,
- sourceDescription="value"):
+ sourceDescription="value",
+ preSuccess=None,
+ postSuccess=None):
"""
Get a template for converting a JS value to a native object based on the
given type and descriptor. If failureCode is given, then we're actually
@@ -706,8 +712,8 @@ for (uint32_t i = 0; i < length; ++i) {
(isinstance(defaultValue, IDLNullValue) and nullable))
unionArgumentObj = "${holderName}"
- if isOptional or nullable:
- unionArgumentObj += ".ref()"
+ #if isOptional or nullable:
+ # unionArgumentObj += ".get_mut_ref()"
memberTypes = type.flatMemberTypes
names = []
@@ -720,7 +726,7 @@ for (uint32_t i = 0; i < length; ++i) {
name = memberType.inner.identifier.name
else:
name = memberType.name
- interfaceObject.append(CGGeneric("(failed = !%s.TrySetTo%s(cx, ${val}, ${valPtr}, tryNext)) || !tryNext" % (unionArgumentObj, name)))
+ interfaceObject.append(CGGeneric("{res = %s.TrySetTo%s(cx, ${val}, ${valPtr}); res.is_err() || !res.unwrap()}" % (unionArgumentObj, name)))
names.append(name)
interfaceObject = CGWrapper(CGList(interfaceObject, " ||\n"), pre="done = ", post=";\n", reindent=True)
else:
@@ -731,7 +737,7 @@ for (uint32_t i = 0; i < length; ++i) {
assert len(arrayObjectMemberTypes) == 1
memberType = arrayObjectMemberTypes[0]
name = memberType.name
- arrayObject = CGGeneric("done = (failed = !%s.TrySetTo%s(cx, ${val}, ${valPtr}, tryNext)) || !tryNext;" % (unionArgumentObj, name))
+ arrayObject = CGGeneric("done = {res = %s.TrySetTo%s(cx, ${val}, ${valPtr}); res.is_err() || !res.unwrap()};" % (unionArgumentObj, name))
# XXX Now we're supposed to check for an array or a platform object
# that supports indexed properties... skip that last for now. It's a
# bit of a pain.
@@ -761,7 +767,7 @@ for (uint32_t i = 0; i < length; ++i) {
assert len(callbackMemberTypes) == 1
memberType = callbackMemberTypes[0]
name = memberType.name
- callbackObject = CGGeneric("done = (failed = !%s.TrySetTo%s(cx, ${val}, ${valPtr}, tryNext)) || !tryNext;" % (unionArgumentObj, name))
+ callbackObject = CGGeneric("done = {res = %s.TrySetTo%s(cx, ${val}, ${valPtr}); res.is_err() || !res.unwrap()};" % (unionArgumentObj, name))
names.append(name)
else:
callbackObject = None
@@ -816,7 +822,7 @@ for (uint32_t i = 0; i < length; ++i) {
if any([arrayObject, dateObject, nonPlatformObject, object]):
templateBody.prepend(CGGeneric("JSObject& argObj = ${val}.toObject();"))
templateBody = CGWrapper(CGIndenter(templateBody),
- pre="if (${val}.isObject()) {\n",
+ pre="if JSVAL_IS_OBJECT(${val}) {\n",
post="\n}")
else:
templateBody = CGGeneric()
@@ -831,7 +837,7 @@ for (uint32_t i = 0; i < length; ++i) {
name = memberType.inner.identifier.name
else:
name = memberType.name
- other = CGGeneric("done = (failed = !%s.TrySetTo%s(cx, ${val}, ${valPtr}, tryNext)) || !tryNext;" % (unionArgumentObj, name))
+ other = CGGeneric("done = {res = %s.TrySetTo%s(cx, ${val}, ${valPtr}); res.is_err() || !res.unwrap()};" % (unionArgumentObj, name))
names.append(name)
if hasObjectTypes:
other = CGWrapper(CGIndenter(other), "{\n", post="\n}")
@@ -844,28 +850,25 @@ for (uint32_t i = 0; i < length; ++i) {
else:
other = None
- templateBody = CGWrapper(templateBody, pre="bool done = false, failed = false, tryNext;\n")
- throw = CGGeneric("if (failed) {\n"
- " return false;\n"
+ templateBody = CGWrapper(templateBody, pre="let mut done = false;\n"
+ "let mut res = Ok(true);\n")
+ throw = CGGeneric("if res.is_err() {\n"
+ " return 0;\n"
"}\n"
- "if (!done) {\n"
- " return ThrowErrorMessage(cx, MSG_NOT_IN_UNION, \"%s\");\n"
+ "if !done {\n"
+ " return throw_not_in_union(cx, \"%s\");\n"
"}" % ", ".join(names))
templateBody = CGWrapper(CGIndenter(CGList([templateBody, throw], "\n")), pre="{\n", post="\n}")
typeName = type.name
argumentTypeName = typeName + "Argument"
if nullable:
- typeName = "Nullable<" + typeName + " >"
- if isOptional:
- nonConstDecl = "const_cast<Optional<" + typeName + " >& >(${declName})"
- else:
- nonConstDecl = "const_cast<" + typeName + "& >(${declName})"
- typeName = "const " + typeName
+ typeName = "Option<" + typeName + " >"
+ nonConstDecl = "${declName}"
def handleNull(templateBody, setToNullVar, extraConditionForNull=""):
- null = CGGeneric("if (%s${val}.isNullOrUndefined()) {\n"
- " %s.SetNull();\n"
+ null = CGGeneric("if %s(RUST_JSVAL_IS_NULL(${val}) != 0 || RUST_JSVAL_IS_VOID(${val}) != 0) {\n"
+ " %s = None;\n"
"}" % (extraConditionForNull, setToNullVar))
templateBody = CGWrapper(CGIndenter(templateBody), pre="{\n", post="\n}")
return CGList([null, templateBody], " else ")
@@ -878,21 +881,24 @@ for (uint32_t i = 0; i < length; ++i) {
if isOptional:
mutableDecl = nonConstDecl + ".Value()"
declType = CGWrapper(declType, pre="const Optional<", post=" >")
- holderType = CGWrapper(holderType, pre="Maybe<", post=" >")
+ holderType = CGWrapper(holderType, pre="Option<", post=" >")
constructDecl = CGGeneric(nonConstDecl + ".Construct();")
if nullable:
- constructHolder = CGGeneric("${holderName}.construct(%s.SetValue());" % mutableDecl)
+ constructHolder = CGGeneric("${holderName} = Some(%s.SetValue());" % mutableDecl)
else:
- constructHolder = CGGeneric("${holderName}.construct(${declName}.Value());")
+ constructHolder = CGGeneric("${holderName} = Some(${declName}.Value());")
else:
mutableDecl = nonConstDecl
constructDecl = None
+ holderInit = "${declName}"
if nullable:
- holderType = CGWrapper(holderType, pre="Maybe<", post=" >")
- constructHolder = CGGeneric("${holderName}.construct(%s.SetValue());" % mutableDecl)
+ holderInit += ".get_mut_ref()"
else:
- constructHolder = CGWrapper(holderType, post=" ${holderName}(${declName});")
- holderType = None
+ holderInit = "&mut " + holderInit
+ constructHolder = CGWrapper(holderType, pre="let mut ${holderName} = ", post="::new(" + holderInit + ");")
+ if nullable:
+ constructHolder = CGWrapper(constructHolder, pre="${declName} = Some(uninit());\n")
+ holderType = None
templateBody = CGList([constructHolder, templateBody], "\n")
if nullable:
@@ -903,9 +909,10 @@ for (uint32_t i = 0; i < length; ++i) {
valueMissing = ""
templateBody = handleNull(templateBody, mutableDecl,
extraConditionForNull=valueMissing)
- templateBody = CGList([constructDecl, templateBody], "\n")
+ templateBody = CGWrapper(CGIndenter(CGList([constructDecl, templateBody], "\n")),
+ pre="{\n", post="\n}")
- return templateBody.define(), declType, holderType, False, None
+ return templateBody.define(), declType, holderType, False, "uninit()" if not nullable else None
if type.isGeckoInterface():
assert not isEnforceRange and not isClamp
@@ -968,7 +975,8 @@ for (uint32_t i = 0; i < length; ++i) {
"JSVAL_TO_OBJECT(${val})",
"${declName}",
failureCode,
- isOptional or argIsPointer or type.nullable()))
+ isOptional or argIsPointer or type.nullable(),
+ preUnwrapped=preSuccess, postUnwrapped=postSuccess))
else:
templateBody += str(FailureFatalCastableObjectUnwrapper(
descriptor,
@@ -1254,32 +1262,41 @@ for (uint32_t i = 0; i < length; ++i) {
elif isClamp:
conversionBehavior = "eClamp"
+ if failureCode is None:
+ failureCode = 'return 0'
+
if type.nullable():
dataLoc = "${declName}.SetValue()"
nullCondition = "(RUST_JSVAL_IS_NULL(${val}) != 0 || RUST_JSVAL_IS_VOID(${val}) != 0)"
if defaultValue is not None and isinstance(defaultValue, IDLNullValue):
nullCondition = "!(${haveValue}) || " + nullCondition
+ successVal = "val_"
+ if preSuccess or postSuccess:
+ successVal = preSuccess + successVal + postSuccess
#XXXjdm support conversionBehavior here
template = (
"if (%s) {\n"
" ${declName} = None;\n"
"} else {\n"
" match JSValConvertible::from_jsval(${val}) {\n"
- " Some(val_) => ${declName} = Some(val_),\n"
- " None => return 0\n"
+ " Some(val_) => ${declName} = Some(%s),\n"
+ " None => %s\n"
" }\n"
- "}" % nullCondition)
+ "}" % (nullCondition, successVal, failureCode))
declType = CGGeneric("Option<" + typeName + ">")
else:
assert(defaultValue is None or
not isinstance(defaultValue, IDLNullValue))
dataLoc = "${declName}"
#XXXjdm conversionBehavior should be used
+ successVal = "v"
+ if preSuccess or postSuccess:
+ successVal = preSuccess + successVal + postSuccess
template = (
"match JSValConvertible::from_jsval(${val}) {\n"
- " None => return 0,\n"
- " Some(v) => %s = v\n"
- "}" % (dataLoc,))
+ " None => %s,\n"
+ " Some(v) => %s = %s\n"
+ "}" % (failureCode, dataLoc, successVal))
declType = CGGeneric(typeName)
if (defaultValue is not None and
# We already handled IDLNullValue, so just deal with the other ones
@@ -2385,6 +2402,112 @@ class CGGeneric(CGThing):
def define(self):
return self.defineText
+def getTypes(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():
+ members.append(descriptor.interface.ctor())
+ signatures = [s for m in members if m.isMethod() for s in m.signatures()]
+ types = []
+ for s in signatures:
+ assert len(s) == 2
+ (returnType, arguments) = s
+ types.append(returnType)
+ types.extend([a.type for a in arguments])
+
+ types.extend(a.type for a in members if a.isAttr())
+ return types
+
+def SortedTuples(l):
+ """
+ Sort a list of tuples based on the first item in the tuple
+ """
+ return sorted(l, key=operator.itemgetter(0))
+
+def SortedDictValues(d):
+ """
+ Returns a list of values from the dict sorted by key.
+ """
+ # Create a list of tuples containing key and value, sorted on key.
+ d = SortedTuples(d.items())
+ # We're only interested in the values.
+ return (i[1] for i in d)
+
+def UnionTypes(descriptors):
+ """
+ Returns a tuple containing a set of header filenames to include, a set of
+ tuples containing a type declaration and a boolean if the type is a struct
+ for member types of the unions and a CGList containing CGUnionStructs for
+ every union.
+ """
+
+ # Now find all the things we'll need as arguments and return values because
+ # we need to wrap or unwrap them.
+ headers = set()
+ declarations = set()
+ unionStructs = dict()
+ for d in descriptors:
+ if d.interface.isExternal():
+ continue
+
+ for t in getTypes(d):
+ t = t.unroll()
+ if t.isUnion():
+ name = str(t)
+ if not name in unionStructs:
+ unionStructs[name] = CGUnionStruct(t, d)
+ for f in t.flatMemberTypes:
+ f = f.unroll()
+ if f.isInterface():
+ if f.isSpiderMonkeyInterface():
+ headers.add("jsfriendapi.h")
+ headers.add("mozilla/dom/TypedArray.h")
+ else:
+ typeDesc = d.getDescriptor(f.inner.identifier.name)
+ if typeDesc is not None:
+ declarations.add((typeDesc.nativeType, False))
+ elif f.isDictionary():
+ declarations.add((f.inner.identifier.name, True))
+
+ return (headers, declarations, CGList(SortedDictValues(unionStructs), "\n"))
+
+def UnionConversions(descriptors):
+ """
+ Returns a CGThing to declare all union argument conversion helper structs.
+ """
+ # Now find all the things we'll need as arguments because we
+ # need to unwrap them.
+ unionConversions = dict()
+ for d in descriptors:
+ if d.interface.isExternal():
+ continue
+
+ def addUnionTypes(type):
+ if type.isUnion():
+ type = type.unroll()
+ name = str(type)
+ if not name in unionConversions:
+ unionConversions[name] = CGUnionConversionStruct(type, d)
+
+ members = [m for m in d.interface.members]
+ if d.interface.ctor():
+ members.append(d.interface.ctor())
+ signatures = [s for m in members if m.isMethod() for s in m.signatures()]
+ for s in signatures:
+ assert len(s) == 2
+ (_, arguments) = s
+ for a in arguments:
+ addUnionTypes(a.type)
+
+ for m in members:
+ if m.isAttr() and not m.readonly:
+ addUnionTypes(m.type)
+
+ return CGWrapper(CGList(SortedDictValues(unionConversions), "\n"),
+ post="\n\n")
+
class Argument():
"""
A class for outputting the type and name of an argument
@@ -3544,6 +3667,282 @@ class CGEnum(CGThing):
""" % (",\n ".join(map(getEnumValueName, self.enum.values())),
",\n ".join(['EnumEntry {value: &"' + val + '", length: ' + str(len(val)) + '}' for val in self.enum.values()]))
+def getUnionAccessorSignatureType(type, descriptorProvider):
+ """
+ Returns the types that are used in the getter and setter signatures for
+ union types
+ """
+ if type.isArray():
+ raise TypeError("Can't handle array arguments yet")
+
+ if type.isSequence():
+ nullable = type.nullable();
+ if nullable:
+ type = type.inner.inner
+ else:
+ type = type.inner
+ (elementTemplate, elementDeclType,
+ elementHolderType, dealWithOptional) = getJSToNativeConversionTemplate(
+ type, descriptorProvider, isSequenceMember=True)
+ typeName = CGWrapper(elementDeclType, pre="Sequence< ", post=" >&")
+ if nullable:
+ typeName = CGWrapper(typeName, pre="Nullable< ", post=" >&")
+
+ return typeName
+
+ if type.isUnion():
+ typeName = CGGeneric(type.name)
+ if type.nullable():
+ typeName = CGWrapper(typeName, pre="Nullable< ", post=" >&")
+
+ return typeName
+
+ if type.isGeckoInterface():
+ descriptor = descriptorProvider.getDescriptor(
+ type.unroll().inner.identifier.name)
+ typeName = CGGeneric(descriptor.nativeType)
+ # Allow null pointers for nullable types and old-binding classes
+ if type.nullable() or type.unroll().inner.isExternal():
+ typeName = CGWrapper(typeName, pre="Option<", post=">")
+ else:
+ typeName = CGWrapper(typeName, pre="&'a ")
+ return typeName
+
+ if type.isSpiderMonkeyInterface():
+ typeName = CGGeneric(type.name)
+ if type.nullable():
+ typeName = CGWrapper(typeName, pre="Option<", post=">")
+ else:
+ typeName = CGWrapper(typeName, pre="&")
+ return typeName
+
+ if type.isString():
+ return CGGeneric("const nsAString&")
+
+ if type.isEnum():
+ if type.nullable():
+ raise TypeError("We don't support nullable enumerated arguments or "
+ "union members yet")
+ return CGGeneric(type.inner.identifier.name)
+
+ if type.isCallback():
+ return CGGeneric("JSObject*")
+
+ if type.isAny():
+ return CGGeneric("JS::Value")
+
+ if type.isObject():
+ typeName = CGGeneric("JSObject")
+ if type.nullable():
+ typeName = CGWrapper(typeName, post="*")
+ else:
+ typeName = CGWrapper(typeName, post="&")
+ return typeName
+
+ if not type.isPrimitive():
+ raise TypeError("Need native type for argument type '%s'" % str(type))
+
+ typeName = CGGeneric(builtinNames[type.tag()])
+ if type.nullable():
+ typeName = CGWrapper(typeName, pre="Nullable< ", post=" >&")
+ return typeName
+
+def getUnionTypeTemplateVars(type, descriptorProvider):
+ # For dictionaries and sequences we need to pass None as the failureCode
+ # for getJSToNativeConversionTemplate.
+ # Also, for dictionaries we would need to handle conversion of
+ # null/undefined to the dictionary correctly.
+ if type.isDictionary() or type.isSequence():
+ raise TypeError("Can't handle dictionaries or sequences in unions")
+
+ if type.isGeckoInterface():
+ name = type.inner.identifier.name
+ typeName = descriptorProvider.getDescriptor(name).nativeType
+ elif type.isEnum():
+ name = type.inner.identifier.name
+ typeName = name
+ elif type.isArray() or type.isSequence():
+ name = str(type)
+ #XXXjdm dunno about typeName here
+ typeName = "/*" + type.name + "*/"
+ elif type.isPrimitive():
+ name = type.name
+ typeName = builtinNames[type.tag()]
+ else:
+ name = type.name
+ typeName = "/*" + type.name + "*/"
+
+ tryNextCode = """{
+ return Ok(true);
+}"""
+ (template, declType, holderType,
+ dealWithOptional, initialValue) = getJSToNativeConversionTemplate(
+ type, descriptorProvider, failureCode=tryNextCode,
+ isDefinitelyObject=True, isOptional=type.nullable(), preSuccess="e" + name + "(", postSuccess=")")
+
+ structType = declType.define()
+ externalType = getUnionAccessorSignatureType(type, descriptorProvider).define()
+
+ if type.isObject():
+ setter = CGGeneric("pub fn SetToObject(obj: *JSObject) {\n"
+ " mUnion = Some(eObject(obj));\n"
+ "}")
+ else:
+ jsConversion = string.Template(template).substitute(
+ {
+ "val": "value",
+ "valPtr": "pvalue",
+ "declName": "*self.mUnion",
+ "holderName": "m" + name + "Holder"
+ }
+ )
+ jsConversion = CGWrapper(CGGeneric(jsConversion),
+ post="\n"
+ "return Ok(false);")
+ setter = CGWrapper(CGIndenter(jsConversion),
+ pre="pub fn TrySetTo" + name + "(&mut self, cx: *JSContext, value: JSVal, pvalue: *JSVal) -> Result<bool,()> {\n",
+ post="\n"
+ "}")
+
+ return {
+ "name": name,
+ "typeName": typeName,
+ "structType": structType,
+ "externalType": externalType,
+ "optRef": 'ref ' if externalType[0] == '&' else '',
+ "setter": CGIndenter(setter).define(),
+ "holderType": holderType.define() if holderType else None
+ }
+
+def mapTemplate(template, templateVarArray):
+ return map(lambda v: string.Template(template).substitute(v),
+ templateVarArray)
+
+class CGUnionStruct(CGThing):
+ def __init__(self, type, descriptorProvider):
+ CGThing.__init__(self)
+ self.type = type.unroll()
+ self.descriptorProvider = descriptorProvider
+
+ def declare(self):
+ templateVars = map(lambda t: getUnionTypeTemplateVars(t, self.descriptorProvider),
+ self.type.flatMemberTypes)
+
+ callDestructors = []
+ enumValues = []
+ methods = []
+ if self.type.hasNullableType:
+ callDestructors.append(" case eNull:\n"
+ " break;")
+ enumValues.append("eNull")
+ methods.append(""" pub fn IsNull(&self) -> bool {
+ match *self {
+ eNull => true,
+ _ => false
+ }
+ }""")
+
+ destructorTemplate = """ fn Destroy${name}(&mut self) {
+ assert!(Is${name}(), "Wrong type!");
+ *self.mUnion = None;
+ }"""
+ destructors = mapTemplate(destructorTemplate, templateVars)
+ callDestructors.extend(mapTemplate(" case e${name}:\n"
+ " Destroy${name}();\n"
+ " break;", templateVars))
+ enumValues.extend(mapTemplate("e${name}(${typeName})", templateVars))
+ methodTemplate = """ pub fn Is${name}(&self) -> bool {
+ match *self {
+ e${name}(_) => true,
+ _ => false
+ }
+ }
+ pub fn GetAs${name}<'a>(&'a self) -> ${externalType} {
+ assert!(self.Is${name}());
+ match *self {
+ e${name}(${optRef}inner) => inner,
+ _ => unreachable!()
+ }
+ }"""
+ methods.extend(mapTemplate(methodTemplate, templateVars))
+ values = mapTemplate("UnionMember<${structType} > m${name};", templateVars)
+ return string.Template("""
+pub enum ${structName} {
+ ${enumValues}
+}
+
+impl ${structName} {
+${methods}
+}
+""").substitute(
+ {
+ "structName": self.type.__str__(),
+ "callDestructors": "\n".join(callDestructors),
+ "destructors": "\n".join(destructors),
+ "methods": "\n\n".join(methods),
+ "enumValues": ",\n ".join(enumValues),
+ "values": "\n ".join(values),
+ })
+
+ def define(self):
+ return """
+"""
+
+class CGUnionConversionStruct(CGThing):
+ def __init__(self, type, descriptorProvider):
+ CGThing.__init__(self)
+ self.type = type.unroll()
+ self.descriptorProvider = descriptorProvider
+
+ def declare(self):
+ setters = []
+
+ if self.type.hasNullableType:
+ setters.append(""" pub fn SetNull(&mut self) -> bool
+ {
+ mUnion = Some(eNull);
+ return true;
+ }""")
+
+ templateVars = map(lambda t: getUnionTypeTemplateVars(t, self.descriptorProvider),
+ self.type.flatMemberTypes)
+ structName = self.type.__str__()
+
+ setters.extend(mapTemplate("${setter}", templateVars))
+ private = "\n".join(mapTemplate(""" fn SetAs${name}() -> &${structType}
+ {
+ mUnion.mType = mUnion.e${name};
+ return mUnion.mValue.m${name}.SetValue();
+ }""", templateVars))
+ private += "\n\n"
+ holders = filter(lambda v: v["holderType"] is not None, templateVars)
+ if len(holders) > 0:
+ private += "\n".join(mapTemplate(" ${holderType} m${name}Holder;", holders))
+ private += "\n\n"
+ private += " " + structName + "& mUnion;"
+ return string.Template("""
+pub struct ${structName}Argument<'a> {
+ mUnion: &'a mut ${innerType}
+}
+
+impl<'a> ${structName}Argument<'a> {
+ pub fn new(union: &'a mut ${innerType}) -> ${structName}Argument<'a> {
+ ${structName}Argument {
+ mUnion: union
+ }
+ }
+
+${setters}
+}
+""").substitute({"structName": structName,
+ "innerType": ("Option<%s>" % structName) if self.type.nullable() else structName,
+ "setters": "\n\n".join(setters),
+ })
+
+ def define(self):
+ return """
+"""
+
class ClassItem:
""" Use with CGClass """
def __init__(self, name, visibility):
@@ -5169,6 +5568,8 @@ class CGBindingRoot(CGThing):
'dom::bindings::callback::*',
'dom::bindings::conversions::*',
'dom::bindings::codegen::*', #XXXjdm
+ 'dom::bindings::codegen::UnionTypes::*', #XXXjdm
+ 'dom::bindings::codegen::UnionConversions::*', #XXXjdm
'script_task::{JSPageInfo, page_from_context}',
'dom::bindings::proxyhandler',
'dom::bindings::proxyhandler::*',
@@ -5180,6 +5581,7 @@ class CGBindingRoot(CGThing):
'std::vec',
'std::str',
'std::num',
+ 'std::unstable::intrinsics::uninit',
'std::unstable::raw::Box',
],
[],
@@ -6254,3 +6656,89 @@ class GlobalGenRoots():
curr = CGList(allprotos)
curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
return curr
+
+ @staticmethod
+ def UnionTypes(config):
+
+ (includes, declarations, unions) = UnionTypes(config.getDescriptors())
+ includes.add("mozilla/dom/BindingUtils.h")
+
+ # Wrap all of that in our namespaces.
+ #curr = CGNamespace.build(['mozilla', 'dom'], unions, public=True)
+ curr = unions
+
+ curr = CGWrapper(curr, post='\n')
+
+ namespaces = []
+ stack = [CGList([])]
+ for (clazz, isStruct) in SortedTuples(declarations):
+ elements = clazz.split("::")
+ elements.pop()
+ #clazz = CGClassForwardDeclare(elements.pop(), isStruct=isStruct)
+ i = 0
+ if len(elements) > 0:
+ common = min(len(namespaces), len(elements))
+ while i < common and namespaces[i] == elements[i]:
+ i += 1
+
+ # pop all the namespaces that should be closed
+ namespaces = namespaces[:i]
+
+ # add all the namespaces that should be opened
+ for j, namespace in enumerate(elements[i:]):
+ namespaces.append(namespace)
+ # every CGNamespace that we add holds a CGList
+ list = CGList([])
+ # add the new namespace to the list on top of the stack
+ stack[i + j].append(CGNamespace(namespace, list))
+ # set the top of the namespace stack to the list of the new
+ # namespace
+ stack[i + j + 1:] = [list]
+
+ #stack[len(elements)].append(clazz)
+
+ curr = CGList([stack[0], curr], "\n")
+
+ #curr = CGHeaders([], [], includes, [], curr)
+
+ # Add include guards.
+ #curr = CGIncludeGuard('UnionTypes', curr)
+
+ curr = CGImports([], [], ['dom::bindings::js::JS',
+ 'dom::types::*'], [], curr)
+
+ # Add the auto-generated comment.
+ curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
+
+ # Done.
+ return curr
+
+ @staticmethod
+ def UnionConversions(config):
+
+ unions = UnionConversions(config.getDescriptors())
+ curr = unions
+
+ # Wrap all of that in our namespaces.
+ #curr = CGNamespace.build(['mozilla', 'dom'], unions)
+
+ curr = CGWrapper(curr, post='\n')
+
+ #curr = CGHeaders([], [], ["nsDebug.h", "mozilla/dom/UnionTypes.h", "nsDOMQS.h"], [], curr)
+
+ # Add include guards.
+ #curr = CGIncludeGuard('UnionConversions', curr)
+
+ curr = CGImports([], [], ['dom::bindings::utils::unwrap_jsmanaged',
+ 'dom::bindings::codegen::UnionTypes::*',
+ 'dom::bindings::codegen::PrototypeList',
+ 'dom::bindings::conversions::JSValConvertible',
+ 'js::*',
+ 'js::jsapi::*',
+ 'js::glue::*'], [], curr)
+
+ # Add the auto-generated comment.
+ curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
+
+ # Done.
+ return curr
diff --git a/src/components/script/dom/bindings/codegen/GlobalGen.py b/src/components/script/dom/bindings/codegen/GlobalGen.py
index ffbf31a4b48..bd8b3d70793 100644
--- a/src/components/script/dom/bindings/codegen/GlobalGen.py
+++ b/src/components/script/dom/bindings/codegen/GlobalGen.py
@@ -89,9 +89,8 @@ def main():
# Generate the module declarations.
generate_file(config, 'BindingDeclarations', 'declare+define')
- #XXXjdm No union support yet
- #generate_file(config, 'UnionTypes', 'declare')
- #generate_file(config, 'UnionConversions', 'declare')
+ generate_file(config, 'UnionTypes', 'declare+define')
+ generate_file(config, 'UnionConversions', 'declare+define')
if __name__ == '__main__':
main()
diff --git a/src/components/script/dom/bindings/conversions.rs b/src/components/script/dom/bindings/conversions.rs
index 06745217365..decb047ab9f 100644
--- a/src/components/script/dom/bindings/conversions.rs
+++ b/src/components/script/dom/bindings/conversions.rs
@@ -4,7 +4,9 @@
use js::jsapi::JSVal;
use js::{JSVAL_FALSE, JSVAL_TRUE};
-use js::glue::{RUST_UINT_TO_JSVAL, RUST_JSVAL_TO_INT, RUST_DOUBLE_TO_JSVAL, RUST_JSVAL_TO_DOUBLE};
+use js::glue::{RUST_UINT_TO_JSVAL, RUST_JSVAL_TO_INT, RUST_DOUBLE_TO_JSVAL};
+use js::glue::{RUST_JSVAL_TO_DOUBLE, RUST_JSVAL_IS_INT, RUST_JSVAL_IS_DOUBLE};
+use js::glue::{RUST_JSVAL_IS_BOOLEAN, RUST_JSVAL_TO_BOOLEAN};
pub trait JSValConvertible {
fn to_jsval(&self) -> JSVal;
@@ -21,7 +23,11 @@ impl JSValConvertible for i64 {
fn from_jsval(val: JSVal) -> Option<i64> {
unsafe {
- Some(RUST_JSVAL_TO_DOUBLE(val) as i64)
+ if RUST_JSVAL_IS_INT(val) != 0 {
+ Some(RUST_JSVAL_TO_DOUBLE(val) as i64)
+ } else {
+ None
+ }
}
}
}
@@ -35,7 +41,11 @@ impl JSValConvertible for u32 {
fn from_jsval(val: JSVal) -> Option<u32> {
unsafe {
- Some(RUST_JSVAL_TO_INT(val) as u32)
+ if RUST_JSVAL_IS_INT(val) != 0 {
+ Some(RUST_JSVAL_TO_INT(val) as u32)
+ } else {
+ None
+ }
}
}
}
@@ -49,7 +59,11 @@ impl JSValConvertible for i32 {
fn from_jsval(val: JSVal) -> Option<i32> {
unsafe {
- Some(RUST_JSVAL_TO_INT(val) as i32)
+ if RUST_JSVAL_IS_INT(val) != 0 {
+ Some(RUST_JSVAL_TO_INT(val) as i32)
+ } else {
+ None
+ }
}
}
}
@@ -63,7 +77,11 @@ impl JSValConvertible for u16 {
fn from_jsval(val: JSVal) -> Option<u16> {
unsafe {
- Some(RUST_JSVAL_TO_INT(val) as u16)
+ if RUST_JSVAL_IS_INT(val) != 0 {
+ Some(RUST_JSVAL_TO_INT(val) as u16)
+ } else {
+ None
+ }
}
}
}
@@ -78,12 +96,12 @@ impl JSValConvertible for bool {
}
fn from_jsval(val: JSVal) -> Option<bool> {
- if val == JSVAL_TRUE {
- Some(true)
- } else if val == JSVAL_FALSE {
- Some(false)
- } else {
- None
+ unsafe {
+ if RUST_JSVAL_IS_BOOLEAN(val) != 0 {
+ Some(RUST_JSVAL_TO_BOOLEAN(val) != 0)
+ } else {
+ None
+ }
}
}
}
@@ -97,7 +115,11 @@ impl JSValConvertible for f32 {
fn from_jsval(val: JSVal) -> Option<f32> {
unsafe {
- Some(RUST_JSVAL_TO_DOUBLE(val) as f32)
+ if RUST_JSVAL_IS_DOUBLE(val) != 0 {
+ Some(RUST_JSVAL_TO_DOUBLE(val) as f32)
+ } else {
+ None
+ }
}
}
}
@@ -111,7 +133,11 @@ impl JSValConvertible for f64 {
fn from_jsval(val: JSVal) -> Option<f64> {
unsafe {
- Some(RUST_JSVAL_TO_DOUBLE(val) as f64)
+ if RUST_JSVAL_IS_DOUBLE(val) != 0 {
+ Some(RUST_JSVAL_TO_DOUBLE(val) as f64)
+ } else {
+ None
+ }
}
}
}
diff --git a/src/components/script/dom/bindings/utils.rs b/src/components/script/dom/bindings/utils.rs
index 595d131c80c..80787d93416 100644
--- a/src/components/script/dom/bindings/utils.rs
+++ b/src/components/script/dom/bindings/utils.rs
@@ -879,6 +879,15 @@ pub fn throw_method_failed_with_details<T>(cx: *JSContext,
return 0;
}
+pub fn throw_not_in_union(cx: *JSContext, names: &'static str) -> JSBool {
+ assert!(unsafe { JS_IsExceptionPending(cx) } == 0);
+ let message = format!("argument could not be converted to any of: {}", names);
+ message.with_c_str(|string| {
+ unsafe { ReportError(cx, string) };
+ });
+ return 0;
+}
+
/// Execute arbitrary code with the JS GC enabled, then disable it afterwards.
pub fn with_gc_enabled<R>(cx: *JSContext, f: || -> R) -> R {
unsafe {
diff --git a/src/components/script/dom/htmlselectelement.rs b/src/components/script/dom/htmlselectelement.rs
index 92925b4ce7c..1b7e452a992 100644
--- a/src/components/script/dom/htmlselectelement.rs
+++ b/src/components/script/dom/htmlselectelement.rs
@@ -4,6 +4,7 @@
use dom::bindings::codegen::HTMLSelectElementBinding;
use dom::bindings::codegen::InheritTypes::HTMLSelectElementDerived;
+use dom::bindings::codegen::UnionTypes::{HTMLElementOrLong, HTMLOptionElementOrHTMLOptGroupElement};
use dom::bindings::js::JS;
use dom::bindings::utils::ErrorResult;
use dom::document::Document;
@@ -175,4 +176,8 @@ impl HTMLSelectElement {
pub fn SetCustomValidity(&mut self, _error: DOMString) {
}
+
+ pub fn Add(&self, _element: HTMLOptionElementOrHTMLOptGroupElement, _before: Option<HTMLElementOrLong>) -> ErrorResult {
+ Ok(())
+ }
}
diff --git a/src/components/script/dom/webidls/HTMLSelectElement.webidl b/src/components/script/dom/webidls/HTMLSelectElement.webidl
index b99bf149280..bb843fd3f92 100644
--- a/src/components/script/dom/webidls/HTMLSelectElement.webidl
+++ b/src/components/script/dom/webidls/HTMLSelectElement.webidl
@@ -32,8 +32,8 @@ interface HTMLSelectElement : HTMLElement {
attribute unsigned long length;
getter Element? item(unsigned long index);
HTMLOptionElement? namedItem(DOMString name);
- /*[Throws]
- void add((HTMLOptionElement or HTMLOptGroupElement) element, optional (HTMLElement or long)? before = null);*/
+ [Throws]
+ void add((HTMLOptionElement or HTMLOptGroupElement) element, optional (HTMLElement or long)? before = null);
void remove(long index);
[Throws]
setter creator void (unsigned long index, HTMLOptionElement? option);
diff --git a/src/components/script/script.rs b/src/components/script/script.rs
index 8cfbc737255..1d23dfc3402 100644
--- a/src/components/script/script.rs
+++ b/src/components/script/script.rs
@@ -40,6 +40,8 @@ pub mod dom {
pub mod PrototypeList;
pub mod RegisterBindings;
pub mod BindingDeclarations;
+ pub mod UnionConversions;
+ pub mod UnionTypes;
}
}
diff --git a/src/test/html/content/harness.js b/src/test/html/content/harness.js
index ade4536a879..72aa2712469 100644
--- a/src/test/html/content/harness.js
+++ b/src/test/html/content/harness.js
@@ -37,6 +37,24 @@ function is_function(val, name) {
starts_with(String(val), "function " + name + "(");
}
+function should_throw(f) {
+ try {
+ f();
+ _fail("operation should have thrown but did not");
+ } catch (x) {
+ _pass("operation successfully threw an exception", x.toString());
+ }
+}
+
+function should_not_throw(f) {
+ try {
+ f();
+ _pass("operation did not throw an exception");
+ } catch (x) {
+ _fail("operation should have not thrown", x.toString());
+ }
+}
+
var _test_complete = false;
var _test_timeout = 10000; //10 seconds
function finish() {
diff --git a/src/test/html/content/test_exception.html b/src/test/html/content/test_exception.html
index ed96fe22867..b2cfda177d4 100644
--- a/src/test/html/content/test_exception.html
+++ b/src/test/html/content/test_exception.html
@@ -1,11 +1,6 @@
<!doctype html>
<script src="harness.js"></script>
<script>
-try {
- document.createElement("1foo");
- is(true, false, "No exception thrown");
-} catch (e) {
- is(true, true, "Exception caught");
-}
+should_throw(function() { document.createElement("1foo") });
finish();
</script>
diff --git a/src/test/html/content/test_union.html b/src/test/html/content/test_union.html
new file mode 100644
index 00000000000..d5483ad8a3a
--- /dev/null
+++ b/src/test/html/content/test_union.html
@@ -0,0 +1,26 @@
+<html>
+<head>
+<script src="harness.js"></script>
+<select id="sel"></select>
+<script>
+ var div = document.createElement('div');
+ var optgroup = document.createElement('optgroup');
+ var sel = document.getElementById('sel');
+
+ should_not_throw(function() {
+ var opt = document.createElement('option');
+ sel.add(opt);
+ sel.add(optgroup);
+ sel.add(opt, div);
+ sel.add(optgroup, div);
+ sel.add(opt, 5);
+ sel.add(optgroup, 5);
+ });
+
+ should_throw(function() { sel.add(div) });
+ should_throw(function() { sel.add(optgroup, function() {}) });
+
+ finish();
+</script>
+</head>
+</html>