summaryrefslogtreecommitdiffstats
path: root/dom/bindings/parser/tests
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /dom/bindings/parser/tests
parentInitial commit. (diff)
downloadfirefox-esr-upstream.tar.xz
firefox-esr-upstream.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/bindings/parser/tests')
-rw-r--r--dom/bindings/parser/tests/test_any_null.py16
-rw-r--r--dom/bindings/parser/tests/test_argument_identifier_conflicts.py16
-rw-r--r--dom/bindings/parser/tests/test_argument_keywords.py22
-rw-r--r--dom/bindings/parser/tests/test_argument_novoid.py16
-rw-r--r--dom/bindings/parser/tests/test_arraybuffer.py95
-rw-r--r--dom/bindings/parser/tests/test_attr.py199
-rw-r--r--dom/bindings/parser/tests/test_attr_sequence_type.py77
-rw-r--r--dom/bindings/parser/tests/test_attributes_on_types.py567
-rw-r--r--dom/bindings/parser/tests/test_builtin_filename.py11
-rw-r--r--dom/bindings/parser/tests/test_builtins.py59
-rw-r--r--dom/bindings/parser/tests/test_bytestring.py125
-rw-r--r--dom/bindings/parser/tests/test_callback.py42
-rw-r--r--dom/bindings/parser/tests/test_callback_constructor.py84
-rw-r--r--dom/bindings/parser/tests/test_callback_interface.py103
-rw-r--r--dom/bindings/parser/tests/test_cereactions.py157
-rw-r--r--dom/bindings/parser/tests/test_conditional_dictionary_member.py128
-rw-r--r--dom/bindings/parser/tests/test_const.py96
-rw-r--r--dom/bindings/parser/tests/test_constructor.py596
-rw-r--r--dom/bindings/parser/tests/test_constructor_global.py69
-rw-r--r--dom/bindings/parser/tests/test_constructor_no_interface_object.py47
-rw-r--r--dom/bindings/parser/tests/test_deduplicate.py17
-rw-r--r--dom/bindings/parser/tests/test_dictionary.py875
-rw-r--r--dom/bindings/parser/tests/test_distinguishability.py421
-rw-r--r--dom/bindings/parser/tests/test_double_null.py16
-rw-r--r--dom/bindings/parser/tests/test_duplicate_qualifiers.py64
-rw-r--r--dom/bindings/parser/tests/test_empty_enum.py14
-rw-r--r--dom/bindings/parser/tests/test_empty_sequence_default_value.py54
-rw-r--r--dom/bindings/parser/tests/test_enum.py107
-rw-r--r--dom/bindings/parser/tests/test_enum_duplicate_values.py13
-rw-r--r--dom/bindings/parser/tests/test_error_colno.py24
-rw-r--r--dom/bindings/parser/tests/test_error_lineno.py38
-rw-r--r--dom/bindings/parser/tests/test_exposed_extended_attribute.py383
-rw-r--r--dom/bindings/parser/tests/test_extended_attributes.py128
-rw-r--r--dom/bindings/parser/tests/test_float_types.py145
-rw-r--r--dom/bindings/parser/tests/test_forward_decl.py15
-rw-r--r--dom/bindings/parser/tests/test_global_extended_attr.py129
-rw-r--r--dom/bindings/parser/tests/test_identifier_conflict.py45
-rw-r--r--dom/bindings/parser/tests/test_incomplete_parent.py18
-rw-r--r--dom/bindings/parser/tests/test_incomplete_types.py61
-rw-r--r--dom/bindings/parser/tests/test_interface.py459
-rw-r--r--dom/bindings/parser/tests/test_interface_const_identifier_conflicts.py17
-rw-r--r--dom/bindings/parser/tests/test_interface_identifier_conflicts_across_members.py168
-rw-r--r--dom/bindings/parser/tests/test_interface_maplikesetlikeiterable.py935
-rw-r--r--dom/bindings/parser/tests/test_interfacemixin.py534
-rw-r--r--dom/bindings/parser/tests/test_lenientSetter.py84
-rw-r--r--dom/bindings/parser/tests/test_method.py430
-rw-r--r--dom/bindings/parser/tests/test_namespace.py232
-rw-r--r--dom/bindings/parser/tests/test_newobject.py73
-rw-r--r--dom/bindings/parser/tests/test_nullable_equivalency.py138
-rw-r--r--dom/bindings/parser/tests/test_nullable_void.py16
-rw-r--r--dom/bindings/parser/tests/test_observableArray.py288
-rw-r--r--dom/bindings/parser/tests/test_optional_constraints.py35
-rw-r--r--dom/bindings/parser/tests/test_overload.py74
-rw-r--r--dom/bindings/parser/tests/test_promise.py177
-rw-r--r--dom/bindings/parser/tests/test_prototype_ident.py107
-rw-r--r--dom/bindings/parser/tests/test_putForwards.py119
-rw-r--r--dom/bindings/parser/tests/test_record.py61
-rw-r--r--dom/bindings/parser/tests/test_replaceable.py84
-rw-r--r--dom/bindings/parser/tests/test_sanity.py7
-rw-r--r--dom/bindings/parser/tests/test_securecontext_extended_attribute.py538
-rw-r--r--dom/bindings/parser/tests/test_special_method_signature_mismatch.py256
-rw-r--r--dom/bindings/parser/tests/test_special_methods.py117
-rw-r--r--dom/bindings/parser/tests/test_special_methods_uniqueness.py51
-rw-r--r--dom/bindings/parser/tests/test_stringifier.py196
-rw-r--r--dom/bindings/parser/tests/test_toJSON.py309
-rw-r--r--dom/bindings/parser/tests/test_treatNonCallableAsNull.py77
-rw-r--r--dom/bindings/parser/tests/test_typedef.py94
-rw-r--r--dom/bindings/parser/tests/test_typedef_identifier_conflict.py19
-rw-r--r--dom/bindings/parser/tests/test_undefined.py243
-rw-r--r--dom/bindings/parser/tests/test_unenumerable_own_properties.py71
-rw-r--r--dom/bindings/parser/tests/test_unforgeable.py311
-rw-r--r--dom/bindings/parser/tests/test_union.py196
-rw-r--r--dom/bindings/parser/tests/test_union_any.py16
-rw-r--r--dom/bindings/parser/tests/test_union_nullable.py60
-rw-r--r--dom/bindings/parser/tests/test_usvstring.py40
-rw-r--r--dom/bindings/parser/tests/test_variadic_callback.py10
-rw-r--r--dom/bindings/parser/tests/test_variadic_constraints.py74
77 files changed, 11808 insertions, 0 deletions
diff --git a/dom/bindings/parser/tests/test_any_null.py b/dom/bindings/parser/tests/test_any_null.py
new file mode 100644
index 0000000000..d03216ed51
--- /dev/null
+++ b/dom/bindings/parser/tests/test_any_null.py
@@ -0,0 +1,16 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface DoubleNull {
+ attribute any? foo;
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/dom/bindings/parser/tests/test_argument_identifier_conflicts.py b/dom/bindings/parser/tests/test_argument_identifier_conflicts.py
new file mode 100644
index 0000000000..3ee8dc6bd6
--- /dev/null
+++ b/dom/bindings/parser/tests/test_argument_identifier_conflicts.py
@@ -0,0 +1,16 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface ArgumentIdentifierConflict {
+ undefined foo(boolean arg1, boolean arg1);
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/dom/bindings/parser/tests/test_argument_keywords.py b/dom/bindings/parser/tests/test_argument_keywords.py
new file mode 100644
index 0000000000..bbed33df92
--- /dev/null
+++ b/dom/bindings/parser/tests/test_argument_keywords.py
@@ -0,0 +1,22 @@
+def WebIDLTest(parser, harness):
+ parser.parse(
+ """
+ interface Foo {
+ undefined foo(object constructor);
+ };
+ """
+ )
+
+ results = parser.finish()
+ harness.check(len(results), 1, "Should have an interface")
+ iface = results[0]
+ harness.check(len(iface.members), 1, "Should have an operation")
+ operation = iface.members[0]
+ harness.check(len(operation.signatures()), 1, "Should have one signature")
+ (retval, args) = operation.signatures()[0]
+ harness.check(len(args), 1, "Should have an argument")
+ harness.check(
+ args[0].identifier.name,
+ "constructor",
+ "Should have an identifier named 'constructor'",
+ )
diff --git a/dom/bindings/parser/tests/test_argument_novoid.py b/dom/bindings/parser/tests/test_argument_novoid.py
new file mode 100644
index 0000000000..571fd0b5eb
--- /dev/null
+++ b/dom/bindings/parser/tests/test_argument_novoid.py
@@ -0,0 +1,16 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface VoidArgument1 {
+ void foo(void arg2);
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/dom/bindings/parser/tests/test_arraybuffer.py b/dom/bindings/parser/tests/test_arraybuffer.py
new file mode 100644
index 0000000000..b762d06ac2
--- /dev/null
+++ b/dom/bindings/parser/tests/test_arraybuffer.py
@@ -0,0 +1,95 @@
+import WebIDL
+
+
+def WebIDLTest(parser, harness):
+ parser.parse(
+ """
+ interface TestArrayBuffer {
+ attribute ArrayBuffer bufferAttr;
+ undefined bufferMethod(ArrayBuffer arg1, ArrayBuffer? arg2, sequence<ArrayBuffer> arg3);
+
+ attribute ArrayBufferView viewAttr;
+ undefined viewMethod(ArrayBufferView arg1, ArrayBufferView? arg2, sequence<ArrayBufferView> arg3);
+
+ attribute Int8Array int8ArrayAttr;
+ undefined int8ArrayMethod(Int8Array arg1, Int8Array? arg2, sequence<Int8Array> arg3);
+
+ attribute Uint8Array uint8ArrayAttr;
+ undefined uint8ArrayMethod(Uint8Array arg1, Uint8Array? arg2, sequence<Uint8Array> arg3);
+
+ attribute Uint8ClampedArray uint8ClampedArrayAttr;
+ undefined uint8ClampedArrayMethod(Uint8ClampedArray arg1, Uint8ClampedArray? arg2, sequence<Uint8ClampedArray> arg3);
+
+ attribute Int16Array int16ArrayAttr;
+ undefined int16ArrayMethod(Int16Array arg1, Int16Array? arg2, sequence<Int16Array> arg3);
+
+ attribute Uint16Array uint16ArrayAttr;
+ undefined uint16ArrayMethod(Uint16Array arg1, Uint16Array? arg2, sequence<Uint16Array> arg3);
+
+ attribute Int32Array int32ArrayAttr;
+ undefined int32ArrayMethod(Int32Array arg1, Int32Array? arg2, sequence<Int32Array> arg3);
+
+ attribute Uint32Array uint32ArrayAttr;
+ undefined uint32ArrayMethod(Uint32Array arg1, Uint32Array? arg2, sequence<Uint32Array> arg3);
+
+ attribute Float32Array float32ArrayAttr;
+ undefined float32ArrayMethod(Float32Array arg1, Float32Array? arg2, sequence<Float32Array> arg3);
+
+ attribute Float64Array float64ArrayAttr;
+ undefined float64ArrayMethod(Float64Array arg1, Float64Array? arg2, sequence<Float64Array> arg3);
+ };
+ """
+ )
+
+ results = parser.finish()
+
+ iface = results[0]
+
+ harness.ok(True, "TestArrayBuffer interface parsed without error")
+ harness.check(len(iface.members), 22, "Interface should have twenty two members")
+
+ members = iface.members
+
+ def checkStuff(attr, method, t):
+ harness.ok(isinstance(attr, WebIDL.IDLAttribute), "Expect an IDLAttribute")
+ harness.ok(isinstance(method, WebIDL.IDLMethod), "Expect an IDLMethod")
+
+ harness.check(str(attr.type), t, "Expect an ArrayBuffer type")
+ harness.ok(attr.type.isSpiderMonkeyInterface(), "Should test as a js interface")
+
+ (retType, arguments) = method.signatures()[0]
+ harness.ok(retType.isUndefined(), "Should have an undefined return type")
+ harness.check(len(arguments), 3, "Expect 3 arguments")
+
+ harness.check(str(arguments[0].type), t, "Expect an ArrayBuffer type")
+ harness.ok(
+ arguments[0].type.isSpiderMonkeyInterface(), "Should test as a js interface"
+ )
+
+ harness.check(
+ str(arguments[1].type), t + "OrNull", "Expect an ArrayBuffer type"
+ )
+ harness.ok(
+ arguments[1].type.inner.isSpiderMonkeyInterface(),
+ "Should test as a js interface",
+ )
+
+ harness.check(
+ str(arguments[2].type), t + "Sequence", "Expect an ArrayBuffer type"
+ )
+ harness.ok(
+ arguments[2].type.inner.isSpiderMonkeyInterface(),
+ "Should test as a js interface",
+ )
+
+ checkStuff(members[0], members[1], "ArrayBuffer")
+ checkStuff(members[2], members[3], "ArrayBufferView")
+ checkStuff(members[4], members[5], "Int8Array")
+ checkStuff(members[6], members[7], "Uint8Array")
+ checkStuff(members[8], members[9], "Uint8ClampedArray")
+ checkStuff(members[10], members[11], "Int16Array")
+ checkStuff(members[12], members[13], "Uint16Array")
+ checkStuff(members[14], members[15], "Int32Array")
+ checkStuff(members[16], members[17], "Uint32Array")
+ checkStuff(members[18], members[19], "Float32Array")
+ checkStuff(members[20], members[21], "Float64Array")
diff --git a/dom/bindings/parser/tests/test_attr.py b/dom/bindings/parser/tests/test_attr.py
new file mode 100644
index 0000000000..ebc47f8780
--- /dev/null
+++ b/dom/bindings/parser/tests/test_attr.py
@@ -0,0 +1,199 @@
+import WebIDL
+
+
+def WebIDLTest(parser, harness):
+ testData = [
+ ("::TestAttr%s::b", "b", "Byte%s", False),
+ ("::TestAttr%s::rb", "rb", "Byte%s", True),
+ ("::TestAttr%s::o", "o", "Octet%s", False),
+ ("::TestAttr%s::ro", "ro", "Octet%s", True),
+ ("::TestAttr%s::s", "s", "Short%s", False),
+ ("::TestAttr%s::rs", "rs", "Short%s", True),
+ ("::TestAttr%s::us", "us", "UnsignedShort%s", False),
+ ("::TestAttr%s::rus", "rus", "UnsignedShort%s", True),
+ ("::TestAttr%s::l", "l", "Long%s", False),
+ ("::TestAttr%s::rl", "rl", "Long%s", True),
+ ("::TestAttr%s::ul", "ul", "UnsignedLong%s", False),
+ ("::TestAttr%s::rul", "rul", "UnsignedLong%s", True),
+ ("::TestAttr%s::ll", "ll", "LongLong%s", False),
+ ("::TestAttr%s::rll", "rll", "LongLong%s", True),
+ ("::TestAttr%s::ull", "ull", "UnsignedLongLong%s", False),
+ ("::TestAttr%s::rull", "rull", "UnsignedLongLong%s", True),
+ ("::TestAttr%s::str", "str", "String%s", False),
+ ("::TestAttr%s::rstr", "rstr", "String%s", True),
+ ("::TestAttr%s::obj", "obj", "Object%s", False),
+ ("::TestAttr%s::robj", "robj", "Object%s", True),
+ ("::TestAttr%s::object", "object", "Object%s", False),
+ ("::TestAttr%s::f", "f", "Float%s", False),
+ ("::TestAttr%s::rf", "rf", "Float%s", True),
+ ]
+
+ parser.parse(
+ """
+ interface TestAttr {
+ attribute byte b;
+ readonly attribute byte rb;
+ attribute octet o;
+ readonly attribute octet ro;
+ attribute short s;
+ readonly attribute short rs;
+ attribute unsigned short us;
+ readonly attribute unsigned short rus;
+ attribute long l;
+ readonly attribute long rl;
+ attribute unsigned long ul;
+ readonly attribute unsigned long rul;
+ attribute long long ll;
+ readonly attribute long long rll;
+ attribute unsigned long long ull;
+ readonly attribute unsigned long long rull;
+ attribute DOMString str;
+ readonly attribute DOMString rstr;
+ attribute object obj;
+ readonly attribute object robj;
+ attribute object _object;
+ attribute float f;
+ readonly attribute float rf;
+ };
+
+ interface TestAttrNullable {
+ attribute byte? b;
+ readonly attribute byte? rb;
+ attribute octet? o;
+ readonly attribute octet? ro;
+ attribute short? s;
+ readonly attribute short? rs;
+ attribute unsigned short? us;
+ readonly attribute unsigned short? rus;
+ attribute long? l;
+ readonly attribute long? rl;
+ attribute unsigned long? ul;
+ readonly attribute unsigned long? rul;
+ attribute long long? ll;
+ readonly attribute long long? rll;
+ attribute unsigned long long? ull;
+ readonly attribute unsigned long long? rull;
+ attribute DOMString? str;
+ readonly attribute DOMString? rstr;
+ attribute object? obj;
+ readonly attribute object? robj;
+ attribute object? _object;
+ attribute float? f;
+ readonly attribute float? rf;
+ };
+ """
+ )
+
+ results = parser.finish()
+
+ def checkAttr(attr, QName, name, type, readonly):
+ harness.ok(isinstance(attr, WebIDL.IDLAttribute), "Should be an IDLAttribute")
+ harness.ok(attr.isAttr(), "Attr is an Attr")
+ harness.ok(not attr.isMethod(), "Attr is not an method")
+ harness.ok(not attr.isConst(), "Attr is not a const")
+ harness.check(attr.identifier.QName(), QName, "Attr has the right QName")
+ harness.check(attr.identifier.name, name, "Attr has the right name")
+ harness.check(str(attr.type), type, "Attr has the right type")
+ harness.check(attr.readonly, readonly, "Attr's readonly state is correct")
+
+ harness.ok(True, "TestAttr interface parsed without error.")
+ harness.check(len(results), 2, "Should be two productions.")
+ iface = results[0]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface), "Should be an IDLInterface")
+ harness.check(
+ iface.identifier.QName(), "::TestAttr", "Interface has the right QName"
+ )
+ harness.check(iface.identifier.name, "TestAttr", "Interface has the right name")
+ harness.check(
+ len(iface.members), len(testData), "Expect %s members" % len(testData)
+ )
+
+ attrs = iface.members
+
+ for i in range(len(attrs)):
+ data = testData[i]
+ attr = attrs[i]
+ (QName, name, type, readonly) = data
+ checkAttr(attr, QName % "", name, type % "", readonly)
+
+ iface = results[1]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface), "Should be an IDLInterface")
+ harness.check(
+ iface.identifier.QName(), "::TestAttrNullable", "Interface has the right QName"
+ )
+ harness.check(
+ iface.identifier.name, "TestAttrNullable", "Interface has the right name"
+ )
+ harness.check(
+ len(iface.members), len(testData), "Expect %s members" % len(testData)
+ )
+
+ attrs = iface.members
+
+ for i in range(len(attrs)):
+ data = testData[i]
+ attr = attrs[i]
+ (QName, name, type, readonly) = data
+ checkAttr(attr, QName % "Nullable", name, type % "OrNull", readonly)
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface A {
+ [SetterThrows] readonly attribute boolean foo;
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "Should not allow [SetterThrows] on readonly attributes")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface A {
+ [Throw] readonly attribute boolean foo;
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "Should spell [Throws] correctly")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface A {
+ [SameObject] readonly attribute boolean foo;
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(
+ threw, "Should not allow [SameObject] on attributes not of interface type"
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface A {
+ [SameObject] readonly attribute A foo;
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(not threw, "Should allow [SameObject] on attributes of interface type")
diff --git a/dom/bindings/parser/tests/test_attr_sequence_type.py b/dom/bindings/parser/tests/test_attr_sequence_type.py
new file mode 100644
index 0000000000..afbc732974
--- /dev/null
+++ b/dom/bindings/parser/tests/test_attr_sequence_type.py
@@ -0,0 +1,77 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface AttrSequenceType {
+ attribute sequence<object> foo;
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Attribute type must not be a sequence type")
+
+ parser.reset()
+
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface AttrUnionWithSequenceType {
+ attribute (sequence<object> or DOMString) foo;
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Attribute type must not be a union with a sequence member type")
+
+ parser.reset()
+
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface AttrNullableUnionWithSequenceType {
+ attribute (sequence<object>? or DOMString) foo;
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw,
+ "Attribute type must not be a union with a nullable sequence " "member type",
+ )
+
+ parser.reset()
+
+ threw = False
+ try:
+ parser.parse(
+ "\n"
+ " interface AttrUnionWithUnionWithSequenceType {\n"
+ " attribute ((sequence<object> or DOMString) or "
+ "AttrUnionWithUnionWithSequenceType) foo;\n"
+ " };\n"
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw,
+ "Attribute type must not be a union type with a union member "
+ "type that has a sequence member type",
+ )
diff --git a/dom/bindings/parser/tests/test_attributes_on_types.py b/dom/bindings/parser/tests/test_attributes_on_types.py
new file mode 100644
index 0000000000..d0a0c7a72c
--- /dev/null
+++ b/dom/bindings/parser/tests/test_attributes_on_types.py
@@ -0,0 +1,567 @@
+def WebIDLTest(parser, harness):
+ # Basic functionality
+ threw = False
+ try:
+ parser.parse(
+ """
+ typedef [EnforceRange] long Foo;
+ typedef [Clamp] long Bar;
+ typedef [LegacyNullToEmptyString] DOMString Baz;
+ dictionary A {
+ required [EnforceRange] long a;
+ required [Clamp] long b;
+ [ChromeOnly, EnforceRange] long c;
+ Foo d;
+ };
+ interface B {
+ attribute Foo typedefFoo;
+ attribute [EnforceRange] long foo;
+ attribute [Clamp] long bar;
+ attribute [LegacyNullToEmptyString] DOMString baz;
+ undefined method([EnforceRange] long foo, [Clamp] long bar,
+ [LegacyNullToEmptyString] DOMString baz);
+ undefined method2(optional [EnforceRange] long foo, optional [Clamp] long bar,
+ optional [LegacyNullToEmptyString] DOMString baz);
+ undefined method3(optional [LegacyNullToEmptyString] UTF8String foo = "");
+ };
+ interface C {
+ attribute [EnforceRange] long? foo;
+ attribute [Clamp] long? bar;
+ undefined method([EnforceRange] long? foo, [Clamp] long? bar);
+ undefined method2(optional [EnforceRange] long? foo, optional [Clamp] long? bar);
+ };
+ interface Setlike {
+ setlike<[Clamp] long>;
+ };
+ interface Maplike {
+ maplike<[Clamp] long, [EnforceRange] long>;
+ };
+ interface Iterable {
+ iterable<[Clamp] long, [EnforceRange] long>;
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(not threw, "Should not have thrown on parsing normal")
+ if not threw:
+ harness.check(
+ results[0].innerType.hasEnforceRange(), True, "Foo is [EnforceRange]"
+ )
+ harness.check(results[1].innerType.hasClamp(), True, "Bar is [Clamp]")
+ harness.check(
+ results[2].innerType.legacyNullToEmptyString,
+ True,
+ "Baz is [LegacyNullToEmptyString]",
+ )
+ A = results[3]
+ harness.check(
+ A.members[0].type.hasEnforceRange(), True, "A.a is [EnforceRange]"
+ )
+ harness.check(A.members[1].type.hasClamp(), True, "A.b is [Clamp]")
+ harness.check(
+ A.members[2].type.hasEnforceRange(), True, "A.c is [EnforceRange]"
+ )
+ harness.check(
+ A.members[3].type.hasEnforceRange(), True, "A.d is [EnforceRange]"
+ )
+ B = results[4]
+ harness.check(
+ B.members[0].type.hasEnforceRange(), True, "B.typedefFoo is [EnforceRange]"
+ )
+ harness.check(
+ B.members[1].type.hasEnforceRange(), True, "B.foo is [EnforceRange]"
+ )
+ harness.check(B.members[2].type.hasClamp(), True, "B.bar is [Clamp]")
+ harness.check(
+ B.members[3].type.legacyNullToEmptyString,
+ True,
+ "B.baz is [LegacyNullToEmptyString]",
+ )
+ method = B.members[4].signatures()[0][1]
+ harness.check(
+ method[0].type.hasEnforceRange(),
+ True,
+ "foo argument of method is [EnforceRange]",
+ )
+ harness.check(
+ method[1].type.hasClamp(), True, "bar argument of method is [Clamp]"
+ )
+ harness.check(
+ method[2].type.legacyNullToEmptyString,
+ True,
+ "baz argument of method is [LegacyNullToEmptyString]",
+ )
+ method2 = B.members[5].signatures()[0][1]
+ harness.check(
+ method2[0].type.hasEnforceRange(),
+ True,
+ "foo argument of method2 is [EnforceRange]",
+ )
+ harness.check(
+ method2[1].type.hasClamp(), True, "bar argument of method2 is [Clamp]"
+ )
+ harness.check(
+ method2[2].type.legacyNullToEmptyString,
+ True,
+ "baz argument of method2 is [LegacyNullToEmptyString]",
+ )
+
+ method3 = B.members[6].signatures()[0][1]
+ harness.check(
+ method3[0].type.legacyNullToEmptyString,
+ True,
+ "bar argument of method2 is [LegacyNullToEmptyString]",
+ )
+ harness.check(
+ method3[0].defaultValue.type.isUTF8String(),
+ True,
+ "default value of bar argument of method2 is correctly coerced to UTF8String",
+ )
+
+ C = results[5]
+ harness.ok(C.members[0].type.nullable(), "C.foo is nullable")
+ harness.ok(C.members[0].type.hasEnforceRange(), "C.foo has [EnforceRange]")
+ harness.ok(C.members[1].type.nullable(), "C.bar is nullable")
+ harness.ok(C.members[1].type.hasClamp(), "C.bar has [Clamp]")
+ method = C.members[2].signatures()[0][1]
+ harness.ok(method[0].type.nullable(), "foo argument of method is nullable")
+ harness.ok(
+ method[0].type.hasEnforceRange(),
+ "foo argument of method has [EnforceRange]",
+ )
+ harness.ok(method[1].type.nullable(), "bar argument of method is nullable")
+ harness.ok(method[1].type.hasClamp(), "bar argument of method has [Clamp]")
+ method2 = C.members[3].signatures()[0][1]
+ harness.ok(method2[0].type.nullable(), "foo argument of method2 is nullable")
+ harness.ok(
+ method2[0].type.hasEnforceRange(),
+ "foo argument of method2 has [EnforceRange]",
+ )
+ harness.ok(method2[1].type.nullable(), "bar argument of method2 is nullable")
+ harness.ok(method2[1].type.hasClamp(), "bar argument of method2 has [Clamp]")
+
+ # Test [AllowShared]
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ typedef [AllowShared] ArrayBufferView Foo;
+ dictionary A {
+ required [AllowShared] ArrayBufferView a;
+ [ChromeOnly, AllowShared] ArrayBufferView b;
+ Foo c;
+ };
+ interface B {
+ attribute Foo typedefFoo;
+ attribute [AllowShared] ArrayBufferView foo;
+ undefined method([AllowShared] ArrayBufferView foo);
+ undefined method2(optional [AllowShared] ArrayBufferView foo);
+ };
+ interface C {
+ attribute [AllowShared] ArrayBufferView? foo;
+ undefined method([AllowShared] ArrayBufferView? foo);
+ undefined method2(optional [AllowShared] ArrayBufferView? foo);
+ };
+ interface Setlike {
+ setlike<[AllowShared] ArrayBufferView>;
+ };
+ interface Maplike {
+ maplike<[Clamp] long, [AllowShared] ArrayBufferView>;
+ };
+ interface Iterable {
+ iterable<[Clamp] long, [AllowShared] ArrayBufferView>;
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(not threw, "Should not have thrown on parsing normal")
+ if not threw:
+ harness.ok(results[0].innerType.hasAllowShared(), "Foo is [AllowShared]")
+ A = results[1]
+ harness.ok(A.members[0].type.hasAllowShared(), "A.a is [AllowShared]")
+ harness.ok(A.members[1].type.hasAllowShared(), "A.b is [AllowShared]")
+ harness.ok(A.members[2].type.hasAllowShared(), "A.c is [AllowShared]")
+ B = results[2]
+ harness.ok(B.members[0].type.hasAllowShared(), "B.typedefFoo is [AllowShared]")
+ harness.ok(B.members[1].type.hasAllowShared(), "B.foo is [AllowShared]")
+ method = B.members[2].signatures()[0][1]
+ harness.ok(
+ method[0].type.hasAllowShared(), "foo argument of method is [AllowShared]"
+ )
+ method2 = B.members[3].signatures()[0][1]
+ harness.ok(
+ method2[0].type.hasAllowShared(), "foo argument of method2 is [AllowShared]"
+ )
+ C = results[3]
+ harness.ok(C.members[0].type.nullable(), "C.foo is nullable")
+ harness.ok(C.members[0].type.hasAllowShared(), "C.foo is [AllowShared]")
+ method = C.members[1].signatures()[0][1]
+ harness.ok(method[0].type.nullable(), "foo argument of method is nullable")
+ harness.ok(
+ method[0].type.hasAllowShared(), "foo argument of method is [AllowShared]"
+ )
+ method2 = C.members[2].signatures()[0][1]
+ harness.ok(method2[0].type.nullable(), "foo argument of method2 is nullable")
+ harness.ok(
+ method2[0].type.hasAllowShared(), "foo argument of method2 is [AllowShared]"
+ )
+
+ ATTRIBUTES = [
+ ("[Clamp]", "long"),
+ ("[EnforceRange]", "long"),
+ ("[LegacyNullToEmptyString]", "DOMString"),
+ ("[AllowShared]", "ArrayBufferView"),
+ ]
+ TEMPLATES = [
+ (
+ "required dictionary members",
+ """
+ dictionary Foo {
+ %s required %s foo;
+ };
+ """,
+ ),
+ (
+ "optional arguments",
+ """
+ interface Foo {
+ undefined foo(%s optional %s foo);
+ };
+ """,
+ ),
+ (
+ "typedefs",
+ """
+ %s typedef %s foo;
+ """,
+ ),
+ (
+ "attributes",
+ """
+ interface Foo {
+ %s attribute %s foo;
+ };
+ """,
+ ),
+ (
+ "readonly attributes",
+ """
+ interface Foo {
+ readonly attribute %s %s foo;
+ };
+ """,
+ ),
+ (
+ "readonly unresolved attributes",
+ """
+ interface Foo {
+ readonly attribute Bar baz;
+ };
+ typedef %s %s Bar;
+ """,
+ ),
+ (
+ "method",
+ """
+ interface Foo {
+ %s %s foo();
+ };
+ """,
+ ),
+ (
+ "interface",
+ """
+ %s
+ interface Foo {
+ attribute %s foo;
+ };
+ """,
+ ),
+ (
+ "partial interface",
+ """
+ interface Foo {
+ undefined foo();
+ };
+ %s
+ partial interface Foo {
+ attribute %s bar;
+ };
+ """,
+ ),
+ (
+ "interface mixin",
+ """
+ %s
+ interface mixin Foo {
+ attribute %s foo;
+ };
+ """,
+ ),
+ (
+ "namespace",
+ """
+ %s
+ namespace Foo {
+ attribute %s foo;
+ };
+ """,
+ ),
+ (
+ "partial namespace",
+ """
+ namespace Foo {
+ undefined foo();
+ };
+ %s
+ partial namespace Foo {
+ attribute %s bar;
+ };
+ """,
+ ),
+ (
+ "dictionary",
+ """
+ %s
+ dictionary Foo {
+ %s foo;
+ };
+ """,
+ ),
+ ]
+
+ for (name, template) in TEMPLATES:
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(template % ("", "long"))
+ parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(not threw, "Template for %s parses without attributes" % name)
+ for (attribute, type) in ATTRIBUTES:
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(template % (attribute, type))
+ parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "Should not allow %s on %s" % (attribute, name))
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ typedef [Clamp, EnforceRange] long Foo;
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should not allow mixing [Clamp] and [EnforceRange]")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ typedef [EnforceRange, Clamp] long Foo;
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should not allow mixing [Clamp] and [EnforceRange]")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ typedef [Clamp] long Foo;
+ typedef [EnforceRange] Foo bar;
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should not allow mixing [Clamp] and [EnforceRange] via typedefs")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ typedef [EnforceRange] long Foo;
+ typedef [Clamp] Foo bar;
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should not allow mixing [Clamp] and [EnforceRange] via typedefs")
+
+ TYPES = [
+ "DOMString",
+ "unrestricted float",
+ "float",
+ "unrestricted double",
+ "double",
+ ]
+
+ for type in TYPES:
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ typedef [Clamp] %s Foo;
+ """
+ % type
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should not allow [Clamp] on %s" % type)
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ typedef [EnforceRange] %s Foo;
+ """
+ % type
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should not allow [EnforceRange] on %s" % type)
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ typedef [LegacyNullToEmptyString] long Foo;
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should not allow [LegacyNullToEmptyString] on long")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ typedef [LegacyNullToEmptyString] JSString Foo;
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should not allow [LegacyNullToEmptyString] on JSString")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ typedef [LegacyNullToEmptyString] DOMString? Foo;
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw, "Should not allow [LegacyNullToEmptyString] on nullable DOMString"
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ typedef [AllowShared] DOMString Foo;
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "[AllowShared] only allowed on buffer source types")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ typedef [AllowShared=something] ArrayBufferView Foo;
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "[AllowShared] must take no arguments")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Foo {
+ undefined foo([Clamp] Bar arg);
+ };
+ typedef long Bar;
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(not threw, "Should allow type attributes on unresolved types")
+ harness.check(
+ results[0].members[0].signatures()[0][1][0].type.hasClamp(),
+ True,
+ "Unresolved types with type attributes should correctly resolve with attributes",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Foo {
+ undefined foo(Bar arg);
+ };
+ typedef [Clamp] long Bar;
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(not threw, "Should allow type attributes on typedefs")
+ harness.check(
+ results[0].members[0].signatures()[0][1][0].type.hasClamp(),
+ True,
+ "Unresolved types that resolve to typedefs with attributes should correctly resolve with "
+ "attributes",
+ )
diff --git a/dom/bindings/parser/tests/test_builtin_filename.py b/dom/bindings/parser/tests/test_builtin_filename.py
new file mode 100644
index 0000000000..25ed32befc
--- /dev/null
+++ b/dom/bindings/parser/tests/test_builtin_filename.py
@@ -0,0 +1,11 @@
+def WebIDLTest(parser, harness):
+ parser.parse(
+ """
+ interface Test {
+ attribute long b;
+ };
+ """
+ )
+
+ attr = parser.finish()[0].members[0]
+ harness.check(attr.type.filename(), "<builtin>", "Filename on builtin type")
diff --git a/dom/bindings/parser/tests/test_builtins.py b/dom/bindings/parser/tests/test_builtins.py
new file mode 100644
index 0000000000..a75a12e814
--- /dev/null
+++ b/dom/bindings/parser/tests/test_builtins.py
@@ -0,0 +1,59 @@
+import WebIDL
+
+
+def WebIDLTest(parser, harness):
+ parser.parse(
+ """
+ interface TestBuiltins {
+ attribute boolean b;
+ attribute byte s8;
+ attribute octet u8;
+ attribute short s16;
+ attribute unsigned short u16;
+ attribute long s32;
+ attribute unsigned long u32;
+ attribute long long s64;
+ attribute unsigned long long u64;
+ };
+ """
+ )
+
+ results = parser.finish()
+
+ harness.ok(True, "TestBuiltins interface parsed without error.")
+ harness.check(len(results), 1, "Should be one production")
+ harness.ok(isinstance(results[0], WebIDL.IDLInterface), "Should be an IDLInterface")
+ iface = results[0]
+ harness.check(
+ iface.identifier.QName(), "::TestBuiltins", "Interface has the right QName"
+ )
+ harness.check(iface.identifier.name, "TestBuiltins", "Interface has the right name")
+ harness.check(iface.parent, None, "Interface has no parent")
+
+ members = iface.members
+ harness.check(len(members), 9, "Should be one production")
+
+ names = ["b", "s8", "u8", "s16", "u16", "s32", "u32", "s64", "u64", "ts"]
+ types = [
+ "Boolean",
+ "Byte",
+ "Octet",
+ "Short",
+ "UnsignedShort",
+ "Long",
+ "UnsignedLong",
+ "LongLong",
+ "UnsignedLongLong",
+ "UnsignedLongLong",
+ ]
+ for i in range(9):
+ attr = members[i]
+ harness.ok(isinstance(attr, WebIDL.IDLAttribute), "Should be an IDLAttribute")
+ harness.check(
+ attr.identifier.QName(),
+ "::TestBuiltins::" + names[i],
+ "Attr has correct QName",
+ )
+ harness.check(attr.identifier.name, names[i], "Attr has correct name")
+ harness.check(str(attr.type), types[i], "Attr type is the correct name")
+ harness.ok(attr.type.isPrimitive(), "Should be a primitive type")
diff --git a/dom/bindings/parser/tests/test_bytestring.py b/dom/bindings/parser/tests/test_bytestring.py
new file mode 100644
index 0000000000..248d2716f3
--- /dev/null
+++ b/dom/bindings/parser/tests/test_bytestring.py
@@ -0,0 +1,125 @@
+# -*- coding: UTF-8 -*-
+
+import WebIDL
+
+
+def WebIDLTest(parser, harness):
+ parser.parse(
+ """
+ interface TestByteString {
+ attribute ByteString bs;
+ attribute DOMString ds;
+ };
+ """
+ )
+
+ results = parser.finish()
+
+ harness.ok(True, "TestByteString interface parsed without error.")
+
+ harness.check(len(results), 1, "Should be one production")
+ harness.ok(isinstance(results[0], WebIDL.IDLInterface), "Should be an IDLInterface")
+ iface = results[0]
+ harness.check(
+ iface.identifier.QName(), "::TestByteString", "Interface has the right QName"
+ )
+ harness.check(
+ iface.identifier.name, "TestByteString", "Interface has the right name"
+ )
+ harness.check(iface.parent, None, "Interface has no parent")
+
+ members = iface.members
+ harness.check(len(members), 2, "Should be two productions")
+
+ attr = members[0]
+ harness.ok(isinstance(attr, WebIDL.IDLAttribute), "Should be an IDLAttribute")
+ harness.check(
+ attr.identifier.QName(), "::TestByteString::bs", "Attr has correct QName"
+ )
+ harness.check(attr.identifier.name, "bs", "Attr has correct name")
+ harness.check(str(attr.type), "ByteString", "Attr type is the correct name")
+ harness.ok(attr.type.isByteString(), "Should be ByteString type")
+ harness.ok(attr.type.isString(), "Should be String collective type")
+ harness.ok(not attr.type.isDOMString(), "Should be not be DOMString type")
+
+ # now check we haven't broken DOMStrings in the process.
+ attr = members[1]
+ harness.ok(isinstance(attr, WebIDL.IDLAttribute), "Should be an IDLAttribute")
+ harness.check(
+ attr.identifier.QName(), "::TestByteString::ds", "Attr has correct QName"
+ )
+ harness.check(attr.identifier.name, "ds", "Attr has correct name")
+ harness.check(str(attr.type), "String", "Attr type is the correct name")
+ harness.ok(attr.type.isDOMString(), "Should be DOMString type")
+ harness.ok(attr.type.isString(), "Should be String collective type")
+ harness.ok(not attr.type.isByteString(), "Should be not be ByteString type")
+
+ # Cannot represent constant ByteString in IDL.
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface ConstByteString {
+ const ByteString foo = "hello"
+ };
+ """
+ )
+ except WebIDL.WebIDLError:
+ threw = True
+ harness.ok(
+ threw, "Should have thrown a WebIDL error for ByteString default in interface"
+ )
+
+ # Can have optional ByteStrings with default values
+ try:
+ parser.parse(
+ """
+ interface OptionalByteString {
+ undefined passByteString(optional ByteString arg = "hello");
+ };
+ """
+ )
+ parser.finish()
+ except WebIDL.WebIDLError as e:
+ harness.ok(
+ False,
+ "Should not have thrown a WebIDL error for ByteString "
+ "default in dictionary. " + str(e),
+ )
+
+ # Can have a default ByteString value in a dictionary
+ try:
+ parser.parse(
+ """
+ dictionary OptionalByteStringDict {
+ ByteString item = "some string";
+ };
+ """
+ )
+ parser.finish()
+ except WebIDL.WebIDLError as e:
+ harness.ok(
+ False,
+ "Should not have thrown a WebIDL error for ByteString "
+ "default in dictionary. " + str(e),
+ )
+
+ # Don't allow control characters in ByteString literals
+ threw = False
+ try:
+ parser.parse(
+ """
+ dictionary OptionalByteStringDict2 {
+ ByteString item = "\x03";
+ };
+ """
+ )
+ parser.finish()
+ except WebIDL.WebIDLError:
+ threw = True
+
+ harness.ok(
+ threw,
+ "Should have thrown a WebIDL error for invalid ByteString "
+ "default in dictionary",
+ )
diff --git a/dom/bindings/parser/tests/test_callback.py b/dom/bindings/parser/tests/test_callback.py
new file mode 100644
index 0000000000..407644a6a8
--- /dev/null
+++ b/dom/bindings/parser/tests/test_callback.py
@@ -0,0 +1,42 @@
+import WebIDL
+
+
+def WebIDLTest(parser, harness):
+ parser.parse(
+ """
+ interface TestCallback {
+ attribute CallbackType? listener;
+ };
+
+ callback CallbackType = boolean (unsigned long arg);
+ """
+ )
+
+ results = parser.finish()
+
+ harness.ok(True, "TestCallback interface parsed without error.")
+ harness.check(len(results), 2, "Should be two productions.")
+ iface = results[0]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface), "Should be an IDLInterface")
+ harness.check(
+ iface.identifier.QName(), "::TestCallback", "Interface has the right QName"
+ )
+ harness.check(iface.identifier.name, "TestCallback", "Interface has the right name")
+ harness.check(len(iface.members), 1, "Expect %s members" % 1)
+
+ attr = iface.members[0]
+ harness.ok(isinstance(attr, WebIDL.IDLAttribute), "Should be an IDLAttribute")
+ harness.ok(attr.isAttr(), "Should be an attribute")
+ harness.ok(not attr.isMethod(), "Attr is not an method")
+ harness.ok(not attr.isConst(), "Attr is not a const")
+ harness.check(
+ attr.identifier.QName(), "::TestCallback::listener", "Attr has the right QName"
+ )
+ harness.check(attr.identifier.name, "listener", "Attr has the right name")
+ t = attr.type
+ harness.ok(not isinstance(t, WebIDL.IDLWrapperType), "Attr has the right type")
+ harness.ok(isinstance(t, WebIDL.IDLNullableType), "Attr has the right type")
+ harness.ok(t.isCallback(), "Attr has the right type")
+
+ callback = results[1]
+ harness.ok(not callback.isConstructor(), "callback is not constructor")
diff --git a/dom/bindings/parser/tests/test_callback_constructor.py b/dom/bindings/parser/tests/test_callback_constructor.py
new file mode 100644
index 0000000000..ea2f46c436
--- /dev/null
+++ b/dom/bindings/parser/tests/test_callback_constructor.py
@@ -0,0 +1,84 @@
+import WebIDL
+
+
+def WebIDLTest(parser, harness):
+ parser.parse(
+ """
+ interface TestCallbackConstructor {
+ attribute CallbackConstructorType? constructorAttribute;
+ };
+
+ callback constructor CallbackConstructorType = TestCallbackConstructor (unsigned long arg);
+ """
+ )
+
+ results = parser.finish()
+
+ harness.ok(True, "TestCallbackConstructor interface parsed without error.")
+ harness.check(len(results), 2, "Should be two productions.")
+ iface = results[0]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface), "Should be an IDLInterface")
+ harness.check(
+ iface.identifier.QName(),
+ "::TestCallbackConstructor",
+ "Interface has the right QName",
+ )
+ harness.check(
+ iface.identifier.name, "TestCallbackConstructor", "Interface has the right name"
+ )
+ harness.check(len(iface.members), 1, "Expect %s members" % 1)
+
+ attr = iface.members[0]
+ harness.ok(isinstance(attr, WebIDL.IDLAttribute), "Should be an IDLAttribute")
+ harness.ok(attr.isAttr(), "Should be an attribute")
+ harness.ok(not attr.isMethod(), "Attr is not an method")
+ harness.ok(not attr.isConst(), "Attr is not a const")
+ harness.check(
+ attr.identifier.QName(),
+ "::TestCallbackConstructor::constructorAttribute",
+ "Attr has the right QName",
+ )
+ harness.check(
+ attr.identifier.name, "constructorAttribute", "Attr has the right name"
+ )
+ t = attr.type
+ harness.ok(not isinstance(t, WebIDL.IDLWrapperType), "Attr has the right type")
+ harness.ok(isinstance(t, WebIDL.IDLNullableType), "Attr has the right type")
+ harness.ok(t.isCallback(), "Attr has the right type")
+
+ callback = results[1]
+ harness.ok(callback.isConstructor(), "Callback is constructor")
+
+ parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ [LegacyTreatNonObjectAsNull]
+ callback constructor CallbackConstructorType = object ();
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw, "Should throw on LegacyTreatNonObjectAsNull callback constructors"
+ )
+
+ parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ [MOZ_CAN_RUN_SCRIPT_BOUNDARY]
+ callback constructor CallbackConstructorType = object ();
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw, "Should not permit MOZ_CAN_RUN_SCRIPT_BOUNDARY callback constructors"
+ )
diff --git a/dom/bindings/parser/tests/test_callback_interface.py b/dom/bindings/parser/tests/test_callback_interface.py
new file mode 100644
index 0000000000..1396eb8a24
--- /dev/null
+++ b/dom/bindings/parser/tests/test_callback_interface.py
@@ -0,0 +1,103 @@
+def WebIDLTest(parser, harness):
+ parser.parse(
+ """
+ callback interface TestCallbackInterface {
+ attribute boolean bool;
+ };
+ """
+ )
+
+ results = parser.finish()
+
+ iface = results[0]
+
+ harness.ok(iface.isCallback(), "Interface should be a callback")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface TestInterface {
+ };
+ callback interface TestCallbackInterface : TestInterface {
+ attribute boolean bool;
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should not allow non-callback parent of callback interface")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface TestInterface : TestCallbackInterface {
+ };
+ callback interface TestCallbackInterface {
+ attribute boolean bool;
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should not allow callback parent of non-callback interface")
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ callback interface TestCallbackInterface1 {
+ undefined foo();
+ };
+ callback interface TestCallbackInterface2 {
+ undefined foo(DOMString arg);
+ undefined foo(TestCallbackInterface1 arg);
+ };
+ callback interface TestCallbackInterface3 {
+ undefined foo(DOMString arg);
+ undefined foo(TestCallbackInterface1 arg);
+ static undefined bar();
+ };
+ callback interface TestCallbackInterface4 {
+ undefined foo(DOMString arg);
+ undefined foo(TestCallbackInterface1 arg);
+ static undefined bar();
+ const long baz = 5;
+ };
+ callback interface TestCallbackInterface5 {
+ static attribute boolean bool;
+ undefined foo();
+ };
+ callback interface TestCallbackInterface6 {
+ undefined foo(DOMString arg);
+ undefined foo(TestCallbackInterface1 arg);
+ undefined bar();
+ };
+ callback interface TestCallbackInterface7 {
+ static attribute boolean bool;
+ };
+ callback interface TestCallbackInterface8 {
+ attribute boolean bool;
+ };
+ callback interface TestCallbackInterface9 : TestCallbackInterface1 {
+ undefined foo();
+ };
+ callback interface TestCallbackInterface10 : TestCallbackInterface1 {
+ undefined bar();
+ };
+ """
+ )
+ results = parser.finish()
+ for (i, iface) in enumerate(results):
+ harness.check(
+ iface.isSingleOperationInterface(),
+ i < 4,
+ "Interface %s should be a single operation interface"
+ % iface.identifier.name,
+ )
diff --git a/dom/bindings/parser/tests/test_cereactions.py b/dom/bindings/parser/tests/test_cereactions.py
new file mode 100644
index 0000000000..376a32b4c4
--- /dev/null
+++ b/dom/bindings/parser/tests/test_cereactions.py
@@ -0,0 +1,157 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Foo {
+ [CEReactions(DOMString a)] undefined foo(boolean arg2);
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown for [CEReactions] with an argument")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Foo {
+ [CEReactions(DOMString b)] readonly attribute boolean bar;
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown for [CEReactions] with an argument")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Foo {
+ [CEReactions] attribute boolean bar;
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception as e:
+ harness.ok(
+ False,
+ "Shouldn't have thrown for [CEReactions] used on writable attribute. %s"
+ % e,
+ )
+ threw = True
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Foo {
+ [CEReactions] undefined foo(boolean arg2);
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception as e:
+ harness.ok(
+ False,
+ "Shouldn't have thrown for [CEReactions] used on regular operations. %s"
+ % e,
+ )
+ threw = True
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Foo {
+ [CEReactions] readonly attribute boolean A;
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw, "Should have thrown for [CEReactions] used on a readonly attribute"
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ [CEReactions]
+ interface Foo {
+ }
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown for [CEReactions] used on a interface")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Foo {
+ [CEReactions] getter any(DOMString name);
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown for [CEReactions] used on a named getter")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Foo {
+ [CEReactions] legacycaller double compute(double x);
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown for [CEReactions] used on a legacycaller")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Foo {
+ [CEReactions] stringifier DOMString ();
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown for [CEReactions] used on a stringifier")
diff --git a/dom/bindings/parser/tests/test_conditional_dictionary_member.py b/dom/bindings/parser/tests/test_conditional_dictionary_member.py
new file mode 100644
index 0000000000..2aef8ebe8f
--- /dev/null
+++ b/dom/bindings/parser/tests/test_conditional_dictionary_member.py
@@ -0,0 +1,128 @@
+def WebIDLTest(parser, harness):
+ parser.parse(
+ """
+ dictionary Dict {
+ any foo;
+ [ChromeOnly] any bar;
+ };
+ """
+ )
+ results = parser.finish()
+ harness.check(len(results), 1, "Should have a dictionary")
+ members = results[0].members
+ harness.check(len(members), 2, "Should have two members")
+ # Note that members are ordered lexicographically, so "bar" comes
+ # before "foo".
+ harness.ok(
+ members[0].getExtendedAttribute("ChromeOnly"), "First member is not ChromeOnly"
+ )
+ harness.ok(
+ not members[1].getExtendedAttribute("ChromeOnly"), "Second member is ChromeOnly"
+ )
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ dictionary Dict {
+ any foo;
+ any bar;
+ };
+
+ interface Iface {
+ [Constant, Cached] readonly attribute Dict dict;
+ };
+ """
+ )
+ results = parser.finish()
+ harness.check(len(results), 2, "Should have a dictionary and an interface")
+
+ parser = parser.reset()
+ exception = None
+ try:
+ parser.parse(
+ """
+ dictionary Dict {
+ any foo;
+ [ChromeOnly] any bar;
+ };
+
+ interface Iface {
+ [Constant, Cached] readonly attribute Dict dict;
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception as e:
+ exception = e
+
+ harness.ok(exception, "Should have thrown.")
+ harness.check(
+ exception.message,
+ "[Cached] and [StoreInSlot] must not be used on an attribute "
+ "whose type contains a [ChromeOnly] dictionary member",
+ "Should have thrown the right exception",
+ )
+
+ parser = parser.reset()
+ exception = None
+ try:
+ parser.parse(
+ """
+ dictionary ParentDict {
+ [ChromeOnly] any bar;
+ };
+
+ dictionary Dict : ParentDict {
+ any foo;
+ };
+
+ interface Iface {
+ [Constant, Cached] readonly attribute Dict dict;
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception as e:
+ exception = e
+
+ harness.ok(exception, "Should have thrown (2).")
+ harness.check(
+ exception.message,
+ "[Cached] and [StoreInSlot] must not be used on an attribute "
+ "whose type contains a [ChromeOnly] dictionary member",
+ "Should have thrown the right exception (2)",
+ )
+
+ parser = parser.reset()
+ exception = None
+ try:
+ parser.parse(
+ """
+ dictionary GrandParentDict {
+ [ChromeOnly] any baz;
+ };
+
+ dictionary ParentDict : GrandParentDict {
+ any bar;
+ };
+
+ dictionary Dict : ParentDict {
+ any foo;
+ };
+
+ interface Iface {
+ [Constant, Cached] readonly attribute Dict dict;
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception as e:
+ exception = e
+
+ harness.ok(exception, "Should have thrown (3).")
+ harness.check(
+ exception.message,
+ "[Cached] and [StoreInSlot] must not be used on an attribute "
+ "whose type contains a [ChromeOnly] dictionary member",
+ "Should have thrown the right exception (3)",
+ )
diff --git a/dom/bindings/parser/tests/test_const.py b/dom/bindings/parser/tests/test_const.py
new file mode 100644
index 0000000000..ea597d1ed2
--- /dev/null
+++ b/dom/bindings/parser/tests/test_const.py
@@ -0,0 +1,96 @@
+import WebIDL
+
+expected = [
+ ("::TestConsts::zero", "zero", "Byte", 0),
+ ("::TestConsts::b", "b", "Byte", -1),
+ ("::TestConsts::o", "o", "Octet", 2),
+ ("::TestConsts::s", "s", "Short", -3),
+ ("::TestConsts::us", "us", "UnsignedShort", 4),
+ ("::TestConsts::l", "l", "Long", -5),
+ ("::TestConsts::ul", "ul", "UnsignedLong", 6),
+ ("::TestConsts::ull", "ull", "UnsignedLongLong", 7),
+ ("::TestConsts::ll", "ll", "LongLong", -8),
+ ("::TestConsts::t", "t", "Boolean", True),
+ ("::TestConsts::f", "f", "Boolean", False),
+ ("::TestConsts::fl", "fl", "Float", 0.2),
+ ("::TestConsts::db", "db", "Double", 0.2),
+ ("::TestConsts::ufl", "ufl", "UnrestrictedFloat", 0.2),
+ ("::TestConsts::udb", "udb", "UnrestrictedDouble", 0.2),
+ ("::TestConsts::fli", "fli", "Float", 2),
+ ("::TestConsts::dbi", "dbi", "Double", 2),
+ ("::TestConsts::ufli", "ufli", "UnrestrictedFloat", 2),
+ ("::TestConsts::udbi", "udbi", "UnrestrictedDouble", 2),
+]
+
+
+def WebIDLTest(parser, harness):
+ parser.parse(
+ """
+ interface TestConsts {
+ const byte zero = 0;
+ const byte b = -1;
+ const octet o = 2;
+ const short s = -3;
+ const unsigned short us = 0x4;
+ const long l = -0X5;
+ const unsigned long ul = 6;
+ const unsigned long long ull = 7;
+ const long long ll = -010;
+ const boolean t = true;
+ const boolean f = false;
+ const float fl = 0.2;
+ const double db = 0.2;
+ const unrestricted float ufl = 0.2;
+ const unrestricted double udb = 0.2;
+ const float fli = 2;
+ const double dbi = 2;
+ const unrestricted float ufli = 2;
+ const unrestricted double udbi = 2;
+ };
+ """
+ )
+
+ results = parser.finish()
+
+ harness.ok(True, "TestConsts interface parsed without error.")
+ harness.check(len(results), 1, "Should be one production.")
+ iface = results[0]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface), "Should be an IDLInterface")
+ harness.check(
+ iface.identifier.QName(), "::TestConsts", "Interface has the right QName"
+ )
+ harness.check(iface.identifier.name, "TestConsts", "Interface has the right name")
+ harness.check(
+ len(iface.members), len(expected), "Expect %s members" % len(expected)
+ )
+
+ for (const, (QName, name, type, value)) in zip(iface.members, expected):
+ harness.ok(isinstance(const, WebIDL.IDLConst), "Should be an IDLConst")
+ harness.ok(const.isConst(), "Const is a const")
+ harness.ok(not const.isAttr(), "Const is not an attr")
+ harness.ok(not const.isMethod(), "Const is not a method")
+ harness.check(const.identifier.QName(), QName, "Const has the right QName")
+ harness.check(const.identifier.name, name, "Const has the right name")
+ harness.check(str(const.type), type, "Const has the right type")
+ harness.ok(const.type.isPrimitive(), "All consts should be primitive")
+ harness.check(
+ str(const.value.type),
+ str(const.type),
+ "Const's value has the same type as the type",
+ )
+ harness.check(const.value.value, value, "Const value has the right value.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface TestConsts {
+ const boolean? zero = 0;
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "Nullable types are not allowed for consts.")
diff --git a/dom/bindings/parser/tests/test_constructor.py b/dom/bindings/parser/tests/test_constructor.py
new file mode 100644
index 0000000000..1e1382336c
--- /dev/null
+++ b/dom/bindings/parser/tests/test_constructor.py
@@ -0,0 +1,596 @@
+import WebIDL
+
+
+def WebIDLTest(parser, harness):
+ def checkArgument(argument, QName, name, type, optional, variadic):
+ harness.ok(isinstance(argument, WebIDL.IDLArgument), "Should be an IDLArgument")
+ harness.check(
+ argument.identifier.QName(), QName, "Argument has the right QName"
+ )
+ harness.check(argument.identifier.name, name, "Argument has the right name")
+ harness.check(str(argument.type), type, "Argument has the right return type")
+ harness.check(
+ argument.optional, optional, "Argument has the right optional value"
+ )
+ harness.check(
+ argument.variadic, variadic, "Argument has the right variadic value"
+ )
+
+ def checkMethod(
+ method,
+ QName,
+ name,
+ signatures,
+ static=True,
+ getter=False,
+ setter=False,
+ deleter=False,
+ legacycaller=False,
+ stringifier=False,
+ chromeOnly=False,
+ htmlConstructor=False,
+ secureContext=False,
+ pref=None,
+ func=None,
+ ):
+ harness.ok(isinstance(method, WebIDL.IDLMethod), "Should be an IDLMethod")
+ harness.ok(method.isMethod(), "Method is a method")
+ harness.ok(not method.isAttr(), "Method is not an attr")
+ harness.ok(not method.isConst(), "Method is not a const")
+ harness.check(method.identifier.QName(), QName, "Method has the right QName")
+ harness.check(method.identifier.name, name, "Method has the right name")
+ harness.check(method.isStatic(), static, "Method has the correct static value")
+ harness.check(method.isGetter(), getter, "Method has the correct getter value")
+ harness.check(method.isSetter(), setter, "Method has the correct setter value")
+ harness.check(
+ method.isDeleter(), deleter, "Method has the correct deleter value"
+ )
+ harness.check(
+ method.isLegacycaller(),
+ legacycaller,
+ "Method has the correct legacycaller value",
+ )
+ harness.check(
+ method.isStringifier(),
+ stringifier,
+ "Method has the correct stringifier value",
+ )
+ harness.check(
+ method.getExtendedAttribute("ChromeOnly") is not None,
+ chromeOnly,
+ "Method has the correct value for ChromeOnly",
+ )
+ harness.check(
+ method.isHTMLConstructor(),
+ htmlConstructor,
+ "Method has the correct htmlConstructor value",
+ )
+ harness.check(
+ len(method.signatures()),
+ len(signatures),
+ "Method has the correct number of signatures",
+ )
+ harness.check(
+ method.getExtendedAttribute("Pref"),
+ pref,
+ "Method has the correct pref value",
+ )
+ harness.check(
+ method.getExtendedAttribute("Func"),
+ func,
+ "Method has the correct func value",
+ )
+ harness.check(
+ method.getExtendedAttribute("SecureContext") is not None,
+ secureContext,
+ "Method has the correct SecureContext value",
+ )
+
+ sigpairs = zip(method.signatures(), signatures)
+ for (gotSignature, expectedSignature) in sigpairs:
+ (gotRetType, gotArgs) = gotSignature
+ (expectedRetType, expectedArgs) = expectedSignature
+
+ harness.check(
+ str(gotRetType), expectedRetType, "Method has the expected return type."
+ )
+
+ for i in range(0, len(gotArgs)):
+ (QName, name, type, optional, variadic) = expectedArgs[i]
+ checkArgument(gotArgs[i], QName, name, type, optional, variadic)
+
+ def checkResults(results):
+ harness.check(len(results), 3, "Should be three productions")
+ harness.ok(
+ isinstance(results[0], WebIDL.IDLInterface), "Should be an IDLInterface"
+ )
+ harness.ok(
+ isinstance(results[1], WebIDL.IDLInterface), "Should be an IDLInterface"
+ )
+ harness.ok(
+ isinstance(results[2], WebIDL.IDLInterface), "Should be an IDLInterface"
+ )
+
+ checkMethod(
+ results[0].ctor(),
+ "::TestConstructorNoArgs::constructor",
+ "constructor",
+ [("TestConstructorNoArgs (Wrapper)", [])],
+ )
+ harness.check(
+ len(results[0].members), 0, "TestConstructorNoArgs should not have members"
+ )
+ checkMethod(
+ results[1].ctor(),
+ "::TestConstructorWithArgs::constructor",
+ "constructor",
+ [
+ (
+ "TestConstructorWithArgs (Wrapper)",
+ [
+ (
+ "::TestConstructorWithArgs::constructor::name",
+ "name",
+ "String",
+ False,
+ False,
+ )
+ ],
+ )
+ ],
+ )
+ harness.check(
+ len(results[1].members),
+ 0,
+ "TestConstructorWithArgs should not have members",
+ )
+ checkMethod(
+ results[2].ctor(),
+ "::TestConstructorOverloads::constructor",
+ "constructor",
+ [
+ (
+ "TestConstructorOverloads (Wrapper)",
+ [
+ (
+ "::TestConstructorOverloads::constructor::foo",
+ "foo",
+ "Object",
+ False,
+ False,
+ )
+ ],
+ ),
+ (
+ "TestConstructorOverloads (Wrapper)",
+ [
+ (
+ "::TestConstructorOverloads::constructor::bar",
+ "bar",
+ "Boolean",
+ False,
+ False,
+ )
+ ],
+ ),
+ ],
+ )
+ harness.check(
+ len(results[2].members),
+ 0,
+ "TestConstructorOverloads should not have members",
+ )
+
+ parser.parse(
+ """
+ interface TestConstructorNoArgs {
+ constructor();
+ };
+
+ interface TestConstructorWithArgs {
+ constructor(DOMString name);
+ };
+
+ interface TestConstructorOverloads {
+ constructor(object foo);
+ constructor(boolean bar);
+ };
+ """
+ )
+ results = parser.finish()
+ checkResults(results)
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ interface TestPrefConstructor {
+ [Pref="dom.webidl.test1"] constructor();
+ };
+ """
+ )
+ results = parser.finish()
+ harness.check(len(results), 1, "Should be one production")
+ harness.ok(isinstance(results[0], WebIDL.IDLInterface), "Should be an IDLInterface")
+
+ checkMethod(
+ results[0].ctor(),
+ "::TestPrefConstructor::constructor",
+ "constructor",
+ [("TestPrefConstructor (Wrapper)", [])],
+ pref=["dom.webidl.test1"],
+ )
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ interface TestChromeOnlyConstructor {
+ [ChromeOnly] constructor();
+ };
+ """
+ )
+ results = parser.finish()
+ harness.check(len(results), 1, "Should be one production")
+ harness.ok(isinstance(results[0], WebIDL.IDLInterface), "Should be an IDLInterface")
+
+ checkMethod(
+ results[0].ctor(),
+ "::TestChromeOnlyConstructor::constructor",
+ "constructor",
+ [("TestChromeOnlyConstructor (Wrapper)", [])],
+ chromeOnly=True,
+ )
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ interface TestSCConstructor {
+ [SecureContext] constructor();
+ };
+ """
+ )
+ results = parser.finish()
+ harness.check(len(results), 1, "Should be one production")
+ harness.ok(isinstance(results[0], WebIDL.IDLInterface), "Should be an IDLInterface")
+
+ checkMethod(
+ results[0].ctor(),
+ "::TestSCConstructor::constructor",
+ "constructor",
+ [("TestSCConstructor (Wrapper)", [])],
+ secureContext=True,
+ )
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ interface TestFuncConstructor {
+ [Func="Document::IsWebAnimationsEnabled"] constructor();
+ };
+ """
+ )
+ results = parser.finish()
+ harness.check(len(results), 1, "Should be one production")
+ harness.ok(isinstance(results[0], WebIDL.IDLInterface), "Should be an IDLInterface")
+
+ checkMethod(
+ results[0].ctor(),
+ "::TestFuncConstructor::constructor",
+ "constructor",
+ [("TestFuncConstructor (Wrapper)", [])],
+ func=["Document::IsWebAnimationsEnabled"],
+ )
+
+ parser = parser.reset()
+ parser.parse(
+ (
+ "\n"
+ " interface TestPrefChromeOnlySCFuncConstructor {\n"
+ ' [ChromeOnly, Pref="dom.webidl.test1", SecureContext, '
+ 'Func="Document::IsWebAnimationsEnabled"]\n'
+ " constructor();\n"
+ " };\n"
+ )
+ )
+ results = parser.finish()
+ harness.check(len(results), 1, "Should be one production")
+ harness.ok(isinstance(results[0], WebIDL.IDLInterface), "Should be an IDLInterface")
+
+ checkMethod(
+ results[0].ctor(),
+ "::TestPrefChromeOnlySCFuncConstructor::constructor",
+ "constructor",
+ [("TestPrefChromeOnlySCFuncConstructor (Wrapper)", [])],
+ func=["Document::IsWebAnimationsEnabled"],
+ pref=["dom.webidl.test1"],
+ chromeOnly=True,
+ secureContext=True,
+ )
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ interface TestHTMLConstructor {
+ [HTMLConstructor] constructor();
+ };
+ """
+ )
+ results = parser.finish()
+ harness.check(len(results), 1, "Should be one production")
+ harness.ok(isinstance(results[0], WebIDL.IDLInterface), "Should be an IDLInterface")
+
+ checkMethod(
+ results[0].ctor(),
+ "::TestHTMLConstructor::constructor",
+ "constructor",
+ [("TestHTMLConstructor (Wrapper)", [])],
+ htmlConstructor=True,
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface TestChromeOnlyConstructor {
+ constructor()
+ [ChromeOnly] constructor(DOMString a);
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Can't have both a constructor and a ChromeOnly constructor")
+
+ # Test HTMLConstructor with argument
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface TestHTMLConstructorWithArgs {
+ [HTMLConstructor] constructor(DOMString a);
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "HTMLConstructor should take no argument")
+
+ # Test HTMLConstructor on a callback interface
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ callback interface TestHTMLConstructorOnCallbackInterface {
+ [HTMLConstructor] constructor();
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "HTMLConstructor can't be used on a callback interface")
+
+ # Test HTMLConstructor and constructor operation
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface TestHTMLConstructorAndConstructor {
+ constructor();
+ [HTMLConstructor] constructor();
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Can't have both a constructor and a HTMLConstructor")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface TestHTMLConstructorAndConstructor {
+ [Throws]
+ constructor();
+ [HTMLConstructor] constructor();
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Can't have both a throwing constructor and a HTMLConstructor")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface TestHTMLConstructorAndConstructor {
+ constructor(DOMString a);
+ [HTMLConstructor] constructor();
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Can't have both a HTMLConstructor and a constructor operation")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface TestHTMLConstructorAndConstructor {
+ [Throws]
+ constructor(DOMString a);
+ [HTMLConstructor] constructor();
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw,
+ "Can't have both a HTMLConstructor and a throwing constructor " "operation",
+ )
+
+ # Test HTMLConstructor and [ChromeOnly] constructor operation
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface TestHTMLConstructorAndConstructor {
+ [ChromeOnly]
+ constructor();
+ [HTMLConstructor] constructor();
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Can't have both a ChromeOnly constructor and a HTMLConstructor")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface TestHTMLConstructorAndConstructor {
+ [Throws, ChromeOnly]
+ constructor();
+ [HTMLConstructor] constructor();
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw,
+ "Can't have both a throwing chromeonly constructor and a " "HTMLConstructor",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface TestHTMLConstructorAndConstructor {
+ [ChromeOnly]
+ constructor(DOMString a);
+ [HTMLConstructor] constructor();
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw,
+ "Can't have both a HTMLConstructor and a chromeonly constructor " "operation",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface TestHTMLConstructorAndConstructor {
+ [Throws, ChromeOnly]
+ constructor(DOMString a);
+ [HTMLConstructor] constructor();
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw,
+ "Can't have both a HTMLConstructor and a throwing chromeonly "
+ "constructor operation",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ [LegacyNoInterfaceObject]
+ interface InterfaceWithoutInterfaceObject {
+ constructor();
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw,
+ "Can't have a constructor operation on a [LegacyNoInterfaceObject] "
+ "interface",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface InterfaceWithPartial {
+ };
+
+ partial interface InterfaceWithPartial {
+ constructor();
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Can't have a constructor operation on a partial interface")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface InterfaceWithMixin {
+ };
+
+ interface mixin Mixin {
+ constructor();
+ };
+
+ InterfaceWithMixin includes Mixin
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Can't have a constructor operation on a mixin")
diff --git a/dom/bindings/parser/tests/test_constructor_global.py b/dom/bindings/parser/tests/test_constructor_global.py
new file mode 100644
index 0000000000..74c81699e3
--- /dev/null
+++ b/dom/bindings/parser/tests/test_constructor_global.py
@@ -0,0 +1,69 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse(
+ """
+ [Global, Exposed=TestConstructorGlobal]
+ interface TestConstructorGlobal {
+ constructor();
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ [Global, Exposed=TestLegacyFactoryFunctionGlobal,
+ LegacyFactoryFunction=FooBar]
+ interface TestLegacyFactoryFunctionGlobal {
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ [LegacyFactoryFunction=FooBar, Global,
+ Exposed=TestLegacyFactoryFunctionGlobal]
+ interface TestLegacyFactoryFunctionGlobal {
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ [Global, Exposed=TestHTMLConstructorGlobal]
+ interface TestHTMLConstructorGlobal {
+ [HTMLConstructor] constructor();
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/dom/bindings/parser/tests/test_constructor_no_interface_object.py b/dom/bindings/parser/tests/test_constructor_no_interface_object.py
new file mode 100644
index 0000000000..513a9ab3f8
--- /dev/null
+++ b/dom/bindings/parser/tests/test_constructor_no_interface_object.py
@@ -0,0 +1,47 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse(
+ """
+ [LegacyNoInterfaceObject]
+ interface TestConstructorLegacyNoInterfaceObject {
+ constructor();
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset()
+
+ parser.parse(
+ """
+ [LegacyNoInterfaceObject, LegacyFactoryFunction=FooBar]
+ interface TestLegacyFactoryFunctionLegacyNoInterfaceObject {
+ };
+ """
+ )
+
+ # Test HTMLConstructor and LegacyNoInterfaceObject
+ parser = parser.reset()
+
+ threw = False
+ try:
+ parser.parse(
+ """
+ [LegacyNoInterfaceObject]
+ interface TestHTMLConstructorLegacyNoInterfaceObject {
+ [HTMLConstructor] constructor();
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/dom/bindings/parser/tests/test_deduplicate.py b/dom/bindings/parser/tests/test_deduplicate.py
new file mode 100644
index 0000000000..2308f6201e
--- /dev/null
+++ b/dom/bindings/parser/tests/test_deduplicate.py
@@ -0,0 +1,17 @@
+def WebIDLTest(parser, harness):
+ parser.parse(
+ """
+ interface Foo;
+ interface Bar;
+ interface Foo;
+ """
+ )
+
+ results = parser.finish()
+
+ # There should be no duplicate interfaces in the result.
+ expectedNames = sorted(["Foo", "Bar"])
+ actualNames = sorted(map(lambda iface: iface.identifier.name, results))
+ harness.check(
+ actualNames, expectedNames, "Parser shouldn't output duplicate names."
+ )
diff --git a/dom/bindings/parser/tests/test_dictionary.py b/dom/bindings/parser/tests/test_dictionary.py
new file mode 100644
index 0000000000..26411b96d7
--- /dev/null
+++ b/dom/bindings/parser/tests/test_dictionary.py
@@ -0,0 +1,875 @@
+def WebIDLTest(parser, harness):
+ parser.parse(
+ """
+ dictionary Dict2 : Dict1 {
+ long child = 5;
+ Dict1 aaandAnother;
+ };
+ dictionary Dict1 {
+ long parent;
+ double otherParent;
+ };
+ """
+ )
+ results = parser.finish()
+
+ dict1 = results[1]
+ dict2 = results[0]
+
+ harness.check(len(dict1.members), 2, "Dict1 has two members")
+ harness.check(len(dict2.members), 2, "Dict2 has four members")
+
+ harness.check(
+ dict1.members[0].identifier.name, "otherParent", "'o' comes before 'p'"
+ )
+ harness.check(
+ dict1.members[1].identifier.name, "parent", "'o' really comes before 'p'"
+ )
+ harness.check(
+ dict2.members[0].identifier.name, "aaandAnother", "'a' comes before 'c'"
+ )
+ harness.check(
+ dict2.members[1].identifier.name, "child", "'a' really comes before 'c'"
+ )
+
+ # Test partial dictionary.
+ parser = parser.reset()
+ parser.parse(
+ """
+ dictionary A {
+ long c;
+ long g;
+ };
+ partial dictionary A {
+ long h;
+ long d;
+ };
+ """
+ )
+ results = parser.finish()
+
+ dict1 = results[0]
+ harness.check(len(dict1.members), 4, "Dict1 has four members")
+ harness.check(dict1.members[0].identifier.name, "c", "c should be first")
+ harness.check(dict1.members[1].identifier.name, "d", "d should come after c")
+ harness.check(dict1.members[2].identifier.name, "g", "g should come after d")
+ harness.check(dict1.members[3].identifier.name, "h", "h should be last")
+
+ # Now reset our parser
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ dictionary Dict {
+ long prop = 5;
+ long prop;
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should not allow name duplication in a dictionary")
+
+ # Test no name duplication across normal and partial dictionary.
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ dictionary A {
+ long prop = 5;
+ };
+ partial dictionary A {
+ long prop;
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw, "Should not allow name duplication across normal and partial dictionary"
+ )
+
+ # Now reset our parser again
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ dictionary Dict1 : Dict2 {
+ long prop = 5;
+ };
+ dictionary Dict2 : Dict3 {
+ long prop2;
+ };
+ dictionary Dict3 {
+ double prop;
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw, "Should not allow name duplication in a dictionary and " "its ancestor"
+ )
+
+ # More reset
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Iface {};
+ dictionary Dict : Iface {
+ long prop;
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should not allow non-dictionary parents for dictionaries")
+
+ # Even more reset
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ dictionary A : B {};
+ dictionary B : A {};
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should not allow cycles in dictionary inheritance chains")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ dictionary A {
+ [LegacyNullToEmptyString] DOMString foo;
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw, "Should not allow [LegacyNullToEmptyString] on dictionary members"
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ dictionary A {
+ };
+ interface X {
+ undefined doFoo(A arg);
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Trailing dictionary arg must be optional")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ dictionary A {
+ };
+ interface X {
+ undefined doFoo(optional A arg);
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Trailing dictionary arg must have a default value")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ dictionary A {
+ };
+ interface X {
+ undefined doFoo((A or DOMString) arg);
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Trailing union arg containing a dictionary must be optional")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ dictionary A {
+ };
+ interface X {
+ undefined doFoo(optional (A or DOMString) arg);
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw, "Trailing union arg containing a dictionary must have a default value"
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ dictionary A {
+ };
+ interface X {
+ undefined doFoo(A arg1, optional long arg2);
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Dictionary arg followed by optional arg must be optional")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ dictionary A {
+ };
+ interface X {
+ undefined doFoo(optional A arg1, optional long arg2);
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Dictionary arg followed by optional arg must have default value")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ dictionary A {
+ };
+ interface X {
+ undefined doFoo(A arg1, optional long arg2, long arg3);
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ not threw,
+ "Dictionary arg followed by non-optional arg doesn't have to be optional",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ dictionary A {
+ };
+ interface X {
+ undefined doFoo((A or DOMString) arg1, optional long arg2);
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw,
+ "Union arg containing dictionary followed by optional arg must " "be optional",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ dictionary A {
+ };
+ interface X {
+ undefined doFoo(optional (A or DOMString) arg1, optional long arg2);
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw,
+ "Union arg containing dictionary followed by optional arg must "
+ "have a default value",
+ )
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ dictionary A {
+ };
+ interface X {
+ undefined doFoo(A arg1, long arg2);
+ };
+ """
+ )
+ parser.finish()
+ harness.ok(True, "Dictionary arg followed by required arg can be required")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ dictionary A {
+ };
+ interface X {
+ undefined doFoo(optional A? arg1 = {});
+ };
+ """
+ )
+ parser.finish()
+ except Exception as x:
+ threw = x
+
+ harness.ok(threw, "Optional dictionary arg must not be nullable")
+ harness.ok(
+ "nullable" in str(threw),
+ "Must have the expected exception for optional nullable dictionary arg",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ dictionary A {
+ required long x;
+ };
+ interface X {
+ undefined doFoo(A? arg1);
+ };
+ """
+ )
+ parser.finish()
+ except Exception as x:
+ threw = x
+
+ harness.ok(threw, "Required dictionary arg must not be nullable")
+ harness.ok(
+ "nullable" in str(threw),
+ "Must have the expected exception for required nullable " "dictionary arg",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ dictionary A {
+ };
+ interface X {
+ undefined doFoo(optional (A or long)? arg1 = {});
+ };
+ """
+ )
+ parser.finish()
+ except Exception as x:
+ threw = x
+
+ harness.ok(threw, "Dictionary arg must not be in an optional nullable union")
+ harness.ok(
+ "nullable" in str(threw),
+ "Must have the expected exception for optional nullable union "
+ "arg containing dictionary",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ dictionary A {
+ required long x;
+ };
+ interface X {
+ undefined doFoo((A or long)? arg1);
+ };
+ """
+ )
+ parser.finish()
+ except Exception as x:
+ threw = x
+
+ harness.ok(threw, "Dictionary arg must not be in a required nullable union")
+ harness.ok(
+ "nullable" in str(threw),
+ "Must have the expected exception for required nullable union "
+ "arg containing dictionary",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ dictionary A {
+ };
+ interface X {
+ undefined doFoo(sequence<A?> arg1);
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(not threw, "Nullable union should be allowed in a sequence argument")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ dictionary A {
+ };
+ interface X {
+ undefined doFoo(optional (A or long?) arg1);
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "Dictionary must not be in a union with a nullable type")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ dictionary A {
+ };
+ interface X {
+ undefined doFoo(optional (long? or A) arg1);
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "A nullable type must not be in a union with a dictionary")
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ dictionary A {
+ };
+ interface X {
+ A? doFoo();
+ };
+ """
+ )
+ parser.finish()
+ harness.ok(True, "Dictionary return value can be nullable")
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ dictionary A {
+ };
+ interface X {
+ undefined doFoo(optional A arg = {});
+ };
+ """
+ )
+ parser.finish()
+ harness.ok(True, "Dictionary arg should actually parse")
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ dictionary A {
+ };
+ interface X {
+ undefined doFoo(optional (A or DOMString) arg = {});
+ };
+ """
+ )
+ parser.finish()
+ harness.ok(True, "Union arg containing a dictionary should actually parse")
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ dictionary A {
+ };
+ interface X {
+ undefined doFoo(optional (A or DOMString) arg = "abc");
+ };
+ """
+ )
+ parser.finish()
+ harness.ok(
+ True,
+ "Union arg containing a dictionary with string default should actually parse",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ dictionary Foo {
+ Foo foo;
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Member type must not be its Dictionary.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ dictionary Foo3 : Foo {
+ short d;
+ };
+
+ dictionary Foo2 : Foo3 {
+ boolean c;
+ };
+
+ dictionary Foo1 : Foo2 {
+ long a;
+ };
+
+ dictionary Foo {
+ Foo1 b;
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw,
+ "Member type must not be a Dictionary that " "inherits from its Dictionary.",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ dictionary Foo {
+ (Foo or DOMString)[]? b;
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw,
+ "Member type must not be a Nullable type "
+ "whose inner type includes its Dictionary.",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ dictionary Foo {
+ (DOMString or Foo) b;
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw,
+ "Member type must not be a Union type, one of "
+ "whose member types includes its Dictionary.",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ dictionary Foo {
+ sequence<sequence<sequence<Foo>>> c;
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw,
+ "Member type must not be a Sequence type "
+ "whose element type includes its Dictionary.",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ dictionary Foo {
+ (DOMString or Foo)[] d;
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw,
+ "Member type must not be an Array type "
+ "whose element type includes its Dictionary.",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ dictionary Foo {
+ Foo1 b;
+ };
+
+ dictionary Foo3 {
+ Foo d;
+ };
+
+ dictionary Foo2 : Foo3 {
+ short c;
+ };
+
+ dictionary Foo1 : Foo2 {
+ long a;
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw,
+ "Member type must not be a Dictionary, one of whose "
+ "members or inherited members has a type that includes "
+ "its Dictionary.",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ dictionary Foo {
+ };
+
+ dictionary Bar {
+ Foo? d;
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Member type must not be a nullable dictionary")
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ dictionary Foo {
+ unrestricted float urFloat = 0;
+ unrestricted float urFloat2 = 1.1;
+ unrestricted float urFloat3 = -1.1;
+ unrestricted float? urFloat4 = null;
+ unrestricted float infUrFloat = Infinity;
+ unrestricted float negativeInfUrFloat = -Infinity;
+ unrestricted float nanUrFloat = NaN;
+
+ unrestricted double urDouble = 0;
+ unrestricted double urDouble2 = 1.1;
+ unrestricted double urDouble3 = -1.1;
+ unrestricted double? urDouble4 = null;
+ unrestricted double infUrDouble = Infinity;
+ unrestricted double negativeInfUrDouble = -Infinity;
+ unrestricted double nanUrDouble = NaN;
+ };
+ """
+ )
+ parser.finish()
+ harness.ok(True, "Parsing default values for unrestricted types succeeded.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ dictionary Foo {
+ double f = Infinity;
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Only unrestricted values can be initialized to Infinity")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ dictionary Foo {
+ double f = -Infinity;
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Only unrestricted values can be initialized to -Infinity")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ dictionary Foo {
+ double f = NaN;
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Only unrestricted values can be initialized to NaN")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ dictionary Foo {
+ float f = Infinity;
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Only unrestricted values can be initialized to Infinity")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ dictionary Foo {
+ float f = -Infinity;
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Only unrestricted values can be initialized to -Infinity")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ dictionary Foo {
+ float f = NaN;
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Only unrestricted values can be initialized to NaN")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ dictionary Foo {
+ long module;
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(not threw, "Should be able to use 'module' as a dictionary member name")
diff --git a/dom/bindings/parser/tests/test_distinguishability.py b/dom/bindings/parser/tests/test_distinguishability.py
new file mode 100644
index 0000000000..8c938d88a3
--- /dev/null
+++ b/dom/bindings/parser/tests/test_distinguishability.py
@@ -0,0 +1,421 @@
+def firstArgType(method):
+ return method.signatures()[0][1][0].type
+
+
+def WebIDLTest(parser, harness):
+ parser.parse(
+ """
+ // Give our dictionary a required member so we don't need to
+ // mess with optional and default values.
+ dictionary Dict {
+ required long member;
+ };
+ callback interface Foo {
+ };
+ interface Bar {
+ // Bit of a pain to get things that have dictionary types
+ undefined passDict(Dict arg);
+ undefined passFoo(Foo arg);
+ undefined passNullableUnion((object? or DOMString) arg);
+ undefined passNullable(Foo? arg);
+ };
+ """
+ )
+ results = parser.finish()
+
+ iface = results[2]
+ harness.ok(iface.isInterface(), "Should have interface")
+ dictMethod = iface.members[0]
+ ifaceMethod = iface.members[1]
+ nullableUnionMethod = iface.members[2]
+ nullableIfaceMethod = iface.members[3]
+
+ dictType = firstArgType(dictMethod)
+ ifaceType = firstArgType(ifaceMethod)
+
+ harness.ok(dictType.isDictionary(), "Should have dictionary type")
+ harness.ok(ifaceType.isInterface(), "Should have interface type")
+ harness.ok(ifaceType.isCallbackInterface(), "Should have callback interface type")
+
+ harness.ok(
+ not dictType.isDistinguishableFrom(ifaceType),
+ "Dictionary not distinguishable from callback interface",
+ )
+ harness.ok(
+ not ifaceType.isDistinguishableFrom(dictType),
+ "Callback interface not distinguishable from dictionary",
+ )
+
+ nullableUnionType = firstArgType(nullableUnionMethod)
+ nullableIfaceType = firstArgType(nullableIfaceMethod)
+
+ harness.ok(nullableUnionType.isUnion(), "Should have union type")
+ harness.ok(nullableIfaceType.isInterface(), "Should have interface type")
+ harness.ok(nullableIfaceType.nullable(), "Should have nullable type")
+
+ harness.ok(
+ not nullableUnionType.isDistinguishableFrom(nullableIfaceType),
+ "Nullable type not distinguishable from union with nullable " "member type",
+ )
+ harness.ok(
+ not nullableIfaceType.isDistinguishableFrom(nullableUnionType),
+ "Union with nullable member type not distinguishable from " "nullable type",
+ )
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ interface TestIface {
+ undefined passKid(Kid arg);
+ undefined passParent(Parent arg);
+ undefined passGrandparent(Grandparent arg);
+ undefined passUnrelated1(Unrelated1 arg);
+ undefined passUnrelated2(Unrelated2 arg);
+ undefined passArrayBuffer(ArrayBuffer arg);
+ undefined passArrayBuffer(ArrayBufferView arg);
+ };
+
+ interface Kid : Parent {};
+ interface Parent : Grandparent {};
+ interface Grandparent {};
+ interface Unrelated1 {};
+ interface Unrelated2 {};
+ """
+ )
+ results = parser.finish()
+
+ iface = results[0]
+ harness.ok(iface.isInterface(), "Should have interface")
+ argTypes = [firstArgType(method) for method in iface.members]
+ unrelatedTypes = [firstArgType(method) for method in iface.members[-3:]]
+
+ for type1 in argTypes:
+ for type2 in argTypes:
+ distinguishable = type1 is not type2 and (
+ type1 in unrelatedTypes or type2 in unrelatedTypes
+ )
+
+ harness.check(
+ type1.isDistinguishableFrom(type2),
+ distinguishable,
+ "Type %s should %sbe distinguishable from type %s"
+ % (type1, "" if distinguishable else "not ", type2),
+ )
+ harness.check(
+ type2.isDistinguishableFrom(type1),
+ distinguishable,
+ "Type %s should %sbe distinguishable from type %s"
+ % (type2, "" if distinguishable else "not ", type1),
+ )
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ interface Dummy {};
+ interface TestIface {
+ undefined method(long arg1, TestIface arg2);
+ undefined method(long arg1, long arg2);
+ undefined method(long arg1, Dummy arg2);
+ undefined method(DOMString arg1, DOMString arg2, DOMString arg3);
+ };
+ """
+ )
+ results = parser.finish()
+ harness.check(len(results[1].members), 1, "Should look like we have one method")
+ harness.check(
+ len(results[1].members[0].signatures()), 4, "Should have four signatures"
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Dummy {};
+ interface TestIface {
+ undefined method(long arg1, TestIface arg2);
+ undefined method(long arg1, long arg2);
+ undefined method(any arg1, Dummy arg2);
+ undefined method(DOMString arg1, DOMString arg2, DOMString arg3);
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw,
+ "Should throw when args before the distinguishing arg are not "
+ "all the same type",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Dummy {};
+ interface TestIface {
+ undefined method(long arg1, TestIface arg2);
+ undefined method(long arg1, long arg2);
+ undefined method(any arg1, DOMString arg2);
+ undefined method(DOMString arg1, DOMString arg2, DOMString arg3);
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should throw when there is no distinguishing index")
+
+ # Now let's test our whole distinguishability table
+ argTypes = [
+ "long",
+ "short",
+ "long?",
+ "short?",
+ "boolean",
+ "boolean?",
+ "undefined",
+ "undefined?",
+ "DOMString",
+ "ByteString",
+ "UTF8String",
+ "Enum",
+ "Enum2",
+ "Interface",
+ "Interface?",
+ "AncestorInterface",
+ "UnrelatedInterface",
+ "CallbackInterface",
+ "CallbackInterface?",
+ "CallbackInterface2",
+ "object",
+ "Callback",
+ "Callback2",
+ "Dict",
+ "Dict2",
+ "sequence<long>",
+ "sequence<short>",
+ "record<DOMString, object>",
+ "record<USVString, Dict>",
+ "record<ByteString, long>",
+ "record<UTF8String, long>",
+ "any",
+ "Promise<any>",
+ "Promise<any>?",
+ "USVString",
+ "JSString",
+ "ArrayBuffer",
+ "ArrayBufferView",
+ "Uint8Array",
+ "Uint16Array",
+ "(long or Callback)",
+ "(long or Dict)",
+ ]
+
+ # Try to categorize things a bit to keep list lengths down
+ def allBut(list1, list2):
+ return [
+ a
+ for a in list1
+ if a not in list2
+ and (a != "any" and a != "Promise<any>" and a != "Promise<any>?")
+ ]
+
+ unions = ["(long or Callback)", "(long or Dict)"]
+ numerics = ["long", "short", "long?", "short?"]
+ booleans = ["boolean", "boolean?"]
+ undefineds = ["undefined", "undefined?"]
+ primitives = numerics + booleans
+ nonNumerics = allBut(argTypes, numerics + unions)
+ nonBooleans = allBut(argTypes, booleans)
+ strings = [
+ "DOMString",
+ "ByteString",
+ "Enum",
+ "Enum2",
+ "USVString",
+ "JSString",
+ "UTF8String",
+ ]
+ nonStrings = allBut(argTypes, strings)
+ nonObjects = undefineds + primitives + strings
+ bufferSourceTypes = ["ArrayBuffer", "ArrayBufferView", "Uint8Array", "Uint16Array"]
+ interfaces = [
+ "Interface",
+ "Interface?",
+ "AncestorInterface",
+ "UnrelatedInterface",
+ ] + bufferSourceTypes
+ nullables = [
+ "long?",
+ "short?",
+ "boolean?",
+ "undefined?",
+ "Interface?",
+ "CallbackInterface?",
+ "Dict",
+ "Dict2",
+ "Date?",
+ "any",
+ "Promise<any>?",
+ ] + allBut(unions, ["(long or Callback)"])
+ sequences = ["sequence<long>", "sequence<short>"]
+ nonUserObjects = nonObjects + interfaces + sequences
+ otherObjects = allBut(argTypes, nonUserObjects + ["object"])
+ notRelatedInterfaces = (
+ nonObjects
+ + ["UnrelatedInterface"]
+ + otherObjects
+ + sequences
+ + bufferSourceTypes
+ )
+ records = [
+ "record<DOMString, object>",
+ "record<USVString, Dict>",
+ "record<ByteString, long>",
+ "record<UTF8String, long>",
+ ] # JSString not supported in records
+ dictionaryLike = (
+ [
+ "Dict",
+ "Dict2",
+ "CallbackInterface",
+ "CallbackInterface?",
+ "CallbackInterface2",
+ ]
+ + records
+ + allBut(unions, ["(long or Callback)"])
+ )
+
+ # Build a representation of the distinguishability table as a dict
+ # of dicts, holding True values where needed, holes elsewhere.
+ data = dict()
+ for type in argTypes:
+ data[type] = dict()
+
+ def setDistinguishable(type, types):
+ for other in types:
+ data[type][other] = True
+
+ setDistinguishable("long", nonNumerics)
+ setDistinguishable("short", nonNumerics)
+ setDistinguishable("long?", allBut(nonNumerics, nullables))
+ setDistinguishable("short?", allBut(nonNumerics, nullables))
+ setDistinguishable("boolean", nonBooleans)
+ setDistinguishable("boolean?", allBut(nonBooleans, nullables))
+ setDistinguishable("undefined", allBut(argTypes, undefineds + dictionaryLike))
+ setDistinguishable(
+ "undefined?", allBut(argTypes, undefineds + dictionaryLike + nullables)
+ )
+ setDistinguishable("DOMString", nonStrings)
+ setDistinguishable("ByteString", nonStrings)
+ setDistinguishable("UTF8String", nonStrings)
+ setDistinguishable("USVString", nonStrings)
+ setDistinguishable("JSString", nonStrings)
+ setDistinguishable("Enum", nonStrings)
+ setDistinguishable("Enum2", nonStrings)
+ setDistinguishable("Interface", notRelatedInterfaces)
+ setDistinguishable("Interface?", allBut(notRelatedInterfaces, nullables))
+ setDistinguishable("AncestorInterface", notRelatedInterfaces)
+ setDistinguishable(
+ "UnrelatedInterface", allBut(argTypes, ["object", "UnrelatedInterface"])
+ )
+ setDistinguishable("CallbackInterface", allBut(nonUserObjects, undefineds))
+ setDistinguishable(
+ "CallbackInterface?", allBut(nonUserObjects, nullables + undefineds)
+ )
+ setDistinguishable("CallbackInterface2", allBut(nonUserObjects, undefineds))
+ setDistinguishable("object", nonObjects)
+ setDistinguishable("Callback", nonUserObjects)
+ setDistinguishable("Callback2", nonUserObjects)
+ setDistinguishable("Dict", allBut(nonUserObjects, nullables + undefineds))
+ setDistinguishable("Dict2", allBut(nonUserObjects, nullables + undefineds))
+ setDistinguishable("sequence<long>", allBut(argTypes, sequences + ["object"]))
+ setDistinguishable("sequence<short>", allBut(argTypes, sequences + ["object"]))
+ setDistinguishable("record<DOMString, object>", allBut(nonUserObjects, undefineds))
+ setDistinguishable("record<USVString, Dict>", allBut(nonUserObjects, undefineds))
+ # JSString not supported in records
+ setDistinguishable("record<ByteString, long>", allBut(nonUserObjects, undefineds))
+ setDistinguishable("record<UTF8String, long>", allBut(nonUserObjects, undefineds))
+ setDistinguishable("any", [])
+ setDistinguishable("Promise<any>", [])
+ setDistinguishable("Promise<any>?", [])
+ setDistinguishable("ArrayBuffer", allBut(argTypes, ["ArrayBuffer", "object"]))
+ setDistinguishable(
+ "ArrayBufferView",
+ allBut(argTypes, ["ArrayBufferView", "Uint8Array", "Uint16Array", "object"]),
+ )
+ setDistinguishable(
+ "Uint8Array", allBut(argTypes, ["ArrayBufferView", "Uint8Array", "object"])
+ )
+ setDistinguishable(
+ "Uint16Array", allBut(argTypes, ["ArrayBufferView", "Uint16Array", "object"])
+ )
+ setDistinguishable("(long or Callback)", allBut(nonUserObjects, numerics))
+ setDistinguishable(
+ "(long or Dict)", allBut(nonUserObjects, numerics + nullables + undefineds)
+ )
+
+ def areDistinguishable(type1, type2):
+ return data[type1].get(type2, False)
+
+ def checkDistinguishability(parser, type1, type2):
+ idlTemplate = """
+ enum Enum { "a", "b" };
+ enum Enum2 { "c", "d" };
+ interface Interface : AncestorInterface {};
+ interface AncestorInterface {};
+ interface UnrelatedInterface {};
+ callback interface CallbackInterface {};
+ callback interface CallbackInterface2 {};
+ callback Callback = any();
+ callback Callback2 = long(short arg);
+ // Give our dictionaries required members so we don't need to
+ // mess with optional and default values.
+ dictionary Dict { required long member; };
+ dictionary Dict2 { required long member; };
+ interface TestInterface {%s
+ };
+ """
+ if type1 in undefineds or type2 in undefineds:
+ methods = """
+ (%s or %s) myMethod();""" % (
+ type1,
+ type2,
+ )
+ else:
+ methodTemplate = """
+ undefined myMethod(%s arg);"""
+ methods = (methodTemplate % type1) + (methodTemplate % type2)
+ idl = idlTemplate % methods
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(idl)
+ parser.finish()
+ except Exception:
+ threw = True
+
+ if areDistinguishable(type1, type2):
+ harness.ok(
+ not threw,
+ "Should not throw for '%s' and '%s' because they are distinguishable"
+ % (type1, type2),
+ )
+ else:
+ harness.ok(
+ threw,
+ "Should throw for '%s' and '%s' because they are not distinguishable"
+ % (type1, type2),
+ )
+
+ # Enumerate over everything in both orders, since order matters in
+ # terms of our implementation of distinguishability checks
+ for type1 in argTypes:
+ for type2 in argTypes:
+ checkDistinguishability(parser, type1, type2)
diff --git a/dom/bindings/parser/tests/test_double_null.py b/dom/bindings/parser/tests/test_double_null.py
new file mode 100644
index 0000000000..5160a53727
--- /dev/null
+++ b/dom/bindings/parser/tests/test_double_null.py
@@ -0,0 +1,16 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface DoubleNull {
+ attribute byte?? foo;
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/dom/bindings/parser/tests/test_duplicate_qualifiers.py b/dom/bindings/parser/tests/test_duplicate_qualifiers.py
new file mode 100644
index 0000000000..2b37cdc0fb
--- /dev/null
+++ b/dom/bindings/parser/tests/test_duplicate_qualifiers.py
@@ -0,0 +1,64 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface DuplicateQualifiers1 {
+ getter getter byte foo(unsigned long index);
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface DuplicateQualifiers2 {
+ setter setter byte foo(unsigned long index, byte value);
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface DuplicateQualifiers4 {
+ deleter deleter byte foo(unsigned long index);
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface DuplicateQualifiers5 {
+ getter deleter getter byte foo(unsigned long index);
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/dom/bindings/parser/tests/test_empty_enum.py b/dom/bindings/parser/tests/test_empty_enum.py
new file mode 100644
index 0000000000..cd6b985dc9
--- /dev/null
+++ b/dom/bindings/parser/tests/test_empty_enum.py
@@ -0,0 +1,14 @@
+def WebIDLTest(parser, harness):
+ try:
+ parser.parse(
+ """
+ enum TestEmptyEnum {
+ };
+ """
+ )
+
+ harness.ok(False, "Should have thrown!")
+ except Exception:
+ harness.ok(True, "Parsing TestEmptyEnum enum should fail")
+
+ parser.finish()
diff --git a/dom/bindings/parser/tests/test_empty_sequence_default_value.py b/dom/bindings/parser/tests/test_empty_sequence_default_value.py
new file mode 100644
index 0000000000..da0b3add82
--- /dev/null
+++ b/dom/bindings/parser/tests/test_empty_sequence_default_value.py
@@ -0,0 +1,54 @@
+import WebIDL
+
+
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface X {
+ const sequence<long> foo = [];
+ };
+ """
+ )
+
+ results = parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Constant cannot have [] as a default value")
+
+ parser = parser.reset()
+
+ parser.parse(
+ """
+ interface X {
+ undefined foo(optional sequence<long> arg = []);
+ };
+ """
+ )
+ results = parser.finish()
+
+ harness.ok(
+ isinstance(
+ results[0].members[0].signatures()[0][1][0].defaultValue,
+ WebIDL.IDLEmptySequenceValue,
+ ),
+ "Should have IDLEmptySequenceValue as default value of argument",
+ )
+
+ parser = parser.reset()
+
+ parser.parse(
+ """
+ dictionary X {
+ sequence<long> foo = [];
+ };
+ """
+ )
+ results = parser.finish()
+
+ harness.ok(
+ isinstance(results[0].members[0].defaultValue, WebIDL.IDLEmptySequenceValue),
+ "Should have IDLEmptySequenceValue as default value of " "dictionary member",
+ )
diff --git a/dom/bindings/parser/tests/test_enum.py b/dom/bindings/parser/tests/test_enum.py
new file mode 100644
index 0000000000..d7305a708f
--- /dev/null
+++ b/dom/bindings/parser/tests/test_enum.py
@@ -0,0 +1,107 @@
+import WebIDL
+
+
+def WebIDLTest(parser, harness):
+ parser.parse(
+ """
+ enum TestEnum {
+ "",
+ "foo",
+ "bar"
+ };
+
+ interface TestEnumInterface {
+ TestEnum doFoo(boolean arg);
+ readonly attribute TestEnum foo;
+ };
+ """
+ )
+
+ results = parser.finish()
+
+ harness.ok(True, "TestEnumInterfaces interface parsed without error.")
+ harness.check(len(results), 2, "Should be one production")
+ harness.ok(isinstance(results[0], WebIDL.IDLEnum), "Should be an IDLEnum")
+ harness.ok(isinstance(results[1], WebIDL.IDLInterface), "Should be an IDLInterface")
+
+ enum = results[0]
+ harness.check(enum.identifier.QName(), "::TestEnum", "Enum has the right QName")
+ harness.check(enum.identifier.name, "TestEnum", "Enum has the right name")
+ harness.check(enum.values(), ["", "foo", "bar"], "Enum has the right values")
+
+ iface = results[1]
+
+ harness.check(
+ iface.identifier.QName(), "::TestEnumInterface", "Interface has the right QName"
+ )
+ harness.check(
+ iface.identifier.name, "TestEnumInterface", "Interface has the right name"
+ )
+ harness.check(iface.parent, None, "Interface has no parent")
+
+ members = iface.members
+ harness.check(len(members), 2, "Should be one production")
+ harness.ok(isinstance(members[0], WebIDL.IDLMethod), "Should be an IDLMethod")
+ method = members[0]
+ harness.check(
+ method.identifier.QName(),
+ "::TestEnumInterface::doFoo",
+ "Method has correct QName",
+ )
+ harness.check(method.identifier.name, "doFoo", "Method has correct name")
+
+ signatures = method.signatures()
+ harness.check(len(signatures), 1, "Expect one signature")
+
+ (returnType, arguments) = signatures[0]
+ harness.check(
+ str(returnType), "TestEnum (Wrapper)", "Method type is the correct name"
+ )
+ harness.check(len(arguments), 1, "Method has the right number of arguments")
+ arg = arguments[0]
+ harness.ok(isinstance(arg, WebIDL.IDLArgument), "Should be an IDLArgument")
+ harness.check(str(arg.type), "Boolean", "Argument has the right type")
+
+ attr = members[1]
+ harness.check(
+ attr.identifier.QName(), "::TestEnumInterface::foo", "Attr has correct QName"
+ )
+ harness.check(attr.identifier.name, "foo", "Attr has correct name")
+
+ harness.check(str(attr.type), "TestEnum (Wrapper)", "Attr type is the correct name")
+
+ # Now reset our parser
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ enum Enum {
+ "a",
+ "b",
+ "c"
+ };
+ interface TestInterface {
+ undefined foo(optional Enum e = "d");
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should not allow a bogus default value for an enum")
+
+ # Now reset our parser
+ parser = parser.reset()
+ parser.parse(
+ """
+ enum Enum {
+ "a",
+ "b",
+ "c",
+ };
+ """
+ )
+ results = parser.finish()
+ harness.check(len(results), 1, "Should allow trailing comma in enum")
diff --git a/dom/bindings/parser/tests/test_enum_duplicate_values.py b/dom/bindings/parser/tests/test_enum_duplicate_values.py
new file mode 100644
index 0000000000..aec28c49e8
--- /dev/null
+++ b/dom/bindings/parser/tests/test_enum_duplicate_values.py
@@ -0,0 +1,13 @@
+def WebIDLTest(parser, harness):
+ try:
+ parser.parse(
+ """
+ enum TestEnumDuplicateValue {
+ "",
+ ""
+ };
+ """
+ )
+ harness.ok(False, "Should have thrown!")
+ except Exception:
+ harness.ok(True, "Enum TestEnumDuplicateValue should throw")
diff --git a/dom/bindings/parser/tests/test_error_colno.py b/dom/bindings/parser/tests/test_error_colno.py
new file mode 100644
index 0000000000..b08ba7cc17
--- /dev/null
+++ b/dom/bindings/parser/tests/test_error_colno.py
@@ -0,0 +1,24 @@
+import WebIDL
+
+
+def WebIDLTest(parser, harness):
+ # Check that error messages put the '^' in the right place.
+
+ threw = False
+ input = "interface ?"
+ try:
+ parser.parse(input)
+ parser.finish()
+ except WebIDL.WebIDLError as e:
+ threw = True
+ lines = str(e).split("\n")
+
+ harness.check(len(lines), 3, "Expected number of lines in error message")
+ harness.check(lines[1], input, "Second line shows error")
+ harness.check(
+ lines[2],
+ " " * (len(input) - 1) + "^",
+ "Correct column pointer in error message",
+ )
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/dom/bindings/parser/tests/test_error_lineno.py b/dom/bindings/parser/tests/test_error_lineno.py
new file mode 100644
index 0000000000..30629be30c
--- /dev/null
+++ b/dom/bindings/parser/tests/test_error_lineno.py
@@ -0,0 +1,38 @@
+import WebIDL
+
+
+def WebIDLTest(parser, harness):
+ # Check that error messages put the '^' in the right place.
+
+ threw = False
+ input = """\
+// This is a comment.
+interface Foo {
+};
+
+/* This is also a comment. */
+interface ?"""
+ try:
+ parser.parse(input)
+ parser.finish()
+ except WebIDL.WebIDLError as e:
+ threw = True
+ lines = str(e).split("\n")
+
+ harness.check(len(lines), 3, "Expected number of lines in error message")
+ harness.ok(
+ lines[0].endswith("line 6:10"),
+ 'First line of error should end with "line 6:10", but was "%s".' % lines[0],
+ )
+ harness.check(
+ lines[1],
+ "interface ?",
+ "Second line of error message is the line which caused the error.",
+ )
+ harness.check(
+ lines[2],
+ " " * (len("interface ?") - 1) + "^",
+ "Correct column pointer in error message.",
+ )
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/dom/bindings/parser/tests/test_exposed_extended_attribute.py b/dom/bindings/parser/tests/test_exposed_extended_attribute.py
new file mode 100644
index 0000000000..0bee74a0db
--- /dev/null
+++ b/dom/bindings/parser/tests/test_exposed_extended_attribute.py
@@ -0,0 +1,383 @@
+import WebIDL
+
+
+def WebIDLTest(parser, harness):
+ parser.parse(
+ """
+ [Global, Exposed=Foo] interface Foo {};
+ [Global=(Bar, Bar1,Bar2), Exposed=Bar] interface Bar {};
+ [Global=(Baz, Baz2), Exposed=Baz] interface Baz {};
+
+ [Exposed=(Foo,Bar1)]
+ interface Iface {
+ undefined method1();
+
+ [Exposed=Bar1]
+ readonly attribute any attr;
+ };
+
+ [Exposed=Foo]
+ partial interface Iface {
+ undefined method2();
+ };
+ """
+ )
+
+ results = parser.finish()
+
+ harness.check(len(results), 5, "Should know about five things")
+ iface = results[3]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface), "Should have an interface here")
+ members = iface.members
+ harness.check(len(members), 3, "Should have three members")
+
+ harness.ok(
+ members[0].exposureSet == set(["Foo", "Bar"]),
+ "method1 should have the right exposure set",
+ )
+ harness.ok(
+ members[0]._exposureGlobalNames == set(["Foo", "Bar1"]),
+ "method1 should have the right exposure global names",
+ )
+
+ harness.ok(
+ members[1].exposureSet == set(["Bar"]),
+ "attr should have the right exposure set",
+ )
+ harness.ok(
+ members[1]._exposureGlobalNames == set(["Bar1"]),
+ "attr should have the right exposure global names",
+ )
+
+ harness.ok(
+ members[2].exposureSet == set(["Foo"]),
+ "method2 should have the right exposure set",
+ )
+ harness.ok(
+ members[2]._exposureGlobalNames == set(["Foo"]),
+ "method2 should have the right exposure global names",
+ )
+
+ harness.ok(
+ iface.exposureSet == set(["Foo", "Bar"]),
+ "Iface should have the right exposure set",
+ )
+ harness.ok(
+ iface._exposureGlobalNames == set(["Foo", "Bar1"]),
+ "Iface should have the right exposure global names",
+ )
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ [Global, Exposed=Foo] interface Foo {};
+ [Global=(Bar, Bar1, Bar2), Exposed=Bar] interface Bar {};
+ [Global=(Baz, Baz2), Exposed=Baz] interface Baz {};
+
+ [Exposed=Foo]
+ interface Iface2 {
+ undefined method3();
+ };
+ """
+ )
+ results = parser.finish()
+
+ harness.check(len(results), 4, "Should know about four things")
+ iface = results[3]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface), "Should have an interface here")
+ members = iface.members
+ harness.check(len(members), 1, "Should have one member")
+
+ harness.ok(
+ members[0].exposureSet == set(["Foo"]),
+ "method3 should have the right exposure set",
+ )
+ harness.ok(
+ members[0]._exposureGlobalNames == set(["Foo"]),
+ "method3 should have the right exposure global names",
+ )
+
+ harness.ok(
+ iface.exposureSet == set(["Foo"]), "Iface2 should have the right exposure set"
+ )
+ harness.ok(
+ iface._exposureGlobalNames == set(["Foo"]),
+ "Iface2 should have the right exposure global names",
+ )
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ [Global, Exposed=Foo] interface Foo {};
+ [Global=(Bar, Bar1, Bar2), Exposed=Bar] interface Bar {};
+ [Global=(Baz, Baz2), Exposed=Baz] interface Baz {};
+
+ [Exposed=Foo]
+ interface Iface3 {
+ undefined method4();
+ };
+
+ [Exposed=(Foo,Bar1)]
+ interface mixin Mixin {
+ undefined method5();
+ };
+
+ Iface3 includes Mixin;
+ """
+ )
+ results = parser.finish()
+ harness.check(len(results), 6, "Should know about six things")
+ iface = results[3]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface), "Should have an interface here")
+ members = iface.members
+ harness.check(len(members), 2, "Should have two members")
+
+ harness.ok(
+ members[0].exposureSet == set(["Foo"]),
+ "method4 should have the right exposure set",
+ )
+ harness.ok(
+ members[0]._exposureGlobalNames == set(["Foo"]),
+ "method4 should have the right exposure global names",
+ )
+
+ harness.ok(
+ members[1].exposureSet == set(["Foo", "Bar"]),
+ "method5 should have the right exposure set",
+ )
+ harness.ok(
+ members[1]._exposureGlobalNames == set(["Foo", "Bar1"]),
+ "method5 should have the right exposure global names",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ [Exposed=Foo]
+ interface Bar {
+ };
+ """
+ )
+
+ results = parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown on invalid Exposed value on interface.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Bar {
+ [Exposed=Foo]
+ readonly attribute bool attr;
+ };
+ """
+ )
+
+ results = parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown on invalid Exposed value on attribute.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Bar {
+ [Exposed=Foo]
+ undefined operation();
+ };
+ """
+ )
+
+ results = parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown on invalid Exposed value on operation.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Bar {
+ [Exposed=Foo]
+ const long constant = 5;
+ };
+ """
+ )
+
+ results = parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown on invalid Exposed value on constant.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ [Global, Exposed=Foo] interface Foo {};
+ [Global, Exposed=Bar] interface Bar {};
+
+ [Exposed=Foo]
+ interface Baz {
+ [Exposed=Bar]
+ undefined method();
+ };
+ """
+ )
+
+ results = parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw, "Should have thrown on member exposed where its interface is not."
+ )
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ [Global, Exposed=Foo] interface Foo {};
+ [Global, Exposed=Bar] interface Bar {};
+
+ [Exposed=Foo]
+ interface Baz {
+ undefined method();
+ };
+
+ [Exposed=Bar]
+ interface mixin Mixin {
+ undefined otherMethod();
+ };
+
+ Baz includes Mixin;
+ """
+ )
+
+ results = parser.finish()
+
+ harness.check(len(results), 5, "Should know about five things")
+ iface = results[2]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface), "Should have an interface here")
+ members = iface.members
+ harness.check(len(members), 2, "Should have two members")
+
+ harness.ok(
+ members[0].exposureSet == set(["Foo"]),
+ "method should have the right exposure set",
+ )
+ harness.ok(
+ members[0]._exposureGlobalNames == set(["Foo"]),
+ "method should have the right exposure global names",
+ )
+
+ harness.ok(
+ members[1].exposureSet == set(["Bar"]),
+ "otherMethod should have the right exposure set",
+ )
+ harness.ok(
+ members[1]._exposureGlobalNames == set(["Bar"]),
+ "otherMethod should have the right exposure global names",
+ )
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ [Global, Exposed=Foo] interface Foo {};
+ [Global, Exposed=Bar] interface Bar {};
+
+ [Exposed=*]
+ interface Baz {
+ undefined methodWild();
+ };
+
+ [Exposed=Bar]
+ interface mixin Mixin {
+ undefined methodNotWild();
+ };
+
+ Baz includes Mixin;
+ """
+ )
+
+ results = parser.finish()
+
+ harness.check(len(results), 5, "Should know about five things")
+ iface = results[2]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface), "Should have an interface here")
+ members = iface.members
+ harness.check(len(members), 2, "Should have two members")
+
+ harness.ok(
+ members[0].exposureSet == set(["Foo", "Bar"]),
+ "methodWild should have the right exposure set",
+ )
+ harness.ok(
+ members[0]._exposureGlobalNames == set(["Foo", "Bar"]),
+ "methodWild should have the right exposure global names",
+ )
+
+ harness.ok(
+ members[1].exposureSet == set(["Bar"]),
+ "methodNotWild should have the right exposure set",
+ )
+ harness.ok(
+ members[1]._exposureGlobalNames == set(["Bar"]),
+ "methodNotWild should have the right exposure global names",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ [Global, Exposed=Foo] interface Foo {};
+ [Global, Exposed=Bar] interface Bar {};
+
+ [Exposed=Foo]
+ interface Baz {
+ [Exposed=*]
+ undefined method();
+ };
+ """
+ )
+
+ results = parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw, "Should have thrown on member exposed where its interface is not."
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ [Global, Exposed=Foo] interface Foo {};
+ [Global, Exposed=Bar] interface Bar {};
+
+ [Exposed=(Foo,*)]
+ interface Baz {
+ undefined method();
+ };
+ """
+ )
+
+ results = parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown on a wildcard in an identifier list.")
diff --git a/dom/bindings/parser/tests/test_extended_attributes.py b/dom/bindings/parser/tests/test_extended_attributes.py
new file mode 100644
index 0000000000..b39ebd1029
--- /dev/null
+++ b/dom/bindings/parser/tests/test_extended_attributes.py
@@ -0,0 +1,128 @@
+def WebIDLTest(parser, harness):
+ parser.parse(
+ """
+ [LegacyNoInterfaceObject]
+ interface TestExtendedAttr {
+ [LegacyUnforgeable] readonly attribute byte b;
+ };
+ """
+ )
+
+ parser.finish()
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ [Pref="foo.bar",Pref=flop]
+ interface TestExtendedAttr {
+ [Pref="foo.bar"] attribute byte b;
+ };
+ """
+ )
+
+ parser.finish()
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ interface TestLegacyLenientThis {
+ [LegacyLenientThis] attribute byte b;
+ };
+ """
+ )
+
+ results = parser.finish()
+ harness.ok(
+ results[0].members[0].hasLegacyLenientThis(), "Should have a lenient this"
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface TestLegacyLenientThis2 {
+ [LegacyLenientThis=something] attribute byte b;
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "[LegacyLenientThis] must take no arguments")
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ interface TestClamp {
+ undefined testClamp([Clamp] long foo);
+ undefined testNotClamp(long foo);
+ };
+ """
+ )
+
+ results = parser.finish()
+ # Pull out the first argument out of the arglist of the first (and
+ # only) signature.
+ harness.ok(
+ results[0].members[0].signatures()[0][1][0].type.hasClamp(), "Should be clamped"
+ )
+ harness.ok(
+ not results[0].members[1].signatures()[0][1][0].type.hasClamp(),
+ "Should not be clamped",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface TestClamp2 {
+ undefined testClamp([Clamp=something] long foo);
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "[Clamp] must take no arguments")
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ interface TestEnforceRange {
+ undefined testEnforceRange([EnforceRange] long foo);
+ undefined testNotEnforceRange(long foo);
+ };
+ """
+ )
+
+ results = parser.finish()
+ # Pull out the first argument out of the arglist of the first (and
+ # only) signature.
+ harness.ok(
+ results[0].members[0].signatures()[0][1][0].type.hasEnforceRange(),
+ "Should be enforceRange",
+ )
+ harness.ok(
+ not results[0].members[1].signatures()[0][1][0].type.hasEnforceRange(),
+ "Should not be enforceRange",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface TestEnforceRange2 {
+ undefined testEnforceRange([EnforceRange=something] long foo);
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "[EnforceRange] must take no arguments")
diff --git a/dom/bindings/parser/tests/test_float_types.py b/dom/bindings/parser/tests/test_float_types.py
new file mode 100644
index 0000000000..5cb9404699
--- /dev/null
+++ b/dom/bindings/parser/tests/test_float_types.py
@@ -0,0 +1,145 @@
+import WebIDL
+
+
+def WebIDLTest(parser, harness):
+ parser.parse(
+ """
+ typedef float myFloat;
+ typedef unrestricted float myUnrestrictedFloat;
+ interface FloatTypes {
+ attribute float f;
+ attribute unrestricted float uf;
+ attribute double d;
+ attribute unrestricted double ud;
+ [LenientFloat]
+ attribute float lf;
+ [LenientFloat]
+ attribute double ld;
+
+ undefined m1(float arg1, double arg2, float? arg3, double? arg4,
+ myFloat arg5, unrestricted float arg6,
+ unrestricted double arg7, unrestricted float? arg8,
+ unrestricted double? arg9, myUnrestrictedFloat arg10);
+ [LenientFloat]
+ undefined m2(float arg1, double arg2, float? arg3, double? arg4,
+ myFloat arg5, unrestricted float arg6,
+ unrestricted double arg7, unrestricted float? arg8,
+ unrestricted double? arg9, myUnrestrictedFloat arg10);
+ [LenientFloat]
+ undefined m3(float arg);
+ [LenientFloat]
+ undefined m4(double arg);
+ [LenientFloat]
+ undefined m5((float or FloatTypes) arg);
+ [LenientFloat]
+ undefined m6(sequence<float> arg);
+ };
+ """
+ )
+
+ results = parser.finish()
+
+ harness.check(len(results), 3, "Should be two typedefs and one interface.")
+ iface = results[2]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface), "Should be an IDLInterface")
+ types = [a.type for a in iface.members if a.isAttr()]
+ harness.ok(types[0].isFloat(), "'float' is a float")
+ harness.ok(not types[0].isUnrestricted(), "'float' is not unrestricted")
+ harness.ok(types[1].isFloat(), "'unrestricted float' is a float")
+ harness.ok(types[1].isUnrestricted(), "'unrestricted float' is unrestricted")
+ harness.ok(types[2].isFloat(), "'double' is a float")
+ harness.ok(not types[2].isUnrestricted(), "'double' is not unrestricted")
+ harness.ok(types[3].isFloat(), "'unrestricted double' is a float")
+ harness.ok(types[3].isUnrestricted(), "'unrestricted double' is unrestricted")
+
+ method = iface.members[6]
+ harness.ok(isinstance(method, WebIDL.IDLMethod), "Should be an IDLMethod")
+ argtypes = [a.type for a in method.signatures()[0][1]]
+ for (idx, type) in enumerate(argtypes):
+ harness.ok(type.isFloat(), "Type %d should be float" % idx)
+ harness.check(
+ type.isUnrestricted(),
+ idx >= 5,
+ "Type %d should %sbe unrestricted" % (idx, "" if idx >= 4 else "not "),
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface FloatTypes {
+ [LenientFloat]
+ long m(float arg);
+ };
+ """
+ )
+ except Exception:
+ threw = True
+ harness.ok(threw, "[LenientFloat] only allowed on methods returning undefined")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface FloatTypes {
+ [LenientFloat]
+ undefined m(unrestricted float arg);
+ };
+ """
+ )
+ except Exception:
+ threw = True
+ harness.ok(
+ threw, "[LenientFloat] only allowed on methods with unrestricted float args"
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface FloatTypes {
+ [LenientFloat]
+ undefined m(sequence<unrestricted float> arg);
+ };
+ """
+ )
+ except Exception:
+ threw = True
+ harness.ok(
+ threw, "[LenientFloat] only allowed on methods with unrestricted float args (2)"
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface FloatTypes {
+ [LenientFloat]
+ undefined m((unrestricted float or FloatTypes) arg);
+ };
+ """
+ )
+ except Exception:
+ threw = True
+ harness.ok(
+ threw, "[LenientFloat] only allowed on methods with unrestricted float args (3)"
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface FloatTypes {
+ [LenientFloat]
+ readonly attribute float foo;
+ };
+ """
+ )
+ except Exception:
+ threw = True
+ harness.ok(threw, "[LenientFloat] only allowed on writable attributes")
diff --git a/dom/bindings/parser/tests/test_forward_decl.py b/dom/bindings/parser/tests/test_forward_decl.py
new file mode 100644
index 0000000000..1ba19e42fc
--- /dev/null
+++ b/dom/bindings/parser/tests/test_forward_decl.py
@@ -0,0 +1,15 @@
+def WebIDLTest(parser, harness):
+ parser.parse(
+ """
+ interface ForwardDeclared;
+ interface ForwardDeclared;
+
+ interface TestForwardDecl {
+ attribute ForwardDeclared foo;
+ };
+ """
+ )
+
+ parser.finish()
+
+ harness.ok(True, "TestForwardDeclared interface parsed without error.")
diff --git a/dom/bindings/parser/tests/test_global_extended_attr.py b/dom/bindings/parser/tests/test_global_extended_attr.py
new file mode 100644
index 0000000000..2de4aa68bd
--- /dev/null
+++ b/dom/bindings/parser/tests/test_global_extended_attr.py
@@ -0,0 +1,129 @@
+def WebIDLTest(parser, harness):
+ parser.parse(
+ """
+ [Global, Exposed=Foo]
+ interface Foo : Bar {
+ getter any(DOMString name);
+ };
+ [Exposed=Foo]
+ interface Bar {};
+ """
+ )
+
+ results = parser.finish()
+
+ harness.ok(
+ results[0].isOnGlobalProtoChain(),
+ "[Global] interface should be on global's proto chain",
+ )
+ harness.ok(
+ results[1].isOnGlobalProtoChain(),
+ "[Global] interface should be on global's proto chain",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ [Global, Exposed=Foo]
+ interface Foo {
+ getter any(DOMString name);
+ setter undefined(DOMString name, any arg);
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw,
+ "Should have thrown for [Global] used on an interface with a " "named setter",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ [Global, Exposed=Foo]
+ interface Foo {
+ getter any(DOMString name);
+ deleter undefined(DOMString name);
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw,
+ "Should have thrown for [Global] used on an interface with a " "named deleter",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ [Global, LegacyOverrideBuiltIns, Exposed=Foo]
+ interface Foo {
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw,
+ "Should have thrown for [Global] used on an interface with a "
+ "[LegacyOverrideBuiltIns]",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ [Global, Exposed=Foo]
+ interface Foo : Bar {
+ };
+ [LegacyOverrideBuiltIns, Exposed=Foo]
+ interface Bar {
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw,
+ "Should have thrown for [Global] used on an interface with an "
+ "[LegacyOverrideBuiltIns] ancestor",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ [Global, Exposed=Foo]
+ interface Foo {
+ };
+ [Exposed=Foo]
+ interface Bar : Foo {
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw,
+ "Should have thrown for [Global] used on an interface with a " "descendant",
+ )
diff --git a/dom/bindings/parser/tests/test_identifier_conflict.py b/dom/bindings/parser/tests/test_identifier_conflict.py
new file mode 100644
index 0000000000..424f4d6285
--- /dev/null
+++ b/dom/bindings/parser/tests/test_identifier_conflict.py
@@ -0,0 +1,45 @@
+def WebIDLTest(parser, harness):
+ try:
+ parser.parse(
+ """
+ enum Foo { "a" };
+ interface Foo;
+ """
+ )
+ parser.finish()
+ harness.ok(False, "Should fail to parse")
+ except Exception as e:
+ harness.ok(
+ "Name collision" in str(e), "Should have name collision for interface"
+ )
+
+ parser = parser.reset()
+ try:
+ parser.parse(
+ """
+ dictionary Foo { long x; };
+ enum Foo { "a" };
+ """
+ )
+ parser.finish()
+ harness.ok(False, "Should fail to parse")
+ except Exception as e:
+ harness.ok(
+ "Name collision" in str(e), "Should have name collision for dictionary"
+ )
+
+ parser = parser.reset()
+ try:
+ parser.parse(
+ """
+ enum Foo { "a" };
+ enum Foo { "b" };
+ """
+ )
+ parser.finish()
+ harness.ok(False, "Should fail to parse")
+ except Exception as e:
+ harness.ok(
+ "Multiple unresolvable definitions" in str(e),
+ "Should have name collision for dictionary",
+ )
diff --git a/dom/bindings/parser/tests/test_incomplete_parent.py b/dom/bindings/parser/tests/test_incomplete_parent.py
new file mode 100644
index 0000000000..80662a7848
--- /dev/null
+++ b/dom/bindings/parser/tests/test_incomplete_parent.py
@@ -0,0 +1,18 @@
+def WebIDLTest(parser, harness):
+ parser.parse(
+ """
+ interface TestIncompleteParent : NotYetDefined {
+ undefined foo();
+ };
+
+ interface NotYetDefined : EvenHigherOnTheChain {
+ };
+
+ interface EvenHigherOnTheChain {
+ };
+ """
+ )
+
+ parser.finish()
+
+ harness.ok(True, "TestIncompleteParent interface parsed without error.")
diff --git a/dom/bindings/parser/tests/test_incomplete_types.py b/dom/bindings/parser/tests/test_incomplete_types.py
new file mode 100644
index 0000000000..0d54f708bb
--- /dev/null
+++ b/dom/bindings/parser/tests/test_incomplete_types.py
@@ -0,0 +1,61 @@
+import WebIDL
+
+
+def WebIDLTest(parser, harness):
+ parser.parse(
+ """
+ interface TestIncompleteTypes {
+ attribute FooInterface attr1;
+
+ FooInterface method1(FooInterface arg);
+ };
+
+ interface FooInterface {
+ };
+ """
+ )
+
+ results = parser.finish()
+
+ harness.ok(True, "TestIncompleteTypes interface parsed without error.")
+ harness.check(len(results), 2, "Should be two productions.")
+ iface = results[0]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface), "Should be an IDLInterface")
+ harness.check(
+ iface.identifier.QName(),
+ "::TestIncompleteTypes",
+ "Interface has the right QName",
+ )
+ harness.check(
+ iface.identifier.name, "TestIncompleteTypes", "Interface has the right name"
+ )
+ harness.check(len(iface.members), 2, "Expect 2 members")
+
+ attr = iface.members[0]
+ harness.ok(isinstance(attr, WebIDL.IDLAttribute), "Should be an IDLAttribute")
+ method = iface.members[1]
+ harness.ok(isinstance(method, WebIDL.IDLMethod), "Should be an IDLMethod")
+
+ harness.check(
+ attr.identifier.QName(),
+ "::TestIncompleteTypes::attr1",
+ "Attribute has the right QName",
+ )
+ harness.check(
+ attr.type.name, "FooInterface", "Previously unresolved type has the right name"
+ )
+
+ harness.check(
+ method.identifier.QName(),
+ "::TestIncompleteTypes::method1",
+ "Attribute has the right QName",
+ )
+ (returnType, args) = method.signatures()[0]
+ harness.check(
+ returnType.name, "FooInterface", "Previously unresolved type has the right name"
+ )
+ harness.check(
+ args[0].type.name,
+ "FooInterface",
+ "Previously unresolved type has the right name",
+ )
diff --git a/dom/bindings/parser/tests/test_interface.py b/dom/bindings/parser/tests/test_interface.py
new file mode 100644
index 0000000000..c078a593bb
--- /dev/null
+++ b/dom/bindings/parser/tests/test_interface.py
@@ -0,0 +1,459 @@
+import WebIDL
+
+
+def WebIDLTest(parser, harness):
+ parser.parse("interface Foo { };")
+ results = parser.finish()
+ harness.ok(True, "Empty interface parsed without error.")
+ harness.check(len(results), 1, "Should be one production")
+ harness.ok(isinstance(results[0], WebIDL.IDLInterface), "Should be an IDLInterface")
+ iface = results[0]
+ harness.check(iface.identifier.QName(), "::Foo", "Interface has the right QName")
+ harness.check(iface.identifier.name, "Foo", "Interface has the right name")
+ harness.check(iface.parent, None, "Interface has no parent")
+
+ parser.parse("interface Bar : Foo { };")
+ results = parser.finish()
+ harness.ok(True, "Empty interface parsed without error.")
+ harness.check(len(results), 2, "Should be two productions")
+ harness.ok(isinstance(results[1], WebIDL.IDLInterface), "Should be an IDLInterface")
+ iface = results[1]
+ harness.check(iface.identifier.QName(), "::Bar", "Interface has the right QName")
+ harness.check(iface.identifier.name, "Bar", "Interface has the right name")
+ harness.ok(isinstance(iface.parent, WebIDL.IDLInterface), "Interface has a parent")
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ interface QNameBase {
+ attribute long foo;
+ };
+
+ interface QNameDerived : QNameBase {
+ attribute long long foo;
+ attribute byte bar;
+ };
+ """
+ )
+ results = parser.finish()
+ harness.check(len(results), 2, "Should be two productions")
+ harness.ok(isinstance(results[0], WebIDL.IDLInterface), "Should be an IDLInterface")
+ harness.ok(isinstance(results[1], WebIDL.IDLInterface), "Should be an IDLInterface")
+ harness.check(results[1].parent, results[0], "Inheritance chain is right")
+ harness.check(len(results[0].members), 1, "Expect 1 productions")
+ harness.check(len(results[1].members), 2, "Expect 2 productions")
+ base = results[0]
+ derived = results[1]
+ harness.check(
+ base.members[0].identifier.QName(),
+ "::QNameBase::foo",
+ "Member has the right QName",
+ )
+ harness.check(
+ derived.members[0].identifier.QName(),
+ "::QNameDerived::foo",
+ "Member has the right QName",
+ )
+ harness.check(
+ derived.members[1].identifier.QName(),
+ "::QNameDerived::bar",
+ "Member has the right QName",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface A : B {};
+ interface B : A {};
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should not allow cycles in interface inheritance chains")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface A : C {};
+ interface C : B {};
+ interface B : A {};
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw, "Should not allow indirect cycles in interface inheritance chains"
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface A;
+ interface B : A {};
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw,
+ "Should not allow inheriting from an interface that is only forward declared",
+ )
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ interface A {
+ constructor();
+ constructor(long arg);
+ readonly attribute boolean x;
+ undefined foo();
+ };
+ partial interface A {
+ readonly attribute boolean y;
+ undefined foo(long arg);
+ };
+ """
+ )
+ results = parser.finish()
+ harness.check(len(results), 2, "Should have two results with partial interface")
+ iface = results[0]
+ harness.check(
+ len(iface.members), 3, "Should have three members with partial interface"
+ )
+ harness.check(
+ iface.members[0].identifier.name,
+ "x",
+ "First member should be x with partial interface",
+ )
+ harness.check(
+ iface.members[1].identifier.name,
+ "foo",
+ "Second member should be foo with partial interface",
+ )
+ harness.check(
+ len(iface.members[1].signatures()),
+ 2,
+ "Should have two foo signatures with partial interface",
+ )
+ harness.check(
+ iface.members[2].identifier.name,
+ "y",
+ "Third member should be y with partial interface",
+ )
+ harness.check(
+ len(iface.ctor().signatures()),
+ 2,
+ "Should have two constructors with partial interface",
+ )
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ partial interface A {
+ readonly attribute boolean y;
+ undefined foo(long arg);
+ };
+ interface A {
+ constructor();
+ constructor(long arg);
+ readonly attribute boolean x;
+ undefined foo();
+ };
+ """
+ )
+ results = parser.finish()
+ harness.check(
+ len(results), 2, "Should have two results with reversed partial interface"
+ )
+ iface = results[1]
+ harness.check(
+ len(iface.members),
+ 3,
+ "Should have three members with reversed partial interface",
+ )
+ harness.check(
+ iface.members[0].identifier.name,
+ "x",
+ "First member should be x with reversed partial interface",
+ )
+ harness.check(
+ iface.members[1].identifier.name,
+ "foo",
+ "Second member should be foo with reversed partial interface",
+ )
+ harness.check(
+ len(iface.members[1].signatures()),
+ 2,
+ "Should have two foo signatures with reversed partial interface",
+ )
+ harness.check(
+ iface.members[2].identifier.name,
+ "y",
+ "Third member should be y with reversed partial interface",
+ )
+ harness.check(
+ len(iface.ctor().signatures()),
+ 2,
+ "Should have two constructors with reversed partial interface",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface A {
+ readonly attribute boolean x;
+ };
+ interface A {
+ readonly attribute boolean y;
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "Should not allow two non-partial interfaces with the same name")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ partial interface A {
+ readonly attribute boolean x;
+ };
+ partial interface A {
+ readonly attribute boolean y;
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "Must have a non-partial interface for a given name")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ dictionary A {
+ boolean x;
+ };
+ partial interface A {
+ readonly attribute boolean y;
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(
+ threw,
+ "Should not allow a name collision between partial interface "
+ "and other object",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ dictionary A {
+ boolean x;
+ };
+ interface A {
+ readonly attribute boolean y;
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(
+ threw, "Should not allow a name collision between interface " "and other object"
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ dictionary A {
+ boolean x;
+ };
+ interface A;
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(
+ threw,
+ "Should not allow a name collision between external interface "
+ "and other object",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface A {
+ readonly attribute boolean x;
+ };
+ interface A;
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(
+ threw,
+ "Should not allow a name collision between external interface " "and interface",
+ )
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ interface A;
+ interface A;
+ """
+ )
+ results = parser.finish()
+ harness.ok(
+ len(results) == 1 and isinstance(results[0], WebIDL.IDLExternalInterface),
+ "Should allow name collisions between external interface " "declarations",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ [SomeRandomAnnotation]
+ interface A {
+ readonly attribute boolean y;
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "Should not allow unknown extended attributes on interfaces")
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ [Global, Exposed=Window] interface Window {};
+ [Exposed=Window, LegacyWindowAlias=A]
+ interface B {};
+ [Exposed=Window, LegacyWindowAlias=(C, D)]
+ interface E {};
+ """
+ )
+ results = parser.finish()
+ harness.check(
+ results[1].legacyWindowAliases, ["A"], "Should support a single identifier"
+ )
+ harness.check(
+ results[2].legacyWindowAliases, ["C", "D"], "Should support an identifier list"
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ [LegacyWindowAlias]
+ interface A {};
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "Should not allow [LegacyWindowAlias] with no value")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ [Exposed=Worker, LegacyWindowAlias=B]
+ interface A {};
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "Should not allow [LegacyWindowAlias] without Window exposure")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ [Global, Exposed=Window] interface Window {};
+ [Exposed=Window]
+ interface A {};
+ [Exposed=Window, LegacyWindowAlias=A]
+ interface B {};
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(
+ threw, "Should not allow [LegacyWindowAlias] to conflict with other identifiers"
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ [Global, Exposed=Window] interface Window {};
+ [Exposed=Window, LegacyWindowAlias=A]
+ interface B {};
+ [Exposed=Window]
+ interface A {};
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(
+ threw, "Should not allow [LegacyWindowAlias] to conflict with other identifiers"
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ [Global, Exposed=Window] interface Window {};
+ [Exposed=Window, LegacyWindowAlias=A]
+ interface B {};
+ [Exposed=Window, LegacyWindowAlias=A]
+ interface C {};
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(
+ threw, "Should not allow [LegacyWindowAlias] to conflict with other identifiers"
+ )
diff --git a/dom/bindings/parser/tests/test_interface_const_identifier_conflicts.py b/dom/bindings/parser/tests/test_interface_const_identifier_conflicts.py
new file mode 100644
index 0000000000..6388af2139
--- /dev/null
+++ b/dom/bindings/parser/tests/test_interface_const_identifier_conflicts.py
@@ -0,0 +1,17 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface IdentifierConflict {
+ const byte thing1 = 1;
+ const unsigned long thing1 = 1;
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/dom/bindings/parser/tests/test_interface_identifier_conflicts_across_members.py b/dom/bindings/parser/tests/test_interface_identifier_conflicts_across_members.py
new file mode 100644
index 0000000000..d8398d46ba
--- /dev/null
+++ b/dom/bindings/parser/tests/test_interface_identifier_conflicts_across_members.py
@@ -0,0 +1,168 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface IdentifierConflictAcrossMembers1 {
+ const byte thing1 = 1;
+ readonly attribute long thing1;
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown for IdentifierConflictAcrossMembers1.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface IdentifierConflictAcrossMembers2 {
+ readonly attribute long thing1;
+ const byte thing1 = 1;
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown for IdentifierConflictAcrossMembers2.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface IdentifierConflictAcrossMembers3 {
+ getter boolean thing1(DOMString name);
+ readonly attribute long thing1;
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown for IdentifierConflictAcrossMembers3.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface IdentifierConflictAcrossMembers4 {
+ const byte thing1 = 1;
+ long thing1();
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown for IdentifierConflictAcrossMembers4.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface IdentifierConflictAcrossMembers5 {
+ static long thing1();
+ undefined thing1();
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ not threw, "Should not have thrown for IdentifierConflictAcrossMembers5."
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface mixin IdentifierConflictAcrossMembers6Mixin {
+ undefined thing1();
+ };
+ interface IdentifierConflictAcrossMembers6 {
+ static long thing1();
+ };
+ IdentifierConflictAcrossMembers6 includes IdentifierConflictAcrossMembers6Mixin;
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ not threw, "Should not have thrown for IdentifierConflictAcrossMembers6."
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface IdentifierConflictAcrossMembers7 {
+ const byte thing1 = 1;
+ static readonly attribute long thing1;
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown for IdentifierConflictAcrossMembers7.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface IdentifierConflictAcrossMembers8 {
+ readonly attribute long thing1 = 1;
+ static readonly attribute long thing1;
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown for IdentifierConflictAcrossMembers8.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface IdentifierConflictAcrossMembers9 {
+ void thing1();
+ static readonly attribute long thing1;
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown for IdentifierConflictAcrossMembers9.")
diff --git a/dom/bindings/parser/tests/test_interface_maplikesetlikeiterable.py b/dom/bindings/parser/tests/test_interface_maplikesetlikeiterable.py
new file mode 100644
index 0000000000..fdd9c00965
--- /dev/null
+++ b/dom/bindings/parser/tests/test_interface_maplikesetlikeiterable.py
@@ -0,0 +1,935 @@
+import WebIDL
+
+
+def WebIDLTest(parser, harness):
+ def shouldPass(prefix, iface, expectedMembers, numProductions=1):
+ p = parser.reset()
+ p.parse(iface)
+ results = p.finish()
+ harness.check(
+ len(results),
+ numProductions,
+ "%s - Should have production count %d" % (prefix, numProductions),
+ )
+ harness.ok(
+ isinstance(results[0], WebIDL.IDLInterface),
+ "%s - Should be an IDLInterface" % (prefix),
+ )
+ # Make a copy, since we plan to modify it
+ expectedMembers = list(expectedMembers)
+ for m in results[0].members:
+ name = m.identifier.name
+ if m.isMethod() and m.isStatic():
+ # None of the expected members are static methods, so ignore those.
+ harness.ok(True, "%s - %s - Should be a %s" % (prefix, name, type(m)))
+ elif (name, type(m)) in expectedMembers:
+ harness.ok(True, "%s - %s - Should be a %s" % (prefix, name, type(m)))
+ expectedMembers.remove((name, type(m)))
+ else:
+ harness.ok(
+ False,
+ "%s - %s - Unknown symbol of type %s" % (prefix, name, type(m)),
+ )
+ # A bit of a hoop because we can't generate the error string if we pass
+ if len(expectedMembers) == 0:
+ harness.ok(True, "Found all the members")
+ else:
+ harness.ok(
+ False,
+ "Expected member not found: %s of type %s"
+ % (expectedMembers[0][0], expectedMembers[0][1]),
+ )
+ return results
+
+ def shouldFail(prefix, iface):
+ try:
+ p = parser.reset()
+ p.parse(iface)
+ p.finish()
+ harness.ok(False, prefix + " - Interface passed when should've failed")
+ except WebIDL.WebIDLError:
+ harness.ok(True, prefix + " - Interface failed as expected")
+ except Exception as e:
+ harness.ok(
+ False,
+ prefix
+ + " - Interface failed but not as a WebIDLError exception: %s" % e,
+ )
+
+ iterableMembers = [
+ (x, WebIDL.IDLMethod) for x in ["entries", "keys", "values", "forEach"]
+ ]
+ setROMembers = (
+ [(x, WebIDL.IDLMethod) for x in ["has"]]
+ + [("__setlike", WebIDL.IDLMaplikeOrSetlike)]
+ + iterableMembers
+ )
+ setROMembers.extend([("size", WebIDL.IDLAttribute)])
+ setRWMembers = [
+ (x, WebIDL.IDLMethod) for x in ["add", "clear", "delete"]
+ ] + setROMembers
+ mapROMembers = (
+ [(x, WebIDL.IDLMethod) for x in ["get", "has"]]
+ + [("__maplike", WebIDL.IDLMaplikeOrSetlike)]
+ + iterableMembers
+ )
+ mapROMembers.extend([("size", WebIDL.IDLAttribute)])
+ mapRWMembers = [
+ (x, WebIDL.IDLMethod) for x in ["set", "clear", "delete"]
+ ] + mapROMembers
+
+ # OK, now that we've used iterableMembers to set up the above, append
+ # __iterable to it for the iterable<> case.
+ iterableMembers.append(("__iterable", WebIDL.IDLIterable))
+
+ asyncIterableMembers = [
+ (x, WebIDL.IDLMethod) for x in ["entries", "keys", "values"]
+ ]
+ asyncIterableMembers.append(("__iterable", WebIDL.IDLAsyncIterable))
+
+ valueIterableMembers = [("__iterable", WebIDL.IDLIterable)]
+ valueIterableMembers.append(("__indexedgetter", WebIDL.IDLMethod))
+ valueIterableMembers.append(("length", WebIDL.IDLAttribute))
+
+ valueAsyncIterableMembers = [("__iterable", WebIDL.IDLAsyncIterable)]
+ valueAsyncIterableMembers.append(("values", WebIDL.IDLMethod))
+
+ disallowedIterableNames = [
+ ("keys", WebIDL.IDLMethod),
+ ("entries", WebIDL.IDLMethod),
+ ("values", WebIDL.IDLMethod),
+ ]
+ disallowedMemberNames = [
+ ("forEach", WebIDL.IDLMethod),
+ ("has", WebIDL.IDLMethod),
+ ("size", WebIDL.IDLAttribute),
+ ] + disallowedIterableNames
+ mapDisallowedMemberNames = [("get", WebIDL.IDLMethod)] + disallowedMemberNames
+ disallowedNonMethodNames = [
+ ("clear", WebIDL.IDLMethod),
+ ("delete", WebIDL.IDLMethod),
+ ]
+ mapDisallowedNonMethodNames = [("set", WebIDL.IDLMethod)] + disallowedNonMethodNames
+ setDisallowedNonMethodNames = [("add", WebIDL.IDLMethod)] + disallowedNonMethodNames
+ unrelatedMembers = [
+ ("unrelatedAttribute", WebIDL.IDLAttribute),
+ ("unrelatedMethod", WebIDL.IDLMethod),
+ ]
+
+ #
+ # Simple Usage Tests
+ #
+
+ shouldPass(
+ "Iterable (key only)",
+ """
+ interface Foo1 {
+ iterable<long>;
+ readonly attribute unsigned long length;
+ getter long(unsigned long index);
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """,
+ valueIterableMembers + unrelatedMembers,
+ )
+
+ shouldPass(
+ "Iterable (key only) inheriting from parent",
+ """
+ interface Foo1 : Foo2 {
+ iterable<long>;
+ readonly attribute unsigned long length;
+ getter long(unsigned long index);
+ };
+ interface Foo2 {
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """,
+ valueIterableMembers,
+ numProductions=2,
+ )
+
+ shouldPass(
+ "Iterable (key and value)",
+ """
+ interface Foo1 {
+ iterable<long, long>;
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """,
+ iterableMembers + unrelatedMembers,
+ # numProductions == 2 because of the generated iterator iface,
+ numProductions=2,
+ )
+
+ shouldPass(
+ "Iterable (key and value) inheriting from parent",
+ """
+ interface Foo1 : Foo2 {
+ iterable<long, long>;
+ };
+ interface Foo2 {
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """,
+ iterableMembers,
+ # numProductions == 3 because of the generated iterator iface,
+ numProductions=3,
+ )
+
+ shouldPass(
+ "Async iterable (key only)",
+ """
+ interface Foo1 {
+ async iterable<long>;
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """,
+ valueAsyncIterableMembers + unrelatedMembers,
+ # numProductions == 2 because of the generated iterator iface,
+ numProductions=2,
+ )
+
+ shouldPass(
+ "Async iterable (key only) inheriting from parent",
+ """
+ interface Foo1 : Foo2 {
+ async iterable<long>;
+ };
+ interface Foo2 {
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """,
+ valueAsyncIterableMembers,
+ # numProductions == 3 because of the generated iterator iface,
+ numProductions=3,
+ )
+
+ shouldPass(
+ "Async iterable with argument (key only)",
+ """
+ interface Foo1 {
+ async iterable<long>(optional long foo);
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """,
+ valueAsyncIterableMembers + unrelatedMembers,
+ # numProductions == 2 because of the generated iterator iface,
+ numProductions=2,
+ )
+
+ shouldPass(
+ "Async iterable (key and value)",
+ """
+ interface Foo1 {
+ async iterable<long, long>;
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """,
+ asyncIterableMembers + unrelatedMembers,
+ # numProductions == 2 because of the generated iterator iface,
+ numProductions=2,
+ )
+
+ shouldPass(
+ "Async iterable (key and value) inheriting from parent",
+ """
+ interface Foo1 : Foo2 {
+ async iterable<long, long>;
+ };
+ interface Foo2 {
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """,
+ asyncIterableMembers,
+ # numProductions == 3 because of the generated iterator iface,
+ numProductions=3,
+ )
+
+ shouldPass(
+ "Async iterable with argument (key and value)",
+ """
+ interface Foo1 {
+ async iterable<long, long>(optional long foo);
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """,
+ asyncIterableMembers + unrelatedMembers,
+ # numProductions == 2 because of the generated iterator iface,
+ numProductions=2,
+ )
+
+ shouldPass(
+ "Maplike (readwrite)",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """,
+ mapRWMembers + unrelatedMembers,
+ )
+
+ shouldPass(
+ "Maplike (readwrite) inheriting from parent",
+ """
+ interface Foo1 : Foo2 {
+ maplike<long, long>;
+ };
+ interface Foo2 {
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """,
+ mapRWMembers,
+ numProductions=2,
+ )
+
+ shouldPass(
+ "Maplike (readwrite)",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """,
+ mapRWMembers + unrelatedMembers,
+ )
+
+ shouldPass(
+ "Maplike (readwrite) inheriting from parent",
+ """
+ interface Foo1 : Foo2 {
+ maplike<long, long>;
+ };
+ interface Foo2 {
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """,
+ mapRWMembers,
+ numProductions=2,
+ )
+
+ shouldPass(
+ "Maplike (readonly)",
+ """
+ interface Foo1 {
+ readonly maplike<long, long>;
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """,
+ mapROMembers + unrelatedMembers,
+ )
+
+ shouldPass(
+ "Maplike (readonly) inheriting from parent",
+ """
+ interface Foo1 : Foo2 {
+ readonly maplike<long, long>;
+ };
+ interface Foo2 {
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """,
+ mapROMembers,
+ numProductions=2,
+ )
+
+ shouldPass(
+ "Setlike (readwrite)",
+ """
+ interface Foo1 {
+ setlike<long>;
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """,
+ setRWMembers + unrelatedMembers,
+ )
+
+ shouldPass(
+ "Setlike (readwrite) inheriting from parent",
+ """
+ interface Foo1 : Foo2 {
+ setlike<long>;
+ };
+ interface Foo2 {
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """,
+ setRWMembers,
+ numProductions=2,
+ )
+
+ shouldPass(
+ "Setlike (readonly)",
+ """
+ interface Foo1 {
+ readonly setlike<long>;
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """,
+ setROMembers + unrelatedMembers,
+ )
+
+ shouldPass(
+ "Setlike (readonly) inheriting from parent",
+ """
+ interface Foo1 : Foo2 {
+ readonly setlike<long>;
+ };
+ interface Foo2 {
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """,
+ setROMembers,
+ numProductions=2,
+ )
+
+ shouldPass(
+ "Inheritance of maplike/setlike",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ };
+ interface Foo2 : Foo1 {
+ };
+ """,
+ mapRWMembers,
+ numProductions=2,
+ )
+
+ shouldFail(
+ "JS Implemented maplike interface",
+ """
+ [JSImplementation="@mozilla.org/dom/test-interface-js-maplike;1"]
+ interface Foo1 {
+ constructor();
+ setlike<long>;
+ };
+ """,
+ )
+
+ shouldFail(
+ "JS Implemented maplike interface",
+ """
+ [JSImplementation="@mozilla.org/dom/test-interface-js-maplike;1"]
+ interface Foo1 {
+ constructor();
+ maplike<long, long>;
+ };
+ """,
+ )
+
+ #
+ # Multiple maplike/setlike tests
+ #
+
+ shouldFail(
+ "Two maplike/setlikes on same interface",
+ """
+ interface Foo1 {
+ setlike<long>;
+ maplike<long, long>;
+ };
+ """,
+ )
+
+ shouldFail(
+ "Two iterable/setlikes on same interface",
+ """
+ interface Foo1 {
+ iterable<long>;
+ maplike<long, long>;
+ };
+ """,
+ )
+
+ shouldFail(
+ "Two iterables on same interface",
+ """
+ interface Foo1 {
+ iterable<long>;
+ iterable<long, long>;
+ };
+ """,
+ )
+
+ shouldFail(
+ "Two iterables on same interface",
+ """
+ interface Foo1 {
+ iterable<long>;
+ async iterable<long>;
+ };
+ """,
+ )
+
+ shouldFail(
+ "Two iterables on same interface",
+ """
+ interface Foo1 {
+ async iterable<long>;
+ async iterable<long, long>;
+ };
+ """,
+ )
+
+ shouldFail(
+ "Async iterable with non-optional arguments",
+ """
+ interface Foo1 {
+ async iterable<long>(long foo);
+ };
+ """,
+ )
+
+ shouldFail(
+ "Async iterable with non-optional arguments",
+ """
+ interface Foo1 {
+ async iterable<long>(optional long foo, long bar);
+ };
+ """,
+ )
+
+ shouldFail(
+ "Async iterable with non-optional arguments",
+ """
+ interface Foo1 {
+ async iterable<long, long>(long foo);
+ };
+ """,
+ )
+
+ shouldFail(
+ "Two maplike/setlikes in partials",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ };
+ partial interface Foo1 {
+ setlike<long>;
+ };
+ """,
+ )
+
+ shouldFail(
+ "Conflicting maplike/setlikes across inheritance",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ };
+ interface Foo2 : Foo1 {
+ setlike<long>;
+ };
+ """,
+ )
+
+ shouldFail(
+ "Conflicting maplike/iterable across inheritance",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ };
+ interface Foo2 : Foo1 {
+ iterable<long>;
+ };
+ """,
+ )
+
+ shouldFail(
+ "Conflicting maplike/setlikes across multistep inheritance",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ };
+ interface Foo2 : Foo1 {
+ };
+ interface Foo3 : Foo2 {
+ setlike<long>;
+ };
+ """,
+ )
+
+ #
+ # Member name collision tests
+ #
+
+ def testConflictingMembers(
+ likeMember, conflict, expectedMembers, methodPasses, numProductions=1
+ ):
+ """
+ Tests for maplike/setlike member generation against conflicting member
+ names. If methodPasses is True, this means we expect the interface to
+ pass in the case of method shadowing, and expectedMembers should be the
+ list of interface members to check against on the passing interface.
+
+ """
+ (conflictName, conflictType) = conflict
+ if methodPasses:
+ shouldPass(
+ "Conflicting method: %s and %s" % (likeMember, conflictName),
+ """
+ interface Foo1 {
+ %s;
+ [Throws]
+ undefined %s(long test1, double test2, double test3);
+ };
+ """
+ % (likeMember, conflictName),
+ expectedMembers,
+ )
+ else:
+ shouldFail(
+ "Conflicting method: %s and %s" % (likeMember, conflictName),
+ """
+ interface Foo1 {
+ %s;
+ [Throws]
+ undefined %s(long test1, double test2, double test3);
+ };
+ """
+ % (likeMember, conflictName),
+ )
+ # Inherited conflicting methods should ALWAYS fail
+ shouldFail(
+ "Conflicting inherited method: %s and %s" % (likeMember, conflictName),
+ """
+ interface Foo1 {
+ undefined %s(long test1, double test2, double test3);
+ };
+ interface Foo2 : Foo1 {
+ %s;
+ };
+ """
+ % (conflictName, likeMember),
+ )
+ if conflictType == WebIDL.IDLAttribute:
+ shouldFail(
+ "Conflicting static method: %s and %s" % (likeMember, conflictName),
+ """
+ interface Foo1 {
+ %s;
+ static undefined %s(long test1, double test2, double test3);
+ };
+ """
+ % (likeMember, conflictName),
+ )
+ else:
+ shouldPass(
+ "Conflicting static method: %s and %s" % (likeMember, conflictName),
+ """
+ interface Foo1 {
+ %s;
+ static undefined %s(long test1, double test2, double test3);
+ };
+ """
+ % (likeMember, conflictName),
+ expectedMembers,
+ numProductions=numProductions,
+ )
+ shouldFail(
+ "Conflicting attribute: %s and %s" % (likeMember, conflictName),
+ """
+ interface Foo1 {
+ %s
+ attribute double %s;
+ };
+ """
+ % (likeMember, conflictName),
+ )
+ shouldFail(
+ "Conflicting const: %s and %s" % (likeMember, conflictName),
+ """
+ interface Foo1 {
+ %s;
+ const double %s = 0;
+ };
+ """
+ % (likeMember, conflictName),
+ )
+ shouldFail(
+ "Conflicting static attribute: %s and %s" % (likeMember, conflictName),
+ """
+ interface Foo1 {
+ %s;
+ static attribute long %s;
+ };
+ """
+ % (likeMember, conflictName),
+ )
+
+ for member in disallowedIterableNames:
+ testConflictingMembers(
+ "iterable<long, long>", member, iterableMembers, False, numProductions=2
+ )
+ for member in mapDisallowedMemberNames:
+ testConflictingMembers("maplike<long, long>", member, mapRWMembers, False)
+ for member in disallowedMemberNames:
+ testConflictingMembers("setlike<long>", member, setRWMembers, False)
+ for member in mapDisallowedNonMethodNames:
+ testConflictingMembers("maplike<long, long>", member, mapRWMembers, True)
+ for member in setDisallowedNonMethodNames:
+ testConflictingMembers("setlike<long>", member, setRWMembers, True)
+
+ shouldPass(
+ "Inheritance of maplike/setlike with child member collision",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ };
+ interface Foo2 : Foo1 {
+ undefined entries();
+ };
+ """,
+ mapRWMembers,
+ numProductions=2,
+ )
+
+ shouldPass(
+ "Inheritance of multi-level maplike/setlike with child member collision",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ };
+ interface Foo2 : Foo1 {
+ };
+ interface Foo3 : Foo2 {
+ undefined entries();
+ };
+ """,
+ mapRWMembers,
+ numProductions=3,
+ )
+
+ shouldFail(
+ "Maplike interface with mixin member collision",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ };
+ interface mixin Foo2 {
+ undefined entries();
+ };
+ Foo1 includes Foo2;
+ """,
+ )
+
+ shouldPass(
+ "Inherited Maplike interface with consequential interface member collision",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ };
+ interface mixin Foo2 {
+ undefined entries();
+ };
+ interface Foo3 : Foo1 {
+ };
+ Foo3 includes Foo2;
+ """,
+ mapRWMembers,
+ numProductions=4,
+ )
+
+ shouldFail(
+ "Inheritance of name collision with child maplike/setlike",
+ """
+ interface Foo1 {
+ undefined entries();
+ };
+ interface Foo2 : Foo1 {
+ maplike<long, long>;
+ };
+ """,
+ )
+
+ shouldFail(
+ "Inheritance of multi-level name collision with child maplike/setlike",
+ """
+ interface Foo1 {
+ undefined entries();
+ };
+ interface Foo2 : Foo1 {
+ };
+ interface Foo3 : Foo2 {
+ maplike<long, long>;
+ };
+ """,
+ )
+
+ shouldPass(
+ "Inheritance of attribute collision with parent maplike/setlike",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ };
+ interface Foo2 : Foo1 {
+ attribute double size;
+ };
+ """,
+ mapRWMembers,
+ numProductions=2,
+ )
+
+ shouldPass(
+ "Inheritance of multi-level attribute collision with parent maplike/setlike",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ };
+ interface Foo2 : Foo1 {
+ };
+ interface Foo3 : Foo2 {
+ attribute double size;
+ };
+ """,
+ mapRWMembers,
+ numProductions=3,
+ )
+
+ shouldFail(
+ "Inheritance of attribute collision with child maplike/setlike",
+ """
+ interface Foo1 {
+ attribute double size;
+ };
+ interface Foo2 : Foo1 {
+ maplike<long, long>;
+ };
+ """,
+ )
+
+ shouldFail(
+ "Inheritance of multi-level attribute collision with child maplike/setlike",
+ """
+ interface Foo1 {
+ attribute double size;
+ };
+ interface Foo2 : Foo1 {
+ };
+ interface Foo3 : Foo2 {
+ maplike<long, long>;
+ };
+ """,
+ )
+
+ shouldFail(
+ "Inheritance of attribute/rw function collision with child maplike/setlike",
+ """
+ interface Foo1 {
+ attribute double set;
+ };
+ interface Foo2 : Foo1 {
+ maplike<long, long>;
+ };
+ """,
+ )
+
+ shouldFail(
+ "Inheritance of const/rw function collision with child maplike/setlike",
+ """
+ interface Foo1 {
+ const double set = 0;
+ };
+ interface Foo2 : Foo1 {
+ maplike<long, long>;
+ };
+ """,
+ )
+
+ shouldPass(
+ "Inheritance of rw function with same name in child maplike/setlike",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ };
+ interface Foo2 : Foo1 {
+ undefined clear();
+ };
+ """,
+ mapRWMembers,
+ numProductions=2,
+ )
+
+ shouldFail(
+ "Inheritance of unforgeable attribute collision with child maplike/setlike",
+ """
+ interface Foo1 {
+ [LegacyUnforgeable]
+ attribute double size;
+ };
+ interface Foo2 : Foo1 {
+ maplike<long, long>;
+ };
+ """,
+ )
+
+ shouldFail(
+ "Inheritance of multi-level unforgeable attribute collision with child maplike/setlike",
+ """
+ interface Foo1 {
+ [LegacyUnforgeable]
+ attribute double size;
+ };
+ interface Foo2 : Foo1 {
+ };
+ interface Foo3 : Foo2 {
+ maplike<long, long>;
+ };
+ """,
+ )
+
+ shouldPass(
+ "Interface with readonly allowable overrides",
+ """
+ interface Foo1 {
+ readonly setlike<long>;
+ readonly attribute boolean clear;
+ };
+ """,
+ setROMembers + [("clear", WebIDL.IDLAttribute)],
+ )
+
+ r = shouldPass(
+ "Check proper override of clear/delete/set",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ long clear(long a, long b, double c, double d);
+ long set(long a, long b, double c, double d);
+ long delete(long a, long b, double c, double d);
+ };
+ """,
+ mapRWMembers,
+ )
+
+ for m in r[0].members:
+ if m.identifier.name in ["clear", "set", "delete"]:
+ harness.ok(m.isMethod(), "%s should be a method" % m.identifier.name)
+ harness.check(
+ m.maxArgCount, 4, "%s should have 4 arguments" % m.identifier.name
+ )
+ harness.ok(
+ not m.isMaplikeOrSetlikeOrIterableMethod(),
+ "%s should not be a maplike/setlike function" % m.identifier.name,
+ )
diff --git a/dom/bindings/parser/tests/test_interfacemixin.py b/dom/bindings/parser/tests/test_interfacemixin.py
new file mode 100644
index 0000000000..4723991937
--- /dev/null
+++ b/dom/bindings/parser/tests/test_interfacemixin.py
@@ -0,0 +1,534 @@
+import WebIDL
+
+
+def WebIDLTest(parser, harness):
+ parser.parse("interface mixin Foo { };")
+ results = parser.finish()
+ harness.ok(True, "Empty interface mixin parsed without error.")
+ harness.check(len(results), 1, "Should be one production")
+ harness.ok(
+ isinstance(results[0], WebIDL.IDLInterfaceMixin),
+ "Should be an IDLInterfaceMixin",
+ )
+ mixin = results[0]
+ harness.check(
+ mixin.identifier.QName(), "::Foo", "Interface mixin has the right QName"
+ )
+ harness.check(mixin.identifier.name, "Foo", "Interface mixin has the right name")
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ interface mixin QNameBase {
+ const long foo = 3;
+ };
+ """
+ )
+ results = parser.finish()
+ harness.check(len(results), 1, "Should be one productions")
+ harness.ok(
+ isinstance(results[0], WebIDL.IDLInterfaceMixin),
+ "Should be an IDLInterfaceMixin",
+ )
+ harness.check(len(results[0].members), 1, "Expect 1 productions")
+ mixin = results[0]
+ harness.check(
+ mixin.members[0].identifier.QName(),
+ "::QNameBase::foo",
+ "Member has the right QName",
+ )
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ interface mixin A {
+ readonly attribute boolean x;
+ undefined foo();
+ };
+ partial interface mixin A {
+ readonly attribute boolean y;
+ undefined foo(long arg);
+ };
+ """
+ )
+ results = parser.finish()
+ harness.check(
+ len(results), 2, "Should have two results with partial interface mixin"
+ )
+ mixin = results[0]
+ harness.check(
+ len(mixin.members), 3, "Should have three members with partial interface mixin"
+ )
+ harness.check(
+ mixin.members[0].identifier.name,
+ "x",
+ "First member should be x with partial interface mixin",
+ )
+ harness.check(
+ mixin.members[1].identifier.name,
+ "foo",
+ "Second member should be foo with partial interface mixin",
+ )
+ harness.check(
+ len(mixin.members[1].signatures()),
+ 2,
+ "Should have two foo signatures with partial interface mixin",
+ )
+ harness.check(
+ mixin.members[2].identifier.name,
+ "y",
+ "Third member should be y with partial interface mixin",
+ )
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ partial interface mixin A {
+ readonly attribute boolean y;
+ undefined foo(long arg);
+ };
+ interface mixin A {
+ readonly attribute boolean x;
+ undefined foo();
+ };
+ """
+ )
+ results = parser.finish()
+ harness.check(
+ len(results), 2, "Should have two results with reversed partial interface mixin"
+ )
+ mixin = results[1]
+ harness.check(
+ len(mixin.members),
+ 3,
+ "Should have three members with reversed partial interface mixin",
+ )
+ harness.check(
+ mixin.members[0].identifier.name,
+ "x",
+ "First member should be x with reversed partial interface mixin",
+ )
+ harness.check(
+ mixin.members[1].identifier.name,
+ "foo",
+ "Second member should be foo with reversed partial interface mixin",
+ )
+ harness.check(
+ len(mixin.members[1].signatures()),
+ 2,
+ "Should have two foo signatures with reversed partial interface mixin",
+ )
+ harness.check(
+ mixin.members[2].identifier.name,
+ "y",
+ "Third member should be y with reversed partial interface mixin",
+ )
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ interface Interface {};
+ interface mixin Mixin {
+ attribute short x;
+ };
+ Interface includes Mixin;
+ """
+ )
+ results = parser.finish()
+ iface = results[0]
+ harness.check(len(iface.members), 1, "Should merge members from mixins")
+ harness.check(
+ iface.members[0].identifier.name, "x", "Should merge members from mixins"
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface mixin A {
+ readonly attribute boolean x;
+ };
+ interface mixin A {
+ readonly attribute boolean y;
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(
+ threw, "Should not allow two non-partial interface mixins with the same name"
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ partial interface mixin A {
+ readonly attribute boolean x;
+ };
+ partial interface mixin A {
+ readonly attribute boolean y;
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "Must have a non-partial interface mixin for a given name")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ dictionary A {
+ boolean x;
+ };
+ partial interface mixin A {
+ readonly attribute boolean y;
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(
+ threw,
+ "Should not allow a name collision between partial interface "
+ "mixin and other object",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ dictionary A {
+ boolean x;
+ };
+ interface mixin A {
+ readonly attribute boolean y;
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(
+ threw,
+ "Should not allow a name collision between interface mixin " "and other object",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface mixin A {
+ readonly attribute boolean x;
+ };
+ interface A;
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(
+ threw,
+ "Should not allow a name collision between external interface "
+ "and interface mixin",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ [SomeRandomAnnotation]
+ interface mixin A {
+ readonly attribute boolean y;
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(
+ threw, "Should not allow unknown extended attributes on interface mixins"
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface mixin A {
+ getter double (DOMString propertyName);
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "Should not allow getters on interface mixins")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface mixin A {
+ setter undefined (DOMString propertyName, double propertyValue);
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "Should not allow setters on interface mixins")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface mixin A {
+ deleter undefined (DOMString propertyName);
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "Should not allow deleters on interface mixins")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface mixin A {
+ legacycaller double compute(double x);
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "Should not allow legacycallers on interface mixins")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface mixin A {
+ inherit attribute x;
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "Should not allow inherited attribute on interface mixins")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Interface {};
+ interface NotMixin {
+ attribute short x;
+ };
+ Interface includes NotMixin;
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "Should fail if the right side does not point an interface mixin")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface mixin NotInterface {};
+ interface mixin Mixin {
+ attribute short x;
+ };
+ NotInterface includes Mixin;
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "Should fail if the left side does not point an interface")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface mixin Mixin {
+ iterable<DOMString>;
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "Should fail if an interface mixin includes iterable")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface mixin Mixin {
+ setlike<DOMString>;
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "Should fail if an interface mixin includes setlike")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface mixin Mixin {
+ maplike<DOMString, DOMString>;
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "Should fail if an interface mixin includes maplike")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Interface {
+ attribute short attr;
+ };
+ interface mixin Mixin {
+ attribute short attr;
+ };
+ Interface includes Mixin;
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(
+ threw, "Should fail if the included mixin interface has duplicated member"
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Interface {};
+ interface mixin Mixin1 {
+ attribute short attr;
+ };
+ interface mixin Mixin2 {
+ attribute short attr;
+ };
+ Interface includes Mixin1;
+ Interface includes Mixin2;
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(
+ threw, "Should fail if the included mixin interfaces have duplicated member"
+ )
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ [Global, Exposed=Window] interface Window {};
+ [Global, Exposed=Worker] interface Worker {};
+ [Exposed=Window]
+ interface Base {};
+ interface mixin Mixin {
+ Base returnSelf();
+ };
+ Base includes Mixin;
+ """
+ )
+ results = parser.finish()
+ base = results[2]
+ attr = base.members[0]
+ harness.check(
+ attr.exposureSet,
+ set(["Window"]),
+ "Should expose on globals where the base interfaces are exposed",
+ )
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ [Global, Exposed=Window] interface Window {};
+ [Global, Exposed=Worker] interface Worker {};
+ [Exposed=Window]
+ interface Base {};
+ [Exposed=Window]
+ interface mixin Mixin {
+ attribute short a;
+ };
+ Base includes Mixin;
+ """
+ )
+ results = parser.finish()
+ base = results[2]
+ attr = base.members[0]
+ harness.check(
+ attr.exposureSet, set(["Window"]), "Should follow [Exposed] on interface mixin"
+ )
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ [Global, Exposed=Window] interface Window {};
+ [Global, Exposed=Worker] interface Worker {};
+ [Exposed=Window]
+ interface Base1 {};
+ [Exposed=Worker]
+ interface Base2 {};
+ interface mixin Mixin {
+ attribute short a;
+ };
+ Base1 includes Mixin;
+ Base2 includes Mixin;
+ """
+ )
+ results = parser.finish()
+ base = results[2]
+ attr = base.members[0]
+ harness.check(
+ attr.exposureSet,
+ set(["Window", "Worker"]),
+ "Should expose on all globals where including interfaces are " "exposed",
+ )
+ base = results[3]
+ attr = base.members[0]
+ harness.check(
+ attr.exposureSet,
+ set(["Window", "Worker"]),
+ "Should expose on all globals where including interfaces are " "exposed",
+ )
diff --git a/dom/bindings/parser/tests/test_lenientSetter.py b/dom/bindings/parser/tests/test_lenientSetter.py
new file mode 100644
index 0000000000..1d2a9f06ce
--- /dev/null
+++ b/dom/bindings/parser/tests/test_lenientSetter.py
@@ -0,0 +1,84 @@
+# 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/.
+
+
+def should_throw(parser, harness, message, code):
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(code)
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown: %s" % message)
+
+
+def WebIDLTest(parser, harness):
+ # The [LegacyLenientSetter] extended attribute MUST take no arguments.
+ should_throw(
+ parser,
+ harness,
+ "no arguments",
+ """
+ interface I {
+ [LegacyLenientSetter=X] readonly attribute long A;
+ };
+ """,
+ )
+
+ # An attribute with the [LegacyLenientSetter] extended attribute MUST NOT
+ # also be declared with the [PutForwards] extended attribute.
+ should_throw(
+ parser,
+ harness,
+ "PutForwards",
+ """
+ interface I {
+ [PutForwards=B, LegacyLenientSetter] readonly attribute J A;
+ };
+ interface J {
+ attribute long B;
+ };
+ """,
+ )
+
+ # An attribute with the [LegacyLenientSetter] extended attribute MUST NOT
+ # also be declared with the [Replaceable] extended attribute.
+ should_throw(
+ parser,
+ harness,
+ "Replaceable",
+ """
+ interface I {
+ [Replaceable, LegacyLenientSetter] readonly attribute J A;
+ };
+ """,
+ )
+
+ # The [LegacyLenientSetter] extended attribute MUST NOT be used on an
+ # attribute that is not read only.
+ should_throw(
+ parser,
+ harness,
+ "writable attribute",
+ """
+ interface I {
+ [LegacyLenientSetter] attribute long A;
+ };
+ """,
+ )
+
+ # The [LegacyLenientSetter] extended attribute MUST NOT be used on a
+ # static attribute.
+ should_throw(
+ parser,
+ harness,
+ "static attribute",
+ """
+ interface I {
+ [LegacyLenientSetter] static readonly attribute long A;
+ };
+ """,
+ )
diff --git a/dom/bindings/parser/tests/test_method.py b/dom/bindings/parser/tests/test_method.py
new file mode 100644
index 0000000000..e11044b742
--- /dev/null
+++ b/dom/bindings/parser/tests/test_method.py
@@ -0,0 +1,430 @@
+import WebIDL
+
+
+def WebIDLTest(parser, harness):
+ parser.parse(
+ """
+ interface TestMethods {
+ undefined basic();
+ static undefined basicStatic();
+ undefined basicWithSimpleArgs(boolean arg1, byte arg2, unsigned long arg3);
+ boolean basicBoolean();
+ static boolean basicStaticBoolean();
+ boolean basicBooleanWithSimpleArgs(boolean arg1, byte arg2, unsigned long arg3);
+ undefined optionalArg(optional byte? arg1, optional sequence<byte> arg2);
+ undefined variadicArg(byte?... arg1);
+ object getObject();
+ undefined setObject(object arg1);
+ undefined setAny(any arg1);
+ float doFloats(float arg1);
+ };
+ """
+ )
+
+ results = parser.finish()
+
+ harness.ok(True, "TestMethods interface parsed without error.")
+ harness.check(len(results), 1, "Should be one production.")
+ iface = results[0]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface), "Should be an IDLInterface")
+ harness.check(
+ iface.identifier.QName(), "::TestMethods", "Interface has the right QName"
+ )
+ harness.check(iface.identifier.name, "TestMethods", "Interface has the right name")
+ harness.check(len(iface.members), 12, "Expect 12 members")
+
+ methods = iface.members
+
+ def checkArgument(argument, QName, name, type, optional, variadic):
+ harness.ok(isinstance(argument, WebIDL.IDLArgument), "Should be an IDLArgument")
+ harness.check(
+ argument.identifier.QName(), QName, "Argument has the right QName"
+ )
+ harness.check(argument.identifier.name, name, "Argument has the right name")
+ harness.check(str(argument.type), type, "Argument has the right return type")
+ harness.check(
+ argument.optional, optional, "Argument has the right optional value"
+ )
+ harness.check(
+ argument.variadic, variadic, "Argument has the right variadic value"
+ )
+
+ def checkMethod(
+ method,
+ QName,
+ name,
+ signatures,
+ static=False,
+ getter=False,
+ setter=False,
+ deleter=False,
+ legacycaller=False,
+ stringifier=False,
+ ):
+ harness.ok(isinstance(method, WebIDL.IDLMethod), "Should be an IDLMethod")
+ harness.ok(method.isMethod(), "Method is a method")
+ harness.ok(not method.isAttr(), "Method is not an attr")
+ harness.ok(not method.isConst(), "Method is not a const")
+ harness.check(method.identifier.QName(), QName, "Method has the right QName")
+ harness.check(method.identifier.name, name, "Method has the right name")
+ harness.check(method.isStatic(), static, "Method has the correct static value")
+ harness.check(method.isGetter(), getter, "Method has the correct getter value")
+ harness.check(method.isSetter(), setter, "Method has the correct setter value")
+ harness.check(
+ method.isDeleter(), deleter, "Method has the correct deleter value"
+ )
+ harness.check(
+ method.isLegacycaller(),
+ legacycaller,
+ "Method has the correct legacycaller value",
+ )
+ harness.check(
+ method.isStringifier(),
+ stringifier,
+ "Method has the correct stringifier value",
+ )
+ harness.check(
+ len(method.signatures()),
+ len(signatures),
+ "Method has the correct number of signatures",
+ )
+
+ sigpairs = zip(method.signatures(), signatures)
+ for (gotSignature, expectedSignature) in sigpairs:
+ (gotRetType, gotArgs) = gotSignature
+ (expectedRetType, expectedArgs) = expectedSignature
+
+ harness.check(
+ str(gotRetType), expectedRetType, "Method has the expected return type."
+ )
+
+ for i in range(0, len(gotArgs)):
+ (QName, name, type, optional, variadic) = expectedArgs[i]
+ checkArgument(gotArgs[i], QName, name, type, optional, variadic)
+
+ checkMethod(methods[0], "::TestMethods::basic", "basic", [("Undefined", [])])
+ checkMethod(
+ methods[1],
+ "::TestMethods::basicStatic",
+ "basicStatic",
+ [("Undefined", [])],
+ static=True,
+ )
+ checkMethod(
+ methods[2],
+ "::TestMethods::basicWithSimpleArgs",
+ "basicWithSimpleArgs",
+ [
+ (
+ "Undefined",
+ [
+ (
+ "::TestMethods::basicWithSimpleArgs::arg1",
+ "arg1",
+ "Boolean",
+ False,
+ False,
+ ),
+ (
+ "::TestMethods::basicWithSimpleArgs::arg2",
+ "arg2",
+ "Byte",
+ False,
+ False,
+ ),
+ (
+ "::TestMethods::basicWithSimpleArgs::arg3",
+ "arg3",
+ "UnsignedLong",
+ False,
+ False,
+ ),
+ ],
+ )
+ ],
+ )
+ checkMethod(
+ methods[3], "::TestMethods::basicBoolean", "basicBoolean", [("Boolean", [])]
+ )
+ checkMethod(
+ methods[4],
+ "::TestMethods::basicStaticBoolean",
+ "basicStaticBoolean",
+ [("Boolean", [])],
+ static=True,
+ )
+ checkMethod(
+ methods[5],
+ "::TestMethods::basicBooleanWithSimpleArgs",
+ "basicBooleanWithSimpleArgs",
+ [
+ (
+ "Boolean",
+ [
+ (
+ "::TestMethods::basicBooleanWithSimpleArgs::arg1",
+ "arg1",
+ "Boolean",
+ False,
+ False,
+ ),
+ (
+ "::TestMethods::basicBooleanWithSimpleArgs::arg2",
+ "arg2",
+ "Byte",
+ False,
+ False,
+ ),
+ (
+ "::TestMethods::basicBooleanWithSimpleArgs::arg3",
+ "arg3",
+ "UnsignedLong",
+ False,
+ False,
+ ),
+ ],
+ )
+ ],
+ )
+ checkMethod(
+ methods[6],
+ "::TestMethods::optionalArg",
+ "optionalArg",
+ [
+ (
+ "Undefined",
+ [
+ (
+ "::TestMethods::optionalArg::arg1",
+ "arg1",
+ "ByteOrNull",
+ True,
+ False,
+ ),
+ (
+ "::TestMethods::optionalArg::arg2",
+ "arg2",
+ "ByteSequence",
+ True,
+ False,
+ ),
+ ],
+ )
+ ],
+ )
+ checkMethod(
+ methods[7],
+ "::TestMethods::variadicArg",
+ "variadicArg",
+ [
+ (
+ "Undefined",
+ [
+ (
+ "::TestMethods::variadicArg::arg1",
+ "arg1",
+ "ByteOrNull",
+ True,
+ True,
+ )
+ ],
+ )
+ ],
+ )
+ checkMethod(methods[8], "::TestMethods::getObject", "getObject", [("Object", [])])
+ checkMethod(
+ methods[9],
+ "::TestMethods::setObject",
+ "setObject",
+ [
+ (
+ "Undefined",
+ [("::TestMethods::setObject::arg1", "arg1", "Object", False, False)],
+ )
+ ],
+ )
+ checkMethod(
+ methods[10],
+ "::TestMethods::setAny",
+ "setAny",
+ [("Undefined", [("::TestMethods::setAny::arg1", "arg1", "Any", False, False)])],
+ )
+ checkMethod(
+ methods[11],
+ "::TestMethods::doFloats",
+ "doFloats",
+ [("Float", [("::TestMethods::doFloats::arg1", "arg1", "Float", False, False)])],
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface A {
+ undefined foo(optional float bar = 1);
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(not threw, "Should allow integer to float type corecion")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface A {
+ [GetterThrows] undefined foo();
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "Should not allow [GetterThrows] on methods")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface A {
+ [SetterThrows] undefined foo();
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "Should not allow [SetterThrows] on methods")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface A {
+ [Throw] undefined foo();
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "Should spell [Throws] correctly on methods")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface A {
+ undefined __noSuchMethod__();
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "Should not allow __noSuchMethod__ methods")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface A {
+ [Throws, LenientFloat]
+ undefined foo(float myFloat);
+ [Throws]
+ undefined foo();
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(not threw, "Should allow LenientFloat to be only in a specific overload")
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ interface A {
+ [Throws]
+ undefined foo();
+ [Throws, LenientFloat]
+ undefined foo(float myFloat);
+ };
+ """
+ )
+ results = parser.finish()
+ iface = results[0]
+ methods = iface.members
+ lenientFloat = methods[0].getExtendedAttribute("LenientFloat")
+ harness.ok(
+ lenientFloat is not None,
+ "LenientFloat in overloads must be added to the method",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface A {
+ [Throws, LenientFloat]
+ undefined foo(float myFloat);
+ [Throws]
+ undefined foo(float myFloat, float yourFloat);
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(
+ threw,
+ "Should prevent overloads from getting different restricted float behavior",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface A {
+ [Throws]
+ undefined foo(float myFloat, float yourFloat);
+ [Throws, LenientFloat]
+ undefined foo(float myFloat);
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(
+ threw,
+ "Should prevent overloads from getting different restricted float behavior (2)",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface A {
+ [Throws, LenientFloat]
+ undefined foo(float myFloat);
+ [Throws, LenientFloat]
+ undefined foo(short myShort);
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "Should prevent overloads from getting redundant [LenientFloat]")
diff --git a/dom/bindings/parser/tests/test_namespace.py b/dom/bindings/parser/tests/test_namespace.py
new file mode 100644
index 0000000000..cf315800ae
--- /dev/null
+++ b/dom/bindings/parser/tests/test_namespace.py
@@ -0,0 +1,232 @@
+def WebIDLTest(parser, harness):
+ parser.parse(
+ """
+ namespace MyNamespace {
+ attribute any foo;
+ any bar();
+ };
+ """
+ )
+
+ results = parser.finish()
+ harness.check(len(results), 1, "Should have a thing.")
+ harness.ok(results[0].isNamespace(), "Our thing should be a namespace")
+ harness.check(len(results[0].members), 2, "Should have two things in our namespace")
+ harness.ok(results[0].members[0].isAttr(), "First member is attribute")
+ harness.ok(results[0].members[0].isStatic(), "Attribute should be static")
+ harness.ok(results[0].members[1].isMethod(), "Second member is method")
+ harness.ok(results[0].members[1].isStatic(), "Operation should be static")
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ namespace MyNamespace {
+ attribute any foo;
+ };
+ partial namespace MyNamespace {
+ any bar();
+ };
+ """
+ )
+
+ results = parser.finish()
+ harness.check(len(results), 2, "Should have things.")
+ harness.ok(results[0].isNamespace(), "Our thing should be a namespace")
+ harness.check(len(results[0].members), 2, "Should have two things in our namespace")
+ harness.ok(results[0].members[0].isAttr(), "First member is attribute")
+ harness.ok(results[0].members[0].isStatic(), "Attribute should be static")
+ harness.ok(results[0].members[1].isMethod(), "Second member is method")
+ harness.ok(results[0].members[1].isStatic(), "Operation should be static")
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ partial namespace MyNamespace {
+ any bar();
+ };
+ namespace MyNamespace {
+ attribute any foo;
+ };
+ """
+ )
+
+ results = parser.finish()
+ harness.check(len(results), 2, "Should have things.")
+ harness.ok(results[1].isNamespace(), "Our thing should be a namespace")
+ harness.check(len(results[1].members), 2, "Should have two things in our namespace")
+ harness.ok(results[1].members[0].isAttr(), "First member is attribute")
+ harness.ok(results[1].members[0].isStatic(), "Attribute should be static")
+ harness.ok(results[1].members[1].isMethod(), "Second member is method")
+ harness.ok(results[1].members[1].isStatic(), "Operation should be static")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ namespace MyNamespace {
+ static attribute any foo;
+ };
+ """
+ )
+
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ namespace MyNamespace {
+ static any bar();
+ };
+ """
+ )
+
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ namespace MyNamespace {
+ any bar();
+ };
+
+ interface MyNamespace {
+ any baz();
+ };
+ """
+ )
+
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface MyNamespace {
+ any baz();
+ };
+
+ namespace MyNamespace {
+ any bar();
+ };
+ """
+ )
+
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ namespace MyNamespace {
+ any baz();
+ };
+
+ namespace MyNamespace {
+ any bar();
+ };
+ """
+ )
+
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ partial namespace MyNamespace {
+ any baz();
+ };
+
+ interface MyNamespace {
+ any bar();
+ };
+ """
+ )
+
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ namespace MyNamespace {
+ any bar();
+ };
+
+ partial interface MyNamespace {
+ any baz();
+ };
+ """
+ )
+
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ partial interface MyNamespace {
+ any baz();
+ };
+
+ namespace MyNamespace {
+ any bar();
+ };
+ """
+ )
+
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface MyNamespace {
+ any bar();
+ };
+
+ partial namespace MyNamespace {
+ any baz();
+ };
+ """
+ )
+
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "Should have thrown.")
diff --git a/dom/bindings/parser/tests/test_newobject.py b/dom/bindings/parser/tests/test_newobject.py
new file mode 100644
index 0000000000..efa0385542
--- /dev/null
+++ b/dom/bindings/parser/tests/test_newobject.py
@@ -0,0 +1,73 @@
+# Import the WebIDL module, so we can do isinstance checks and whatnot
+def WebIDLTest(parser, harness):
+ # Basic functionality
+ parser.parse(
+ """
+ interface Iface {
+ [NewObject] readonly attribute Iface attr;
+ [NewObject] Iface method();
+ };
+ """
+ )
+ results = parser.finish()
+ harness.ok(results, "Should not have thrown on basic [NewObject] usage")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Iface {
+ [Pure, NewObject] readonly attribute Iface attr;
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "[NewObject] attributes must depend on something")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Iface {
+ [Pure, NewObject] Iface method();
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "[NewObject] methods must depend on something")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Iface {
+ [Cached, NewObject, Affects=Nothing] readonly attribute Iface attr;
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "[NewObject] attributes must not be [Cached]")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Iface {
+ [StoreInSlot, NewObject, Affects=Nothing] readonly attribute Iface attr;
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "[NewObject] attributes must not be [StoreInSlot]")
diff --git a/dom/bindings/parser/tests/test_nullable_equivalency.py b/dom/bindings/parser/tests/test_nullable_equivalency.py
new file mode 100644
index 0000000000..278794f719
--- /dev/null
+++ b/dom/bindings/parser/tests/test_nullable_equivalency.py
@@ -0,0 +1,138 @@
+def WebIDLTest(parser, harness):
+ parser.parse(
+ """
+ interface TestNullableEquivalency1 {
+ attribute long a;
+ attribute long? b;
+ };
+
+ interface TestNullableEquivalency2 {
+ attribute ArrayBuffer a;
+ attribute ArrayBuffer? b;
+ };
+
+ /* Can't have dictionary-valued attributes, so can't test that here */
+
+ enum TestNullableEquivalency4Enum {
+ "Foo",
+ "Bar"
+ };
+
+ interface TestNullableEquivalency4 {
+ attribute TestNullableEquivalency4Enum a;
+ attribute TestNullableEquivalency4Enum? b;
+ };
+
+ interface TestNullableEquivalency5 {
+ attribute TestNullableEquivalency4 a;
+ attribute TestNullableEquivalency4? b;
+ };
+
+ interface TestNullableEquivalency6 {
+ attribute boolean a;
+ attribute boolean? b;
+ };
+
+ interface TestNullableEquivalency7 {
+ attribute DOMString a;
+ attribute DOMString? b;
+ };
+
+ interface TestNullableEquivalency8 {
+ attribute float a;
+ attribute float? b;
+ };
+
+ interface TestNullableEquivalency9 {
+ attribute double a;
+ attribute double? b;
+ };
+
+ interface TestNullableEquivalency10 {
+ attribute object a;
+ attribute object? b;
+ };
+ """
+ )
+
+ for decl in parser.finish():
+ if decl.isInterface():
+ checkEquivalent(decl, harness)
+
+
+def checkEquivalent(iface, harness):
+ type1 = iface.members[0].type
+ type2 = iface.members[1].type
+
+ harness.check(type1.nullable(), False, "attr1 should not be nullable")
+ harness.check(type2.nullable(), True, "attr2 should be nullable")
+
+ # We don't know about type1, but type2, the nullable type, definitely
+ # shouldn't be builtin.
+ harness.check(type2.builtin, False, "attr2 should not be builtin")
+
+ # Ensure that all attributes of type2 match those in type1, except for:
+ # - names on an ignore list,
+ # - names beginning with '_',
+ # - functions which throw when called with no args, and
+ # - class-level non-callables ("static variables").
+ #
+ # Yes, this is an ugly, fragile hack. But it finds bugs...
+ for attr in dir(type1):
+ if (
+ attr.startswith("_")
+ or attr
+ in [
+ "nullable",
+ "builtin",
+ "filename",
+ "location",
+ "inner",
+ "QName",
+ "getDeps",
+ "name",
+ "prettyName",
+ ]
+ or (hasattr(type(type1), attr) and not callable(getattr(type1, attr)))
+ ):
+ continue
+
+ a1 = getattr(type1, attr)
+
+ if callable(a1):
+ try:
+ v1 = a1()
+ except Exception:
+ # Can't call a1 with no args, so skip this attriute.
+ continue
+
+ try:
+ a2 = getattr(type2, attr)
+ except Exception:
+ harness.ok(
+ False,
+ "Missing %s attribute on type %s in %s" % (attr, type2, iface),
+ )
+ continue
+
+ if not callable(a2):
+ harness.ok(
+ False,
+ "%s attribute on type %s in %s wasn't callable"
+ % (attr, type2, iface),
+ )
+ continue
+
+ v2 = a2()
+ harness.check(v2, v1, "%s method return value" % attr)
+ else:
+ try:
+ a2 = getattr(type2, attr)
+ except Exception:
+ harness.ok(
+ False,
+ "Missing %s attribute on type %s in %s" % (attr, type2, iface),
+ )
+ continue
+
+ harness.check(a2, a1, "%s attribute should match" % attr)
diff --git a/dom/bindings/parser/tests/test_nullable_void.py b/dom/bindings/parser/tests/test_nullable_void.py
new file mode 100644
index 0000000000..5acba727e5
--- /dev/null
+++ b/dom/bindings/parser/tests/test_nullable_void.py
@@ -0,0 +1,16 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface NullableVoid {
+ void? foo();
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/dom/bindings/parser/tests/test_observableArray.py b/dom/bindings/parser/tests/test_observableArray.py
new file mode 100644
index 0000000000..601f626bcf
--- /dev/null
+++ b/dom/bindings/parser/tests/test_observableArray.py
@@ -0,0 +1,288 @@
+# 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/.
+
+
+def WebIDLTest(parser, harness):
+
+ # Test dictionary as inner type
+ harness.should_throw(
+ parser,
+ """
+ dictionary A {
+ boolean member;
+ };
+ interface B {
+ attribute ObservableArray<A> foo;
+ };
+ """,
+ "use dictionary as inner type",
+ )
+
+ # Test sequence as inner type
+ harness.should_throw(
+ parser,
+ """
+ interface A {
+ attribute ObservableArray<sequence<boolean>> foo;
+ };
+ """,
+ "use sequence as inner type",
+ )
+
+ # Test sequence<dictionary> as inner type
+ harness.should_throw(
+ parser,
+ """
+ dictionary A {
+ boolean member;
+ };
+ interface B {
+ attribute ObservableArray<sequence<A>> foo;
+ };
+ """,
+ "use sequence<dictionary> as inner type",
+ )
+
+ # Test record as inner type
+ harness.should_throw(
+ parser,
+ """
+ interface A {
+ attribute ObservableArray<record<DOMString, boolean>> foo;
+ };
+ """,
+ "use record as inner type",
+ )
+
+ # Test record<dictionary> as inner type
+ harness.should_throw(
+ parser,
+ """
+ dictionary A {
+ boolean member;
+ };
+ interface B {
+ attribute ObservableArray<record<DOMString, A>> foo;
+ };
+ """,
+ "use record<dictionary> as inner type",
+ )
+
+ # Test observable array as inner type
+ harness.should_throw(
+ parser,
+ """
+ interface A {
+ attribute ObservableArray<ObservableArray<boolean>> foo;
+ };
+ """,
+ "use ObservableArray as inner type",
+ )
+
+ # Test nullable attribute
+ harness.should_throw(
+ parser,
+ """
+ interface A {
+ attribute ObservableArray<boolean>? foo;
+ };
+ """,
+ "nullable",
+ )
+
+ # Test sequence
+ harness.should_throw(
+ parser,
+ """
+ interface A {
+ undefined foo(sequence<ObservableArray<boolean>> foo);
+ };
+ """,
+ "used in sequence",
+ )
+
+ # Test record
+ harness.should_throw(
+ parser,
+ """
+ interface A {
+ undefined foo(record<DOMString, ObservableArray<boolean>> foo);
+ };
+ """,
+ "used in record",
+ )
+
+ # Test promise
+ harness.should_throw(
+ parser,
+ """
+ interface A {
+ Promise<ObservableArray<boolean>> foo();
+ };
+ """,
+ "used in promise",
+ )
+
+ # Test union
+ harness.should_throw(
+ parser,
+ """
+ interface A {
+ attribute (DOMString or ObservableArray<boolean>>) foo;
+ };
+ """,
+ "used in union",
+ )
+
+ # Test dictionary member
+ harness.should_throw(
+ parser,
+ """
+ dictionary A {
+ ObservableArray<boolean> foo;
+ };
+ """,
+ "used on dictionary member type",
+ )
+
+ # Test argument
+ harness.should_throw(
+ parser,
+ """
+ interface A {
+ undefined foo(ObservableArray<boolean> foo);
+ };
+ """,
+ "used on argument",
+ )
+
+ # Test static attribute
+ harness.should_throw(
+ parser,
+ """
+ interface A {
+ static attribute ObservableArray<boolean> foo;
+ };
+ """,
+ "used on static attribute type",
+ )
+
+ # Test iterable
+ harness.should_throw(
+ parser,
+ """
+ interface A {
+ iterable<ObservableArray<boolean>>;
+ };
+ """,
+ "used in iterable",
+ )
+
+ # Test maplike
+ harness.should_throw(
+ parser,
+ """
+ interface A {
+ maplike<long, ObservableArray<boolean>>;
+ };
+ """,
+ "used in maplike",
+ )
+
+ # Test setlike
+ harness.should_throw(
+ parser,
+ """
+ interface A {
+ setlike<ObservableArray<boolean>>;
+ };
+ """,
+ "used in setlike",
+ )
+
+ # Test JS implemented interface
+ harness.should_throw(
+ parser,
+ """
+ [JSImplementation="@mozilla.org/dom/test-interface-js;1"]
+ interface A {
+ readonly attribute ObservableArray<boolean> foo;
+ };
+ """,
+ "used in JS implemented interface",
+ )
+
+ # Test namespace
+ harness.should_throw(
+ parser,
+ """
+ namespace A {
+ readonly attribute ObservableArray<boolean> foo;
+ };
+ """,
+ "used in namespaces",
+ )
+
+ # Test [Cached] extended attribute
+ harness.should_throw(
+ parser,
+ """
+ interface A {
+ [Cached, Pure]
+ readonly attribute ObservableArray<boolean> foo;
+ };
+ """,
+ "have Cached extended attribute",
+ )
+
+ # Test [StoreInSlot] extended attribute
+ harness.should_throw(
+ parser,
+ """
+ interface A {
+ [StoreInSlot, Pure]
+ readonly attribute ObservableArray<boolean> foo;
+ };
+ """,
+ "have StoreInSlot extended attribute",
+ )
+
+ # Test regular attribute
+ parser = parser.reset()
+ parser.parse(
+ """
+ interface A {
+ readonly attribute ObservableArray<boolean> foo;
+ attribute ObservableArray<[Clamp] octet> bar;
+ attribute ObservableArray<long?> baz;
+ attribute ObservableArray<(boolean or long)> qux;
+ };
+ """
+ )
+ results = parser.finish()
+ A = results[0]
+ foo = A.members[0]
+ harness.ok(foo.readonly, "A.foo is readonly attribute")
+ harness.ok(foo.type.isObservableArray(), "A.foo is ObservableArray type")
+ harness.check(
+ foo.slotIndices[A.identifier.name], 0, "A.foo should be stored in slot"
+ )
+ bar = A.members[1]
+ harness.ok(bar.type.isObservableArray(), "A.bar is ObservableArray type")
+ harness.check(
+ bar.slotIndices[A.identifier.name], 1, "A.bar should be stored in slot"
+ )
+ harness.ok(bar.type.inner.hasClamp(), "A.bar's inner type should be clamped")
+ baz = A.members[2]
+ harness.ok(baz.type.isObservableArray(), "A.baz is ObservableArray type")
+ harness.check(
+ baz.slotIndices[A.identifier.name], 2, "A.baz should be stored in slot"
+ )
+ harness.ok(baz.type.inner.nullable(), "A.baz's inner type should be nullable")
+ qux = A.members[3]
+ harness.ok(qux.type.isObservableArray(), "A.qux is ObservableArray type")
+ harness.check(
+ qux.slotIndices[A.identifier.name], 3, "A.qux should be stored in slot"
+ )
+ harness.ok(qux.type.inner.isUnion(), "A.qux's inner type should be union")
diff --git a/dom/bindings/parser/tests/test_optional_constraints.py b/dom/bindings/parser/tests/test_optional_constraints.py
new file mode 100644
index 0000000000..78e71df544
--- /dev/null
+++ b/dom/bindings/parser/tests/test_optional_constraints.py
@@ -0,0 +1,35 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface OptionalConstraints1 {
+ undefined foo(optional byte arg1, byte arg2);
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ not threw,
+ "Should not have thrown on non-optional argument following "
+ "optional argument.",
+ )
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ interface OptionalConstraints2 {
+ undefined foo(optional byte arg1 = 1, optional byte arg2 = 2,
+ optional byte arg3, optional byte arg4 = 4,
+ optional byte arg5, optional byte arg6 = 9);
+ };
+ """
+ )
+ results = parser.finish()
+ args = results[0].members[0].signatures()[0][1]
+ harness.check(len(args), 6, "Should have 6 arguments")
+ harness.check(args[5].defaultValue.value, 9, "Should have correct default value")
diff --git a/dom/bindings/parser/tests/test_overload.py b/dom/bindings/parser/tests/test_overload.py
new file mode 100644
index 0000000000..7816276aa6
--- /dev/null
+++ b/dom/bindings/parser/tests/test_overload.py
@@ -0,0 +1,74 @@
+import WebIDL
+
+
+def WebIDLTest(parser, harness):
+ parser.parse(
+ """
+ interface TestOverloads {
+ undefined basic();
+ undefined basic(long arg1);
+ boolean abitharder(TestOverloads foo);
+ boolean abitharder(boolean foo);
+ undefined abitharder(ArrayBuffer? foo);
+ undefined withVariadics(long... numbers);
+ undefined withVariadics(TestOverloads iface);
+ undefined withVariadics(long num, TestOverloads iface);
+ undefined optionalTest();
+ undefined optionalTest(optional long num1, long num2);
+ };
+ """
+ )
+
+ results = parser.finish()
+
+ harness.ok(True, "TestOverloads interface parsed without error.")
+ harness.check(len(results), 1, "Should be one production.")
+ iface = results[0]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface), "Should be an IDLInterface")
+ harness.check(
+ iface.identifier.QName(), "::TestOverloads", "Interface has the right QName"
+ )
+ harness.check(
+ iface.identifier.name, "TestOverloads", "Interface has the right name"
+ )
+ harness.check(len(iface.members), 4, "Expect %s members" % 4)
+
+ member = iface.members[0]
+ harness.check(
+ member.identifier.QName(),
+ "::TestOverloads::basic",
+ "Method has the right QName",
+ )
+ harness.check(member.identifier.name, "basic", "Method has the right name")
+ harness.check(member.hasOverloads(), True, "Method has overloads")
+
+ signatures = member.signatures()
+ harness.check(len(signatures), 2, "Method should have 2 signatures")
+
+ (retval, argumentSet) = signatures[0]
+
+ harness.check(str(retval), "Undefined", "Expect an undefined retval")
+ harness.check(len(argumentSet), 0, "Expect an empty argument set")
+
+ (retval, argumentSet) = signatures[1]
+ harness.check(str(retval), "Undefined", "Expect an undefined retval")
+ harness.check(len(argumentSet), 1, "Expect an argument set with one argument")
+
+ argument = argumentSet[0]
+ harness.ok(isinstance(argument, WebIDL.IDLArgument), "Should be an IDLArgument")
+ harness.check(
+ argument.identifier.QName(),
+ "::TestOverloads::basic::arg1",
+ "Argument has the right QName",
+ )
+ harness.check(argument.identifier.name, "arg1", "Argument has the right name")
+ harness.check(str(argument.type), "Long", "Argument has the right type")
+
+ member = iface.members[3]
+ harness.check(
+ len(member.overloadsForArgCount(0)), 1, "Only one overload for no args"
+ )
+ harness.check(len(member.overloadsForArgCount(1)), 0, "No overloads for one arg")
+ harness.check(
+ len(member.overloadsForArgCount(2)), 1, "Only one overload for two args"
+ )
diff --git a/dom/bindings/parser/tests/test_promise.py b/dom/bindings/parser/tests/test_promise.py
new file mode 100644
index 0000000000..e8051c36f0
--- /dev/null
+++ b/dom/bindings/parser/tests/test_promise.py
@@ -0,0 +1,177 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface A {
+ legacycaller Promise<any> foo();
+ };
+ """
+ )
+ parser.finish()
+
+ except Exception:
+ threw = True
+ harness.ok(threw, "Should not allow Promise return values for legacycaller.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface A {
+ Promise<any> foo();
+ long foo(long arg);
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(
+ threw,
+ "Should not allow overloads which have both Promise and "
+ "non-Promise return types.",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface A {
+ long foo(long arg);
+ Promise<any> foo();
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(
+ threw,
+ "Should not allow overloads which have both Promise and "
+ "non-Promise return types.",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface A {
+ Promise<any>? foo();
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "Should not allow nullable Promise return values.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface A {
+ undefined foo(Promise<any>? arg);
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "Should not allow nullable Promise arguments.")
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ interface A {
+ Promise<any> foo();
+ Promise<any> foo(long arg);
+ };
+ """
+ )
+ parser.finish()
+
+ harness.ok(
+ True, "Should allow overloads which only have Promise and return " "types."
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface A {
+ attribute Promise<any> attr;
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "Should not allow writable Promise-typed attributes.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface A {
+ [LegacyLenientSetter] readonly attribute Promise<any> attr;
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(
+ threw, "Should not allow [LegacyLenientSetter] Promise-typed attributes."
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface A {
+ [PutForwards=bar] readonly attribute Promise<any> attr;
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "Should not allow [PutForwards] Promise-typed attributes.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface A {
+ [Replaceable] readonly attribute Promise<any> attr;
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "Should not allow [Replaceable] Promise-typed attributes.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface A {
+ [SameObject] readonly attribute Promise<any> attr;
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "Should not allow [SameObject] Promise-typed attributes.")
diff --git a/dom/bindings/parser/tests/test_prototype_ident.py b/dom/bindings/parser/tests/test_prototype_ident.py
new file mode 100644
index 0000000000..a47f42af9d
--- /dev/null
+++ b/dom/bindings/parser/tests/test_prototype_ident.py
@@ -0,0 +1,107 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface TestIface {
+ static attribute boolean prototype;
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "The identifier of a static attribute must not be 'prototype'")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface TestIface {
+ static boolean prototype();
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "The identifier of a static operation must not be 'prototype'")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface TestIface {
+ const boolean prototype = true;
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "The identifier of a constant must not be 'prototype'")
+
+ # Make sure that we can parse non-static attributes with 'prototype' as identifier.
+ parser = parser.reset()
+ parser.parse(
+ """
+ interface TestIface {
+ attribute boolean prototype;
+ };
+ """
+ )
+ results = parser.finish()
+
+ testIface = results[0]
+ harness.check(
+ testIface.members[0].isStatic(), False, "Attribute should not be static"
+ )
+ harness.check(
+ testIface.members[0].identifier.name,
+ "prototype",
+ "Attribute identifier should be 'prototype'",
+ )
+
+ # Make sure that we can parse non-static operations with 'prototype' as identifier.
+ parser = parser.reset()
+ parser.parse(
+ """
+ interface TestIface {
+ boolean prototype();
+ };
+ """
+ )
+ results = parser.finish()
+
+ testIface = results[0]
+ harness.check(
+ testIface.members[0].isStatic(), False, "Operation should not be static"
+ )
+ harness.check(
+ testIface.members[0].identifier.name,
+ "prototype",
+ "Operation identifier should be 'prototype'",
+ )
+
+ # Make sure that we can parse dictionary members with 'prototype' as identifier.
+ parser = parser.reset()
+ parser.parse(
+ """
+ dictionary TestDict {
+ boolean prototype;
+ };
+ """
+ )
+ results = parser.finish()
+
+ testDict = results[0]
+ harness.check(
+ testDict.members[0].identifier.name,
+ "prototype",
+ "Dictionary member should be 'prototype'",
+ )
diff --git a/dom/bindings/parser/tests/test_putForwards.py b/dom/bindings/parser/tests/test_putForwards.py
new file mode 100644
index 0000000000..4e8504f766
--- /dev/null
+++ b/dom/bindings/parser/tests/test_putForwards.py
@@ -0,0 +1,119 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface I {
+ [PutForwards=B] readonly attribute long A;
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface I {
+ [PutForwards=B] readonly attribute J A;
+ };
+ interface J {
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface I {
+ [PutForwards=B] attribute J A;
+ };
+ interface J {
+ attribute long B;
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface I {
+ [PutForwards=B] static readonly attribute J A;
+ };
+ interface J {
+ attribute long B;
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ callback interface I {
+ [PutForwards=B] readonly attribute J A;
+ };
+ interface J {
+ attribute long B;
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface I {
+ [PutForwards=C] readonly attribute J A;
+ [PutForwards=C] readonly attribute J B;
+ };
+ interface J {
+ [PutForwards=D] readonly attribute K C;
+ };
+ interface K {
+ [PutForwards=A] readonly attribute I D;
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/dom/bindings/parser/tests/test_record.py b/dom/bindings/parser/tests/test_record.py
new file mode 100644
index 0000000000..930b0e1a90
--- /dev/null
+++ b/dom/bindings/parser/tests/test_record.py
@@ -0,0 +1,61 @@
+import WebIDL
+
+
+def WebIDLTest(parser, harness):
+ parser.parse(
+ """
+ dictionary Dict {};
+ interface RecordArg {
+ undefined foo(record<DOMString, Dict> arg);
+ };
+ """
+ )
+
+ results = parser.finish()
+
+ harness.check(len(results), 2, "Should know about two things")
+ harness.ok(
+ isinstance(results[1], WebIDL.IDLInterface), "Should have an interface here"
+ )
+ members = results[1].members
+ harness.check(len(members), 1, "Should have one member")
+ harness.ok(members[0].isMethod(), "Should have method")
+ signature = members[0].signatures()[0]
+ args = signature[1]
+ harness.check(len(args), 1, "Should have one arg")
+ harness.ok(args[0].type.isRecord(), "Should have a record type here")
+ harness.ok(args[0].type.inner.isDictionary(), "Should have a dictionary inner type")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface RecordUndefinedArg {
+ undefined foo(record<DOMString, undefined> arg);
+ };
+ """
+ )
+
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(
+ threw, "Should have thrown because record can't have undefined as value type."
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ dictionary Dict {
+ record<DOMString, Dict> val;
+ };
+ """
+ )
+
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "Should have thrown on dictionary containing itself via record.")
diff --git a/dom/bindings/parser/tests/test_replaceable.py b/dom/bindings/parser/tests/test_replaceable.py
new file mode 100644
index 0000000000..3b6df65c07
--- /dev/null
+++ b/dom/bindings/parser/tests/test_replaceable.py
@@ -0,0 +1,84 @@
+# 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/.
+
+
+def should_throw(parser, harness, message, code):
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(code)
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown: %s" % message)
+
+
+def WebIDLTest(parser, harness):
+ # The [Replaceable] extended attribute MUST take no arguments.
+ should_throw(
+ parser,
+ harness,
+ "no arguments",
+ """
+ interface I {
+ [Replaceable=X] readonly attribute long A;
+ };
+ """,
+ )
+
+ # An attribute with the [Replaceable] extended attribute MUST NOT also be
+ # declared with the [PutForwards] extended attribute.
+ should_throw(
+ parser,
+ harness,
+ "PutForwards",
+ """
+ interface I {
+ [PutForwards=B, Replaceable] readonly attribute J A;
+ };
+ interface J {
+ attribute long B;
+ };
+ """,
+ )
+
+ # The [Replaceable] extended attribute MUST NOT be used on an attribute
+ # that is not read only.
+ should_throw(
+ parser,
+ harness,
+ "writable attribute",
+ """
+ interface I {
+ [Replaceable] attribute long A;
+ };
+ """,
+ )
+
+ # The [Replaceable] extended attribute MUST NOT be used on a static
+ # attribute.
+ should_throw(
+ parser,
+ harness,
+ "static attribute",
+ """
+ interface I {
+ [Replaceable] static readonly attribute long A;
+ };
+ """,
+ )
+
+ # The [Replaceable] extended attribute MUST NOT be used on an attribute
+ # declared on a callback interface.
+ should_throw(
+ parser,
+ harness,
+ "callback interface",
+ """
+ callback interface I {
+ [Replaceable] readonly attribute long A;
+ };
+ """,
+ )
diff --git a/dom/bindings/parser/tests/test_sanity.py b/dom/bindings/parser/tests/test_sanity.py
new file mode 100644
index 0000000000..d3184c0073
--- /dev/null
+++ b/dom/bindings/parser/tests/test_sanity.py
@@ -0,0 +1,7 @@
+def WebIDLTest(parser, harness):
+ parser.parse("")
+ parser.finish()
+ harness.ok(True, "Parsing nothing doesn't throw.")
+ parser.parse("interface Foo {};")
+ parser.finish()
+ harness.ok(True, "Parsing a silly interface doesn't throw.")
diff --git a/dom/bindings/parser/tests/test_securecontext_extended_attribute.py b/dom/bindings/parser/tests/test_securecontext_extended_attribute.py
new file mode 100644
index 0000000000..f7848bf092
--- /dev/null
+++ b/dom/bindings/parser/tests/test_securecontext_extended_attribute.py
@@ -0,0 +1,538 @@
+def WebIDLTest(parser, harness):
+ parser.parse(
+ """
+ [SecureContext]
+ interface TestSecureContextOnInterface {
+ const octet TEST_CONSTANT = 0;
+ readonly attribute byte testAttribute;
+ undefined testMethod(byte foo);
+ };
+ partial interface TestSecureContextOnInterface {
+ const octet TEST_CONSTANT_2 = 0;
+ readonly attribute byte testAttribute2;
+ undefined testMethod2(byte foo);
+ };
+ """
+ )
+ results = parser.finish()
+ harness.check(
+ len(results[0].members),
+ 6,
+ "TestSecureContextOnInterface should have six members",
+ )
+ harness.ok(
+ results[0].getExtendedAttribute("SecureContext"),
+ "Interface should have [SecureContext] extended attribute",
+ )
+ harness.ok(
+ results[0].members[0].getExtendedAttribute("SecureContext"),
+ "[SecureContext] should propagate from interface to constant members",
+ )
+ harness.ok(
+ results[0].members[1].getExtendedAttribute("SecureContext"),
+ "[SecureContext] should propagate from interface to attribute members",
+ )
+ harness.ok(
+ results[0].members[2].getExtendedAttribute("SecureContext"),
+ "[SecureContext] should propagate from interface to method members",
+ )
+ harness.ok(
+ results[0].members[3].getExtendedAttribute("SecureContext"),
+ (
+ "[SecureContext] should propagate from interface to "
+ "constant members from partial interface"
+ ),
+ )
+ harness.ok(
+ results[0].members[4].getExtendedAttribute("SecureContext"),
+ (
+ "[SecureContext] should propagate from interface to "
+ "attribute members from partial interface"
+ ),
+ )
+ harness.ok(
+ results[0].members[5].getExtendedAttribute("SecureContext"),
+ "[SecureContext] should propagate from interface to method members from partial interface",
+ )
+
+ # Same thing, but with the partial interface specified first:
+ parser = parser.reset()
+ parser.parse(
+ """
+ partial interface TestSecureContextOnInterfaceAfterPartialInterface {
+ const octet TEST_CONSTANT_2 = 0;
+ readonly attribute byte testAttribute2;
+ undefined testMethod2(byte foo);
+ };
+ [SecureContext]
+ interface TestSecureContextOnInterfaceAfterPartialInterface {
+ const octet TEST_CONSTANT = 0;
+ readonly attribute byte testAttribute;
+ undefined testMethod(byte foo);
+ };
+ """
+ )
+ results = parser.finish()
+ harness.check(
+ len(results[1].members),
+ 6,
+ "TestSecureContextOnInterfaceAfterPartialInterface should have six members",
+ )
+ harness.ok(
+ results[1].getExtendedAttribute("SecureContext"),
+ "Interface should have [SecureContext] extended attribute",
+ )
+ harness.ok(
+ results[1].members[0].getExtendedAttribute("SecureContext"),
+ "[SecureContext] should propagate from interface to constant members",
+ )
+ harness.ok(
+ results[1].members[1].getExtendedAttribute("SecureContext"),
+ "[SecureContext] should propagate from interface to attribute members",
+ )
+ harness.ok(
+ results[1].members[2].getExtendedAttribute("SecureContext"),
+ "[SecureContext] should propagate from interface to method members",
+ )
+ harness.ok(
+ results[1].members[3].getExtendedAttribute("SecureContext"),
+ (
+ "[SecureContext] should propagate from interface to constant members from "
+ "partial interface"
+ ),
+ )
+ harness.ok(
+ results[1].members[4].getExtendedAttribute("SecureContext"),
+ (
+ "[SecureContext] should propagate from interface to attribute members from "
+ "partial interface"
+ ),
+ )
+ harness.ok(
+ results[1].members[5].getExtendedAttribute("SecureContext"),
+ (
+ "[SecureContext] should propagate from interface to method members from partial "
+ "interface"
+ ),
+ )
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ interface TestSecureContextOnPartialInterface {
+ const octet TEST_CONSTANT = 0;
+ readonly attribute byte testAttribute;
+ undefined testMethod(byte foo);
+ };
+ [SecureContext]
+ partial interface TestSecureContextOnPartialInterface {
+ const octet TEST_CONSTANT_2 = 0;
+ readonly attribute byte testAttribute2;
+ undefined testMethod2(byte foo);
+ };
+ """
+ )
+ results = parser.finish()
+ harness.check(
+ len(results[0].members),
+ 6,
+ "TestSecureContextOnPartialInterface should have six members",
+ )
+ harness.ok(
+ results[0].getExtendedAttribute("SecureContext") is None,
+ "[SecureContext] should not propagate from a partial interface to the interface",
+ )
+ harness.ok(
+ results[0].members[0].getExtendedAttribute("SecureContext") is None,
+ (
+ "[SecureContext] should not propagate from a partial interface to the interface's "
+ "constant members"
+ ),
+ )
+ harness.ok(
+ results[0].members[1].getExtendedAttribute("SecureContext") is None,
+ (
+ "[SecureContext] should not propagate from a partial interface to the interface's "
+ "attribute members"
+ ),
+ )
+ harness.ok(
+ results[0].members[2].getExtendedAttribute("SecureContext") is None,
+ (
+ "[SecureContext] should not propagate from a partial interface to the interface's "
+ "method members"
+ ),
+ )
+ harness.ok(
+ results[0].members[3].getExtendedAttribute("SecureContext"),
+ "Constant members from [SecureContext] partial interface should be [SecureContext]",
+ )
+ harness.ok(
+ results[0].members[4].getExtendedAttribute("SecureContext"),
+ "Attribute members from [SecureContext] partial interface should be [SecureContext]",
+ )
+ harness.ok(
+ results[0].members[5].getExtendedAttribute("SecureContext"),
+ "Method members from [SecureContext] partial interface should be [SecureContext]",
+ )
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ interface TestSecureContextOnInterfaceMembers {
+ const octet TEST_NON_SECURE_CONSTANT_1 = 0;
+ [SecureContext]
+ const octet TEST_SECURE_CONSTANT = 1;
+ const octet TEST_NON_SECURE_CONSTANT_2 = 2;
+ readonly attribute byte testNonSecureAttribute1;
+ [SecureContext]
+ readonly attribute byte testSecureAttribute;
+ readonly attribute byte testNonSecureAttribute2;
+ undefined testNonSecureMethod1(byte foo);
+ [SecureContext]
+ undefined testSecureMethod(byte foo);
+ undefined testNonSecureMethod2(byte foo);
+ };
+ """
+ )
+ results = parser.finish()
+ harness.check(
+ len(results[0].members),
+ 9,
+ "TestSecureContextOnInterfaceMembers should have nine members",
+ )
+ harness.ok(
+ results[0].getExtendedAttribute("SecureContext") is None,
+ "[SecureContext] on members should not propagate up to the interface",
+ )
+ harness.ok(
+ results[0].members[0].getExtendedAttribute("SecureContext") is None,
+ "Constant should not have [SecureContext] extended attribute",
+ )
+ harness.ok(
+ results[0].members[1].getExtendedAttribute("SecureContext"),
+ "Constant should have [SecureContext] extended attribute",
+ )
+ harness.ok(
+ results[0].members[2].getExtendedAttribute("SecureContext") is None,
+ "Constant should not have [SecureContext] extended attribute",
+ )
+ harness.ok(
+ results[0].members[3].getExtendedAttribute("SecureContext") is None,
+ "Attribute should not have [SecureContext] extended attribute",
+ )
+ harness.ok(
+ results[0].members[4].getExtendedAttribute("SecureContext"),
+ "Attribute should have [SecureContext] extended attribute",
+ )
+ harness.ok(
+ results[0].members[5].getExtendedAttribute("SecureContext") is None,
+ "Attribute should not have [SecureContext] extended attribute",
+ )
+ harness.ok(
+ results[0].members[6].getExtendedAttribute("SecureContext") is None,
+ "Method should not have [SecureContext] extended attribute",
+ )
+ harness.ok(
+ results[0].members[7].getExtendedAttribute("SecureContext"),
+ "Method should have [SecureContext] extended attribute",
+ )
+ harness.ok(
+ results[0].members[8].getExtendedAttribute("SecureContext") is None,
+ "Method should not have [SecureContext] extended attribute",
+ )
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ interface TestSecureContextOnPartialInterfaceMembers {
+ };
+ partial interface TestSecureContextOnPartialInterfaceMembers {
+ const octet TEST_NON_SECURE_CONSTANT_1 = 0;
+ [SecureContext]
+ const octet TEST_SECURE_CONSTANT = 1;
+ const octet TEST_NON_SECURE_CONSTANT_2 = 2;
+ readonly attribute byte testNonSecureAttribute1;
+ [SecureContext]
+ readonly attribute byte testSecureAttribute;
+ readonly attribute byte testNonSecureAttribute2;
+ undefined testNonSecureMethod1(byte foo);
+ [SecureContext]
+ undefined testSecureMethod(byte foo);
+ undefined testNonSecureMethod2(byte foo);
+ };
+ """
+ )
+ results = parser.finish()
+ harness.check(
+ len(results[0].members),
+ 9,
+ "TestSecureContextOnPartialInterfaceMembers should have nine members",
+ )
+ harness.ok(
+ results[0].members[0].getExtendedAttribute("SecureContext") is None,
+ "Constant from partial interface should not have [SecureContext] extended attribute",
+ )
+ harness.ok(
+ results[0].members[1].getExtendedAttribute("SecureContext"),
+ "Constant from partial interface should have [SecureContext] extended attribute",
+ )
+ harness.ok(
+ results[0].members[2].getExtendedAttribute("SecureContext") is None,
+ "Constant from partial interface should not have [SecureContext] extended attribute",
+ )
+ harness.ok(
+ results[0].members[3].getExtendedAttribute("SecureContext") is None,
+ "Attribute from partial interface should not have [SecureContext] extended attribute",
+ )
+ harness.ok(
+ results[0].members[4].getExtendedAttribute("SecureContext"),
+ "Attribute from partial interface should have [SecureContext] extended attribute",
+ )
+ harness.ok(
+ results[0].members[5].getExtendedAttribute("SecureContext") is None,
+ "Attribute from partial interface should not have [SecureContext] extended attribute",
+ )
+ harness.ok(
+ results[0].members[6].getExtendedAttribute("SecureContext") is None,
+ "Method from partial interface should not have [SecureContext] extended attribute",
+ )
+ harness.ok(
+ results[0].members[7].getExtendedAttribute("SecureContext"),
+ "Method from partial interface should have [SecureContext] extended attribute",
+ )
+ harness.ok(
+ results[0].members[8].getExtendedAttribute("SecureContext") is None,
+ "Method from partial interface should not have [SecureContext] extended attribute",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ [SecureContext=something]
+ interface TestSecureContextTakesNoValue1 {
+ const octet TEST_SECURE_CONSTANT = 0;
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "[SecureContext] must take no arguments (testing on interface)")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface TestSecureContextForOverloads1 {
+ [SecureContext]
+ undefined testSecureMethod(byte foo);
+ };
+ partial interface TestSecureContextForOverloads1 {
+ undefined testSecureMethod(byte foo, byte bar);
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(
+ threw,
+ (
+ "If [SecureContext] appears on an overloaded operation, then it MUST appear on all "
+ "overloads"
+ ),
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface TestSecureContextForOverloads2 {
+ [SecureContext]
+ undefined testSecureMethod(byte foo);
+ };
+ partial interface TestSecureContextForOverloads2 {
+ [SecureContext]
+ undefined testSecureMethod(byte foo, byte bar);
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(
+ not threw,
+ "[SecureContext] can appear on an overloaded operation if it appears on all overloads",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ [SecureContext]
+ interface TestSecureContextOnInterfaceAndMember {
+ [SecureContext]
+ undefined testSecureMethod(byte foo);
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(
+ threw, "[SecureContext] must not appear on an interface and interface member"
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface TestSecureContextOnPartialInterfaceAndMember {
+ };
+ [SecureContext]
+ partial interface TestSecureContextOnPartialInterfaceAndMember {
+ [SecureContext]
+ undefined testSecureMethod(byte foo);
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(
+ threw,
+ (
+ "[SecureContext] must not appear on a partial interface and one of the partial "
+ "interface's member's"
+ ),
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ [SecureContext]
+ interface TestSecureContextOnInterfaceAndPartialInterfaceMember {
+ };
+ partial interface TestSecureContextOnInterfaceAndPartialInterfaceMember {
+ [SecureContext]
+ undefined testSecureMethod(byte foo);
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(
+ threw,
+ (
+ "[SecureContext] must not appear on an interface and one of its partial interface's "
+ "member's"
+ ),
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ [SecureContext]
+ interface TestSecureContextOnInheritedInterface {
+ };
+ interface TestSecureContextNotOnInheritingInterface : TestSecureContextOnInheritedInterface {
+ undefined testSecureMethod(byte foo);
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(
+ threw,
+ (
+ "[SecureContext] must appear on interfaces that inherit from another [SecureContext] "
+ "interface"
+ ),
+ )
+
+ # Test 'includes'.
+ parser = parser.reset()
+ parser.parse(
+ """
+ [SecureContext]
+ interface TestSecureContextInterfaceThatIncludesNonSecureContextMixin {
+ const octet TEST_CONSTANT = 0;
+ };
+ interface mixin TestNonSecureContextMixin {
+ const octet TEST_CONSTANT_2 = 0;
+ readonly attribute byte testAttribute2;
+ undefined testMethod2(byte foo);
+ };
+ TestSecureContextInterfaceThatIncludesNonSecureContextMixin includes TestNonSecureContextMixin;
+ """
+ )
+ results = parser.finish()
+ harness.check(
+ len(results[0].members),
+ 4,
+ (
+ "TestSecureContextInterfaceThatImplementsNonSecureContextInterface should have four "
+ "members"
+ ),
+ )
+ harness.ok(
+ results[0].getExtendedAttribute("SecureContext"),
+ "Interface should have [SecureContext] extended attribute",
+ )
+ harness.ok(
+ results[0].members[0].getExtendedAttribute("SecureContext"),
+ (
+ "[SecureContext] should propagate from interface to constant members even when other "
+ "members are copied from a non-[SecureContext] interface"
+ ),
+ )
+ harness.ok(
+ results[0].members[1].getExtendedAttribute("SecureContext") is None,
+ "Constants copied from non-[SecureContext] mixin should not be [SecureContext]",
+ )
+ harness.ok(
+ results[0].members[2].getExtendedAttribute("SecureContext") is None,
+ "Attributes copied from non-[SecureContext] mixin should not be [SecureContext]",
+ )
+ harness.ok(
+ results[0].members[3].getExtendedAttribute("SecureContext") is None,
+ "Methods copied from non-[SecureContext] mixin should not be [SecureContext]",
+ )
+
+ # Test SecureContext and LegacyNoInterfaceObject
+ parser = parser.reset()
+ parser.parse(
+ """
+ [LegacyNoInterfaceObject, SecureContext]
+ interface TestSecureContextLegacyNoInterfaceObject {
+ undefined testSecureMethod(byte foo);
+ };
+ """
+ )
+ results = parser.finish()
+ harness.check(
+ len(results[0].members),
+ 1,
+ "TestSecureContextLegacyNoInterfaceObject should have only one member",
+ )
+ harness.ok(
+ results[0].getExtendedAttribute("SecureContext"),
+ "Interface should have [SecureContext] extended attribute",
+ )
+ harness.ok(
+ results[0].members[0].getExtendedAttribute("SecureContext"),
+ "Interface member should have [SecureContext] extended attribute",
+ )
diff --git a/dom/bindings/parser/tests/test_special_method_signature_mismatch.py b/dom/bindings/parser/tests/test_special_method_signature_mismatch.py
new file mode 100644
index 0000000000..edbb396bd3
--- /dev/null
+++ b/dom/bindings/parser/tests/test_special_method_signature_mismatch.py
@@ -0,0 +1,256 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface SpecialMethodSignatureMismatch1 {
+ getter long long foo(long index);
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface SpecialMethodSignatureMismatch2 {
+ getter undefined foo(unsigned long index);
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface SpecialMethodSignatureMismatch3 {
+ getter boolean foo(unsigned long index, boolean extraArg);
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface SpecialMethodSignatureMismatch4 {
+ getter boolean foo(unsigned long... index);
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface SpecialMethodSignatureMismatch5 {
+ getter boolean foo(optional unsigned long index);
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface SpecialMethodSignatureMismatch6 {
+ getter boolean foo();
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface SpecialMethodSignatureMismatch7 {
+ deleter long long foo(long index);
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface SpecialMethodSignatureMismatch9 {
+ deleter boolean foo(unsigned long index, boolean extraArg);
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface SpecialMethodSignatureMismatch10 {
+ deleter boolean foo(unsigned long... index);
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface SpecialMethodSignatureMismatch11 {
+ deleter boolean foo(optional unsigned long index);
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface SpecialMethodSignatureMismatch12 {
+ deleter boolean foo();
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface SpecialMethodSignatureMismatch13 {
+ setter long long foo(long index, long long value);
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface SpecialMethodSignatureMismatch15 {
+ setter boolean foo(unsigned long index, boolean value, long long extraArg);
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface SpecialMethodSignatureMismatch16 {
+ setter boolean foo(unsigned long index, boolean... value);
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface SpecialMethodSignatureMismatch17 {
+ setter boolean foo(unsigned long index, optional boolean value);
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface SpecialMethodSignatureMismatch18 {
+ setter boolean foo();
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/dom/bindings/parser/tests/test_special_methods.py b/dom/bindings/parser/tests/test_special_methods.py
new file mode 100644
index 0000000000..64b6cea9e0
--- /dev/null
+++ b/dom/bindings/parser/tests/test_special_methods.py
@@ -0,0 +1,117 @@
+import WebIDL
+
+
+def WebIDLTest(parser, harness):
+ parser.parse(
+ """
+ interface SpecialMethods {
+ getter long long (unsigned long index);
+ setter long long (unsigned long index, long long value);
+ getter boolean (DOMString name);
+ setter boolean (DOMString name, boolean value);
+ deleter boolean (DOMString name);
+ readonly attribute unsigned long length;
+ };
+
+ interface SpecialMethodsCombination {
+ getter deleter boolean (DOMString name);
+ };
+ """
+ )
+
+ results = parser.finish()
+
+ def checkMethod(
+ method,
+ QName,
+ name,
+ static=False,
+ getter=False,
+ setter=False,
+ deleter=False,
+ legacycaller=False,
+ stringifier=False,
+ ):
+ harness.ok(isinstance(method, WebIDL.IDLMethod), "Should be an IDLMethod")
+ harness.check(method.identifier.QName(), QName, "Method has the right QName")
+ harness.check(method.identifier.name, name, "Method has the right name")
+ harness.check(method.isStatic(), static, "Method has the correct static value")
+ harness.check(method.isGetter(), getter, "Method has the correct getter value")
+ harness.check(method.isSetter(), setter, "Method has the correct setter value")
+ harness.check(
+ method.isDeleter(), deleter, "Method has the correct deleter value"
+ )
+ harness.check(
+ method.isLegacycaller(),
+ legacycaller,
+ "Method has the correct legacycaller value",
+ )
+ harness.check(
+ method.isStringifier(),
+ stringifier,
+ "Method has the correct stringifier value",
+ )
+
+ harness.check(len(results), 2, "Expect 2 interfaces")
+
+ iface = results[0]
+ harness.check(len(iface.members), 6, "Expect 6 members")
+
+ checkMethod(
+ iface.members[0],
+ "::SpecialMethods::__indexedgetter",
+ "__indexedgetter",
+ getter=True,
+ )
+ checkMethod(
+ iface.members[1],
+ "::SpecialMethods::__indexedsetter",
+ "__indexedsetter",
+ setter=True,
+ )
+ checkMethod(
+ iface.members[2],
+ "::SpecialMethods::__namedgetter",
+ "__namedgetter",
+ getter=True,
+ )
+ checkMethod(
+ iface.members[3],
+ "::SpecialMethods::__namedsetter",
+ "__namedsetter",
+ setter=True,
+ )
+ checkMethod(
+ iface.members[4],
+ "::SpecialMethods::__nameddeleter",
+ "__nameddeleter",
+ deleter=True,
+ )
+
+ iface = results[1]
+ harness.check(len(iface.members), 1, "Expect 1 member")
+
+ checkMethod(
+ iface.members[0],
+ "::SpecialMethodsCombination::__namedgetterdeleter",
+ "__namedgetterdeleter",
+ getter=True,
+ deleter=True,
+ )
+
+ parser = parser.reset()
+
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface IndexedDeleter {
+ deleter undefined(unsigned long index);
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "There are no indexed deleters")
diff --git a/dom/bindings/parser/tests/test_special_methods_uniqueness.py b/dom/bindings/parser/tests/test_special_methods_uniqueness.py
new file mode 100644
index 0000000000..b7781207ee
--- /dev/null
+++ b/dom/bindings/parser/tests/test_special_methods_uniqueness.py
@@ -0,0 +1,51 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface SpecialMethodUniqueness1 {
+ getter deleter boolean (DOMString name);
+ getter boolean (DOMString name);
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface SpecialMethodUniqueness1 {
+ deleter boolean (DOMString name);
+ getter deleter boolean (DOMString name);
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface SpecialMethodUniqueness1 {
+ setter boolean (DOMString name);
+ setter boolean (DOMString name);
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/dom/bindings/parser/tests/test_stringifier.py b/dom/bindings/parser/tests/test_stringifier.py
new file mode 100644
index 0000000000..13c7ce4af8
--- /dev/null
+++ b/dom/bindings/parser/tests/test_stringifier.py
@@ -0,0 +1,196 @@
+import WebIDL
+
+
+def WebIDLTest(parser, harness):
+ parser.parse(
+ """
+ interface TestStringifier {
+ stringifier;
+ };
+ """
+ )
+
+ results = parser.finish()
+
+ harness.ok(
+ isinstance(results[0].members[0], WebIDL.IDLMethod),
+ "Stringifer should be method",
+ )
+
+ parser = parser.reset()
+
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface TestStringifier {
+ stringifier;
+ stringifier;
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should not allow two 'stringifier;'")
+
+ parser = parser.reset()
+
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface TestStringifier {
+ stringifier;
+ stringifier DOMString foo();
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should not allow a 'stringifier;' and a 'stringifier()'")
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ interface TestStringifier {
+ stringifier attribute DOMString foo;
+ };
+ """
+ )
+ results = parser.finish()
+ harness.ok(
+ isinstance(results[0].members[0], WebIDL.IDLAttribute),
+ "Stringifier attribute should be an attribute",
+ )
+ stringifier = results[0].members[1]
+ harness.ok(
+ isinstance(stringifier, WebIDL.IDLMethod),
+ "Stringifier attribute should insert a method",
+ )
+ harness.ok(stringifier.isStringifier(), "Inserted method should be a stringifier")
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ interface TestStringifier {};
+ interface mixin TestStringifierMixin {
+ stringifier attribute DOMString foo;
+ };
+ TestStringifier includes TestStringifierMixin;
+ """
+ )
+ results = parser.finish()
+ harness.ok(
+ isinstance(results[0].members[0], WebIDL.IDLAttribute),
+ "Stringifier attribute should be an attribute",
+ )
+ stringifier = results[0].members[1]
+ harness.ok(
+ isinstance(stringifier, WebIDL.IDLMethod),
+ "Stringifier attribute should insert a method",
+ )
+ harness.ok(stringifier.isStringifier(), "Inserted method should be a stringifier")
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ interface TestStringifier {
+ stringifier attribute USVString foo;
+ };
+ """
+ )
+ results = parser.finish()
+ stringifier = results[0].members[1]
+ harness.ok(
+ stringifier.signatures()[0][0].isUSVString(),
+ "Stringifier attributes should allow USVString",
+ )
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ interface TestStringifier {
+ [Throws, NeedsSubjectPrincipal]
+ stringifier attribute USVString foo;
+ };
+ """
+ )
+ results = parser.finish()
+ stringifier = results[0].members[1]
+ harness.ok(
+ stringifier.getExtendedAttribute("Throws"),
+ "Stringifier attributes should support [Throws]",
+ )
+ harness.ok(
+ stringifier.getExtendedAttribute("NeedsSubjectPrincipal"),
+ "Stringifier attributes should support [NeedsSubjectPrincipal]",
+ )
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ interface TestStringifier {
+ stringifier attribute UTF8String foo;
+ };
+ """
+ )
+ results = parser.finish()
+ stringifier = results[0].members[1]
+ harness.ok(
+ stringifier.signatures()[0][0].isUTF8String(),
+ "Stringifier attributes should allow UTF8String",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface TestStringifier {
+ stringifier attribute ByteString foo;
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should not allow ByteString")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface TestStringifier {
+ stringifier;
+ stringifier attribute DOMString foo;
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should not allow a 'stringifier;' and a stringifier attribute")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface TestStringifier {
+ stringifier attribute DOMString foo;
+ stringifier attribute DOMString bar;
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should not allow multiple stringifier attributes")
diff --git a/dom/bindings/parser/tests/test_toJSON.py b/dom/bindings/parser/tests/test_toJSON.py
new file mode 100644
index 0000000000..42bad30e50
--- /dev/null
+++ b/dom/bindings/parser/tests/test_toJSON.py
@@ -0,0 +1,309 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Test {
+ object toJSON();
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(not threw, "Should allow a toJSON method.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Test {
+ object toJSON(object arg);
+ object toJSON(long arg);
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "Should not allow overloads of a toJSON method.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Test {
+ object toJSON(object arg);
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "Should not allow a toJSON method with arguments.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Test {
+ long toJSON();
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(not threw, "Should allow a toJSON method with 'long' as return type.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Test {
+ [Default] object toJSON();
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(
+ not threw, "Should allow a default toJSON method with 'object' as return type."
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Test {
+ [Default] long toJSON();
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(
+ threw,
+ "Should not allow a default toJSON method with non-'object' as return type.",
+ )
+
+ JsonTypes = [
+ "byte",
+ "octet",
+ "short",
+ "unsigned short",
+ "long",
+ "unsigned long",
+ "long long",
+ "unsigned long long",
+ "float",
+ "unrestricted float",
+ "double",
+ "unrestricted double",
+ "boolean",
+ "DOMString",
+ "ByteString",
+ "UTF8String",
+ "USVString",
+ "Enum",
+ "InterfaceWithToJSON",
+ "object",
+ ]
+
+ nonJsonTypes = [
+ "InterfaceWithoutToJSON",
+ "any",
+ "Int8Array",
+ "Int16Array",
+ "Int32Array",
+ "Uint8Array",
+ "Uint16Array",
+ "Uint32Array",
+ "Uint8ClampedArray",
+ "Float32Array",
+ "Float64Array",
+ "ArrayBuffer",
+ ]
+
+ def doTest(testIDL, shouldThrow, description):
+ p = parser.reset()
+ threw = False
+ try:
+ p.parse(
+ testIDL
+ + """
+ enum Enum { "a", "b", "c" };
+ interface InterfaceWithToJSON { long toJSON(); };
+ interface InterfaceWithoutToJSON {};
+ """
+ )
+ p.finish()
+ except Exception as x:
+ threw = True
+ harness.ok(x.message == "toJSON method has non-JSON return type", x)
+ harness.check(threw, shouldThrow, description)
+
+ for type in JsonTypes:
+ doTest(
+ "interface Test { %s toJSON(); };" % type,
+ False,
+ "%s should be a JSON type" % type,
+ )
+
+ doTest(
+ "interface Test { sequence<%s> toJSON(); };" % type,
+ False,
+ "sequence<%s> should be a JSON type" % type,
+ )
+
+ doTest(
+ "dictionary Foo { %s foo; }; " "interface Test { Foo toJSON(); }; " % type,
+ False,
+ "dictionary containing only JSON type (%s) should be a JSON type" % type,
+ )
+
+ doTest(
+ "dictionary Foo { %s foo; }; dictionary Bar : Foo { }; "
+ "interface Test { Bar toJSON(); }; " % type,
+ False,
+ "dictionary whose ancestors only contain JSON types should be a JSON type",
+ )
+
+ doTest(
+ "dictionary Foo { any foo; }; dictionary Bar : Foo { %s bar; };"
+ "interface Test { Bar toJSON(); };" % type,
+ True,
+ "dictionary whose ancestors contain non-JSON types should not be a JSON type",
+ )
+
+ doTest(
+ "interface Test { record<DOMString, %s> toJSON(); };" % type,
+ False,
+ "record<DOMString, %s> should be a JSON type" % type,
+ )
+
+ doTest(
+ "interface Test { record<ByteString, %s> toJSON(); };" % type,
+ False,
+ "record<ByteString, %s> should be a JSON type" % type,
+ )
+
+ doTest(
+ "interface Test { record<UTF8String, %s> toJSON(); };" % type,
+ False,
+ "record<UTF8String, %s> should be a JSON type" % type,
+ )
+
+ doTest(
+ "interface Test { record<USVString, %s> toJSON(); };" % type,
+ False,
+ "record<USVString, %s> should be a JSON type" % type,
+ )
+
+ otherUnionType = "Foo" if type != "object" else "long"
+ doTest(
+ "interface Foo { object toJSON(); };"
+ "interface Test { (%s or %s) toJSON(); };" % (otherUnionType, type),
+ False,
+ "union containing only JSON types (%s or %s) should be a JSON type"
+ % (otherUnionType, type),
+ )
+
+ doTest(
+ "interface test { %s? toJSON(); };" % type,
+ False,
+ "Nullable type (%s) should be a JSON type" % type,
+ )
+
+ doTest(
+ "interface Foo : InterfaceWithoutToJSON { %s toJSON(); };"
+ "interface Test { Foo toJSON(); };" % type,
+ False,
+ "interface with toJSON should be a JSON type",
+ )
+
+ doTest(
+ "interface Foo : InterfaceWithToJSON { };" "interface Test { Foo toJSON(); };",
+ False,
+ "inherited interface with toJSON should be a JSON type",
+ )
+
+ for type in nonJsonTypes:
+ doTest(
+ "interface Test { %s toJSON(); };" % type,
+ True,
+ "%s should not be a JSON type" % type,
+ )
+
+ doTest(
+ "interface Test { sequence<%s> toJSON(); };" % type,
+ True,
+ "sequence<%s> should not be a JSON type" % type,
+ )
+
+ doTest(
+ "dictionary Foo { %s foo; }; " "interface Test { Foo toJSON(); }; " % type,
+ True,
+ "Dictionary containing a non-JSON type (%s) should not be a JSON type"
+ % type,
+ )
+
+ doTest(
+ "dictionary Foo { %s foo; }; dictionary Bar : Foo { }; "
+ "interface Test { Bar toJSON(); }; " % type,
+ True,
+ "dictionary whose ancestors only contain non-JSON types should not be a JSON type",
+ )
+
+ doTest(
+ "interface Test { record<DOMString, %s> toJSON(); };" % type,
+ True,
+ "record<DOMString, %s> should not be a JSON type" % type,
+ )
+
+ doTest(
+ "interface Test { record<ByteString, %s> toJSON(); };" % type,
+ True,
+ "record<ByteString, %s> should not be a JSON type" % type,
+ )
+
+ doTest(
+ "interface Test { record<USVString, %s> toJSON(); };" % type,
+ True,
+ "record<USVString, %s> should not be a JSON type" % type,
+ )
+
+ if type != "any":
+ doTest(
+ "interface Foo { object toJSON(); }; "
+ "interface Test { (Foo or %s) toJSON(); };" % type,
+ True,
+ "union containing a non-JSON type (%s) should not be a JSON type"
+ % type,
+ )
+
+ doTest(
+ "interface test { %s? toJSON(); };" % type,
+ True,
+ "Nullable type (%s) should not be a JSON type" % type,
+ )
+
+ doTest(
+ "dictionary Foo { long foo; any bar; };" "interface Test { Foo toJSON(); };",
+ True,
+ "dictionary containing a non-JSON type should not be a JSON type",
+ )
+
+ doTest(
+ "interface Foo : InterfaceWithoutToJSON { }; "
+ "interface Test { Foo toJSON(); };",
+ True,
+ "interface without toJSON should not be a JSON type",
+ )
diff --git a/dom/bindings/parser/tests/test_treatNonCallableAsNull.py b/dom/bindings/parser/tests/test_treatNonCallableAsNull.py
new file mode 100644
index 0000000000..fb44cf7fdf
--- /dev/null
+++ b/dom/bindings/parser/tests/test_treatNonCallableAsNull.py
@@ -0,0 +1,77 @@
+def WebIDLTest(parser, harness):
+ parser.parse(
+ """
+ [TreatNonCallableAsNull] callback Function = any(any... arguments);
+
+ interface TestTreatNonCallableAsNull1 {
+ attribute Function? onfoo;
+ attribute Function onbar;
+ };
+ """
+ )
+
+ results = parser.finish()
+
+ iface = results[1]
+ attr = iface.members[0]
+ harness.check(attr.type.treatNonCallableAsNull(), True, "Got the expected value")
+ attr = iface.members[1]
+ harness.check(attr.type.treatNonCallableAsNull(), False, "Got the expected value")
+
+ parser = parser.reset()
+
+ threw = False
+ try:
+ parser.parse(
+ """
+ callback Function = any(any... arguments);
+
+ interface TestTreatNonCallableAsNull2 {
+ [TreatNonCallableAsNull] attribute Function onfoo;
+ };
+ """
+ )
+
+ results = parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset()
+
+ threw = False
+ try:
+ parser.parse(
+ """
+ callback Function = any(any... arguments);
+
+ [TreatNonCallableAsNull]
+ interface TestTreatNonCallableAsNull3 {
+ attribute Function onfoo;
+ };
+ """
+ )
+
+ results = parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset()
+
+ threw = False
+ try:
+ parser.parse(
+ """
+ [TreatNonCallableAsNull, LegacyTreatNonObjectAsNull]
+ callback Function = any(any... arguments);
+ """
+ )
+
+ results = parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/dom/bindings/parser/tests/test_typedef.py b/dom/bindings/parser/tests/test_typedef.py
new file mode 100644
index 0000000000..8179f90d5c
--- /dev/null
+++ b/dom/bindings/parser/tests/test_typedef.py
@@ -0,0 +1,94 @@
+def WebIDLTest(parser, harness):
+ parser.parse(
+ """
+ typedef long mylong;
+ typedef long? mynullablelong;
+ interface Foo {
+ const mylong X = 5;
+ undefined foo(optional mynullablelong arg = 7);
+ undefined bar(optional mynullablelong arg = null);
+ undefined baz(mylong arg);
+ };
+ """
+ )
+
+ results = parser.finish()
+
+ harness.check(
+ results[2].members[1].signatures()[0][1][0].type.name,
+ "LongOrNull",
+ "Should expand typedefs",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ typedef long? mynullablelong;
+ interface Foo {
+ undefined foo(mynullablelong? Y);
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown on nullable inside nullable arg.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ typedef long? mynullablelong;
+ interface Foo {
+ const mynullablelong? X = 5;
+ };
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown on nullable inside nullable const.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Foo {
+ const mynullablelong? X = 5;
+ };
+ typedef long? mynullablelong;
+ """
+ )
+ results = parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw,
+ "Should have thrown on nullable inside nullable const typedef "
+ "after interface.",
+ )
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ interface Foo {
+ const mylong X = 5;
+ };
+ typedef long mylong;
+ """
+ )
+
+ results = parser.finish()
+
+ harness.check(
+ results[0].members[0].type.name,
+ "Long",
+ "Should expand typedefs that come before interface",
+ )
diff --git a/dom/bindings/parser/tests/test_typedef_identifier_conflict.py b/dom/bindings/parser/tests/test_typedef_identifier_conflict.py
new file mode 100644
index 0000000000..90e45ddb7d
--- /dev/null
+++ b/dom/bindings/parser/tests/test_typedef_identifier_conflict.py
@@ -0,0 +1,19 @@
+def WebIDLTest(parser, harness):
+ exception = None
+ try:
+ parser.parse(
+ """
+ typedef long foo;
+ typedef long foo;
+ """
+ )
+
+ parser.finish()
+ except Exception as e:
+ exception = e
+
+ harness.ok(exception, "Should have thrown.")
+ harness.ok(
+ "Multiple unresolvable definitions of identifier 'foo'" in str(exception),
+ "Should have a sane exception message",
+ )
diff --git a/dom/bindings/parser/tests/test_undefined.py b/dom/bindings/parser/tests/test_undefined.py
new file mode 100644
index 0000000000..3b6f18292f
--- /dev/null
+++ b/dom/bindings/parser/tests/test_undefined.py
@@ -0,0 +1,243 @@
+def WebIDLTest(parser, harness):
+ try:
+ parser.parse(
+ """
+ dictionary Dict {
+ undefined undefinedMember;
+ double bar;
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "undefined must not be used as the type of a dictionary member")
+
+ parser = parser.reset()
+ threw = False
+
+ try:
+ parser.parse(
+ """
+ dictionary Dict {
+ (undefined or double) undefinedMemberOfUnionInDict;
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw,
+ "undefined must not be used as the type of a dictionary member, "
+ "whether directly or in a union",
+ )
+
+ parser = parser.reset()
+ threw = False
+
+ try:
+ parser.parse(
+ """
+ interface Foo {
+ double bar(undefined foo);
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw,
+ "undefined must not be used as the type of an argument in any "
+ "circumstance (so not as the argument of a regular operation)",
+ )
+
+ parser = parser.reset()
+ threw = False
+
+ try:
+ parser.parse(
+ """
+ interface Foo {
+ getter double(undefined name);
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw,
+ "undefined must not be used as the type of an argument in any "
+ "circumstance (so not as the argument of a getter)",
+ )
+
+ parser = parser.reset()
+ threw = False
+
+ try:
+ parser.parse(
+ """
+ interface Foo {
+ setter undefined(DOMString name, undefined value);
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw,
+ "undefined must not be used as the type of an argument in any "
+ "circumstance (so not as the argument of a setter)",
+ )
+
+ parser = parser.reset()
+ threw = False
+
+ try:
+ parser.parse(
+ """
+ interface Foo {
+ deleter undefined (undefined name);
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw,
+ "undefined must not be used as the type of an argument in any "
+ "circumstance (so not as the argument of a deleter)",
+ )
+
+ parser = parser.reset()
+ threw = False
+
+ try:
+ parser.parse(
+ """
+ interface Foo {
+ constructor (undefined foo);
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw,
+ "undefined must not be used as the type of an argument in any "
+ "circumstance (so not as the argument of a constructor)",
+ )
+
+ parser = parser.reset()
+ threw = False
+
+ try:
+ parser.parse(
+ """
+ callback Callback = undefined (undefined foo);
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw,
+ "undefined must not be used as the type of an argument in any "
+ "circumstance (so not as the argument of a callback)",
+ )
+
+ parser = parser.reset()
+ threw = False
+
+ try:
+ parser.parse(
+ """
+ interface Foo {
+ async iterable(undefined name);
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw,
+ "undefined must not be used as the type of an argument in any "
+ "circumstance (so not as the argument of an async iterable "
+ "iterator)",
+ )
+
+ parser = parser.reset()
+ threw = False
+
+ try:
+ parser.parse(
+ """
+ interface Foo {
+ static double bar(undefined foo);
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw,
+ "undefined must not be used as the type of an argument in any "
+ "circumstance (so not as the argument of a static operation)",
+ )
+
+ parser = parser.reset()
+ threw = False
+
+ try:
+ parser.parse(
+ """
+ interface Foo {
+ const undefined FOO = undefined;
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw,
+ "undefined is not a valid type for a constant",
+ )
+
+ parser = parser.reset()
+ threw = False
+
+ try:
+ parser.parse(
+ """
+ interface Foo {
+ const any FOO = undefined;
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw,
+ "undefined is not a valid value for a constant",
+ )
diff --git a/dom/bindings/parser/tests/test_unenumerable_own_properties.py b/dom/bindings/parser/tests/test_unenumerable_own_properties.py
new file mode 100644
index 0000000000..37e0167696
--- /dev/null
+++ b/dom/bindings/parser/tests/test_unenumerable_own_properties.py
@@ -0,0 +1,71 @@
+def WebIDLTest(parser, harness):
+
+ parser.parse(
+ """
+ interface Foo {};
+ [LegacyUnenumerableNamedProperties]
+ interface Bar : Foo {
+ getter long(DOMString name);
+ };
+ interface Baz : Bar {
+ getter long(DOMString name);
+ };
+ """
+ )
+ results = parser.finish()
+ harness.check(len(results), 3, "Should have three interfaces")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ [LegacyUnenumerableNamedProperties]
+ interface NoNamedGetter {
+ };
+ """
+ )
+
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ [LegacyUnenumerableNamedProperties=Foo]
+ interface ShouldNotHaveArg {
+ getter long(DOMString name);
+ };
+ """
+ )
+
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ [LegacyUnenumerableNamedProperties]
+ interface Foo {
+ getter long(DOMString name);
+ };
+ interface Bar : Foo {};
+ [LegacyUnenumerableNamedProperties]
+ interface Baz : Bar {
+ getter long(DOMString name);
+ };
+ """
+ )
+
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(threw, "Should have thrown.")
diff --git a/dom/bindings/parser/tests/test_unforgeable.py b/dom/bindings/parser/tests/test_unforgeable.py
new file mode 100644
index 0000000000..18946bcc50
--- /dev/null
+++ b/dom/bindings/parser/tests/test_unforgeable.py
@@ -0,0 +1,311 @@
+def WebIDLTest(parser, harness):
+ parser.parse(
+ """
+ interface Child : Parent {
+ };
+ interface Parent {
+ [LegacyUnforgeable] readonly attribute long foo;
+ };
+ """
+ )
+
+ results = parser.finish()
+ harness.check(
+ len(results),
+ 2,
+ "Should be able to inherit from an interface with "
+ "[LegacyUnforgeable] properties.",
+ )
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ interface Child : Parent {
+ const short foo = 10;
+ };
+ interface Parent {
+ [LegacyUnforgeable] readonly attribute long foo;
+ };
+ """
+ )
+
+ results = parser.finish()
+ harness.check(
+ len(results),
+ 2,
+ "Should be able to inherit from an interface with "
+ "[LegacyUnforgeable] properties even if we have a constant with "
+ "the same name.",
+ )
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ interface Child : Parent {
+ static attribute short foo;
+ };
+ interface Parent {
+ [LegacyUnforgeable] readonly attribute long foo;
+ };
+ """
+ )
+
+ results = parser.finish()
+ harness.check(
+ len(results),
+ 2,
+ "Should be able to inherit from an interface with "
+ "[LegacyUnforgeable] properties even if we have a static attribute "
+ "with the same name.",
+ )
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ interface Child : Parent {
+ static undefined foo();
+ };
+ interface Parent {
+ [LegacyUnforgeable] readonly attribute long foo;
+ };
+ """
+ )
+
+ results = parser.finish()
+ harness.check(
+ len(results),
+ 2,
+ "Should be able to inherit from an interface with "
+ "[LegacyUnforgeable] properties even if we have a static operation "
+ "with the same name.",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Child : Parent {
+ undefined foo();
+ };
+ interface Parent {
+ [LegacyUnforgeable] readonly attribute long foo;
+ };
+ """
+ )
+
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(
+ threw,
+ "Should have thrown when shadowing unforgeable attribute on "
+ "parent with operation.",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Child : Parent {
+ undefined foo();
+ };
+ interface Parent {
+ [LegacyUnforgeable] undefined foo();
+ };
+ """
+ )
+
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(
+ threw,
+ "Should have thrown when shadowing unforgeable operation on "
+ "parent with operation.",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Child : Parent {
+ attribute short foo;
+ };
+ interface Parent {
+ [LegacyUnforgeable] readonly attribute long foo;
+ };
+ """
+ )
+
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(
+ threw,
+ "Should have thrown when shadowing unforgeable attribute on "
+ "parent with attribute.",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Child : Parent {
+ attribute short foo;
+ };
+ interface Parent {
+ [LegacyUnforgeable] undefined foo();
+ };
+ """
+ )
+
+ results = parser.finish()
+ except Exception:
+ threw = True
+ harness.ok(
+ threw,
+ "Should have thrown when shadowing unforgeable operation on "
+ "parent with attribute.",
+ )
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ interface Child : Parent {
+ };
+ interface Parent {};
+ interface mixin Mixin {
+ [LegacyUnforgeable] readonly attribute long foo;
+ };
+ Parent includes Mixin;
+ """
+ )
+
+ results = parser.finish()
+ harness.check(
+ len(results),
+ 4,
+ "Should be able to inherit from an interface with a "
+ "mixin with [LegacyUnforgeable] properties.",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Child : Parent {
+ undefined foo();
+ };
+ interface Parent {};
+ interface mixin Mixin {
+ [LegacyUnforgeable] readonly attribute long foo;
+ };
+ Parent includes Mixin;
+ """
+ )
+
+ results = parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw,
+ "Should have thrown when shadowing unforgeable attribute "
+ "of parent's consequential interface.",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Child : Parent {
+ };
+ interface Parent : GrandParent {};
+ interface GrandParent {};
+ interface mixin Mixin {
+ [LegacyUnforgeable] readonly attribute long foo;
+ };
+ GrandParent includes Mixin;
+ interface mixin ChildMixin {
+ undefined foo();
+ };
+ Child includes ChildMixin;
+ """
+ )
+
+ results = parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw,
+ "Should have thrown when our consequential interface shadows unforgeable attribute "
+ "of ancestor's consequential interface.",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Child : Parent {
+ };
+ interface Parent : GrandParent {};
+ interface GrandParent {};
+ interface mixin Mixin {
+ [LegacyUnforgeable] undefined foo();
+ };
+ GrandParent includes Mixin;
+ interface mixin ChildMixin {
+ undefined foo();
+ };
+ Child includes ChildMixin;
+ """
+ )
+
+ results = parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw,
+ "Should have thrown when our consequential interface shadows unforgeable operation "
+ "of ancestor's consequential interface.",
+ )
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ interface iface {
+ [LegacyUnforgeable] attribute long foo;
+ };
+ """
+ )
+
+ results = parser.finish()
+ harness.check(
+ len(results), 1, "Should allow writable [LegacyUnforgeable] attribute."
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface iface {
+ [LegacyUnforgeable] static readonly attribute long foo;
+ };
+ """
+ )
+
+ results = parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown for static [LegacyUnforgeable] attribute.")
diff --git a/dom/bindings/parser/tests/test_union.py b/dom/bindings/parser/tests/test_union.py
new file mode 100644
index 0000000000..675e2d0264
--- /dev/null
+++ b/dom/bindings/parser/tests/test_union.py
@@ -0,0 +1,196 @@
+import string
+
+# We'd like to use itertools.chain but it's 2.6 or higher.
+
+
+def chain(*iterables):
+ # chain('ABC', 'DEF') --> A B C D E F
+ for it in iterables:
+ for element in it:
+ yield element
+
+
+# We'd like to use itertools.combinations but it's 2.6 or higher.
+def combinations(iterable, r):
+ # combinations('ABCD', 2) --> AB AC AD BC BD CD
+ # combinations(range(4), 3) --> 012 013 023 123
+ pool = tuple(iterable)
+ n = len(pool)
+ if r > n:
+ return
+ indices = list(range(r))
+ yield tuple(pool[i] for i in indices)
+ while True:
+ for i in reversed(range(r)):
+ if indices[i] != i + n - r:
+ break
+ else:
+ return
+ indices[i] += 1
+ for j in range(i + 1, r):
+ indices[j] = indices[j - 1] + 1
+ yield tuple(pool[i] for i in indices)
+
+
+# We'd like to use itertools.combinations_with_replacement but it's 2.7 or
+# higher.
+def combinations_with_replacement(iterable, r):
+ # combinations_with_replacement('ABC', 2) --> AA AB AC BB BC CC
+ pool = tuple(iterable)
+ n = len(pool)
+ if not n and r:
+ return
+ indices = [0] * r
+ yield tuple(pool[i] for i in indices)
+ while True:
+ for i in reversed(range(r)):
+ if indices[i] != n - 1:
+ break
+ else:
+ return
+ indices[i:] = [indices[i] + 1] * (r - i)
+ yield tuple(pool[i] for i in indices)
+
+
+def WebIDLTest(parser, harness):
+ types = [
+ "float",
+ "double",
+ "short",
+ "unsigned short",
+ "long",
+ "unsigned long",
+ "long long",
+ "unsigned long long",
+ "boolean",
+ "byte",
+ "octet",
+ "DOMString",
+ "ByteString",
+ "USVString",
+ # "sequence<float>",
+ "object",
+ "ArrayBuffer",
+ # "Date",
+ "TestInterface1",
+ "TestInterface2",
+ ]
+
+ testPre = """
+ interface TestInterface1 {
+ };
+ interface TestInterface2 {
+ };
+ """
+
+ interface = (
+ testPre
+ + """
+ interface PrepareForTest {
+ """
+ )
+ for (i, type) in enumerate(types):
+ interface += string.Template(
+ """
+ readonly attribute ${type} attr${i};
+ """
+ ).substitute(i=i, type=type)
+ interface += """
+ };
+ """
+
+ parser.parse(interface)
+ results = parser.finish()
+
+ iface = results[2]
+
+ parser = parser.reset()
+
+ def typesAreDistinguishable(t):
+ return all(u[0].isDistinguishableFrom(u[1]) for u in combinations(t, 2))
+
+ def typesAreNotDistinguishable(t):
+ return any(not u[0].isDistinguishableFrom(u[1]) for u in combinations(t, 2))
+
+ def unionTypeName(t):
+ if len(t) > 2:
+ t[0:2] = [unionTypeName(t[0:2])]
+ return "(" + " or ".join(t) + ")"
+
+ # typeCombinations is an iterable of tuples containing the name of the type
+ # as a string and the parsed IDL type.
+ def unionTypes(typeCombinations, predicate):
+ for c in typeCombinations:
+ if predicate(t[1] for t in c):
+ yield unionTypeName([t[0] for t in c])
+
+ # We limit invalid union types with a union member type to the subset of 3
+ # types with one invalid combination.
+ # typeCombinations is an iterable of tuples containing the name of the type
+ # as a string and the parsed IDL type.
+ def invalidUnionWithUnion(typeCombinations):
+ for c in typeCombinations:
+ if (
+ typesAreNotDistinguishable((c[0][1], c[1][1]))
+ and typesAreDistinguishable((c[1][1], c[2][1]))
+ and typesAreDistinguishable((c[0][1], c[2][1]))
+ ):
+ yield unionTypeName([t[0] for t in c])
+
+ # Create a list of tuples containing the name of the type as a string and
+ # the parsed IDL type.
+ types = zip(types, (a.type for a in iface.members))
+
+ validUnionTypes = chain(
+ unionTypes(combinations(types, 2), typesAreDistinguishable),
+ unionTypes(combinations(types, 3), typesAreDistinguishable),
+ )
+ invalidUnionTypes = chain(
+ unionTypes(combinations_with_replacement(types, 2), typesAreNotDistinguishable),
+ invalidUnionWithUnion(combinations(types, 3)),
+ )
+ interface = (
+ testPre
+ + """
+ interface TestUnion {
+ """
+ )
+ for (i, type) in enumerate(validUnionTypes):
+ interface += string.Template(
+ """
+ undefined method${i}(${type} arg);
+ ${type} returnMethod${i}();
+ attribute ${type} attr${i};
+ undefined optionalMethod${i}(${type}? arg);
+ """
+ ).substitute(i=i, type=type)
+ interface += """
+ };
+ """
+ parser.parse(interface)
+ results = parser.finish()
+
+ parser = parser.reset()
+
+ for invalid in invalidUnionTypes:
+ interface = (
+ testPre
+ + string.Template(
+ """
+ interface TestUnion {
+ undefined method(${type} arg);
+ };
+ """
+ ).substitute(type=invalid)
+ )
+
+ threw = False
+ try:
+ parser.parse(interface)
+ results = parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset()
diff --git a/dom/bindings/parser/tests/test_union_any.py b/dom/bindings/parser/tests/test_union_any.py
new file mode 100644
index 0000000000..83ee7493b7
--- /dev/null
+++ b/dom/bindings/parser/tests/test_union_any.py
@@ -0,0 +1,16 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface AnyNotInUnion {
+ undefined foo((any or DOMString) arg);
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/dom/bindings/parser/tests/test_union_nullable.py b/dom/bindings/parser/tests/test_union_nullable.py
new file mode 100644
index 0000000000..d3255a7e05
--- /dev/null
+++ b/dom/bindings/parser/tests/test_union_nullable.py
@@ -0,0 +1,60 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface OneNullableInUnion {
+ undefined foo((object? or DOMString?) arg);
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Two nullable member types of a union should have thrown.")
+
+ parser.reset()
+ threw = False
+
+ try:
+ parser.parse(
+ """
+ interface NullableInNullableUnion {
+ undefined foo((object? or DOMString)? arg);
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw,
+ "A nullable union type with a nullable member type should have " "thrown.",
+ )
+
+ parser.reset()
+ threw = False
+
+ try:
+ parser.parse(
+ """
+ interface NullableInUnionNullableUnionHelper {
+ };
+ interface NullableInUnionNullableUnion {
+ undefined foo(((object? or DOMString) or NullableInUnionNullableUnionHelper)? arg);
+ };
+ """
+ )
+
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw,
+ "A nullable union type with a nullable member type should have " "thrown.",
+ )
diff --git a/dom/bindings/parser/tests/test_usvstring.py b/dom/bindings/parser/tests/test_usvstring.py
new file mode 100644
index 0000000000..effede391c
--- /dev/null
+++ b/dom/bindings/parser/tests/test_usvstring.py
@@ -0,0 +1,40 @@
+# -*- coding: UTF-8 -*-
+
+import WebIDL
+
+
+def WebIDLTest(parser, harness):
+ parser.parse(
+ """
+ interface TestUSVString {
+ attribute USVString svs;
+ };
+ """
+ )
+
+ results = parser.finish()
+
+ harness.check(len(results), 1, "Should be one production")
+ harness.ok(isinstance(results[0], WebIDL.IDLInterface), "Should be an IDLInterface")
+ iface = results[0]
+ harness.check(
+ iface.identifier.QName(), "::TestUSVString", "Interface has the right QName"
+ )
+ harness.check(
+ iface.identifier.name, "TestUSVString", "Interface has the right name"
+ )
+ harness.check(iface.parent, None, "Interface has no parent")
+
+ members = iface.members
+ harness.check(len(members), 1, "Should be one member")
+
+ attr = members[0]
+ harness.ok(isinstance(attr, WebIDL.IDLAttribute), "Should be an IDLAttribute")
+ harness.check(
+ attr.identifier.QName(), "::TestUSVString::svs", "Attr has correct QName"
+ )
+ harness.check(attr.identifier.name, "svs", "Attr has correct name")
+ harness.check(str(attr.type), "USVString", "Attr type is the correct name")
+ harness.ok(attr.type.isUSVString(), "Should be USVString type")
+ harness.ok(attr.type.isString(), "Should be String collective type")
+ harness.ok(not attr.type.isDOMString(), "Should be not be DOMString type")
diff --git a/dom/bindings/parser/tests/test_variadic_callback.py b/dom/bindings/parser/tests/test_variadic_callback.py
new file mode 100644
index 0000000000..fab1c7ef10
--- /dev/null
+++ b/dom/bindings/parser/tests/test_variadic_callback.py
@@ -0,0 +1,10 @@
+def WebIDLTest(parser, harness):
+ parser.parse(
+ """
+ callback TestVariadicCallback = any(any... arguments);
+ """
+ )
+
+ parser.finish()
+
+ harness.ok(True, "TestVariadicCallback callback parsed without error.")
diff --git a/dom/bindings/parser/tests/test_variadic_constraints.py b/dom/bindings/parser/tests/test_variadic_constraints.py
new file mode 100644
index 0000000000..8ffbfe0374
--- /dev/null
+++ b/dom/bindings/parser/tests/test_variadic_constraints.py
@@ -0,0 +1,74 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface VariadicConstraints1 {
+ undefined foo(byte... arg1, byte arg2);
+ };
+ """
+ )
+ parser.finish()
+
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw,
+ "Should have thrown on variadic argument followed by required " "argument.",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface VariadicConstraints2 {
+ undefined foo(byte... arg1, optional byte arg2);
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw,
+ "Should have thrown on variadic argument followed by optional " "argument.",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface VariadicConstraints3 {
+ undefined foo(optional byte... arg1);
+ };
+ """
+ )
+ parser.finish()
+
+ except Exception:
+ threw = True
+
+ harness.ok(
+ threw,
+ "Should have thrown on variadic argument explicitly flagged as " "optional.",
+ )
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface VariadicConstraints4 {
+ undefined foo(byte... arg1 = 0);
+ };
+ """
+ )
+ parser.finish()
+ except Exception:
+ threw = True
+
+ harness.ok(threw, "Should have thrown on variadic argument with default value.")